Sign in
Create account
Sign in
Home
Payments
Business operations
Financial services
Developer tools
Security
All products
Home
Payments
Business operations
Home
Payments
Business operations
Financial services
Developer tools
Support
Overview
Overview
Sample integration
Example applications
Designing an integration
Integrate your application and readers
Getting started
JavaScript
iOS
Android
Readers
Reader setup
Connecting to a reader
Fleet management
Creating reader locations
Using reader locations
Placing orders
Setting a custom splash screen
Transactions
Collecting payments
Accept Interac payments
Connect platforms
Saving cards
Refunds
Checkout experience
Cart display
Receipts
Beta
Beta migration guide
Testing
Checklist
Testing
terminal
·
HomePaymentsIn-person payments

Collecting payments

Prepare your application and backend to collect payments using the Stripe Terminal SDK.

Learn More

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

  • The Payment Intents API
  • The PaymentIntent object
  • More payment scenarios

Collecting payments with Stripe Terminal requires writing a payment flow in your application. Use the Stripe Terminal SDK to create and update a PaymentIntent, 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 payment intent
  2. Collect a payment method
  3. Process the payment
  4. Capture the PaymentIntent

Authorization on the customer’s card takes place in Step 3, when the SDK processes the payment.

Currently, Stripe Terminal is only available in the U.S. (including Puerto Rico but excluding other territories), Canada, and other select countries (on an invite-only basis). You can only collect payments in your local currency. Hardware must be shipped to physical addresses (not PO boxes). If you are outside the U.S. and Canada, you can request an invite to test Terminal in your country.

Create a payment intent
Client-side
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.

SDK Reference

  • createPaymentIntent (iOS)
  • createPaymentIntent (Android)

With the iOS and Android SDKs, you can create a PaymentIntent on the client or server. The JavaScript SDK only supports server-side creation.

Use test amounts to try producing different results. An amount ending in 00 results in an approved payment.

Client-side

Create a PaymentIntent from your client using the iOS or Android SDK:

Client-side PaymentIntent creation is possible with the iOS or Android SDKs. If you’re using the JavaScript SDK for Stripe Terminal, create a PaymentIntent server-side.

Server-side

The JavaScript SDK requires you to create the PaymentIntent on your server. For iOS or Android, you can create the PaymentIntent on your server if the information required to start a payment isn’t readily available in your app.

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

Terminal
curl https://api.stripe.com/v1/payment_intents \ -u
sk_test_4eC39HqLyjWDarjtT1zdp7dc
: \ -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. To control the payment flow for card_present payments, set the capture_method to manual.

To accept Interac payments in Canada, you will need to also include interac_present in payment_method_types. For more details, visit our Canada documentation.

The PaymentIntent contains a client secret, a key that is 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.

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

SDK Reference

  • retrievePaymentIntent (iOS)
  • retrievePaymentIntent (Android)

For JavaScript, use the client secret as a parameter when calling collectPaymentMethod. For iOS and Android, first use the client secret to call retrievePaymentIntent, and then use the retrieved PaymentIntent to call collectPaymentMethod.

For JavaScript, the client_secret is all you need in your client-side application to proceed to payment method collection.

Collect a payment method
Client-side

SDK Reference

  • collectPaymentMethod (JavaScript)
  • collectPaymentMethod (iOS)
  • collectPaymentMethod (Android)

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

In order to collect a payment method, your app needs to be connected to a reader. The connected reader will wait for a card to be presented after your app calls collectPaymentMethod.

function checkout() { // clientSecret is the client_secret from the PaymentIntent you created in Step 1. terminal.collectPaymentMethod(clientSecret).then(function(result) { if (result.error) { // Placeholder for handling result.error } else { // Placeholder for processing result.paymentIntent } }); }

SDK Reference

  • Cancelable (iOS)
  • Cancelable (Android)

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

You can cancel collecting a payment method using the Cancelable object returned by the iOS or Android SDK, or calling cancelCollectPaymentMethod in the JavaScript SDK.

Collecting a payment method happens locally and requires no authorization or updates to the Payment Intents API object until the next step, process the payment.

Handling events

SDK Reference

  • ReaderDisplayListener (Android)

When collecting a payment method using a reader like the BBPOS Chipper 2X BT, without a built-in display, your app must be able to display events from the payment method collection process to users. These events help users successfully collect payments (e.g., retrying a card, trying a different card, or using a different read method).

When a transaction begins, the SDK passes a ReaderInputOptions value to your app’s reader display handler, denoting the acceptable types of input (e.g., Swipe, Insert, Tap). In your app’s checkout UI, prompt the user to present a card using one of these options.

During the transaction, the SDK might request your app to display additional prompts (e.g., Retry Card) to your user by passing a ReaderDisplayMessage value to your app’s reader display handler. Make sure your checkout UI displays these messages to the user.

The JavaScript SDK only supports the Verifone P400, which has a built-in display. Your application doesn’t need to display events from the payment method collection process to users, as the reader displays them. To clear the payment method on a transaction with the Verifone P400, the cashier can press the red X key.

Process the payment
Client-side

SDK Reference

  • processPayment (JavaScript)
  • processPayment (iOS)
  • processPayment (Android)

After successfully collecting a payment method from the customer, the next step is to process the payment with the SDK. You can either process automatically or display a confirmation screen, where the customer can choose to proceed with the payment or cancel (e.g., to pay with cash, or use a different payment method).

When you’re ready to proceed with the payment, call processPayment with the updated PaymentIntent from Step 2. A successful processPayment call will result in a PaymentIntent with a status of requires_capture.

terminal.processPayment(paymentIntent).then(function(result) { 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 payments processed by the Terminal SDKs. Set up your backend to capture the PaymentIntent within 24 hours. Otherwise, the authorization expires and funds get released back to the customer.

Handling processing failures

SDK Reference

  • Error codes (JavaScript)
  • ProcessPaymentError (iOS)
  • TerminalException (Android)

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

PaymentIntent StatusMeaningResolution
requires_payment_methodPayment method declinedTry collecting a different payment method by calling collectPaymentMethod again with the same PaymentIntent.
requires_confirmationTemporary connectivity problemCall processPayment again with the same PaymentIntent to retry the request.
PaymentIntent is nilRequest to Stripe timed out, unknown PaymentIntent statusRetry processing the original PaymentIntent. Do not create a new one, as that could 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 is able to communicate with the internet.

Capture the Payment Intent
Server-side

Stripe Terminal uses a two-step process to prevent unintended and duplicate payments. When the SDK returns a processed PaymentIntent to your app, the payment is authorized but not captured. Read the auth and capture documentation for more information about the difference.

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

Terminal
curl https://api.stripe.com/v1/payment_intents/{{PAYMENT_INTENT_ID}}/capture \ -u
sk_test_4eC39HqLyjWDarjtT1zdp7dc
: \ -X "POST"

A successful capture call will result in a PaymentIntent with a status of succeeded.

Reconciling payments

To monitor the payments activity of your business, you may 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 may 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 is not associated with a completed order on your server, you can cancel it. A canceled PaymentIntent can no longer be used to perform charges.

Incomplete collection of funds from a customer

  • Cause: Failure of the request from your app notifying your backend to capture the PaymentIntent
  • Solution: If the uncaptured PaymentIntent is associated with a completed order on your server, and no other payment has been taken for the order (e.g., a cash payment), you can capture it.

Next steps

Congratulations! Your Terminal integration is now set up to collect in-person payments. Next, configure the customer-facing checkout experience in your app, or test your current integration with a physical reader.

  • Cart Display
  • Receipts
  • Reader Types
Was this page helpful?
Questions? Contact us.
Developer tutorials on YouTube.
You can unsubscribe at any time. Read our privacy policy.
On this page
Create a payment intent
Collect a payment method
Process the payment
Capture the Payment Intent
Next steps