Skip to main content

Accept Apple Pay™

Decrypt and vault Apple Pay™ tokens to route one-time and recurring payments to any processor without touching raw card data.

When a customer authorizes an Apple Pay™ payment, their device produces an encrypted payload containing a token and a one-time cryptogram. Basis Theory decrypts the payload, vaults the credentials, and gives you a token reference you can route to any processor via the Basis Theory Proxy.

Accept Apple Pay™ payment flow

Token Types​

Apple returns one of two token types depending on how you configure the payment request and whether the payment network supports it:

  • Device-specific payment token (DPAN) — a device-scoped token. The DPAN persists on the device and is reused across transactions, but each authorization generates a unique one-time cryptogram. This is the default token type. DPANs can be used for recurring payments, but because they are tied to a specific device, they can be deactivated if the customer switches devices or removes the card from their wallet.

  • Merchant token (MPAN) — a merchant-scoped token tied to a business and customer rather than a device. Apple recommends merchant tokens for recurring payment scenarios because they work across all of the customer's devices, enable recurring payments independent of a device, and remain active even when removed from a lost or stolen device.

Both token types follow the same implementation flow — the only difference is how you configure the payment request upfront. The tokenization response includes a type field (dpan or mpan) so your backend knows which was issued.

Requesting an MPAN​

To receive an MPAN instead of a DPAN, your payment request must include one of Apple's recurring payment configurations. Apple's documentation covers the required fields and payment sheet behavior for each scenario:

  • Recurring Payments (subscriptions, memberships): Web | iOS
  • Automatic Reload (balance top-ups): Web | iOS
  • Deferred Payments (pre-orders, delayed charges): Web | iOS

All three configurations share these fields:

  • managementURL (required) — a URL on your site where the customer can manage their recurring payment.
  • tokenNotificationURL (optional but recommended) — the URL Apple calls when lifecycle events affect the merchant token, so Basis Theory can keep the token current. The URL depends on your setup:
    • Managed: https://api.basistheory.com/apple-pay/<YOUR_TENANT_ID>
    • BYOK: https://api.basistheory.com/apple-pay/<YOUR_TENANT_ID>/merchants/<YOUR_MERCHANT_REGISTRATION_ID>. BYOK merchant tokens are issued under your own Apple merchant identity, so the URL must carry your merchant_registration_id (from BYOK setup) for Basis Theory to fetch the events. If a BYOK token uses the managed URL, its events are dropped.

If the payment network does not support MPAN generation for the transaction, a DPAN is returned instead. Your backend should check the type field and handle both cases — the tokenization and payment processing flows are identical regardless of which type is issued.

Getting Started​

To get started, you will need to create a Basis Theory Account and a TEST Tenant.

Be sure to use your work email (e.g., john.doe@yourcompany.com)
Ensure you have completed the Apple Pay™ Setup guide. This step is only required once.

Public Application​

You will need a Public Application to authenticate requests. Click here to create one using the Basis Theory Customer Portal.

This will create an application with the following Access Controls:

  • Permissions: apple-pay:session, apple-pay:create
Save the key from the created Public Application as it will be used later in this guide.

Private Application​

You will need a Private Application to allow your backend to call Basis Theory APIs. Click here to create one using the Basis Theory Customer Portal.

This will create an application with the following Access Controls:

  • Permissions: proxy:invoke
Save the key from the created Private Application as it will be used later in this guide.

Create a Session (Web)​

When the customer taps the Apple Pay button on your website, your page must initiate merchant validation before the payment sheet appears. Handle the onvalidatemerchant event by calling the Create Merchant Session endpoint.

<html>
<head>
<script crossorigin src="https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js"></script>
<style>
apple-pay-button {
--apple-pay-button-width: 194px;
--apple-pay-button-height: 64px;
--apple-pay-button-border-radius: 13px;
--apple-pay-button-padding: 0px 0px;
--apple-pay-button-box-sizing: border-box;
}
</style>
<script>
function onApplePayButtonClicked() {
if (!ApplePaySession) {
return;
}

const session = new ApplePaySession(3, {
countryCode: "US",
currencyCode: "USD",
merchantCapabilities: ["supports3DS", "supportsCredit", "supportsDebit"],
supportedNetworks: ["visa", "masterCard", "amex", "discover"],
total: {
label: "My Store",
type: "final",
amount: "1.99"
},
// MPAN only: include one of the following to request a merchant token.
// See "Requesting an MPAN" above for details. The tokenNotificationURL below is the
// managed URL; BYOK integrations append /merchants/<YOUR_MERCHANT_REGISTRATION_ID>.
// recurringPaymentRequest: {
// paymentDescription: "Monthly Premium Subscription",
// regularBilling: { label: "Monthly Premium", amount: "9.99", type: "final" },
// managementURL: "https://yoursite.com/manage-subscription",
// tokenNotificationURL: "https://api.basistheory.com/apple-pay/<YOUR_TENANT_ID>"
// }
// automaticReloadPaymentRequest: {
// paymentDescription: "Automatic Account Reload",
// automaticReloadBilling: { label: "Account Reload", amount: "25.00", type: "final" },
// managementURL: "https://yoursite.com/manage-auto-reload",
// tokenNotificationURL: "https://api.basistheory.com/apple-pay/<YOUR_TENANT_ID>"
// }
// deferredPaymentRequest: {
// paymentDescription: "Pre-order Payment",
// deferredBilling: { label: "Item Charge", amount: "149.99", type: "final" },
// managementURL: "https://yoursite.com/manage-preorders",
// tokenNotificationURL: "https://api.basistheory.com/apple-pay/<YOUR_TENANT_ID>"
// }
});

session.onvalidatemerchant = async (event) => {
const merchantSession = await createSession();
session.completeMerchantValidation(merchantSession);
};

session.begin();
}

async function createSession() {
const response = await fetch("https://api.basistheory.com/apple-pay/session", {
method: "POST",
headers: {
"Content-Type": "application/json",
"BT-API-KEY": "<PUBLIC_APPLICATION_KEY>"
},
body: JSON.stringify({
display_name: "My Store",
domain: window.location.host,
// BYOK only: include the merchant_registration_id from Setup (BYOK) step 3
// merchant_registration_id: "<MERCHANT_REGISTRATION_ID>"
})
});

if (!response.ok) {
throw new Error(`Session request failed: ${response.status}`);
}

return response.json();
}
</script>
</head>
<body>
<apple-pay-button buttonstyle="black" type="plain" locale="en-US"></apple-pay-button>
<script>
document.querySelector("apple-pay-button")
.addEventListener("click", onApplePayButtonClicked);
</script>
</body>
</html>

This step is web-only. iOS apps using PassKit skip merchant validation entirely — Apple enforces the merchantIdentifier against your app's Merchant IDs Entitlement at payment sheet presentation, which removes the need for a server-side session handshake.

Tokenize​

After the customer authorizes the payment, Apple passes the encrypted payment token to your handler. Send it to the Tokenize endpoint so Basis Theory can decrypt and vault the credentials.

Add the onpaymentauthorized handler to the session you created in the previous step:

session.onpaymentauthorized = async (event) => {
try {
const applePayToken = await tokenize(event.payment.token);
session.completePayment(ApplePaySession.STATUS_SUCCESS);
// send applePayToken.id to your backend for processing
await chargePayment(applePayToken.id);
} catch (e) {
console.error(e);
session.completePayment(ApplePaySession.STATUS_FAILURE);
}
};

async function tokenize(paymentToken) {
const response = await fetch("https://api.basistheory.com/apple-pay", {
method: "POST",
headers: {
"Content-Type": "application/json",
"BT-API-KEY": "<PUBLIC_APPLICATION_KEY>"
},
body: JSON.stringify({
apple_payment_data: {
paymentData: paymentToken.paymentData,
transactionIdentifier: paymentToken.transactionIdentifier,
},
// BYOK only: include the merchant_registration_id from Setup (BYOK) step 3
// merchant_registration_id: "<MERCHANT_REGISTRATION_ID>"
})
});

if (!response.ok) {
throw new Error(`Tokenization failed: ${response.status}`);
}

const data = await response.json();
return data.apple_pay;
}

The response includes an apple_pay object. Save the id and pass it to your backend to process the payment. Decrypted card data is never returned to the client — it is only accessible server-side through proxy expressions when forwarding the payment to your processor.

Response
{
"apple_pay": {
"id": "4bdbd69e-2678-4c1e-8650-26e3e6a98949",
"type": "dpan",
"authentication": {
"eci_indicator": "07",
"threeds_cryptogram": "AAAAAA..."
}
}
}

First Charge​

The first transaction after tokenization is a customer-initiated transaction (CIT) that consumes the one-time cryptogram included with the DPAN or MPAN. Process this charge immediately — the cryptogram expires quickly, and if it expires before the first charge, subsequent merchant-initiated transactions linked to it will also fail.

From your backend, invoke an ephemeral Proxy to forward the decrypted credentials to your payment processor. The proxy resolves the Apple Pay token references in the request body before forwarding to the processor.

The cryptogram is one-time use and expires quickly. If the first charge is not immediate (e.g., free trials, deferred payments), use a $0 authorization or setup transaction to consume the cryptogram before it expires. Consult your processor's documentation for the equivalent operation.

In Checkout.com, you can charge a card by calling the /payments API.

Request a Payment
curl 'https://api.basistheory.com/proxy' \
-X 'POST' \
-H 'BT-API-KEY: <PRIVATE_API_KEY>' \
-H 'BT-PROXY-URL: https://<CHECKOUT_PREFIX>.api.sandbox.checkout.com/payments' \
-H 'Authorization: Bearer <CHECKOUT_AUTH_TOKEN>' \
-H 'Content-Type: application/json' \
--data '{
"source": {
"type": "network_token",
"token_type": "applepay",
"token": "{{ apple_pay: <APPLE_PAY_TOKEN_ID> | json: \"$.data.number\" }}",
"expiry_month": "{{ apple_pay: <APPLE_PAY_TOKEN_ID> | json: \"$.data\" | card_exp: \"MM\" }}",
"expiry_year": "{{ apple_pay: <APPLE_PAY_TOKEN_ID> | json: \"$.data\" | card_exp: \"YYYY\" }}",
"eci": "{{ apple_pay: <APPLE_PAY_TOKEN_ID> | json: \"$.authentication.eci_indicator\" }}",
"cryptogram": "{{ apple_pay: <APPLE_PAY_TOKEN_ID> | json: \"$.authentication.threeds_cryptogram\" }}"
},
"amount": 1000,
"currency": "USD",
"processing_channel_id": "<CHECKOUT_PROCESSING_CHANNEL_ID>"
}'
Payment Response
{
"id": "pay_myvj6zxrt45edgrswrliyxbtnq",
"approved": true,
"status": "Authorized",
"amount": 1000,
"currency": "USD",
"source": {
"type": "network_token",
"id": "src_gguer4xlawke3ctljczft7573a",
...
},
"scheme_id": "647537243670773",
...
}

Beware:

  • Some payment processors require you to explicitly enable decrypted Apple Pay acceptance on your account before they will accept these payloads. Confirm with your processor before going live.
  • You are not limited to the processors listed above. Any processor that accepts decrypted Apple Pay credentials can be reached through the ephemeral proxy.
  • Store the network transaction ID: processors need this to link subsequent merchant-initiated transactions to the original CIT and achieve the best approval rates.

Subsequent Charges​

Once the first charge has consumed the cryptogram, you can process merchant-initiated transactions (MITs) for recurring billing, subscriptions, or follow-up charges without the customer being present. These transactions reuse the Apple Pay token with the network transaction ID from the original CIT — no new cryptogram is needed.

Apple Pay terms require a new authorization and cryptogram for each transaction where the customer is present (on-session). You cannot reuse a saved token for on-session payments — only for merchant-initiated transactions when the customer is not in the checkout flow.

In Checkout.com, you can charge a saved card by calling the /payments API. Pass the network transaction ID from the first charge into the previous_payment_id field.

Request a Payment
curl 'https://api.basistheory.com/proxy' \
-X 'POST' \
-H 'BT-API-KEY: <PRIVATE_API_KEY>' \
-H 'BT-PROXY-URL: https://<CHECKOUT_PREFIX>.api.sandbox.checkout.com/payments' \
-H 'Authorization: Bearer <CHECKOUT_AUTH_TOKEN>' \
-H 'Content-Type: application/json' \
--data '{
"source": {
"type": "network_token",
"token_type": "applepay",
"token": "{{ apple_pay: <APPLE_PAY_TOKEN_ID> | json: \"$.data.number\" }}",
"expiry_month": "{{ apple_pay: <APPLE_PAY_TOKEN_ID> | json: \"$.data\" | card_exp: \"MM\" }}",
"expiry_year": "{{ apple_pay: <APPLE_PAY_TOKEN_ID> | json: \"$.data\" | card_exp: \"YYYY\" }}",
"stored": true
},
"amount": 1000,
"currency": "USD",
"processing_channel_id": "<CHECKOUT_PROCESSING_CHANNEL_ID>",
"previous_payment_id": "<NETWORK_TRANSACTION_ID>"
}'

You are not limited to the processors listed above. Any processor that accepts decrypted Apple Pay credentials can be reached through the ephemeral proxy.

FAQ​

Common questions about token types, testing, ECI handling, recurring payments and processor behavior are answered in the Apple Pay™ FAQ.