Apple Pay

Accept Apple Pay for fast, secure checkout on Safari and iOS devices.
View as Markdown

Apple Pay lets customers pay using cards saved to their Apple Wallet with a single tap or glance.

Prerequisites

PaymentKit.js prepares Apple Pay automatically when it initializes — the recommended flow has no manual prepare step. You reveal your button once the SDK reports Apple Pay is ready (see Show the express button), and the customer’s tap submits the payment directly.

Auto-prepare only succeeds when the checkout session’s account has an express-checkout processor configured with Apple Pay enabled. If none is configured, the SDK stays in the not-ready state and your onApplePayReady callback reports false — so your button simply never appears.

Add the browser script for your processor:

Add Stripe.js to your page:

1<script src="https://js.stripe.com/v3/"></script>

Airwallex processors must also have Apple Pay activated in your Airwallex dashboard. Enabling Apple Pay in your PaymentKit dashboard alone is not enough — Apple Pay must be enabled as a payment method on your Airwallex account (including any required domain registration) before it can be tested or charged. If it isn’t activated on the Airwallex side, the Apple Pay sheet will fail to complete even when your PaymentKit configuration is correct.

Setup

1<script src="https://unpkg.com/@payment-kit-js/vanilla/dist/cdn/paymentkit.min.js"></script>
2<script>
3 const paymentKit = PaymentKit.default({
4 environment: 'production',
5 secureToken: 'your_secure_token',
6 paymentMethods: [PaymentKit.PaymentMethods.applePay]
7 });
8</script>

Choose the wallet processor

By default the SDK auto-selects your account’s express-checkout processor for each wallet. If you have more than one account (Stripe or Airwallex) and want a specific one to handle a wallet, pass processorIds when you construct PaymentKit — a map of payment method to processor id. The SDK then mints and charges that wallet on the exact account you name (Stripe PaymentMethods are account-scoped, so mint and charge must be the same account — this keeps them aligned).

Pass ids only; the processor family (Stripe/Airwallex) is resolved from the account. Apple Pay and Google Pay are set independently:

1const paymentKit = PaymentKit({
2 environment: 'production',
3 secureToken: 'your_secure_token',
4 paymentMethods: [ApplePayPaymentMethod],
5 processorIds: {
6 applePay: 'proc_prod_1e769fc8bf77493c', // EU account
7 },
8});

processorIds is read once, when PaymentKit is constructed, and applies for that instance’s lifetime. To steer wallets per checkout — e.g. the EU account for EU shoppers, the US account for US shoppers — compute the id and pass it when you initialize PaymentKit for that session. A single instance reused across checkouts keeps its original value until you re-init.

1const paymentKit = PaymentKit({
2 environment: 'production',
3 secureToken: 'your_secure_token',
4 paymentMethods: [ApplePayPaymentMethod],
5 processorIds: {
6 applePay: isEuShopper
7 ? 'proc_prod_1e769fc8bf77493c' // EU account
8 : 'proc_prod_53cd0b7dbdc4449e', // US account
9 },
10});

Leave a wallet’s entry out of processorIds (or omit processorIds entirely) to keep the automatic express-checkout selection. The processor family (Stripe or Airwallex) is resolved from your account — the SDK looks the pinned id up in the account’s wallet processors — so pinning an Airwallex processor routes through the Airwallex flow automatically, with no type field to set. If the pinned id isn’t one of the account’s wallet-eligible processors for the session, that wallet simply stays unavailable (its button never appears) rather than failing mid-payment.

Show the express button

The SDK prepares Apple Pay automatically as soon as it initializes — you no longer need to call prepareApplePay yourself. Use onApplePayReady to show or hide your express button based on whether Apple Pay is available, and notifyAmountChanged to re-prepare whenever the amount changes.

React to readiness

Register a callback with onApplePayReady. It fires immediately with the current state, then again on every change (for example when a re-prepare starts or finishes). Register as many callbacks as you need — none are overwritten.

1paymentKit.apple_pay.onApplePayReady((isReady) => {
2 applePayButton.hidden = !isReady;
3});

While the SDK is re-preparing (see below), it reports isReady: false, then true again once the new session is ready — so the same callback naturally disables the button during the gap.

Re-prepare after the amount changes

Apple Pay sessions are pinned to a specific amount. Whenever the cart total changes — a coupon is applied, quantity is updated, shipping is added — call notifyAmountChanged() so the SDK clears the stale session and prepares a fresh one. It returns a promise that resolves once the new session is ready.

1await paymentKit.apple_pay.notifyAmountChanged();

Your onApplePayReady callback fires across the re-prepare (false while in flight, then true), so the button hides and reappears on its own. Calling it again while a prepare is already running coalesces the calls — the promise resolves once the latest prepare completes.

onApplePayReady and notifyAmountChanged are methods on paymentKit.apple_pay. They are the recommended way to drive the express button — the manual prepareApplePay flow below is retained for backward compatibility.

Prepare Apple Pay (legacy)

prepareApplePay is deprecated. The SDK now prepares automatically on init — observe readiness with onApplePayReady and re-prepare with notifyAmountChanged instead (see above). This section documents the older manual flow.

If you opt into the legacy manual flow, call prepareApplePay before showing the Apple Pay button to check availability and pre-initialize the session — this must happen before the user clicks. With auto-prepare (the recommended flow above), you don’t call this at all.

1import { prepareApplePay, isApplePayPrepared, clearPreparedApplePay } from '@payment-kit-js/vanilla/payment-methods/apple-pay';
2
3const result = await prepareApplePay(
4 'https://app.paymentkit.com', // API base URL
5 'your_secure_token',
6 {
7 processorId: 'proc_abc123',
8 processorType: 'airwallex', // Required for Airwallex processors
9 customerInfo: {
10 first_name: 'Jane',
11 last_name: 'Smith'
12 },
13 country: 'US'
14 },
15 'production' // environment
16);
17
18if (result.success) {
19 // Show Apple Pay button
20}

Airwallex processors: You must pass processorType: 'airwallex' in both prepareApplePay and submit options. Without this, the SDK will route the payment through the Stripe flow instead of Airwallex, causing a backend error. Stripe processors work without processorType.

Submit payment

Attach submit to the same button you reveal with onApplePayReady. In the auto-prepare flow the SDK fills processorId, processorType, and country from the prepared session, and the Apple Pay sheet supplies the final payer details — so you don’t pass them.

1applePayButton.addEventListener('click', () => {
2 paymentKit.submit({
3 fields: {},
4 paymentMethod: 'apple_pay',
5 options: {
6 customerInfo: {
7 first_name: 'Jane',
8 last_name: 'Smith'
9 }
10 },
11 onSuccess: (result) => {
12 console.log('Transaction ID:', result.transaction_id);
13 console.log('Checkout Session:', result.checkout_session_id);
14 window.location.href = '/success';
15 },
16 onError: (errors) => {
17 if (errors.apple_pay) {
18 // Surface the error or fall back to the card form
19 }
20 }
21 });
22});

If you use the legacy manual flow, pass processorId, processorType ("airwallex" for Airwallex), and country in options to match what you passed to prepareApplePay.

Which Stripe account handles the wallet. For Stripe, the Apple Pay PaymentMethod is minted in the browser on the processor used to prepare the sheet, and Stripe PaymentMethods are account-scoped — so the payment is always charged on that same processor. Choose the account with the processorIds init option (or the account’s express-checkout processor when unset). A different processorId passed at submit is ignored for an already-prepared sheet — the SDK keeps mint and charge on one account and logs a warning.

Cleanup

In the auto-prepare flow the SDK manages prepared state per instance, so no manual cleanup call is required. If you use the legacy prepareApplePay flow, clear the prepared state when unmounting or navigating away:

1clearPreparedApplePay();

Options

Submit options

In the auto-prepare flow, processorId, processorType, and country are filled from the prepared session — you only pass them if you use the legacy manual flow.

OptionTypeDescription
processorIdstringThe processor that handles the whole wallet payment — the sheet mints its PaymentMethod on this account and the charge is made on the same one (Stripe PMs are account-scoped). Fixed at prepare time (auto-filled from the prepared session); a different value passed here is ignored for an already-prepared sheet. Required only for the legacy flow.
processorType"stripe" | "airwallex"Auto-filled from the session. For the legacy flow, set to "airwallex" for Airwallex processors.
customerInfo.first_namestringCustomer’s first name. The Apple Pay sheet supplies the final payer name.
customerInfo.last_namestringCustomer’s last name.
countrystringTwo-letter country code (e.g., "US"). Auto-filled from the session in the express flow.
amountnumberAmount in atomic units (cents) for the Apple Pay sheet display
currencystringCurrency code (e.g., "usd")
merchantNamestringMerchant display name shown on the Apple Pay sheet
mockScenarioApplePayMockScenarioTesting only. Use "success" or "cancelled"

prepareApplePay options (legacy)

These apply only to the deprecated manual prepareApplePay flow.

OptionTypeDescription
processorIdstringRequired. The processor that handles the whole wallet payment — the sheet mints on it and the charge is made on it. Pass the same value at submit.
processorType"stripe" | "airwallex"Required for Airwallex. Set to "airwallex" when using an Airwallex processor
customerInfo.first_namestringRequired. Customer’s first name
customerInfo.last_namestringRequired. Customer’s last name
countrystringRequired. Two-letter country code (e.g., "US")
amountnumberAmount in atomic units (cents) for the Apple Pay sheet display
currencystringCurrency code (e.g., "usd")
merchantNamestringMerchant display name shown on the Apple Pay sheet
mockScenarioApplePayMockScenarioTesting only. Use "success" or "cancelled" to simulate Apple Pay flows without a real device

Browser support

Apple Pay works on:

  • Safari on macOS (with Touch ID or a paired iPhone/Apple Watch)
  • Safari on iOS and iPadOS (with Face ID, Touch ID, or passcode)

Apple Pay requires Safari. For Stripe processors, PaymentKit.js uses Stripe’s Payment Request API, which also only surfaces Apple Pay in Safari. For Airwallex processors, the native ApplePaySession API is used directly, which is Safari-only. Always provide card payments as a fallback.

Error handling

In the auto-prepare flow, availability problems surface through onApplePayReady(false) — the button never appears — rather than as submit errors. Processor ID is required means the checkout session has no express-checkout processor configured.

ErrorCause
Processor ID is requiredNo processor available — the session has no express-checkout processor configured (or none passed in the legacy flow)
Apple Pay not available on this device (requires Safari)Airwallex: device or browser does not support Apple Pay (non-Safari)
Apple Pay not available on this deviceAirwallex: canMakePayment() returned false — no cards in wallet or hardware restriction
Apple Pay is not available on this device or Stripe accountStripe: prepareApplePay could not confirm Apple Pay availability via Payment Request API
Stripe.js not loaded. Add <script src="https://js.stripe.com/v3/"></script> to your page.Stripe.js script not present when initializing the Stripe adapter
Apple Pay cancelled by userCustomer dismissed the Apple Pay sheet
Failed to start Apple PayAPI call to the /apple-pay/start endpoint failed
Apple Pay failedAirwallex payment sheet returned a non-cancelled failure
3DS authentication failedAirwallex 3DS challenge completed without success
Too many authentication attempts. Please try again.Airwallex 3DS loop exceeded the maximum retry limit
Payment failedConfirm or verify endpoint returned a non-success charge status
Apple Pay error: {message}Unexpected exception during the payment flow