Tap to Pay
Tap to Pay collects contactless payments on a compatible phone or tablet without a separate card reader. Use TerminalConnectTypes.TapToPay after configuration and a working connection token.
The official demo exercises Tap to Pay, Internet, and Bluetooth in demo/angular.
Platform prerequisites
| Platform | Supported | Notes |
|---|---|---|
| Android | Yes | NFC-capable device, location permission, Stripe Tap to Pay on Android eligibility. minSdkVersion 26. |
| iOS | Yes | Tap to Pay on iPhone, iOS 16.4+ for the account-link check. setTapToPayUxConfiguration is unimplemented. |
| Web | No | discoverReaders({ type: TapToPay }) is unavailable. |
Complete Stripe Dashboard Terminal setup and create a Location. Pass that locationId into discoverReaders; the plugin uses it when connecting the Tap to Pay reader.
Android initialize requests the location permission listed in Configuration. Bluetooth permissions are requested only when discovering Bluetooth or Simulated readers; Tap to Pay discovery itself does not request them.
Setup sequence
- Register application-level listeners.
- Register an authenticated connection-token provider with
RequestedConnectionToken+setConnectionToken, then callinitialize. - On iOS, call
isTapToPayAccountLinked(do not cache the result). - On Android, optionally call
setTapToPayUxConfiguration. discoverReaderswithtype: TerminalConnectTypes.TapToPayandlocationId.connectReaderwith the discovered reader.- Collect and confirm a
card_presentPaymentIntent as in Collect a Payment.
method initialize(...)
initialize(options: { tokenProviderEndpoint?: string; isTest: boolean; }) => Promise<void>
Account-link check
isTapToPayAccountLinked is iOS only and requires iOS 16.4 or later. initialize() must have run so the SDK has a connection token provider. No reader connection is required and the call does not activate NFC.
The answer is read from Apple on every call. Do not cache isLinked. For Stripe Connect, pass onBehalfOf as the connected account ID; omit it to check the account that owns the API key.
Android and web reject the call (unimplemented / unavailable). Guard with a platform check or .catch() like the official demo does for Android-only UX configuration.
method isTapToPayAccountLinked(...)
Check whether the merchant has accepted Apple's Tap to Pay on iPhone
Terms and Conditions.
iOS only, and requires iOS 16.4 or later. initialize() must have been
called first because the SDK needs a connection token provider, but no
reader connection is required and the call does not activate the device.
The answer is read from Apple on every call. Apple's Tap to Pay on iPhone
requirements state that acceptance state must be retrieved from Apple
rather than from a local variable, so do not cache the result.
Stripe docs reference
isTapToPayAccountLinked(options?: IsTapToPayAccountLinkedOptions | undefined) => Promise<{ isLinked: boolean; }>
interface IsTapToPayAccountLinkedOptions
Options for isTapToPayAccountLinked.
| Prop | Type | Description |
|---|---|---|
onBehalfOf |
string |
Connected account ID, for Stripe Connect platforms. Omit to check the account that owns the API key. |
UX configuration
setTapToPayUxConfiguration is Android only. Call it after initialize() and before connectReader(). iOS returns unimplemented; web logs and returns.
The installed Android implementation applies colors (primary, success, error as 'default' or a hex string such as '#FF5733') and darkMode (SYSTEM, DARK, LIGHT). The TypeScript tapZone field is declared but not applied on the current Android Terminal SDK used by v8.2.0.
method setTapToPayUxConfiguration(...)
Configure the Tap to Pay UX appearance (Android only).
Call this after initialize() but before connectReader().
Has no effect on iOS or web platforms.
setTapToPayUxConfiguration(options: TapToPayUxConfiguration) => Promise<void>
interface TapToPayUxConfiguration
Configuration for the Tap to Pay UX (Android only).
| Prop | Type | Description |
|---|---|---|
colors |
TapToPayColorScheme |
Color scheme for the Tap to Pay screen |
darkMode |
TapToPayDarkMode |
Dark mode setting |
tapZone |
TapToPayTapZone |
Tap zone position configuration |
interface TapToPayColorScheme
Color scheme for the Tap to Pay screen.
| Prop | Type | Description |
|---|---|---|
primary |
TapToPayColor |
Primary color (tap zone indicator). Hex string or 'default'. |
success |
TapToPayColor |
Success state color. Hex string or 'default'. |
error |
TapToPayColor |
Error state color. Hex string or 'default'. |
type alias TapToPayColor
'default' | string
type alias TapToPayTapZone
{ type: 'default' } | { type: 'front'; xBias: number; yBias: number } | { type: 'behind'; xBias: number; yBias: number } | { type: 'above'; bias?: number } | { type: 'below'; bias?: number } | { type: 'left'; bias?: number } | { type: 'right'; bias?: number }
enum TapToPayDarkMode
| Member | Value |
|---|---|
System |
'SYSTEM' |
Dark |
'DARK' |
Light |
'LIGHT' |
Discover and connect
Discover with TerminalConnectTypes.TapToPay and a locationId. Simulated Tap to Pay uses isTest: true on initialize, not TerminalConnectTypes.Simulated.
Connect the reader from the discovery result. autoReconnectOnUnexpectedDisconnect defaults to false and is supported for Tap to Pay. On iOS, merchantDisplayName and onBehalfOf are passed into the Tap to Pay connection configuration. On Android, set those values on the PaymentIntent instead.
method discoverReaders(...)
discoverReaders(options: DiscoverReadersOptions) => Promise<{ readers: ReaderInterface[]; }>
method connectReader(...)
connectReader(options: { reader: ReaderInterface; autoReconnectOnUnexpectedDisconnect?: boolean; merchantDisplayName?: string; onBehalfOf?: string; }) => Promise<void>
After connect, use collectPaymentMethod and confirmPaymentIntent with a server-created card_present PaymentIntent.
Limitations
- Web cannot discover or connect Tap to Pay.
- UX colors and dark mode are Android-only; iOS uses the system Tap to Pay on iPhone UI.
- Account-link status is iOS-only and must be re-fetched from Apple each time.
tapZoneis part of the TypeScript API but is not wired through on the installed Android SDK.- Optional reader software updates still follow Reader Lifecycle rules: do not install during checkout.
- Keep Stripe secret keys and connection-token creation on the backend.
import {
StripeTerminal,
TapToPayDarkMode,
TerminalConnectTypes,
} from '@capacitor-community/stripe-terminal';
import { Capacitor } from '@capacitor/core';
// Register the authenticated RequestedConnectionToken provider first.
await StripeTerminal.initialize({ isTest: true });
if (Capacitor.getPlatform() === 'ios') {
const { isLinked } = await StripeTerminal.isTapToPayAccountLinked();
console.log(isLinked);
}
if (Capacitor.getPlatform() === 'android') {
await StripeTerminal.setTapToPayUxConfiguration({
colors: { primary: '#FF5733' },
darkMode: TapToPayDarkMode.Light,
});
}
const { readers } = await StripeTerminal.discoverReaders({
type: TerminalConnectTypes.TapToPay,
locationId: '**************',
});
const reader = readers[0];
if (!reader) throw new Error('Tap to Pay is not available on this device');
await StripeTerminal.connectReader({
reader,
autoReconnectOnUnexpectedDisconnect: true,
});