Apple Pay Integration Guide
Accept native in-app Apple Pay in your iOS app. Your app produces an encrypted Apple Pay token; you send it to TruePay's wallet-pay endpoint, and TruePay decrypts it with your own Payment Processing certificate and charges the card. Your app never handles the card number, and you never share a certificate with anyone but TruePay.
You keep your own Apple Merchant ID and certificates. TruePay is your payment processor — it decrypts the token and settles through the acquirer.
What you need
- An Apple Developer account (Apple Pay requires a paid membership).
- An Apple Merchant ID (e.g.
merchant.com.yourcompany.app) and its
Apple Pay Payment Processing certificate.
- Your TruePay merchant account with the Apple Pay (wallet) capability
enabled and your API key/secret.
Step 1 — Create the Merchant ID + Payment Processing certificate (Apple)
- Apple Developer → **Certificates, Identifiers & Profiles → Identifiers →
Merchant IDs** → create one (e.g. merchant.com.yourcompany.app).
- On that Merchant ID, create an Apple Pay Payment Processing Certificate.
- In Keychain Access, export it (and its private key) as a
.p12file.
Remember the export password (or leave it blank).
Step 2 — Upload your certificate to TruePay
In the Merchant Portal → Apple Pay:
- Apple merchant identifier — the Merchant ID from Step 1 (must match the certificate).
- Payment Processing certificate — upload the
.p12(+ its password, if any). - Enable Apple Pay, and Save.
Only the Payment Processing certificate is required for in-app payments. The Merchant Identity certificate is optional (it's only for Apple Pay on the web).
Step 3 — Add Apple Pay to your iOS app
Use PassKit (PKPaymentAuthorizationController) with your Merchant ID. A complete, ready-to-run sample app is available on the developer portal — see the iOS sample source. Add your Config.swift values (TruePay base URL + API key/secret + your Apple merchant id), set your Team and the Apple Pay capability, and run on a real device.
Step 4 — Send the token to TruePay
Once the customer authorizes, POST the encrypted Apple Pay token to wallet-pay. Authenticate with your API credentials.
POST /api/v1/transactions/wallet-pay
OMPAY-API-Key: <your key>
OMPAY-API-Secret: <your secret>
Content-Type: application/json
{
"amount": 7.250,
"currency": "OMR",
"reference_number": "order-1042",
"wallet": {
"type": "APPLE_PAY",
"payment_token": {
"paymentData": { "version": "EC_v1", "data": "…", "header": { … }, "signature": "…" },
"paymentMethod": { "displayName": "Visa 1234", "network": "Visa", "type": "debit" },
"transactionIdentifier": "…"
}
}
}payment_token is the full PKPaymentToken from PassKit (the paymentData is the encrypted part — do not decrypt it yourself; TruePay does that with your certificate).
Response
{
"success": true,
"data": {
"transaction_id": "…",
"reference_number": "order-1042",
"status": "SUCCESSFUL",
"rrn": "…",
"error_message": null
}
}status is SUCCESSFUL on capture, otherwise FAILED with an error_message.
Step 5 — Confirm the outcome server-to-server
Never trust a client result as final. Confirm from your backend:
POST /api/v1/transactions/inquiry
{ "transaction_id": "…" } // or { "reference_number": "order-1042" }Apple Pay on your own website
Everything above is the in-app (PassKit) flow. Apple Pay on your own site is the same payment call with one extra step in front of it: merchant validation. PassKit has no such step, which is why an in-app integration that works gives you nothing on the web — without it the sheet never fires onpaymentauthorized and you never get a payment token.
What you need first
- Your own Apple Pay merchant identifier, with your domain registered and verified
against it in your Apple Developer account. Apple enforces this; TruePay does not check domains.
- A Merchant Identity certificate for that merchant identifier, set up in the merchant
portal (Apple Pay → Merchant Identity: generate a CSR, enroll it with Apple, upload the .cer). TruePay holds the key and makes the call — it is never downloaded.
- Your Payment Processing certificate. You may reuse the one your app uses, or enroll
a separate one for the web. If you enroll a separate one, add it in the merchant portal under *Additional Payment Processing certificates* — TruePay matches each payment to the right certificate by the token's public key hash, so both keep working at once.
Step W1 — Merchant validation
Your page gets a one-time validationURL from Safari. Send it to TruePay from your own backend (it is an authenticated call — never put your API key in the browser) and hand the response straight back to Apple:
POST /api/v1/transactions/apple-pay/validate-merchant
Authorization: <your API key>
{
"validationURL": "https://apple-pay-gateway.apple.com/paymentservices/startSession",
"domain": "shop.example.om"
}domain is the fully-qualified host serving the button. The response is Apple's merchant session, returned verbatim — pass it to completeMerchantValidation() unchanged.
session.onvalidatemerchant = async (event) => {
const r = await fetch('/your-backend/apple-pay/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ validationURL: event.validationURL }),
});
session.completeMerchantValidation(await r.json());
};Step W2 — The payment
Identical to the app. onpaymentauthorized gives you event.payment.token — send it to your backend and post it to the same endpoint your app already uses:
session.onpaymentauthorized = async (event) => {
const r = await fetch('/your-backend/apple-pay/pay', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token: event.payment.token }),
});
const result = await r.json();
session.completePayment(result.success
? ApplePaySession.STATUS_SUCCESS
: ApplePaySession.STATUS_FAILURE);
};Your backend then calls, exactly as it does for the app:
POST /api/v1/transactions/wallet-pay
{
"amount": 10.500,
"currency": "OMR",
"reference_number": "order-1042",
"wallet": { "type": "APPLE_PAY", "payment_token": <event.payment.token> }
}payment_token is Apple's token object exactly as the browser gave it to you — the same shape PassKit produces. Don't unwrap it, don't send only paymentData.
If it doesn't work
| What you see | What it usually is |
|---|---|
| The sheet closes immediately, no token | Merchant validation failed. Check the response from Step W1. |
NO_IDENTITY_CERT | The Merchant Identity certificate isn't set up in the merchant portal. |
NO_MERCHANT_ID | Your Apple merchant identifier isn't set in the merchant portal. |
| Apple returns a non-2xx to Step W1 | Usually the domain isn't registered against that merchant identifier, or domain doesn't exactly match the host serving the page. |
DECRYPT_FAILED on Step W2 | The token was encrypted to a Payment Processing certificate TruePay doesn't hold — you enrolled a separate one for the web. Add it in the merchant portal. The error carries the token's public key hash so you can match it against the hashes listed under *Active certificates*. |
Notes
- Currency is OMR (3 decimal places).
- Testing: use a real device with a card in Wallet (the iOS Simulator can't
produce decryptable tokens). Use an Apple Sandbox tester account + Apple's test cards for test money.
- Don't want to build the web flow? TruePay's bank-hosted pay-page already has
Apple Pay in the browser (Safari and, on recent Apple devices, Chrome/Edge/Firefox) on TruePay's own domain — you redirect, we handle validation and the sheet. No certificates, no merchant identifier, no domain registration. Ask us to enable the bank-hosted checkout.