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
OverviewAccept a paymentUpgrade your integration
Online payments
OverviewFind your use case
Use Payment Links
Use a prebuilt checkout page
Build a custom integration with Elements
Build an in-app integration
Use Managed PaymentsRecurring payments
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
      SDK v4 migration guide
      SDK v3 migration guide
    Deployment checklist
    Stripe Terminal reader product sheets
Payment methods
Add payment methods
Manage payment methods
Faster checkout with Link
Payment operations
Analytics
Balances and settlement time
Compliance and security
Currencies
Declines
Disputes
Fraud prevention
Radar fraud protection
Payouts
ReceiptsRefunds and cancellations
Advanced integrations
Custom payment flows
Flexible acquiring
Multiprocessor orchestration
Beyond payments
Incorporate your company
Crypto
Agentic commerce
Financial Connections
Climate
Verify identities
United States
English (United States)
HomePaymentsTerminal

Terminal SDK migration guide

Learn how to migrate to version 5.0.0 of the Stripe Terminal SDK.

The Stripe Terminal iOS and Android SDKs have been updated with a number of breaking changes in APIs and behavior, some of which require you to update your integration with the Stripe Terminal SDK. To improve consistency between our SDKs and to simplify your application logic and integration, we regularly make changes in major version updates that might affect the way your integration works or behaves. This guide explains the latest changes to help you upgrade your integration.

Note

Building a new Stripe Terminal integration? Visit our Design an integration page to learn how to get started.

Migrate to version 5.0.0

Here’s what you need to know about the version 5.0.0 Stripe Terminal iOS and Android SDKs:

  • Simplified payment integration
    • Process payments, setup intents, and refunds with a single method call combining collect and confirm steps.
  • Supports modern Swift async variants and Kotlin Coroutines for simplifying complex asynchronous flows
    • Swift concurrency (async/await) for iOS and Kotlin Coroutines for Android.
  • Customer cancellation enabled by default
    • On supported readers, customers can now cancel transactions by default during payment, setup, refund, and data collection flows.
  • Improved mobile reader and Tap to Pay reader auto reconnection observability
    • Enhanced reader auto-reconnection handling with more connection status states for mobile readers (Bluetooth and USB) and Tap to Pay readers.
  • Discover card acceptance support for Tap to Pay on Android Public preview
    • Accept Discover card payments with Tap to Pay on Android.
  • Updates to minimum supported platform versions from iOS 14.0 to iOS 15.0

If your application currently uses a Terminal Android SDK version earlier than 5.0.0, there are several changes you need to make to upgrade. For a detailed list of the changes from version 4.x to 5.0.0, see the SDK changelog.

Simplified payment integration

Update to unified payment processing

The v5 SDK introduces streamlined methods that combine the collect and confirm steps into a single operation. While the existing collectPaymentMethod and confirmPaymentIntent methods continue to work, we recommend using the new unified methods for simpler integrations.

Processing payments with processPaymentIntent

Replace two-step collect and confirm with a single processPaymentIntent method call.

Before

Kotlin
Java
No results
// Step 1: Collect payment method Terminal.getInstance().collectPaymentMethod( paymentIntent, collectConfig, object : PaymentIntentCallback { override fun onSuccess(paymentIntent: PaymentIntent) { // Step 2: Confirm the payment Terminal.getInstance().confirmPaymentIntent(paymentIntent, object : PaymentIntentCallback { override fun onSuccess(confirmedPaymentIntent: PaymentIntent) { // Payment successful } override fun onFailure(e: TerminalException) { // Payment confirmation failed } }) } override fun onFailure(e: TerminalException) { // Payment method collection failed } } )

After

Kotlin
Java
No results
// Process and confirm the payment in one step Terminal.getInstance().processPaymentIntent( paymentIntent, collectConfig, confirmConfig, object : PaymentIntentCallback { override fun onSuccess(paymentIntent: PaymentIntent) { // Payment successful } override fun onFailure(e: TerminalException) { // Payment failed } } )

Processing refunds with processRefund

The collectRefundPaymentMethod and confirmRefund methods are now deprecated. Use processRefund instead.

Before

Kotlin
Java
No results
// Step 1: Collect refund payment method val refundParams = RefundParameters.ByChargeId( id = "ch_123", amount = 1000L, currency = "cad" ).build() Terminal.getInstance().collectRefundPaymentMethod( refundParams, object : Callback { override fun onSuccess() { // Step 2: Confirm the refund Terminal.getInstance().confirmRefund(object : RefundCallback { override fun onSuccess(refund: Refund) { // Refund successful } override fun onFailure(e: TerminalException) { // Refund confirmation failed } }) } override fun onFailure(e: TerminalException) { // Refund collection failed } } )

After

Kotlin
Java
No results
val refundParams = RefundParameters.ByChargeId( id = "ch_123", amount = 1000, currency = "cad" ).build() // Process the refund in one step Terminal.getInstance().processRefund( refundParams, object : RefundCallback { override fun onSuccess(refund: Refund) { // Refund successful } override fun onFailure(e: TerminalException) { // Refund failed } } )

Processing setup intents with processSetupIntent

Replace two-step collect and confirm with a single processSetupIntent method call.

Before

Kotlin
Java
No results
// Step 1: Collect setup intent payment method Terminal.getInstance().collectSetupIntentPaymentMethod( intent = setupIntent, allowRedisplay = AllowRedisplay.ALWAYS, callback = object : SetupIntentCallback { override fun onSuccess(setupIntent: SetupIntent) { // Step 2: Confirm the setup intent Terminal.getInstance().confirmSetupIntent(setupIntent, object : SetupIntentCallback { override fun onSuccess(confirmedSetupIntent: SetupIntent) { // Setup intent successful } override fun onFailure(e: TerminalException) { // Setup intent confirmation failed } }) } override fun onFailure(e: TerminalException) { // Setup intent collection failed } } )

After

Kotlin
Java
No results
// Configure with allowRedisplay val config = CollectSetupIntentConfiguration.Builder() .build() // Process the setup intent in one step Terminal.getInstance().processSetupIntent( intent = setupIntent, allowRedisplay = AllowRedisplay.ALWAYS, collectConfig = config, callback = object : SetupIntentCallback { override fun onSuccess(setupIntent: SetupIntent) { // Setup intent successful } override fun onFailure(e: TerminalException) { // Setup intent failed } } )

Kotlin Coroutines support

For Kotlin developers, a new optional module stripeterminal-ktx provides suspend function wrappers for asynchronous Terminal APIs.

Note

Add this dependency: implementation("com.stripe:stripeterminal-ktx:5.0.0")

Before

Terminal.getInstance().discoverReaders(config, object : DiscoveryListener { override fun onUpdateDiscoveredReaders(readers: List<Reader>) { val selectedReader = readers[0] Terminal.getInstance().connectReader(selectedReader, connectionConfig, object : ReaderCallback { override fun onSuccess(reader: Reader) { // Handle successful connection } override fun onFailure(e: TerminalException) { // Handle connection failure } }) } })

After

// Add dependency: implementation("com.stripe:stripeterminal-ktx:5.0.0") coroutineScope { try { val readers = Terminal.getInstance().discoverReaders(discoveryConfig) .filter { it.isNotEmpty() } .first() val selectedReader = readers.first() val reader = Terminal.getInstance().connectReader(selectedReader, connectConfig) // Handle successful connection } catch(e: TerminalException) { // Handle failures on discovery or connect } }

Platform and initialization

Update Terminal initialization

The Terminal.initTerminal method has been renamed to Terminal.init. It now requires a nullable OfflineListener parameter.

Before

Kotlin
Java
No results
Terminal.initTerminal(applicationContext, LogLevel.VERBOSE, tokenProvider, terminalListener)

After

Kotlin
Java
No results
Terminal.init(applicationContext, LogLevel.VERBOSE, tokenProvider, terminalListener, offlineListener)

Reader discovery and connection

Handle reconnection status changes

A new RECONNECTING value has been added to the ConnectionStatus enum. During initial connection Terminal.getInstance().getConnectedReader() will now be null until the connection attempt succeeds.

Before

Kotlin
Java
No results
override fun onConnectionStatusChange(status: ConnectionStatus) { when (status) { ConnectionStatus.NOT_CONNECTED -> { // Handle not connected } ConnectionStatus.CONNECTED -> { // Handle connected } } }

After

Kotlin
Java
No results
override fun onConnectionStatusChange(status: ConnectionStatus) { when (status) { ConnectionStatus.NOT_CONNECTED -> { // Handle not connected } ConnectionStatus.CONNECTED -> { // Handle connected } ConnectionStatus.RECONNECTING -> { // Handle reconnection in progress } } }

Streamlined connection with easyConnect

For smart readers, Tap to Pay, and Apps on Devices integrations, you can now use Terminal.easyConnect, which combines discovery and connection into a single method call.

Before

Kotlin
Java
No results
// Step 1: Discover the reader Terminal.getInstance().discoverReaders(config, object : DiscoveryListener { override fun onUpdateDiscoveredReaders(readers: List<Reader>) { val selectedReader = readers[0] // Step 2: Connect to the reader Terminal.getInstance().connectReader(selectedReader, connectionConfig, readerCallback) } })

After

Kotlin
Java
No results
// Discover and connect in one step by providing discovery filter val easyConnectConfig = InternetEasyConnectConfiguration( discoveryConfiguration = DiscoveryConfiguration.InternetDiscoveryConfiguration( location = "YOUR-LOCATION-ID", // optional discoveryFilter = DiscoveryFilter.BySerial("YOUR-READER-SERIAL-NUMBER"), ), connectionConfiguration = ConnectionConfiguration.InternetConnectionConfiguration( internetReaderListener = internetReaderListener, ) ) Terminal.getInstance().easyConnect( easyConnectConfig, object : ReaderCallback { override fun onSuccess(reader: Reader) { // Handle successful connection } override fun onFailure(e: TerminalException) { // Handle failure } } )

Internet reader discovery filtering

Internet reader discovery now supports filtering by reader ID or serial number. Set the discoveryFilter property on InternetDiscoveryConfiguration to discover a specific reader.

Before

Kotlin
Java
No results
val config = InternetDiscoveryConfiguration(location = "tml_1234567890")

After

Kotlin
Java
No results
val config = InternetDiscoveryConfiguration( location = "tml_1234567890", // optional discoveryFilter = DiscoveryFilter.BySerial("READER-SERIAL-NUMBER"), // or DiscoveryFilter.ByReaderId("tmr_YOUR-READER-STRIPE-ID) to filter by reader id )

Payment acceptance and data collection

Customer cancellation is now enabled by default

On Android-based readers, the ability for customers to cancel transactions is now enabled by default. You can disable this feature by setting customerCancellation to DISABLE_IF_AVAILABLE.

Before

Kotlin
Java
No results
val config = CollectConfiguration.Builder() .setEnableCustomerCancellation(false) .build()

After

Kotlin
Java
No results
val config = CollectPaymentIntentConfiguration.Builder() .setCustomerCancellation(CustomerCancellation.DISABLE_IF_AVAILABLE) .build()

Interac refund parameter updates

If you create RefundParameters for an Interac refund using a PaymentIntent ID, you must now also pass the PaymentIntent’s clientSecret. You can alternatively continue using the charge ID, which doesn’t require the clientSecret.

Before

Kotlin
Java
No results
val refundParams = RefundParameters.Builder( RefundParameters.Id.PaymentIntent("pi_123"), 1000, "cad" ).build()

After

Kotlin
Java
No results
val refundParams = RefundParameters.ByPaymentIntentId( paymentIntentId = "pi_123", clientSecret = "pi_123_secret_abc", amount = 1000, currency = "cad" ).build()

Update your Apps on Devices integration

The Maven coordinates for the Apps on Devices feature have changed to com.stripe:stripeterminal-appsondevices:5.0.0. Update your build dependencies to point to the new artifact name. Stripe will no longer update the old handoffclient artifact.

We renamed all Handoff class names to AppsOnDevices to better describe the feature’s functionality.

Before

build.gradle.kts
Kotlin
Groovy
No results
dependencies { implementation("com.stripe:stripeterminal-handoffclient:4.0.0") }

After

build.gradle.kts
Kotlin
Groovy
No results
dependencies { implementation("com.stripe:stripeterminal-appsondevices:5.0.0") }

Rename Handoff classes to AppsOnDevices

We’ve renamed all Handoff class names to AppsOnDevices across discovery configuration, connection configuration, listeners, and token providers.

Before

Kotlin
Java
No results
val discoveryConfig = HandoffDiscoveryConfiguration() Terminal.getInstance().discoverReaders( discoveryConfig, object : DiscoveryListener { override fun onUpdateDiscoveredReaders(readers: List<Reader>) { val reader = readers.first() val connectionConfig = HandoffConnectionConfiguration( handoffReaderListener = object : HandoffReaderListener { override fun onDisconnect(reason: DisconnectReason) { // Handle disconnect } } ) Terminal.getInstance().connectReader(reader, connectionConfig, readerCallback) } } ) val tokenProvider = HandoffConnectionTokenProvider()

After

Kotlin
Java
No results
val discoveryConfig = AppsOnDevicesDiscoveryConfiguration() Terminal.getInstance().discoverReaders( discoveryConfig, object : DiscoveryListener { override fun onUpdateDiscoveredReaders(readers: List<Reader>) { val reader = readers.first() val connectionConfig = AppsOnDevicesConnectionConfiguration( appsOnDevicesListener = object : AppsOnDevicesListener { override fun onDisconnect(reason: DisconnectReason) { // Handle disconnect } } ) Terminal.getInstance().connectReader(reader, connectionConfig, readerCallback) } } ) val tokenProvider = AppsOnDevicesConnectionTokenProvider()

Update Tap to Pay on Android integration

System requirements

Tap to Pay on Android 5.0.0 and above requires your Android device to be running Android 13 or higher.

This version also requires that your Android device’s KeyStore supports hardware-backed key agreements. This is checked automatically for you by supportsReadersOfType(), but can also be verified by checking that your device’s FEATURE_HARDWARE_KEYSTORE version is 100 or above. Since this requirement depends on the hardware capabilities of a device, it might not be met by devices that were originally released with Android 12 or lower, even if they’ve been upgraded to meet the Android 13 runtime requirement. This new requirement means that devices like the Samsung Galaxy Tab Active4 Pro are no longer supported in SDK versions 5.0.0 and above.

For production environments, reader discovery fails with a TAP_TO_PAY_INSECURE_ENVIRONMENT error if developer options, USB or Wi-Fi debugging, or other debug options are enabled on the device. This doesn’t apply to usage of the simulated Tap to Pay reader.

TapZone configuration refactoring

The TapToPayUxConfiguration.TapZone class has been refactored. The indicator and position fields are replaced by a single TapZone object.

Before

Kotlin
Java
No results
val config = TapToPayUxConfiguration.Builder() .setTapZone( indicator = TapZoneIndicator.ABOVE, position = TapZonePosition.Manual(0.5f, 0.2f) ) .build()

After

Kotlin
Java
No results
// Position the tap zone above the reader UI val config = TapToPayUxConfiguration.Builder() .setTapZone(TapZone.Above(horizontalBias = 0.2f)) .build() // Or position it on the left side of the screen val config2 = TapToPayUxConfiguration.Builder() .setTapZone(TapZone.Left()) // Center vertically by default .build()
Was this page helpful?
YesNo
  • Need help? Contact Support.
  • Check out our changelog.
  • Questions? Contact Sales.
  • LLM? Read llms.txt.
  • Powered by Markdoc