Integration Guide ← Back to Sandbox

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

  1. An Apple Developer account (Apple Pay requires a paid membership).
  2. An Apple Merchant ID (e.g. merchant.com.yourcompany.app) and its

Apple Pay Payment Processing certificate.

  1. 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)

  1. Apple Developer → **Certificates, Identifiers & Profiles → Identifiers →

Merchant IDs** → create one (e.g. merchant.com.yourcompany.app).

  1. On that Merchant ID, create an Apple Pay Payment Processing Certificate.
  2. In Keychain Access, export it (and its private key) as a .p12 file.

Remember the export password (or leave it blank).

Step 2 — Upload your certificate to TruePay

In the Merchant Portal → Apple Pay:

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

against it in your Apple Developer account. Apple enforces this; TruePay does not check domains.

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.

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 seeWhat it usually is
The sheet closes immediately, no tokenMerchant validation failed. Check the response from Step W1.
NO_IDENTITY_CERTThe Merchant Identity certificate isn't set up in the merchant portal.
NO_MERCHANT_IDYour Apple merchant identifier isn't set in the merchant portal.
Apple returns a non-2xx to Step W1Usually the domain isn't registered against that merchant identifier, or domain doesn't exactly match the host serving the page.
DECRYPT_FAILED on Step W2The 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

produce decryptable tokens). Use an Apple Sandbox tester account + Apple's test cards for test money.

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.