Accept Apple Pay™ Recurring Payments
This guide explains how to use Apple Pay Merchant Tokens (MPANs) with MoneyHash APIs and iOS SDK to enable recurring, unscheduled, and automatic reload payments using Apple Pay network tokens.
Overview
Apple Pay Merchant Tokens (MPANs) allow you to securely reuse Apple Pay network tokens for recurring and merchant-initiated transactions (MITs).
MoneyHash supports this by converting Apple Pay tokens into Universal Card Tokens, which you can use across payment providers.
This flow lets you:
- Tokenize an Apple Pay network token via MoneyHash's iOS SDK.
- Store it as a reusable universal card token in the Vault.
- Charge it for both CIT and MIT unscheduled payments.
Apple provides MPAN tokens for various payment scenarios:
- Recurring Payments - Subscriptions, memberships, and regular billing cycles
- Automatic Reload - Topping up accounts when balances fall below thresholds
- Deferred Payment - Pre-authorization for services delivered at a later date
When using MPANs, the payment flow includes additional configuration in the Apple Pay™ payment request to specify the type of recurring payment and associated parameters.
Getting Started
Prerequisites
Before starting, make sure you have:
- A payment flow that exposes self‑serve Apple Pay as an express method (so Apple Pay appears in expressMethods with nativePayData).
- Integrate MoneyHash iOS SDK v3.0.0+ (for tokenizeReceipt).
- Apple Pay configured in your iOS app with proper entitlements.
- A customer in MoneyHash.
Step-by-Step Integration
Step 1 — From backend create or get customer
- Use the Customer API to either create a new customer (POST /api/v1.4/customers/) or reuse an existing one.
- Store the customer ID. You will:
- send it when creating the card token intent
- reuse it on all CIT / MIT payment intents.
Step 2 — From backend create a card token intent
(send the customer here, and card type universal)
Create a card intent / card token intent so you can tokenize the Apple Pay card into a UNIVERSAL card token. This uses the Card Token APIs mentioned in "Save card for future use".
Example (shape only, exact schema per your internal API):
POST /api/v1.4/tokens/cards/
{
"customer": "<CUSTOMER_ID>",
"card_token_type": "UNIVERSAL", // key bit for cross‑provider usage
"webhook_url": "https://example.com/webhook",
"metadata": {
"source": "apple_pay_network_token"
}
}
You'll get back something like:
{
"id": "<CARD_TOKEN_INTENT_ID>",
...
}
Keep cardTokenIntentId – the SDK will need it when calling moneyHashSDK.tokenizeReceipt.
This is conceptually the same "card intent" used in
Save card for future use
Step 3 — Using SDK get all methods, send the flow here
(keep the returned method id here)
In your iOS app, call getMethods and pass the flowId of a flow that exposes Apple Pay as an express method.
let moneyHashSDK = MoneyHashSDKBuilder().build()
do {
let methods = try await moneyHashSDK.getMethods(
currency: "<currency>",
amount: <amount>,
customer: "<customer_id>",
flowId: "<flow_id>"
)
// Find Apple Pay express method
guard let applePayMethod = methods.expressMethods?.first(where: { method in
method.id == "APPLE_PAY" || method.type == "APPLE_PAY"
}) else {
throw PaymentError.applePayNotAvailable
}
guard let applePayData = applePayMethod.nativePayData as? ApplePayData else {
throw PaymentError.invalidApplePayData
}
// Native Pay config from MoneyHash
// This is what Apple Pay docs refer to as applePayNativeData
let methodId = applePayData.methodID!
} catch {
throw error
}
- Keep methodId (applePayData.methodID):
- It's used today in Apple Pay merchant validation.
- In this new flow you will also pass it to tokenizeReceipt.
You can also optionally use fields from applePayData to pre‑configure the Apple Pay request (amount, supported networks, country, currency) instead of hard‑coding them.
Step 4 — Generate Apple Pay receipt using MoneyHash SDK
Use the MoneyHash SDK's generateApplePayReceipt method with the Apple Pay data from Step 3. This method handles the Apple Pay sheet presentation and processes the payment automatically.
do {
// MoneyHash SDK handles the entire Apple Pay flow including:
// 1. Presenting the Apple Pay sheet
// 2. Handling user authorization
// 3. Processing the payment token
// 4. Returning the receipt for tokenization
let receipt = try await moneyHashSDK.generateApplePayReceipt(
depositAmount: amount,
applePayData: applePayData
)
print("✅ Apple Pay receipt generated successfully")
print(receipt)
} catch {
print("❌ Failed to generate Apple Pay receipt: \(error)")
throw error
}
The MoneyHash SDK's generateApplePayReceipt method automatically:
- Configures the Apple Pay request with the appropriate payment details
- Presents the Apple Pay authorization sheet to the user
- Handles the payment authorization flow
- Processes the Apple Pay token and converts it to a receipt format suitable for tokenization
- Returns an
NativePayReceiptobject that contains the receipt string that can be passed directly totokenizeReceipt
Step 5 — Submit receipt to moneyHashSDK.tokenizeReceipt
(with card token intent id, method id → get card token id back)
Once you have:
- cardTokenIntentId (Step 2),
- methodId (from applePayData.methodID in Step 3), and
- applePayReceipt (Step 4),
call the iOS SDK helper:
do {
let cardTokenId = try await moneyHashSDK.tokenizeReceipt(
receipt: receipt.receipt, // Receipt generated from Apple Pay
methodId: methodId, // applePayData.methodID
cardTokenIntentId: cardTokenIntentId // from backend card intent creation
)
// cardTokenId is a MoneyHash "card token" backed by a network token
print("✅ Card token created: \(cardTokenId)")
print(cardTokenId)
} catch {
print("❌ Failed to tokenize receipt: \(error)")
throw error
}
This mirrors the "Save card for future use" flow (card intent + tokenization), but instead of cardForm.createCardToken, you're feeding in an Apple Pay receipt via tokenizeReceipt.
Keep cardTokenId — it's what you'll pass into the CIT and MIT payment intents.
Step 6 — Create the CIT payment intent
(card token id + customer + CIT data + pay with network token + recurring data, payment type UNSCHEDULED)
Now you create the first unscheduled CIT payment using:
- the Apple Pay card token (cardTokenId),
- payment_type: "UNSCHEDULED",
- "merchant_initiated": false,
- "paying_with_network_token": true,
- an agreement_id in recurring_data.
Example:
POST /api/v1.4/payments/intent/
{
"amount": 20,
"amount_currency": "USD",
"operation": "purchase",
"customer": "8c1a11c0-6ec6-4888-9e5c-8d07d7b5fed0",
"card_token": "<APPLE_PAY_CARD_TOKEN_ID>", // from tokenizeReceipt
"merchant_initiated": false, // CIT
"payment_type": "UNSCHEDULED",
"paying_with_network_token": true,
"recurring_data": {
"agreement_id": "[YOUR_GENERATED_UNSCHEDULED_ID]"
},
"webhook_url": "https://example.com/webhook"
}
Compared to the generic Unscheduled payments – Initial transaction example, the main differences are:
- you already have a card_token from Apple Pay (so you don't need tokenize_card: true here),
- and you explicitly mark this as a network token payment with "paying_with_network_token": true.
The customer still participates actively (CIT), but the credential being used is a stored network token, not a raw PAN.
Step 7 — Create the MIT payment intent
(card token id + customer + MIT data + pay with network token + recurring data, payment type UNSCHEDULED)
Every subsequent charge in the agreement is a MIT unscheduled payment that:
- reuses the same card_token (Apple Pay network token),
- sets "merchant_initiated": true,
- keeps "payment_type": "UNSCHEDULED",
- sets "paying_with_network_token": true,
- and sends the same agreement_id in recurring_data.
Example:
POST /api/v1.4/payments/intent/
{
"amount": 20,
"amount_currency": "USD",
"operation": "purchase",
"customer": "8c1a11c0-6ec6-4888-9e5c-8d07d7b5fed0",
"card_token": "<APPLE_PAY_CARD_TOKEN_ID>",
"merchant_initiated": true, // MIT
"payment_type": "UNSCHEDULED",
"paying_with_network_token": true,
"recurring_data": {
"agreement_id": "[YOUR_GENERATED_UNSCHEDULED_ID]"
},
"webhook_url": "https://example.com/webhook"
}
This is equivalent to "Request an unscheduled payment" in the docs, with the extra hint that:
- the card token is a network token provisioned via Apple Pay, and
- the intent is explicitly flagged with "paying_with_network_token": true.
Updated about 2 hours ago