Apple Pay
Apple Pay lets customers pay with a card already added to Wallet, using Face ID or Touch ID. With Checkout and the Web SDK, Onerway renders the Apple Pay button and handles the token, and you only need to complete domain registration. With the Direct API, you render the button, drive ApplePaySession, and submit the encrypted payment token returned by Apple to Onerway.
Choose an integration method
| Integration method | Button and session | Token handling | What you do |
|---|---|---|---|
| Checkout | Rendered and driven by the checkout page | Never reaches your system | Submit productType=ALL, or lpmsInfo.lpmsType=ApplePay to lock to Apple Pay |
| Web SDK | Rendered and driven by the SDK on your page; appearance in Configure wallet buttons | Never reaches your system | Verify your domain; receive the result through payment_result, see SDK-owned buttons |
| Direct API | Your own button and ApplePaySession | Decrypted by Onerway or by you | Verify your domain; every step under "Direct API integration" on this page |
The checkout page is hosted by Onerway and its domain is already registered with Apple, so no domain verification is needed. The Web SDK and the Direct API show the Apple Pay button on your own domain, which Apple requires to be verified first; see "Setup".
All three methods require HTTPS across your site with TLS 1.2 or later and Apple Pay enabled on your Onerway merchant account. Sandbox prerequisites and test cards are listed in Apple Pay sandbox testing.
Browser and device support
Apple Pay on the Web is no longer limited to Safari:
- Safari, and third-party browsers on iOS / iPadOS (all WebKit-based): the payment completes on the device.
- Compatible third-party browsers on Mac, Windows, and other devices: the page shows a QR code that the customer scans with an iPhone or iPad running iOS 18 / iPadOS 18 or later. Direct API merchants must use the
<apple-pay-button>element from Apple Pay JS SDK 1.2.0 or later; CSS-rendered buttons do not work in non-Safari browsers. - China mainland: Safari on iPhone and iPad only; third-party browsers are not available.
Checkout and the Web SDK handle these differences for you; Direct API merchants get the same coverage by using the official SDK and button element as shown on this page. See the Apple support article for the authoritative list.
Setup
Apple requires every merchant domain that shows the Apple Pay button to pass domain verification, which is bound to a Merchant ID in an Apple Developer account. Depending on who holds the Merchant ID, setup falls into three tiers:
| Tier | Domain and merchant validation | Token decryption | Suitable for |
|---|---|---|---|
| Default | Your own Apple Developer account: create the Merchant ID, verify the domains yourself, and request the merchant session from Apple with your own Merchant Identity certificate | Onerway decryption: Onerway generates the CSR for the payment processing certificate, and you upload it to your Merchant ID | Most merchants |
| Merchant decryption | Same as Default | You hold the payment processing certificate and submit the decrypted result in cardInfo; PCI DSS required | Merchants with PCI DSS and certificate management capability |
| Onerway proxy | Domains are registered under the Onerway Apple Developer account, and you call Validate Apple Pay merchant to obtain the merchant session | Onerway decryption | Merchants without an Apple Developer account, or that do not want to maintain one |
The Web SDK only involves domain verification, so complete either the Default or the Onerway proxy tier. Do not mix your own Merchant ID with Onerway proxy validation.
In both domain verification approaches, your own account and the Onerway proxy, the verification file is deployed under the .well-known path of your site (follow the exact path given by Apple Developer or by Onerway). The address must not sit behind a proxy or redirect, and must be reachable by Apple's verification servers:
https://your-store.com/.well-known/apple-developer-merchantid-domain-association
Your own account
- Create a Merchant ID: sign in to Apple Developer, open Merchant IDs under Certificates, Identifiers & Profiles, and register one with a Description and an Identifier such as
merchant.com.yourcompany.appname. Skip this step if you already have one. - Configure the payment processing certificate: for the Default tier, first give the Merchant ID to Onerway technical support and obtain the CSR that Onerway generates. Create the certificate under Apple Pay Payment Processing Certificate for that Merchant ID, answer No to "Will payments be processed exclusively in China mainland?", and upload that CSR. Once the certificate is created, download the
.cerfile and send it back to Onerway: Onerway holds the matching private key and can decrypt Apple Pay tokens only after receiving the certificate. For the Merchant decryption tier, generate the CSR yourself and keep the private key. - Configure the Merchant Identity certificate (Direct API only): create and download it under Apple Pay Merchant Identity Certificate following Apple's CSR guide, store it securely on your merchant validation server, and use it to request an Apple Pay payment session.
- Verify the domain: add the domain under Merchant Domains, download the verification file, deploy it to the path above, confirm it is reachable over HTTPS, and click Verify in Apple Developer. Domain verification expires together with your site's SSL certificate: Apple re-checks 30, 15, and 7 days before expiry, so renewing the SSL certificate in advance keeps the verification valid; if you replace the certificate only after it expires, verify the domain again. The payment processing and Merchant Identity certificates each expire after 25 months; the Merchant ID does not expire.
Onerway proxy
- Give Onerway technical support every domain that shows the Apple Pay button, including subdomains and both sandbox and production domains, in the form
https://your-store.com. - Onerway registers the domains and returns the verification file; deploy it to the path above and confirm it is reachable:
curl -I https://your-store.com/.well-known/apple-developer-merchantid-domain-association
- Onerway completes domain verification with Apple and notifies you. The payment processing and Merchant Identity certificates are held and renewed by Onerway; you do not manage them.
In every tier, keep certificates in a controlled environment with least-privilege access, and monitor the expiry of both your SSL certificate and the Apple certificates.
Direct API integration
You render the button and drive ApplePaySession; the encrypted payment token returned by Apple is submitted to Onerway according to the decryption mode. Choose one mode; never submit tokenInfo and cardInfo together.
Onerway decryption (recommended; Default and Onerway proxy tiers): serialize the whole event.payment.token to a string and put it in tokenInfo.tokenId unchanged, without taking apart paymentData, paymentMethod, and transactionIdentifier.
"tokenInfo": "{\"provider\":\"ApplePay\",\"tokenId\":\"<event.payment.token serialized as a string>\"}"
Merchant decryption (this tier; PCI DSS required): omit tokenInfo and put the decrypted result in cardInfo as shown below.
| Decrypted Apple field | cardInfo field |
|---|---|
applicationPrimaryAccountNumber | cardNumber |
applicationExpirationDate (YYMMDD) | month from digits 3 and 4; year from the first two digits expanded to four, for example 30 becomes 2030 |
onlinePaymentCryptogram | cryptogram |
eciIndicator | eci |
paymentDataType | wallet.applePay.paymentDataType, 3DSecure or EMV, whichever your decryption returns |
Complete requests are shown in the "Onerway-decrypted Apple Pay payment" and "Merchant-decrypted Apple Pay payment" examples of Create direct transaction.
Load the Apple Pay JS SDK on the page first: the auto-updating 1.latest address is recommended; cross-browser support requires 1.2.0 or later; if you pin a version, use the v1.x.y path and add integrity, which 1.latest does not support. The SDK injects ApplePaySession in non-Safari browsers.
Check availability and show the button
After loading the Apple Pay JS SDK, show the button only when ApplePaySession exists and canMakePayments() returns true. To check whether the customer already has a usable card, use applePayCapabilities(); it returns only paymentCredentialStatusUnknown in non-Safari browsers, in which case you should still show the button. canMakePaymentsWithActiveCard() is deprecated. Use the <apple-pay-button> element provided by the SDK; style attributes are documented in Apple Pay button and branding in the Human Interface Guidelines.
<script src="https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js"></script>
<apple-pay-button buttonstyle="black" type="buy" locale="en-US"></apple-pay-button>
Fetch the Onerway configuration
On your server, call List available payment methods, take the paymentMethod=ApplePay record, and return countryCode and subCardTypes to the page as the payment request countryCode and supportedNetworks; both are already in the format Apple expects, so pass them through unchanged. applePayCapabilities() needs a merchant identifier: your own Merchant ID in the own-account tiers, or the merchantId returned by List available payment methods in the Onerway proxy tier.
Create the payment session
Use supportsVersion() to find the highest version the browser supports, from newest to oldest, then create the ApplePaySession and call begin(). The payment request needs at least countryCode, currencyCode, supportedNetworks, merchantCapabilities including supports3DS, and total with label, amount, and type: 'final', where amount is a string.
Check the validationURL and obtain the merchant session
In the onvalidatemerchant event, take validationURL, confirm that its host is an Apple validation gateway (apple-pay-gateway.apple.com, cn-apple-pay-gateway.apple.com for China mainland, with the matching -cert hosts in the sandbox) and call abort() for anything else, then hand it to your server. Your server must re-check the host before forwarding it, never rely on the page check alone, and never hard-code the validation address. In the own-account tiers, your server makes an mTLS request to Apple with the Merchant Identity certificate, sending merchantIdentifier, displayName (a stable store name, not localized and without order numbers), initiative as web, and initiativeContext as the full domain. In the Onerway proxy tier, call Validate Apple Pay merchant and parse the data in the response into an object.
Complete merchant validation
Call completeMerchantValidation() as soon as you have the session. A merchant session can be used once and expires five minutes after creation: request it on the server at that moment, never from the client directly against Apple.
Authorize the payment
In the onpaymentauthorized event, send event.payment.token to your server, which calls Create direct transaction with productType=CARD, subProductType=DIRECT, and txnType=SALE, submitting tokenInfo or cardInfo according to the decryption mode. Based on the server result, the page calls completePayment() with the success or failure status, exactly once; otherwise the Apple Pay sheet stays open.
Confirm the final result
In the synchronous response, status=S means success and P means processing; the final state is determined by the payment result webhook, which carries walletTypeName=ApplePay for wallet transactions. Signature verification, acknowledgement, and deduplication are covered in Webhooks.
Front-end example
The three internal server endpoints are yours to implement; they correspond to fetching the configuration, validating the merchant, and authorizing the payment.
<apple-pay-button id="applePayButton" buttonstyle="black" type="buy" locale="en-US" style="display:none;"></apple-pay-button>
<script src="https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js"></script>
<script>
const button = document.getElementById('applePayButton')
// Server calls List available payment methods and returns { countryCode, subCardTypes }
const fetchConfig = () => fetch('/api/apple-pay/config').then(r => r.json())
// Server obtains the merchant session for your tier: own account via the Merchant Identity certificate, Onerway proxy via Validate Apple Pay merchant
const validateMerchant = (validationURL, website) =>
fetch('/api/apple-pay/validate-merchant', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ validationURL, website })
}).then(r => r.json())
// Server calls Create direct transaction (tokenInfo.provider=ApplePay) and returns { success: boolean }
const processPayment = (paymentToken) =>
fetch('/api/apple-pay/process-payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ paymentToken })
}).then(r => r.json())
// Accept only Apple's validation gateway hosts, including the China mainland and sandbox domains
const APPLE_PAY_GATEWAY = /^(cn-)?apple-pay-gateway(-[a-z0-9-]+)?\.apple\.com$/
const highestSupportedVersion = () => {
for (let version = 14; version >= 3; version -= 1) {
if (ApplePaySession.supportsVersion(version)) return version
}
return 3
}
function startSession(config) {
const paymentRequest = {
countryCode: config.countryCode,
currencyCode: 'USD',
supportedNetworks: config.subCardTypes,
merchantCapabilities: ['supports3DS'],
total: { label: 'Example Store', amount: '99.99', type: 'final' }
}
const session = new ApplePaySession(highestSupportedVersion(), paymentRequest)
session.onvalidatemerchant = async (event) => {
// Your server must validate the host again; do not rely on this check alone
if (!APPLE_PAY_GATEWAY.test(new URL(event.validationURL).hostname)) {
session.abort()
return
}
try {
const merchantSession = await validateMerchant(event.validationURL, window.location.hostname)
session.completeMerchantValidation(merchantSession)
} catch {
session.abort()
}
}
session.onpaymentauthorized = async (event) => {
try {
const result = await processPayment(event.payment.token)
session.completePayment(result.success ? ApplePaySession.STATUS_SUCCESS : ApplePaySession.STATUS_FAILURE)
} catch {
session.completePayment(ApplePaySession.STATUS_FAILURE)
}
}
session.oncancel = () => {
// The customer closed the Apple Pay sheet; restore the page
}
session.begin()
}
async function init() {
if (!window.ApplePaySession || !ApplePaySession.canMakePayments()) return
const config = await fetchConfig()
button.style.display = 'block'
button.addEventListener('click', () => startSession(config))
}
init()
</script>
Apple's interactive demo walks through the complete flow.
Wallet subscriptions
Apple Pay can be used for subscriptions: submit subProductType=SUBSCRIBE with subscription on the initial subscription; both selfExecute=1 (managed by Onerway) and selfExecute=2 (you initiate each billing) are supported. On Checkout, lock to Apple Pay with lpmsInfo.lpmsType=ApplePay; on the Direct API, submit the encrypted wallet token in tokenInfo to create the initial subscription. After the initial subscription succeeds, the subscription payment webhook returns contractId and the subscription tokenId; renewals and plan changes then work the same as card subscriptions, see Subscription payments.
Differences from card subscriptions: the encrypted wallet token is single-use and cannot be saved for reuse, so the only billing credential during the subscription is the subscription token; wallet subscriptions produce no saved payment method result webhook and return no card token.
Common issues
| Symptom | Common causes | What to do |
|---|---|---|
| The button does not appear | Apple Pay JS SDK not loaded or a CSS button used, not HTTPS, unsupported device or browser, or Apple Pay not available in the country or region | Use the SDK <apple-pay-button>; show it only when canMakePayments() is true; an unknown status from applePayCapabilities() in non-Safari browsers is normal; offer other payment methods |
| The sheet flashes and closes after tapping | Merchant validation failed: validationURL rejected, the merchant session older than five minutes or reused, the domain verification file missing or unreachable, domain verification lapsed with an expired SSL certificate, or initiativeContext not matching the verified domain | Confirm completeMerchantValidation is called; confirm the verification file returns 200 with curl -I; check the domains match; contact Onerway to regenerate certificates; do not mix sandbox and production merchant identifiers |
| The page reports the payment incomplete after confirmation | completePayment() not called or called more than once; total.amount not a string or with wrong precision; payment processing certificate in an abnormal state | Check the page logic; contact Onerway support if it still fails |
| Sandbox test cards are declined | No sandbox tester account, device region not matching the test card network, or the card not added to Wallet | Complete the prerequisites in Apple Pay sandbox testing |
When contacting Onerway technical support, provide your merchant number and setup tier, the time and environment (sandbox or production), the device, OS, and browser version, complete console and network logs, and reproduction steps.
Go-live checklist
- Every sandbox and production domain is verified, the verification file is reachable, and SSL certificate renewal is monitored.
- Merchant sessions are requested only on the server, and
validationURLis checked on both the page and the server. completePayment()is called exactly once on both success and failure.- Apple Pay payment tokens are never logged or stored.
- Webhook signature verification and deduplication are in place.
Payment methods overview
The three kinds of payment methods — cards, wallets, and local payment method options — how each integration method supports them, and the availability check to run before displaying payment methods.
Google Pay
How Google Pay differs across Checkout, Web SDK, and Direct API, website registration before going live, choosing the merchant identifier and decryption mode, the Direct API front-end configuration and transaction flow, the two paths for PAN_ONLY tokens, wallet subscriptions, and common issues.