Skip to content
Create account or Sign in
The Stripe Docs logo
/
Ask AI
Create accountSign in
Get started
Payments
Revenue
Platforms and marketplaces
Money management
Developer resources
APIs & SDKsHelp
Overview
About Stripe payments
Upgrade your integration
Payments analytics
Online payments
OverviewFind your use caseUse Managed Payments
Use Payment Links
Use a prebuilt checkout page
Build a custom integration with Elements
Build an in-app integration
In-person payments
Terminal
    Overview
    Accept in-person payments
    Global availability
    Integration design
    Select your reader
    Design an integration
    Quickstart
    Example applications
    Testing
    Terminal setup
    Set up your integration
    Multiparty payments with Connect
    Connect to a reader
    Accepting a payment
    Collect card payments
    Additional payment methods
    Accept offline payments
    Mail order and telephone order payments
    Regional considerations
    During checkout
    Collect tips
    Collect and save payment details for future use
    Flexible authorizations
    After checkout
    Refund transactions
    Provide receipts
    Customize checkout
    Cart display
    Collect on-screen inputs
    Collect swiped data
    Collect tapped data for NFC instruments
    Apps on devices
    Manage readers
    Order, return, replace readers
    Register readers
    Manage locations and zones
    Configure readers
    Monitor Readers
    References
    API references
    Mobile readers
    Smart readers
    Tap to Pay readers
    SDK migration guide
    Deployment checklist
    Stripe Terminal reader product sheets
Payment methods
Add payment methods
Manage payment methods
Faster checkout with Link
Payment scenarios
Handle multiple currencies
Custom payment flows
Flexible acquiring
Orchestration
Beyond payments
Incorporate your company
Crypto
Agentic commerce
Financial Connections
Climate
Understand fraud
Radar fraud protection
Manage disputes
Verify identities
United States
English (United States)
HomePaymentsTerminal

Collect card payments

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

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 PaymentIntent.
  2. Process the payment. Authorization on the customer’s card takes place when the SDK processes the payment.
  3. (Optional) Capture the payment

Create a PaymentIntent
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 (Android)

You can create a PaymentIntent on the client or server.

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

Common mistake

Don’t recreate a PaymentIntent if a card is declined. Instead, re-use the same PaymentIntent to help avoid double charges.

Client-side

Create a PaymentIntent from your client:

Warning

If your app is connected to the Verifone P400, you can’t create a PaymentIntent from the Android SDK. Instead, you must create the PaymentIntent server-side, and retrieve the PaymentIntent in your app using the Terminal.retrievePaymentIntent method in the SDK.

PaymentActivity.kt
Kotlin
Java
No results
val params = PaymentIntentParameters.Builder() .setAmount(1000) .setCurrency("usd") .build() Terminal.getInstance().createPaymentIntent( params, object : PaymentIntentCallback { override fun onSuccess(paymentIntent: PaymentIntent) { // Placeholder for handling successful operation } override fun onFailure(e: TerminalException) { // Placeholder for handling exception } } )

Server-side

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:

Command Line
curl
Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
curl https://api.stripe.com/v1/payment_intents \ -u
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:
\ -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 payments in Australia, you need to set capture_method to automatic or manual_preferred. For more details, visit our Australia documentation. To accept Interac payments in Canada, you must also include interac_present in payment_method_types. For more details, visit our Canada documentation.

The PaymentIntent contains a 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.

Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
post '/create_payment_intent' do intent = # ... Create or retrieve the PaymentIntent {client_secret: intent.client_secret}.to_json end

SDK Reference

  • retrievePaymentIntent (Android)

To retrieve a PaymentIntent, use the client secret to call retrievePaymentIntent.

After you retrieve the PaymentIntent, use it to call processPaymentIntent.

PaymentActivity.kt
Kotlin
Java
No results
Terminal.getInstance().retrievePaymentIntent( clientSecret, object : PaymentIntentCallback { override fun onSuccess(paymentIntent: PaymentIntent) { // Placeholder for handling successful operation } override fun onFailure(e: TerminalException) { // Placeholder for handling exception } } )

Process the payment
Client-side

You can process a payment immediately with the card presented by a customer, or instead inspect card details before proceeding to process the payment. For most use cases, we recommend processing immediately, because it’s a simpler integration with fewer API calls. However, if you want to insert your own business logic before authorizing the card, use the two-step collect-and-confirm flow.

After you create a PaymentIntent, the next step is to process the payment. The reader prompts the customer to insert or tap their card and then attempts to authorize the payment.

SDK Reference

  • processPaymentIntent (Android)

While processing a payment, cardholder might take a few seconds to get their card from their wallet or pose a question to the operator during payment.

PaymentActivity.kt
Kotlin
Java
No results
val cancelable = Terminal.getInstance().processPaymentIntent( paymentIntent = paymentIntent, collectConfig = CollectPaymentIntentConfiguration.Builder().build(), confirmConfig = ConfirmPaymentIntentConfiguration.Builder().build(), callback = object : PaymentIntentCallback { override fun onSuccess(paymentIntent: PaymentIntent) { println("processPaymentIntent succeeded") // Notify your backend to capture the PaymentIntent if (paymentIntent.id != null) { ApiClient.capturePaymentIntent(paymentIntent.id) { error -> if (error != null) { println("capturePaymentIntent failed: $error") } else { println("capturePaymentIntent succeeded") } } } else { println("Payment collected offline") } } override fun onFailure(e: TerminalException) { println("processPaymentIntent failed: $e") } } )

Cancel collection

Programmatic cancellation

SDK Reference

  • Cancelable (Android)

You can cancel processing a PaymentIntent using the Cancelable object returned by the Android SDK.

Customer-initiated cancellation

SDK Reference

  • setCustomerCancellation (Android)
  • Customer Cancellation (Android)

Smart readers show customers a cancel button by default. You can disable this by setting customerCancellation to DISABLE_IF_AVAILABLE.

Tapping the cancel button cancels the active transaction.

PaymentActivity.kt
Kotlin
Java
No results
Terminal.getInstance().collectPaymentMethod( paymentIntent, object : PaymentIntentCallback { override fun onSuccess(paymentIntent: PaymentIntent) { // Placeholder for handling successful operation } override fun onFailure(e: TerminalException) { // Placeholder for handling exception } }, CollectPaymentIntentConfiguration.Builder().build(), )

Handle events

SDK Reference

  • MobileReaderListener (Android)

When collecting a payment method using a reader such as the Stripe M2, 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 (for example, 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 (for example, Swipe, Insert, or 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 (for example, 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.

ReaderActivity.kt
Kotlin
Java
No results
class ReaderActivity : AppCompatActivity(), MobileReaderListener { // ... override fun onRequestReaderInput(options: ReaderInputOptions) { Toast.makeText(activity, options.toString(), Toast.LENGTH_SHORT).show() } override fun onRequestReaderDisplayMessage(message: ReaderDisplayMessage) { Toast.makeText(activity, message.toString(), Toast.LENGTH_SHORT).show() } // ... }

Collect payments with Tap to Pay on Android

When your application is ready to collect a payment, the Stripe Android SDK takes over the display to handle the collection process. After calling the process payment method, your application remains running. The Android device displays a full-screen prompt to the cardholder, instructing them to present their card or NFC-based mobile wallet. If there’s an error reading the card, a prompt for retry displays. A successful presentation returns a success indication, and then control returns to your application.

Tap to pay on Android

Payment collection

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

Warning

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

Handle failures

SDK Reference

  • TerminalException (Android)

When processing a payment fails, the SDK returns an error that includes the updated PaymentIntent if it was declined by stripe. Your application needs to 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 processPaymentIntent again with the same PaymentIntent.
requires_confirmationTemporary connectivity problemCall processPaymentIntent again with the same PaymentIntent to retry the request.
PaymentIntent is nilRequest to Stripe timed out, unknown PaymentIntent statusRetry 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.

Avoid 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 processPaymentIntent 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, the SDK returns an authorized but not captured PaymentIntent to your application. Learn more about the difference between authorization and capture.

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:

Command Line
cURL
Stripe CLI
Ruby
Python
PHP
Java
Node.js
Go
.NET
No results
curl -X POST https://api.stripe.com/v1/payment_intents/{{PAYMENT_INTENT_ID}}/capture \ -u "
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:"

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

Note

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 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 it.

Collect tips US only

In the US, eligible users can collect tips when capturing payments.

Was this page helpful?
YesNo
  • Need help? Contact Support.
  • Check out our changelog.
  • Questions? Contact Sales.
  • LLM? Read llms.txt.
  • Powered by Markdoc