# Collect card payments

Prepare your application and back end to collect card payments using Stripe Terminal.

# JavaScript


> #### Recommendation for smart readers
> 
> For smart readers, such as the [BBPOS WisePOS E reader](https://docs.stripe.com/terminal/payments/setup-reader/bbpos-wisepos-e.md), [Stripe Reader S700/S710](https://docs.stripe.com/terminal/readers/stripe-reader-s700-s710.md), and [Verifone readers](https://docs.stripe.com/terminal/payments/setup-reader/verifone.md), we recommend using the [server-driven integration](https://docs.stripe.com/terminal/payments/setup-integration.md?terminal-sdk-platform=server-driven) instead of the JavaScript SDK.
> 
> The JavaScript SDK requires your POS and reader on the same local network with working local DNS. The server-driven integration uses the Stripe API instead, which can be simpler in complex network environments. See our [platform comparison](https://docs.stripe.com/terminal/payments/setup-reader.md#sdk) to help you choose the best platform for your needs.

New to the Payment Intents API? Here are some helpful resources:

- [The Payment Intents API](https://docs.stripe.com/payments/payment-intents.md)
- [The PaymentIntent object](https://docs.stripe.com/api/payment_intents.md)
- [More payment scenarios](https://docs.stripe.com/payments/more-payment-scenarios.md)

Collecting payments with Stripe Terminal requires writing a payment flow in your application. Use the Stripe Terminal SDK to create and update a [PaymentIntent](https://docs.stripe.com/api.md#payment_intents), an object representing a single payment session.

Designed to be robust to failures, the Terminal integration splits the payment process into several steps, each of which can be retried safely:

1. [Create a PaymentIntent](https://docs.stripe.com/terminal/payments/collect-card-payment.md#create-payment).
2. [Collect a payment method](https://docs.stripe.com/terminal/payments/collect-card-payment.md#collect-payment). You can define whether to [automatically](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-capture_method) or [manually](https://docs.stripe.com/payments/place-a-hold-on-a-payment-method.md) capture your payments.
3. [Process the payment](https://docs.stripe.com/terminal/payments/collect-card-payment.md#confirm-payment). Authorization on the customer’s card takes place when the SDK processes the payment.
4. (Optional) [Capture the payment](https://docs.stripe.com/terminal/payments/collect-card-payment.md#capture-payment)

> This integration shape doesn’t support [offline card payments](https://docs.stripe.com/terminal/features/operate-offline/collect-card-payments.md).

## Create a PaymentIntent [Server-side]

The first step when collecting payments is to start the payment flow. When a customer begins checking out, your application must create a `PaymentIntent` object. This represents a new payment session on Stripe.

Use [test amounts](https://docs.stripe.com/terminal/references/testing.md#physical-test-cards) to try producing different results. An amount ending in `00` results in an approved payment.

> #### Don't recreate PaymentIntents for declined cards
> 
> Don’t recreate a PaymentIntent if a card is declined. Instead, reuse the same PaymentIntent to help [avoid double charges](https://docs.stripe.com/terminal/payments/collect-card-payment.md#avoiding-double-charges).

The following example shows how to create a `PaymentIntent` on your server:

#### curl

```bash
curl https://api.stripe.com/v1/payment_intents \
  -u <<YOUR_SECRET_KEY>>: \
  -d "amount"=1000 \
  -d "currency"="usd" \
  -d "payment_method_types[]"="card_present" \
  -d "capture_method"="manual"
```

For Terminal payments, the `payment_method_types` parameter must include `card_present`.

You can control the payment flow as follows:

- To fully control the payment flow for `card_present` payments, set the `capture_method` to `manual`. This allows you to add a reconciliation step before finalizing the payment.
- To authorize and capture payments in one step, set the `capture_method` to `automatic`.

To accept Interac payments in Canada, you must also include `interac_present` in `payment_method_types`. For more details, visit our [Canada documentation](https://docs.stripe.com/terminal/payments/regional.md?integration-country=CA).

The `PaymentIntent` contains a [client secret](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-client_secret), a key that’s unique to the individual `PaymentIntent`. To use the client secret, you must obtain it from the `PaymentIntent` on your server and [pass it to the client side](https://docs.stripe.com/payments/payment-intents.md#passing-to-client).

#### Ruby

```ruby
post '/create_payment_intent' do
  intent = # ... Create or retrieve the PaymentIntent
  {client_secret: intent.client_secret}.to_json
end
```

Use the client secret as a parameter when calling [collectPaymentMethod](https://docs.stripe.com/terminal/references/api/js-sdk.md#collect-payment-method).

The `client_secret` is all you need in your client-side application to proceed to payment method collection.

## Collect a payment method  [Client-side]

- [collectPaymentMethod (JavaScript)](https://docs.stripe.com/terminal/references/api/js-sdk.md#collect-payment-method)

After you’ve created a `PaymentIntent`, the next step is to collect a payment method with the SDK.

To collect a payment method, your app needs to be connected to a reader. The connected reader waits for a card to be presented after your app calls `collectPaymentMethod`.

```javascript
async () => {
  // clientSecret is the client_secret from the PaymentIntent you created in Step 1.
  const result = await terminal.collectPaymentMethod(clientSecret);
  if (result.error) {
    // Placeholder for handling result.error
  } else {
    // Placeholder for processing result.paymentIntent
  }
}
```

This method collects encrypted payment method data using the connected card reader, and associates the encrypted data with the local `PaymentIntent`.

### Optionally inspect payment method details 

- [collectPaymentMethod config_override (JavaScript)](https://docs.stripe.com/terminal/references/api/js-sdk.md#collect-payment-method)

For advanced use cases, you can examine the payment method details of the presented card and perform your own business logic prior to authorization.

Use the `update_payment_intent` parameter to attach a `PaymentMethod` to the server-side `PaymentIntent`. This data is returned in the `collectPaymentMethod` response.  

```javascript
async () => {
  // clientSecret is the client_secret from the PaymentIntent you created in Step 1.
  const result = await terminal.collectPaymentMethod(clientSecret, {
    config_override: {
      update_payment_intent: true
    }
  });
  if (result.error) {
    // Placeholder for handling result.error
  } else {
    const pm = result.paymentIntent.payment_method
    const card = pm?.card_present ?? pm?.interac_present

    // Placeholder for business logic on card before processing result.paymentIntent
  }
}
```

This method attaches the collected encrypted payment method data with an update to the `PaymentIntent` object. It doesn’t requires authorization until you [process the payment](https://docs.stripe.com/terminal/payments/collect-card-payment.md#confirm-payment).

After payment method collection you must authorize the payment or cancel collection within 30 seconds.

If the SDK is [operating offline](https://docs.stripe.com/terminal/features/operate-offline/collect-card-payments.md), the `paymentMethod` field isn’t present in the `PaymentIntent` object.

You can access attributes like card brand, funding, and other useful data at this point.

Stripe attempts to detect whether a mobile wallet is used in a transaction as shown in the `wallet.type` attribute. However, the attribute isn’t populated if the card’s issuing bank doesn’t support reader-driven identification of a mobile wallet, so accurate detection isn’t guaranteed. After authorization in the [confirmation](https://docs.stripe.com/terminal/payments/collect-card-payment.md#confirm-payment) step, Stripe receives up-to-date information from the networks and updates `wallet.type` reliably

### Cancel collection 

#### Programmatic cancellation

You can cancel collecting a payment method by calling [cancelCollectPaymentMethod](https://docs.stripe.com/terminal/references/api/js-sdk.md#cancel-collect-payment-method) in the JavaScript SDK.

#### Customer-initiated cancellation

- [enable_customer_cancellation (JavaScript)](https://docs.stripe.com/terminal/references/api/js-sdk.md#collect-payment-method)

When you set `enable_customer_cancellation` to true for a transaction, smart reader users see a cancel button.

Tapping the cancel button cancels the active transaction.

```javascript
terminal.collectPaymentMethod(
  clientSecret,
  {
    config_override: {
      enable_customer_cancellation: true,
    }
  }
)
```

### Handle events 

> The JavaScript SDK only supports the Stripe Reader S700/S710 and BBPOS WisePOS E, which have a built-in display. Your application doesn’t need to display events from the payment method collection process to users, because the reader displays them. To clear the payment method on a transaction, the cashier can press the cancel (❌) key.

## Confirm the payment [Client-side]

- [processPayment (JavaScript)](https://docs.stripe.com/terminal/references/api/js-sdk.md#process-payment)

After successfully collecting a payment method from the customer, the next step is to process the payment with the SDK. When you’re ready to proceed with the payment, call `processPayment` with the updated `PaymentIntent` from [Step 2](https://docs.stripe.com/terminal/payments/collect-card-payment.md#collect-payment).

- For manual capture of payments, a successful `processPayment` call results in a `PaymentIntent` with a status of `requires_capture`.
- For automatic capture of payments, the `PaymentIntent` transitions to a `succeeded` state.

Always confirm PaymentIntents using the Terminal SDK on the client side. Server-side confirmation bypasses critical interactions, such as PIN prompts, and can result in transaction failures.

```javascript
async () => {
  const result = await terminal.processPayment(paymentIntent);
  if (result.error) {
    // Placeholder for handling result.error
  } else if (result.paymentIntent) {
    // Placeholder for notifying your backend to capture result.paymentIntent.id
  }
}
```

You must manually capture a PaymentIntent within 2 days or the authorization expires and funds are released to the customer.

### Handle failures 

- [Error codes (JavaScript)](https://docs.stripe.com/terminal/references/api/js-sdk.md#error-codes)

When processing a payment fails, the SDK returns an error that includes the updated `PaymentIntent`. Your application needs to inspect the `PaymentIntent` to decide how to deal with the error.

| PaymentIntent Status      | Meaning                                                     | Resolution                                                                                                                                      |
| ------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `requires_payment_method` | Payment method declined                                     | Try collecting a different payment method by calling `collectPaymentMethod` again with the same `PaymentIntent`.                                |
| `requires_confirmation`   | Temporary connectivity problem                              | Call `processPayment` again with the same `PaymentIntent` to retry the request.                                                                 |
| `PaymentIntent` is `nil`  | Request to Stripe timed out, unknown `PaymentIntent` status | Retry processing the original `PaymentIntent`. Don’t create a new one, because that might result in multiple authorizations for the cardholder. |

If you encounter multiple, consecutive timeouts, there might be a problem with your connectivity. Make sure that your app can communicate with the internet.

### Avoiding double charges 

The `PaymentIntent` object enables money movement at Stripe—use a single `PaymentIntent` to represent a transaction.

Re-use the same `PaymentIntent` after a card is declined (for example, if it has insufficient funds), so your customer can try again with a different card.

If you edit the `PaymentIntent`, you must call `collectPaymentMethod` to update the payment information on the reader.

A `PaymentIntent` must be in the `requires_payment_method` state before Stripe can process it. An authorized, captured, or canceled `PaymentIntent` can’t be processed by a reader.

## Capture the payment [Server-side]

If you defined `capture_method` as `manual` during `PaymentIntent` creation in [Step 1](https://docs.stripe.com/terminal/payments/collect-card-payment.md#create-payment), the SDK returns an authorized but not captured `PaymentIntent` to your application. Learn more about the difference between [authorization and capture](https://docs.stripe.com/payments/place-a-hold-on-a-payment-method.md).

When your app receives a confirmed `PaymentIntent` from the SDK, make sure it notifies your backend to capture the payment. Create an endpoint on your backend that accepts a `PaymentIntent` ID and sends a request to the Stripe API to capture it:

```curl
curl -X POST https://api.stripe.com/v1/payment_intents/{{PAYMENT_INTENT_ID}}/capture \
  -u "<<YOUR_SECRET_KEY>>:"
```

A successful `capture` call results in a `PaymentIntent` with a status of `succeeded`.

To make sure the application fee captured is correct for connected accounts, inspect each `PaymentIntent` and modify the application fee, if needed, before manually capturing the payment.

### Reconcile payments 

To monitor the payments activity of your business, you might want to reconcile PaymentIntents with your internal orders system on your server at the end of a day’s activity.

A `PaymentIntent` that retains a `requires_capture` status might represent two things:

**Unnecessary authorization on your customer’s card statement**

- Cause: User abandons your app’s checkout flow in the middle of a transaction
- Solution: If the uncaptured `PaymentIntent` isn’t associated with a completed order on your server, you can [cancel](https://docs.stripe.com/api/payment_intents/cancel.md) it. You can’t use a canceled `PaymentIntent` to perform charges.

**Incomplete collection of funds from a customer**

- Cause: Failure of the request from your app notifying your backend to capture the payment
- Solution: If the uncaptured `PaymentIntent` is associated with a completed order on your server, and no other payment has been taken for the order (for example, a cash payment), you can [capture](https://docs.stripe.com/api/payment_intents/capture.md) it.

### Collect tips (US only)

In the US, eligible users can [collect a tip on the receipt when capturing payments](https://docs.stripe.com/terminal/features/collecting-tips/on-receipt.md).

