Stripe Tap To Pay API
Accept in-person card payments directly on a customer's phone no extra hardware required. The WebToNative Stripe plugin integrates Stripe Terminal's native Tap to Pay SDKs for Android and iOS
You'll need to import the javascript file in your website before starting from this link.
Platform support: Android and iOS. iOS Tap to Pay requires a physical iPhone (XS or later) on a supported iOS version and Apple's explicit approval see Limitations below.
Limitations read before purchasing
Stripe Tap to Pay is a paid add-on with hard requirements outside WebToNative's control. Please review this table before purchasing.
Apple approval required
Hard requirement
Tap to Pay on iPhone requires Apple's explicit entitlement approval before it works on real devices in production. Purchasing this add-on does not guarantee Apple will grant approval.
Backend required
Hard requirement
The SDK does not provide backend APIs. You must build and host the Connection Token API and PaymentIntent API on your own server.
Stripe account required
Hard requirement
You must use your own Stripe account. WebToNative does not provide or manage Stripe accounts, Stripe Dashboard configuration, or merchant onboarding.
Country restrictions
Verify first
Stripe Terminal and Tap to Pay are only available in Stripe-supported countries. Check stripe.com for the current list before purchasing.
No refund processing
The SDK does not process refunds. Refunds must be handled by your backend using Stripe's refund APIs.
No webhook replacement
The SDK does not replace Stripe Webhooks. Your backend should process Stripe Webhooks independently for reliable payment status updates.
Real device required for testing
Final testing must be done on a supported physical device. Simulator and emulator testing has limited support use isSimulated: true for development only.
Internet connection required
Most payment operations require an active internet connection. Limited offline support depends on Stripe Terminal's own offline capabilities and your implementation.
How It Works
Your website calls
makeTapToPay()with a Stripe Terminal connection token, an amount/currency (or an existing PaymentIntent'sclientSecret), and a Stripe Terminal Location ID.The native SDK initializes Stripe Terminal, discovers a Tap to Pay reader on the device, and connects to it.
The SDK creates (or retrieves) a PaymentIntent, then prompts the customer to tap their card, phone, or wallet on the back of the device.
Stripe confirms the payment, and the result is returned to your
callback.
Platform difference: On iOS, the SDK only supports retrieving an existing PaymentIntent you must create the PaymentIntent on your backend first and pass its clientSecret. On Android, clientSecret is optional: if you omit it, the SDK creates the PaymentIntent itself from amount and currency. To keep behaviour identical across platforms, always create the PaymentIntent on your backend and pass clientSecret.
Setting Up Stripe Tap to Pay
1. Set Up Your Stripe Account
Create or use an existing account at stripe.com and enable Stripe Terminal.
Create a Location in the Stripe Dashboard (or via the API) and note its Location ID (
tml_...) this is yourstripeLocationId.Apply for the Tap to Pay on iPhone entitlement directly with Apple if you plan to support iOS. This is a manual approval process handled entirely by Apple, WebToNative cannot request or expedite it on your behalf.
2. Build Your Backend Endpoints
The SDK does not talk to Stripe's servers on its own for these two steps your server must:
Connection Token endpoint - a POST endpoint that creates a Stripe Terminal connection token and returns it as JSON:
{ "secret": "<connection_token>" }. Your website fetches this and passes the token asconnectionToken.PaymentIntent endpoint (required for iOS, recommended for Android) - a POST endpoint that creates a PaymentIntent with
payment_method_types: ["card_present"]andcapture_methodset toautomaticormanual, and returns itsclient_secret. Your website passes this asclientSecret.Handle capture (for
manualcapture method) and refunds using Stripe's standard APIs, the SDK does not do this for you.Process Stripe Webhooks independently to keep your own order/payment records in sync the SDK's callback is not a substitute for webhooks.
3. Enable the Add-on in WebToNative
Open your WebToNative Dashboard → Add-ons → Stripe Tap to Pay and purchase/enable it.
Unlike some other add-ons, there is no dashboard configuration screen for Stripe, your Stripe keys, connection tokens, and location IDs are all supplied at runtime from your JavaScript, as shown below.
4. Platform Permissions
WebToNative's Stripe plugin already declares the required permissions and usage-description strings for both platforms. You still need to make sure your app can obtain them at runtime:
Android - the plugin adds ACCESS_FINE_LOCATION, BLUETOOTH_CONNECT, BLUETOOTH_SCAN, and NFC to your manifest. Tap to Pay will fail with LOCATION_PERMISSION_NOT_GRANTED or GPS_NOT_ENABLED if these aren't granted/enabled, prompt the user for location and nearby-devices (Bluetooth) permissions, and ensure GPS is turned on, before calling makeTapToPay.
iOS - the plugin adds NSBluetoothAlwaysUsageDescription and NSBluetoothPeripheralUsageDescription to your Info.plist. The device also needs NFC reading capability, and location services must be enabled and authorized, otherwise the callback returns NO_NFC_SUPPORT_ON_DEVICE, BLUETOOTH_PERMISSION_NOT_GRANTED, GPS_NOT_ENABLED, or LOCATION_PERMISSION_NOT_GRANTED.
JavaScript API Reference
makeTapToPay
Discovers a Tap to Pay reader on the device, connects to it, and collects a payment.
Parameters:
connectionToken
String
Yes
A Stripe Terminal connection token, fetched from your backend's connection-token endpoint before calling makeTapToPay.
stripeLocationId
String
Yes
The Stripe Terminal Location ID (tml_...) to associate the reader with.
clientSecret
String
Required on iOS, optional on Android
The client_secret of a PaymentIntent created on your backend. If provided, the SDK retrieves and confirms that PaymentIntent. On Android only, if omitted, the SDK creates a new PaymentIntent from amount/currency instead.
amount
Number
Yes, unless clientSecret is set
Payment amount in the smallest currency unit (e.g. 1999 = $19.99 USD).
currency
String
Yes, unless clientSecret is set
Three-letter ISO currency code (e.g. "usd").
captureMethod
String
No - default "automatic"
"automatic" or "manual". Ignored when clientSecret is provided, since the capture method is already set on the existing PaymentIntent.
isSimulated
Boolean
No - default false
Use Stripe's simulated reader for development. Must be false for real transactions on a physical device.
apiUrl
String
No
URL of a backend endpoint that returns a Stripe Terminal connection token as { "secret": "..." }. Currently only honored on iOS as a fallback when connectionToken isn't supplied, Android always requires connectionToken. Prefer always passing connectionToken directly.
callback
Function
No
Function invoked with the payment result. See Response below.
Response:
type
String
Always "makeTapToPayStripePayment".
paymentStatus
String
"SUCCESS" or "FAILED".
token
String
The connection token used for this payment.
paymentId
String
The Stripe PaymentIntent ID (e.g. "pi_..."), present when paymentStatus is "SUCCESS".
paymentIntent
String
A stringified representation of the full Stripe PaymentIntent object, present on success.
failureReason
String
Present only when paymentStatus is "FAILED". See Failure Reasons below.
Failure Reasons:
JSON_UNDEFINED
Android
The options passed to makeTapToPay could not be parsed.
GPS_NOT_ENABLED
Android, iOS
Device location services are turned off.
LOCATION_PERMISSION_NOT_GRANTED
Android, iOS
The app does not have location permission.
BLUETOOTH_PERMISSION_NOT_GRANTED
iOS
Bluetooth permission was denied.
NO_NFC_SUPPORT_ON_DEVICE
iOS
The device doesn't support NFC and isSimulated was false.
LOCATION_ID_MISSING
iOS
stripeLocationId was missing.
TOKEN_ISSUE
Android
connectionToken was empty or invalid.
API_CONNECTION_FAILED
Android
The Stripe Terminal SDK could not be initialized with the given token.
INVALID_TOKEN
Android
Stripe rejected the connection token.
NO_NEARBY_READER_DEVICE_FOUND
Android
Reader discovery completed without finding a Tap to Pay reader.
READER_FAILURE - <code> / other message
Android, iOS
Reader connection, card collection, or payment confirmation failed the value contains the underlying Stripe Terminal SDK error.
Typical Implementation Flow
Create a PaymentIntent call your backend to create a PaymentIntent for the order total, and get back its
client_secret.Fetch a connection token call your backend's connection-token endpoint to get a fresh Stripe Terminal
connectionToken.Collect payment call
makeTapToPay()with theconnectionToken,clientSecret, andstripeLocationId, and prompt the customer to tap their card.Handle the result on
"SUCCESS", storepaymentIdagainst the order. On"FAILED", inspectfailureReasonand show the customer an appropriate retry message.Reconcile via webhooks use Stripe Webhooks on your backend as the source of truth for payment status, independent of the app callback.
Implementation Checklist
Stripe Dashboard
Your Backend
WebToNative Dashboard
Your Website
Frequently Asked Questions
Do I need my own Stripe account?
Yes. WebToNative does not provide, manage, or proxy a Stripe account for you, you supply your own Stripe keys, connection tokens, and PaymentIntents from your own backend.
Will this work in production on iPhone without any extra steps?
No. Tap to Pay on iPhone requires Apple's explicit entitlement approval for your app. Purchasing this WebToNative add-on does not guarantee or expedite that approval, you must apply directly with Apple.
Can I test without a physical reader-capable device?
Partially. Pass isSimulated: true to use Stripe's simulated reader during development. Final testing before release must be done on a real, supported device, simulator/emulator support is limited.
Does the SDK handle refunds or webhooks?
No. Refunds must be processed by your backend using Stripe's refund APIs, and you should independently process Stripe Webhooks on your backend for reliable payment status, the JavaScript callback is not a substitute for either.
Why does iOS require `clientSecret` but Android doesn't?
The current iOS implementation only supports retrieving an existing PaymentIntent by client_secret; it does not create one from amount/currency on-device. Android can create a PaymentIntent directly from amount/currency if clientSecret is omitted. To keep your integration consistent across both platforms, always create the PaymentIntent on your backend and pass clientSecret.
What happens if the customer's card is declined?
The callback fires with paymentStatus: "FAILED" and a failureReason describing the underlying Stripe Terminal error (for example, a decline reason from Stripe's API). Show the customer a retry option.
Last updated