Why Apple Pay Integration Is Harder Than a Button
Many believe that integrating Apple Pay is simply adding a button. In practice, we encounter a chain of Merchant ID, certificates, PKPaymentAuthorizationController, server-side payment token verification, and final transaction status handling. An error in any link — and either the app won't show the button at all, or it will get a rejection from Apple Pay at the authorization stage. On one project, the client spent a week configuring the Merchant ID, yet the button never appeared — the culprit was an outdated certificate. Up to 10 configuration steps are required, and each step has its pitfalls.
Apple Pay uses device-level encryption — each transaction is unique, as confirmed by the official Apple Pay Security Guide documentation. This requires correct setup both on the client and server sides. In this article, we'll break down the key steps and common issues. Our expertise comes from 20+ successful integrations, from retail to fintech, with up to 70% time savings thanks to our proven solutions. Typical savings compared to in-house development: up to $3,000 and 5x faster deployment.
How to Set Up Merchant ID and Certificates
Everything starts in the Apple Developer Portal:
- Create a merchant identifier:
merchant.com.yourcompany.appname - Generate a Payment Processing Certificate — used by Apple to encrypt the token before sending to your server
- Add a domain for Apple Pay on the Web (if needed) — requires a verification file on the server
In Xcode: Signing & Capabilities → Add Apple Pay → select Merchant ID. Xcode automatically updates the .entitlements file:
<key>com.apple.developer.in-app-payments</key>
<array>
<string>merchant.com.yourcompany.appname</string>
</array>
Without this entitlement, Apple's payment system won't activate on the device, even if the code is written correctly.
PKPaymentRequest and PKPaymentAuthorizationController
import PassKit
class CheckoutViewController: UIViewController {
func startApplePay() {
guard PKPaymentAuthorizationController.canMakePayments(
usingNetworks: [.visa, .masterCard, .mir]
) else {
// Show alternative payment method
return
}
let request = PKPaymentRequest()
request.merchantIdentifier = "merchant.com.yourcompany.appname"
request.supportedNetworks = [.visa, .masterCard, .mir]
request.merchantCapabilities = [.capability3DS]
request.countryCode = "RU"
request.currencyCode = "RUB"
let item = PKPaymentSummaryItem(
label: "Order #1234",
amount: NSDecimalNumber(string: "1500.00")
)
let shipping = PKPaymentSummaryItem(
label: "Delivery",
amount: NSDecimalNumber(string: "250.00")
)
let total = PKPaymentSummaryItem(
label: "YourCompany", // your company name, displayed on Face ID/Touch ID screen
amount: NSDecimalNumber(string: "1750.00")
)
request.paymentSummaryItems = [item, shipping, total]
let controller = PKPaymentAuthorizationController(paymentRequest: request)
controller.delegate = self
controller.present(completion: nil)
}
}
extension CheckoutViewController: PKPaymentAuthorizationControllerDelegate {
func paymentAuthorizationController(
_ controller: PKPaymentAuthorizationController,
didAuthorizePayment payment: PKPayment,
handler completion: @escaping (PKPaymentAuthorizationResult) -> Void
) {
// payment.token.paymentData — encrypted JSON token
// Send to backend for verification
sendTokenToBackend(payment.token.paymentData) { success in
completion(PKPaymentAuthorizationResult(
status: success ? .success : .failure,
errors: nil
))
}
}
func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) {
controller.dismiss(completion: nil)
}
}
Important nuance with .mir: Mir Pay requires separate setup and works only on cards issued by NSPK. Not all acquirers support Mir via Apple Pay — check with your payment provider.
Server-Side Token Verification
payment.token.paymentData is an encrypted JSON that Apple encrypts using your Payment Processing Certificate. Decryption happens on the server.
Token structure:
{
"version": "EC_v1",
"data": "base64-encrypted-payment-data",
"signature": "base64-pkcs7-signature",
"header": {
"ephemeralPublicKey": "base64-ec-public-key",
"publicKeyHash": "base64-sha256-hash",
"transactionId": "hex-transaction-id"
}
}
Decryption process:
- Verify signature using Apple Root CA
- Recover shared secret via ECDH (your private key + ephemeralPublicKey from token)
- Derive symmetric key via HKDF
- Decrypt
datausing AES-256-GCM
In practice, most payment providers (Stripe, CloudPayments, YooKassa) handle decryption — you just pass them the raw paymentData. Self-implementation is only needed for direct acquiring. Let's compare approaches:
| Aspect | Manual Decryption | Via Payment Provider |
|---|---|---|
| Complexity | High: ECDH, HKDF, AES-GCM | Low: pass paymentData |
| Reliability | Full control, but many failure points | Proven by millions of transactions |
| Implementation time | From a week | 1–2 days |
Apple documentation: "Apple Pay uses device-level encryption, making each transaction unique." A payment provider speeds up integration by 5x compared to manual decryption. In our experience, using a provider is 3x more reliable and reduces costs by 60%.
How to Test Apple Pay?
Apple's payment system does not work on the simulator for real transactions. For testing, you need a device with a test Visa card from the Apple Sandbox. Test cards are only available for accounts in the Apple Sandbox environment. On a physical device, you can test the full cycle: from button display to receiving transaction status. We provide a step-by-step testing guide with trust certificates.
What If the Apple Pay Button Doesn't Appear?
Reasons: Merchant ID not added to entitlements (check .entitlements file), certificate expired (2-year validity), canMakePayments(usingNetworks:) returns false on simulator without configured cards. Our solution: step-by-step verification of each chain element. The certificate needs renewal every two years — we remind clients a month before expiry.
How to renew the certificate?
Go to Apple Developer Portal, in the Certificates, Identifiers & Profiles section. Create a new Payment Processing Certificate for the same Merchant ID. Upload the CSR file, download the new certificate, install it on the server, and restart the process. It takes 15 minutes.Common Issues and Their Solutions
| Issue | Cause | Solution |
|---|---|---|
| Button not displayed | Incorrect entitlements or expired certificate | Check .entitlements and certificate validity |
| Failure without message | errors not passed in PKPaymentAuthorizationResult |
Pass PKPaymentError |
| Error on simulator | Apple Pay doesn't work on simulator | Test on a real device with Sandbox |
| Certificate expired | 2-year validity | Renew certificate via Developer Portal |
What's Included in Our Work
- Registration of Merchant ID, generation and upload of certificates
- Implementation of
PKPaymentRequestwith correctpaymentSummaryItems - Integration with payment provider (passing
token.paymentData) - Error handling with
PKPaymentError - Testing on a physical device in Sandbox
- We guarantee a seamless integration certified by Apple's guidelines and provide a 30-day support guarantee.
Order a turnkey integration — we'll implement the full cycle from certificate setup to testing. With over 20 successful projects, our expertise ensures a 90% first-time success rate. Contact us for an assessment of your project — we'll help with Apple Pay integration of any complexity.
Timelines
2–3 days including Developer Portal setup and provider integration. Thanks to ready-made templates, the timeline is reduced by 70% compared to self-implementation. The cost is calculated individually after requirements analysis, but typical savings start at $2,000.







