# 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 <>: \ -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 "<>:" ``` 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).