Apple Pay Integration
Apple Pay on the web enables customers to pay using cards in Wallet with Face ID or Touch ID. By integrating Apple Pay through Onerway, you can keep Apple Pay UI and token handling on the frontend while offloading payment processing and (optionally) merchant validation to Onerway.
TL;DR
1. Check Device Support
Frontend checks if device supports Apple Pay:
window.ApplePaySession+ApplePaySession.canMakePayments()- Show Apple Pay button if supported, otherwise hide
2. Get Configuration
Call POST /v1/txn/consultPaymentMethod, filter paymentMethod="ApplePay", get countryCode and subCardTypes.
3. Create Payment Session
When user clicks button, create payment session:
new ApplePaySession(version, paymentRequest)→session.begin()
4. Merchant Validation
onvalidatemerchant gets event.validationURL and sends to backend, choose one:
- Onerway Proxy API (Recommended)
- Merchant-owned Merchant Identity certificate
Frontend calls completeMerchantValidation(merchantSession) to complete validation.
5. Payment Authorization
onpaymentauthorized gets event.payment.token and sends to backend, choose one:
- Onerway Decryption Mode (Recommended):
POST /v1/txn/doTransaction, passtokenInfo.provider="ApplePay",tokenId=<token> - Merchant Decryption Mode: Decrypt token yourself, pass decrypted
cardInfo(cryptogram,eci,wallet.*)
Frontend must call completePayment(STATUS_SUCCESS|STATUS_FAILURE)
6. Final Result
Rely on webhook as source of truth (verify signature + idempotency).
Integration Overview
Apple Pay integration with Onerway involves three main phases:
- Configuration Retrieval → Query Onerway API for Apple Pay settings
- Apple Pay Session → Create
ApplePaySessionand display the payment sheet - Payment Processing → Submit Apple Pay token to Onerway and handle the result (plus webhook)
Prerequisites
Before implementing Apple Pay integration, ensure you have:
- HTTPS Website: Apple Pay on the web requires HTTPS
- Supported Browser: Safari (supported Apple Pay platforms only)
- Onerway Merchant Account: Active account with Apple Pay enabled
- Backend Integration: Server capability to call Onerway APIs and host merchant validation endpoints
See also:
Integration Flow
Frontend Page Preparation
Before diving into detailed integration steps, complete the basic frontend page setup.
1. Add Apple Pay Button
Add the button following Apple's brand guidelines:
<!-- Apple Pay button (official style) -->
<apple-pay-button
buttonstyle="black"
type="buy"
locale="en-US"
id="applePayButton"
style="display: none; --apple-pay-button-width: 200px; --apple-pay-button-height: 40px;">
</apple-pay-button>2
3
4
5
6
7
8
Official Demo & Button Customization
- Interactive Demo: Apple provides an official demo site where you can experience the complete Apple Pay payment flow.
- Button Styles: For complete button customization options and attributes, see Apple Pay Button Documentation.
2. Check Device Support and Show Button
// Check device supports Apple Pay
if (window.ApplePaySession && ApplePaySession.canMakePayments()) {
document.getElementById('applePayButton').style.display = 'block'
}2
3
4
3. Create Payment Session
Create ApplePaySession when button is clicked:
button.addEventListener('click', () => {
// Build payment request
const paymentRequest = {
countryCode: config.countryCode, // From Onerway config
currencyCode: 'USD',
supportedNetworks: config.supportedNetworks, // From Onerway config
merchantCapabilities: ['supports3DS'],
total: { label: 'Your Store', amount: '10.00' }
}
// Create session (version 3+)
const session = new ApplePaySession(3, paymentRequest)
// Handle events (detailed implementation below)
session.onvalidatemerchant = async (event) => { /* ... */ }
session.onpaymentauthorized = async (event) => { /* ... */ }
session.oncancel = () => { /* User cancelled */ }
// Start Apple Pay sheet
session.begin()
})2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Full runnable example: Frontend Complete Example (HTML).
1. Getting Onerway Configuration
Before initializing Apple Pay, call the Payment Methods Query API to get countryCode and subCardTypes.
Backend Implementation
Call the Payment Methods Query API, filter for Apple Pay config, and return to frontend:
// Merchant Backend → Onerway: POST /v1/txn/consultPaymentMethod
config = consultPaymentMethod({ /* ... */ })
applePay = config.find(paymentMethod == "ApplePay")
// Return to frontend
return {
countryCode: applePay.countryCode, // Country code
supportedNetworks: applePay.subCardTypes // Supported card networks
}2
3
4
5
6
7
8
9
Frontend Usage
// Get config from your backend
const config = await fetch('/api/apple-pay/config').then(r => r.json())
// Use to create ApplePaySession
const paymentRequest = {
countryCode: config.countryCode, // From Onerway query
supportedNetworks: config.supportedNetworks, // From Onerway query
currencyCode: 'USD',
merchantCapabilities: ['supports3DS'],
total: { label: 'Your Store', amount: '10.00' }
}2
3
4
5
6
7
8
9
10
11
TIP
Complete API call example (with signature): Payment Methods Query
2. Merchant Validation
Merchant validation is required for each Apple Pay session:
- Server-only: your server must request the payment session from Apple. Do not request it from the client.
- Allowlist: only access validation URLs provided by Apple.
- Expiry: the payment session expires after 5 minutes, handle immediately.
Frontend Implementation
In the ApplePaySession's onvalidatemerchant event, get validationURL and send to backend:
session.onvalidatemerchant = async (event) => {
await handleMerchantValidation(session, event)
}2
3
async function handleMerchantValidation(session, event) {
// 1. Security check validationURL
if (!event.validationURL.startsWith('https://apple-pay-gateway')) {
session.abort()
return
}
// 2. Send to backend for merchant validation
const response = await fetch('/api/validate-merchant', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
validationURL: event.validationURL,
website: window.location.hostname
})
})
// 3. Complete validation
const merchantSession = await response.json()
session.completeMerchantValidation(merchantSession)
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Backend Implementation
// Merchant Backend → Onerway: POST /txn/apiCheckApplePay
merchantSession = validateMerchant({
verifyUrl: event.validationURL, // Onerway proxy mode uses `verifyUrl`, Merchant-owned mode uses `validationURL`
website: window.location.hostname, // Onerway proxy mode uses `website`, Merchant-owned mode uses `initiativeContext`
/* ... */
})
// Return to frontend
return merchantSession // JSON string (needs parsing)2
3
4
5
6
7
8
9
Onerway Proxy Mode (Recommended)
Use Onerway proxy API to obtain merchantSession
| Parameter | Type | Length | Required | Signed | Description |
|---|---|---|---|---|---|
appId | String | 20 | Yes | Yes | Merchant application ID assigned by Onerway for website identification. |
merchantNo | String | 20 | Yes | Yes | Merchant number assigned by |
requestId | String | 64 | Yes | Yes | Unique request identifier for tracking and deduplication. |
sign | String | / | Yes | No | Digital signature string for request verification and security. Please refer to Signature for signature generation method. |
verifyUrl | String | 256 | Yes | Yes | Apple Pay merchant validation URL (from |
website | String | 128 | Yes | Yes | Merchant domain name registered with Apple Pay (maps to Apple Pay |
Response
| Name | Type | Description |
|---|---|---|
respCode | String | Response code from |
respMsg | String | Response message from |
data | Object | Response data. Refer to object data |
merchantSession is returned as a JSON string in data:
| Name | Type | Description |
|---|---|---|
└data | String | Apple Pay merchant session data as JSON string. This data should be passed directly to the Apple Pay session completion handler. |
Minimal Request/Response Example
{
"verifyUrl": "https://apple-pay-gateway-cert.apple.com/paymentservices/startSession",
"website": "example.com",
"appId": "...",
"merchantNo": "...",
"requestId": "...",
"sign": "..."
}2
3
4
5
6
7
8
{
"respCode": "20000",
"respMsg": "Success",
"data": "{...merchantSession json string...}"
}2
3
4
5
Merchant-Owned Account Mode (Direct to Apple)
Use your own Merchant Identity certificate
If you manage your own Merchant Identity certificate, your backend must request the payment session from Apple using mutual TLS (mTLS).
Request payload (Apple Pay on the web):
merchantIdentifierdisplayName(stable store name)initiative = "web"initiativeContext = "<your fully qualified domain>"
Return the merchantSession object to the frontend and call completeMerchantValidation(merchantSession).
For details, see: Apple Pay - Requesting an Apple Pay Payment Session.
3. Payment Authorization
Frontend Implementation
In the ApplePaySession's onpaymentauthorized event, get payment token and send to backend for processing:
session.onpaymentauthorized = async (event) => {
await handlePaymentAuthorization(session, event)
}2
3
async function handlePaymentAuthorization(session, event) {
// 1. Get payment token from Apple event
const paymentToken = event.payment.token
// 2. Send to your backend for payment processing
const response = await fetch('/api/process-payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ paymentToken })
})
const result = await response.json()
// 3. Complete payment based on result (must call)
if (result.success) {
session.completePayment(ApplePaySession.STATUS_SUCCESS)
} else {
session.completePayment(ApplePaySession.STATUS_FAILURE)
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Must Call completePayment
Whether successful or failed, the frontend MUST call session.completePayment(), otherwise the Apple Pay sheet will freeze.
Backend Implementation
// Merchant Backend → Onerway: POST /v1/txn/doTransaction
result = processPayment({
tokenInfo: {
provider: "ApplePay",
tokenId: event.payment.token // From frontend
},
/* ... */
})
// Return to frontend
if (result.respCode != "20000") {
return { status: "F" } // Request failed
} else if (result.data.status == "S") {
return { status: "S" } // Payment successful
} else {
return { status: "P" } // Processing (wait for webhook)
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
API Detailed Documentation
View complete API parameters and response definitions
Request Parameters
Note
- All
JSONfields must be stringified before submission - Nested objects must be serialized to
JSONstring format JSONfields must not contain unescaped special characters- Arrays in
JSONshould be properly formatted - Example of
JSONstring field:
{
"object": "{\"obj-key1\":\"v1\",\"obj-key2\":\"v2\"}",
"complex": "{\"k1\":\"v1\",\"array\":\"[{\\\"obj-key3\\\":\\\"v3\\\",\\\"obj-key4\\\":\\\"v4\\\"}]\"}"
}2
3
4
| Parameter | Type | Length | Required | Signed | Description |
|---|---|---|---|---|---|
billingInformation | String | / | Yes | Yes | Transaction billing information in JSON string format. See TransactionAddressfor complete structure. |
cardInfo | String | / | Conditional | Yes | Card information for wallet payments in JSON string format (merchant decrypt mode). See TxnCardInfoWalletfor complete structure. |
merchantCustId | String | 50 | Conditional | Yes | Unique customer identifier in merchant system. |
merchantNo | String | 20 | Yes | Yes | Merchant number assigned by |
merchantTxnId | String | 64 | Yes | Yes | Unique transaction identifier for each customer payment, generated by the merchant system. |
merchantTxnOriginalId | String | 128 | No | Yes | Master transaction ID generated by merchant for grouping related transactions. |
merchantTxnTime | String | / | No | Yes | Transaction timestamp when the merchant initiated the transaction. |
merchantTxnTimeZone | String | 64 | No | Yes | Transaction timezone offset for the merchant transaction time. |
orderAmount | String | 19 | Yes | Yes | Transaction amount in the specified currency, formatted as a decimal string. |
orderCurrency | String | 8 | Yes | Yes | |
productType | String | 16 | Yes | Yes | Payment method category that determines which payment options are available to customers. See ProductTypeEnumfor all available options. |
shippingInformation | String | / | Yes | Yes | Transaction shipping information in JSON string format. See TransactionAddressfor complete structure. |
sign | String | / | Yes | No | Digital signature string for request verification and security. Please refer to Signature for signature generation method. |
subProductType | String | 16 | Yes | Yes | Specific implementation method within the selected product type, defining how the payment is processed. See SubProductTypeEnumfor all available options. |
tokenInfo | String | / | Conditional | Yes | Wallet token information in JSON string format (Onerway decrypt mode). See WalletTokenInfofor the wallet token structure. |
txnOrderMsg | String | / | Yes | Yes | Transaction business information in JSON string format. See TxnOrderMsgfor complete structure. |
txnType | String | 16 | Yes | Yes | Transaction type that defines the payment operation to be performed. See TxnTypeEnumfor all available options and detailed usage scenarios. |
Token decryption mode (choose one)
Pick exactly one mode per transaction:
- Onerway decrypt mode (recommended): Send the original Apple Pay wallet token via
tokenInfoand Onerway will verify/decrypt it (provider="ApplePay",tokenId=<paymentToken>). - Merchant decrypt mode: Verify/decrypt the Apple Pay token yourself, then send decrypted fields via
cardInfo(for example:cryptogram,eci,wallet.*). In this mode, do not sendtokenInfo.
Security & Compliance
Merchant decrypt mode means your backend directly handles decrypted payment data and 3DS-related values. Ensure you follow PCI DSS and internal security requirements for sensitive payment data handling.
Merchant decrypt mode: Decrypted token example
{
"applicationPrimaryAccountNumber": "4111111111111111",
"applicationExpirationDate": "2510",
"currencyCode": "840",
"transactionAmount": 1.00,
"deviceManufacturerIdentifier": "040010030273",
"paymentDataType": "3DSecure",
"onlinePaymentCryptogram": "Af9x5...base64-3ds-cryptogram...",
"eciIndicator": "05"
}2
3
4
5
6
7
8
9
10
Response
| Name | Type | Description |
|---|---|---|
respCode | String | Response code from |
respMsg | String | Response message from |
data | Object | Response data. Refer to object data |
data
| Name | Type | Description |
|---|---|---|
transactionId | String | Transaction order number created by |
responseTime | String | Interface response time Format: |
txnTime | String | Transaction completion time Format: |
txnTimeZone | String | Transaction time zone Format: |
orderAmount | String | Order amount |
orderCurrency | String | |
txnAmount | String | Order amount after conversion to settlement currency |
txnCurrency | String | |
status | String | Transaction processing result Refer to TxnStatusEnum |
redirectUrl | String | Redirection URL for 3D Secure verification |
contractId | String | Subscription contract number |
tokenId | String | Payment token |
eci | String | Electronic Commerce Indicator |
periodValue | String | Installment payment number of periods |
codeForm | String | Code form for specific payment methods See CodeFormEnum |
presentContext | String | Context information for presentation layer |
actionType | String | Action type for the transaction See ActionTypeEnum |
subscriptionManageUrl | String | Subscription management URL |
sign | String |
4. Webhook Notifications
You must handle webhook notifications as the source of truth for final status:
- Verify signature
- Ensure idempotency (dedupe by
merchantTxnId) - Return
transactionIdas acknowledgement
API Usage Examples
{
"appId": "1727880846378401792",
"merchantNo": "800209",
"merchantTxnId": "0104b16f-cc96-44ed-94bd-e814f6b64bec",
"orderAmount": "1",
"orderCurrency": "USD",
"productType": "CARD",
"subProductType": "DIRECT",
"txnType": "SALE",
"tokenInfo": "{\"provider\":\"ApplePay\",\"tokenId\":\"{\\\"paymentData\\\":{\\\"data\\\":\\\"Zg8fgQuOqRemQ7XPmartChBCZbfaaWBEk6AVRS6bcO7Qfqmm90S90l1nnsYKqwBMmDQcpsOfFF+HHgMAGwzo1XurrbZprbZc1Quw6mNJMrLwMiUMva4hf2sNNGCp9EHeSMx+MofeeMbHjI5fladdD19/GzXVNR1OAFdfuZLB/rkTKI5cVsKFqzeshxJiUAbHQvAwkdNvyjpEuGoWhSrWpaAbX6SBB6NlcUXQ4KT46LJJ+05QiwIO+drTdP9j/0Fbl+lg2UYcQPIE/3lownbSRPOq9JMpqF4xOZmO+3gYOaME5Xhev95g8kG3Hogw6MYBpGxmRA5Btu9Cma9fNEekA+KXqS78TQT24IktEpYUW9yKO+xUXTWqg0auFwb1bKOo2D2rSIAoYSW6Vnehdo0gmLP4BApyDf5Nug1YQEaKoKk=\\\",\\\"signature\\\":\\\"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\\\",\\\"header\\\":{\\\"publicKeyHash\\\":\\\"H8goTIjHUSUL1pcwkMV8ljgmU73N51JAIvz6Ti05LlI=\\\",\\\"ephemeralPublicKey\\\":\\\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEV5gfSdV5Hf71RaGUoqrwQitC7SE7A/uQ7biDcM41u7ksBeIEeoBDGs4koPA8vDLDn9kgpEC38aXpBqvHp4IREw==\\\",\\\"transactionId\\\":\\\"fc46411c0070e4ca0ed2de2a7a12b9796ba94f00b72c4bdf406a1deb03d7487b\\\"},\\\"version\\\":\\\"EC_v1\\\"},\\\"paymentMethod\\\":{\\\"displayName\\\":\\\"Visa 0121\\\",\\\"network\\\":\\\"Visa\\\",\\\"type\\\":\\\"credit\\\"},\\\"transactionIdentifier\\\":\\\"fc46411c0070e4ca0ed2de2a7a12b9796ba94f00b72c4bdf406a1deb03d7487b\\\"}\"}",
"shippingInformation": "{\"address\":\"72981 Alejandrin Port\",\"city\":\"Lake Judd\",\"country\":\"US\",\"email\":\"Tracy_Stanton58@gmail.com\",\"firstName\":\"Noble\",\"lastName\":\"Baumbach\",\"phone\":\"18434119887\",\"postalCode\":\"12053\",\"province\":\"CO\"}",
"txnOrderMsg": "{\"returnUrl\":\"https://docs.onerway.com/\",\"notifyUrl\":\"https://sandbox-acq.onerway.com/callback/testReceiveNotification\",\"products\":\"[{\\\"currency\\\":\\\"USD\\\",\\\"name\\\":\\\"butternut pumpkin\\\",\\\"num\\\":\\\"96\\\",\\\"price\\\":\\\"708.69\\\"}]\"}",
"sign": "8816e1f0326f2ee34f8bc909f7cd5d259aed37e59410f178d22f275f2f38fbe9"
}2
3
4
5
6
7
8
9
10
11
12
13
14
{
"appId": "1727880846378401792",
"merchantNo": "800209",
"merchantTxnId": "0104b16f-cc96-44ed-94bd-e814f6b64bec",
"orderAmount": "1",
"orderCurrency": "USD",
"productType": "CARD",
"subProductType": "DIRECT",
"txnType": "SALE",
"cardInfo": "{\"cardNumber\":\"4111111111111111\",\"month\":\"10\",\"year\":\"2025\",\"cryptogram\":\"AAAAAA...\",\"eci\":\"05\",\"wallet\":{\"type\":\"ApplePay\",\"applePay\":{\"paymentDataType\":\"3DSecure\"}}}",
"billingInformation": "{\"country\":\"US\"}",
"shippingInformation": "{\"country\":\"US\"}",
"txnOrderMsg": "{\"returnUrl\":\"https://example.com/return\",\"notifyUrl\":\"https://example.com/webhook\"}",
"sign": "..."
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"respCode": "20000",
"respMsg": "Success",
"data": {
"transactionId": "1962880981921042432",
"responseTime": "2025-09-02 22:11:13",
"txnTime": "2025-09-02 22:11:09",
"txnTimeZone": "+08:00",
"orderAmount": "1.00",
"orderCurrency": "USD",
"status": "S",
"redirectUrl": null,
"sign": "719c7533ebe9b57dfc232176091eda1242a228045ffa6481d6a3bb048ff6a20e"
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"notifyType": "TXN",
"transactionId": "1962880981921042432",
"txnType": "SALE",
"merchantNo": "800209",
"merchantTxnId": "0104b16f-cc96-44ed-94bd-e814f6b64bec",
"responseTime": "2025-09-02 22:11:13",
"txnTime": "2025-09-02 22:11:09",
"txnTimeZone": "+08:00",
"orderAmount": "1.00",
"orderCurrency": "USD",
"status": "S",
"cardBinCountry": "CA",
"reason": "{\"respCode\":\"20000\",\"respMsg\":\"Success\"}",
"paymentMethod": "VISA",
"walletTypeName": "ApplePay",
"channelRequestId": "8002091962880984022126593",
"sign": "b0cf37c8c79138a3248dc422de191416beb05d345110869045b3076819f7b2d8"
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Official Demo & Button Customization
- Interactive Demo: Apple provides an official demo site where you can test Apple Pay integration with real payment flows.
- Button Styling: For complete button customization options and properties, see the Apple Pay button documentation.
Frontend Complete Example (HTML)
HTML Complete Implementation
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Apple Pay Integration (Demo)</title>
<style>
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial; }
#container { max-width: 520px; margin: 24px auto; }
#status { margin-top: 12px; font-size: 14px; white-space: pre-wrap; }
apple-pay-button { --apple-pay-button-width: 100%; --apple-pay-button-height: 44px; }
</style>
</head>
<body>
<div id="container">
<apple-pay-button id="applePayButton" buttonstyle="black" type="buy" locale="en-US" style="display:none;"></apple-pay-button>
<div id="status"></div>
</div>
<script>
// ============================================
// 1. DOM Elements and Utility Functions
// ============================================
const statusElement = document.getElementById('status')
const applePayButton = document.getElementById('applePayButton')
function updateStatus(message) {
statusElement.textContent = message
}
// ============================================
// 2. Backend API Functions (Merchant Implementation Required)
// ============================================
/**
* Fetch Apple Pay configuration
* Backend call: POST /v1/txn/consultPaymentMethod
* Returns: { countryCode: string, subCardTypes: string[] }
* Example: { countryCode: 'US', subCardTypes: ['visa', 'masterCard', 'discover'] }
*/
async function fetchApplePayConfig() {
// TODO: Replace with actual backend API call
const response = await fetch('/api/apple-pay/config')
return response.json()
}
/**
* Merchant validation
* Backend call: POST /txn/apiCheckApplePay (Onerway proxy) or direct to Apple
* Parameters: { validationURL: string, website: string }
* Returns: merchantSession object
*/
async function validateMerchant(validationURL, website) {
// TODO: Replace with actual backend API call
const response = await fetch('/api/validate-merchant', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ validationURL, website })
})
return response.json()
}
/**
* Process payment
* Backend call: POST /v1/txn/doTransaction
* Parameters: { tokenInfo: { provider: 'ApplePay', tokenId: paymentToken } }
* Returns: { success: boolean, error?: string }
*/
async function processPayment(paymentToken) {
// TODO: Replace with actual backend API call
const response = await fetch('/api/process-payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ paymentToken })
})
return response.json()
}
// ============================================
// 3. Apple Pay Event Handlers
// ============================================
/**
* Handle merchant validation
*/
async function handleMerchantValidation(session, event) {
// Security check: only accept Apple official validation URLs
if (!event.validationURL.startsWith('https://apple-pay-gateway')) {
updateStatus('Error: Invalid validation URL')
session.abort()
return
}
try {
updateStatus('Validating merchant...')
const merchantSession = await validateMerchant(
event.validationURL,
window.location.hostname
)
session.completeMerchantValidation(merchantSession)
} catch (error) {
updateStatus('Merchant validation failed: ' + error.message)
session.abort()
}
}
/**
* Handle payment authorization
*/
async function handlePaymentAuthorization(session, event) {
try {
updateStatus('Processing payment...')
const result = await processPayment(event.payment.token)
const isSuccess = result && result.success === true
// Must call completePayment, otherwise Apple Pay sheet will freeze
session.completePayment(
isSuccess ? ApplePaySession.STATUS_SUCCESS : ApplePaySession.STATUS_FAILURE
)
updateStatus(isSuccess ? 'Payment successful' : 'Payment failed')
} catch (error) {
session.completePayment(ApplePaySession.STATUS_FAILURE)
updateStatus('Payment failed: ' + error.message)
}
}
// ============================================
// 4. Create Payment Session
// ============================================
/**
* Create and start Apple Pay payment session
*/
function createPaymentSession(config) {
// Build payment request
const paymentRequest = {
countryCode: config.countryCode, // From Onerway
currencyCode: 'USD',
supportedNetworks: config.subCardTypes, // From Onerway, already in Apple Pay format
merchantCapabilities: ['supports3DS'],
total: {
label: 'Example Merchant',
amount: '1.00',
type: 'final'
}
}
// Create Apple Pay session (version 3+)
const session = new ApplePaySession(3, paymentRequest)
// Register event handlers
session.onvalidatemerchant = (event) => handleMerchantValidation(session, event)
session.onpaymentauthorized = (event) => handlePaymentAuthorization(session, event)
session.oncancel = () => updateStatus('User canceled payment')
// Start payment interface
session.begin()
}
// ============================================
// 5. Initialization
// ============================================
async function initializeApplePay() {
// Check browser support
if (!window.ApplePaySession) {
return updateStatus('Current browser does not support Apple Pay')
}
// Check device support
if (!ApplePaySession.canMakePayments()) {
return updateStatus('This device does not support Apple Pay')
}
try {
// Fetch configuration
const config = await fetchApplePayConfig()
// Show button and bind click event
applePayButton.style.display = 'block'
applePayButton.addEventListener('click', () => createPaymentSession(config))
updateStatus('Ready, click button to start payment')
} catch (error) {
updateStatus('Initialization failed: ' + error.message)
}
}
// Initialize after page load
initializeApplePay()
</script>
</body>
</html>2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
Error Handling
Common Errors
- Button not displayed: not Safari / not HTTPS / no cards in Wallet → see Troubleshooting
- Sheet appears then disappears: merchant validation failed → validate domain file and validation URL allowlist
- Payment stuck: missing
completePayment()call
Security Best Practices
- Never request Apple payment session from the client (server-only).
- Strictly allowlist Apple validation URLs; reject any other URL.
- Never log or store Apple Pay payment tokens.
- Verify webhook signatures and enforce idempotency.
Implementation Best Practices
- Cache Onerway configuration for fast button response (but refresh on changes).
- Use consistent
displayName(do not localize, do not include dynamic values). - Treat webhook as the source of truth for final status.
Integration Checklist
- Onerway account enabled for Apple Pay
- Domain verification file deployed under
/.well-known/(see setup guide) - Backend config endpoint returns
countryCode/subCardTypes -
onvalidatemerchantimplemented (server-only + allowlist + 5-min expiry) -
onpaymentauthorizedimplemented and always callscompletePayment() - Backend calls
doTransactionwith correcttokenInfo - Webhook verification + idempotency implemented
Related Documentation
Onerway Documentation
- Payment Methods Query API - Get Apple Pay configuration
- Transaction Notification - Handle webhook notifications
Official Apple Pay Resources
- Complete Documentation - Apple's full web integration guide
- Interactive Demo - Test Apple Pay with real payment flows
- Button Customization - Button styling and properties reference
- Human Interface Guidelines - Apple Pay design and branding guidelines