Apple Pay 支付授权
支付授权是 Apple Pay 支付流程的最后阶段,当用户通过生物识别验证并确认支付后,系统会触发 onpaymentauthorized
事件。此阶段需要处理加密的支付令牌并通过 Onerway 支付基础设施完成交易处理。
本文档重点介绍:前端处理 onpaymentauthorized
事件、后端调用 Onerway 支付接口完成交易。
流程概述
支付授权阶段包括四个核心步骤:
- 用户身份验证: Touch ID、Face ID 或 Apple Watch 生物识别验证
- 支付数据收集: 加密支付令牌和账单/送货信息
- 支付处理: 向 Onerway 提交加密数据进行处理
- 结果处理: 使用适当状态完成支付会话
授权流程
支付授权流程
实现指南
前端实现
在前端处理 onpaymentauthorized
事件:
// 设置 Apple Pay 支付授权事件监听
session.onpaymentauthorized = function(event) {
// 获取 Apple Pay 支付令牌和联系信息
const paymentData = {
paymentToken: event.payment.token,
billingContact: event.payment.billingContact,
shippingContact: event.payment.shippingContact
// 商户根据业务需要添加其他参数,如 orderId, amount 等
};
// 提交到后端处理
fetch('/api/apple-pay/process-payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(paymentData)
})
.then(response => response.json())
.then(result => {
if (result.success) {
// 支付成功,必须调用 completePayment 结束会话
session.completePayment(ApplePaySession.STATUS_SUCCESS);
// todo: 处理成功后续逻辑
} else {
// 支付失败,必须调用 completePayment 结束会话
session.completePayment(ApplePaySession.STATUS_FAILURE);
// 处理失败后续逻辑
showErrorMessage(result.error);
}
})
.catch(error => {
// 处理异常,必须调用 completePayment 结束会话
session.completePayment(ApplePaySession.STATUS_FAILURE);
console.error('支付处理错误:', error);
});
};
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
重要提醒
- 仅当后端确认交易成功(例如
respCode = 20000
且data.status = "S"
)时,前端才应调用STATUS_SUCCESS
;否则应调用STATUS_FAILURE
。 - 无论支付成功或失败,都必须调用
session.completePayment()
方法来结束 Apple Pay 会话,否则会话将一直处于等待状态。
后端实现
注意
- 所有
JSON
字段必须在提交前进行字符串化处理 - 嵌套对象必须序列化为
JSON
字符串格式 JSON
字段不能包含未转义的特殊字符JSON
中的数组应该正确格式化JSON
字符串字段示例:
{
"object": "{\"obj-key1\":\"v1\",\"obj-key2\":\"v2\"}",
"complex": "{\"k1\":\"v1\",\"array\":\"[{\\\"obj-key3\\\":\\\"v3\\\",\\\"obj-key4\\\":\\\"v4\\\"}]\"}"
}
2
3
4
Apple Pay 请求参数
Parameter | Type | Length | Required | Signed | Description |
---|---|---|---|---|---|
billingInformation | String | / | Yes | Yes | Transaction billing information in JSON string format. See TransactionAddressfor 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 | Token information in JSON string format. See TokenInfofor complete 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. |
TokenInfo 参数说明
重要:Apple Pay 支付令牌必须赋值给 tokenInfo.tokenId
字段
Name | Type | Length | Required | Description |
---|---|---|---|---|
tokenId | String | / | Yes | Token ID from payment method binding. For digital wallets: • Apple Pay: Use • Google Pay: Use |
provider | String | / | Conditional | Token provider. Defaults to for other scenarios. |
响应
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 |
API 使用示例
{
"tokenInfo": {
"provider": "ApplePay",
"tokenId": {
"paymentData": { "data": "...", "signature": "...", "header": { "publicKeyHash": "...", "ephemeralPublicKey": "...", "transactionId": "..." }, "version": "EC_v1" },
"paymentMethod": { "displayName": "Visa 0121", "network": "Visa", "type": "credit" },
"transactionIdentifier": "..."
}
}
}
2
3
4
5
6
7
8
9
10
{
"billingInformation": "{\"address\":\"889 Mill Close\",\"city\":\"Buckridgefield\",\"country\":\"US\",\"email\":\"Ryan.Purdy@gmail.com\",\"firstName\":\"Frederik\",\"identityNumber\":\"92066033023\",\"lastName\":\"Pfannerstill\",\"phone\":\"15209608948\",\"postalCode\":\"96542\",\"province\":\"CO\"}",
"cardInfo": "{\"cvv\":\"123\"}",
"merchantCustId": "CustId-75S6-256P",
"merchantNo": "800209",
"merchantTxnId": "0104b16f-cc96-44ed-94bd-e814f6b64bec",
"merchantTxnTime": "2025-06-25 20:18:57",
"orderAmount": "1",
"orderCurrency": "USD",
"productType": "CARD",
"shippingInformation": "{\"address\":\"72981 Alejandrin Port\",\"city\":\"Lake Judd\",\"country\":\"US\",\"email\":\"Tracy_Stanton58@gmail.com\",\"firstName\":\"Noble\",\"identityNumber\":\"84880795738\",\"lastName\":\"Baumbach\",\"phone\":\"18434119887\",\"postalCode\":\"12053\",\"province\":\"CO\"}",
"sign": "8816e1f0326f2ee34f8bc909f7cd5d259aed37e59410f178d22f275f2f38fbe9",
"subProductType": "DIRECT",
"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\\\"}\"}",
"txnOrderMsg": "{\"accept\":\"*/*\",\"appId\":\"1727880846378401792\",\"colorDepth\":\"24\",\"contentLength\":\"65536\",\"javaEnabled\":false,\"language\":\"en-US\",\"products\":\"[{\\\"currency\\\":\\\"USD\\\",\\\"name\\\":\\\"butternut pumpkin\\\",\\\"num\\\":\\\"96\\\",\\\"price\\\":\\\"708.69\\\",\\\"type\\\":\\\"est\\\"},{\\\"currency\\\":\\\"USD\\\",\\\"name\\\":\\\"kiwi fruit\\\",\\\"num\\\":\\\"35\\\",\\\"price\\\":\\\"276.59\\\",\\\"type\\\":\\\"amet aliquip eiusmod\\\"}]\",\"returnUrl\":\"https://docs.onerway.com/\",\"notifyUrl\":\"https://sandbox-acq.onerway.com/callback/testReceiveNotification\",\"screenHeight\":\"1200\",\"screenWidth\":\"375\",\"timeZoneOffset\":\"180\",\"transactionIp\":\"252.238.110.248\",\"userAgent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36\"}",
"txnType": "SALE"
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
提示:tokenInfo
应作为对象传递(而非字符串)。对象形式示例:
{
"merchantNo": "800209",
"merchantTxnId": "0104b16f-cc96-44ed-94bd-e814f6b64bec",
"orderAmount": "1",
"orderCurrency": "USD",
"productType": "CARD",
"subProductType": "DIRECT",
"tokenInfo": {
"provider": "ApplePay",
"tokenId": {
"paymentData": { "data": "..." },
"paymentMethod": { "network": "Visa", "type": "credit" },
"transactionIdentifier": "..."
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"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",
"txnAmount": null,
"txnCurrency": null,
"status": "S",
"redirectUrl": null,
"contractId": null,
"tokenId": null,
"eci": null,
"periodValue": null,
"codeForm": null,
"presentContext": null,
"actionType": null,
"subscriptionManageUrl": null,
"sign": "719c7533ebe9b57dfc232176091eda1242a228045ffa6481d6a3bb048ff6a20e"
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"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\"}",
"sign": "b0cf37c8c79138a3248dc422de191416beb05d345110869045b3076819f7b2d8",
"paymentMethod": "VISA",
"walletTypeName": "ApplePay",
"channelRequestId": "8002091962880984022126593"
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
代码实现示例
@PostMapping("/api/apple-pay/process-payment")
public ResponseEntity<PaymentResponse> processPayment(@RequestBody ApplePayRequest request) {
// 构建 tokenInfo - Apple Pay 的关键字段
Map<String, Object> tokenInfo = new HashMap<>();
tokenInfo.put("provider", "ApplePay"); // 必须设置为 ApplePay
tokenInfo.put("tokenId", request.getPaymentToken()); // Apple Pay 支付令牌
// 构建基础请求参数
Map<String, Object> params = new HashMap<>();
params.put("tokenInfo", tokenInfo); // 重点:使用 tokenInfo
// todo:补充其他参数
// 调用 Onerway API
OnerwayResponse result = callOnerwayAPI(params);
if ("20000".equals(result.getRespCode()) && "S".equals(result.getData().getStatus())) {
return ResponseEntity.ok(PaymentResponse.success(result.getData().getTransactionId()));
} else {
return ResponseEntity.ok(PaymentResponse.failure(result.getRespMsg()));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
app.post('/api/apple-pay/process-payment', async (req, res) => {
const { paymentToken } = req.body;
const requestData = {
// 重点:Apple Pay 使用 tokenInfo 字段
tokenInfo: {
provider: 'ApplePay', // 必须设置为 ApplePay
tokenId: paymentToken // Apple Pay 支付令牌
}
// todo:补充其他参数
};
try {
const response = await fetch(`${BASE_URL}/v1/txn/doTransaction`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestData)
});
const result = await response.json();
if (result.respCode === '20000' && result.data.status === 'S') {
res.json({ success: true, transactionId: result.data.transactionId });
} else {
res.json({ success: false, error: result.respMsg || '支付失败' });
}
} catch (error) {
res.status(500).json({ success: false, error: '支付处理失败' });
}
});
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
关键要点
tokenInfo.provider
必须设置为"ApplePay"
tokenInfo.tokenId
必须是完整的 Apple Pay 支付令牌对象(来自event.payment.token
)
Webhook 异步通知
接收 Onerway 的支付结果通知:
@PostMapping("/webhooks/apple-pay")
public ResponseEntity<String> handleWebhook(@RequestBody OnerwayNotification notification, HttpServletRequest request) {
// 验证签名(按 Onerway 网关规范)
if (!verifyOnerwaySignature(request, notification)) { // 验证通知来源
return ResponseEntity.status(401).body("无效签名");
}
// 更新订单状态
updateOrderStatus(notification.getMerchantTxnId(), notification.getStatus()); // 更新业务状态
// 响应 transactionId 确认收到通知(用于通知回执)
return ResponseEntity.ok(notification.getTransactionId()); // 必须返回交易ID
}
2
3
4
5
6
7
8
9
10
11
12
13
app.post('/api/webhooks/apple-pay', async (req, res) => {
// 验证签名(按 Onerway 网关规范)
if (!verifyOnerwaySignature(req)) { // 验证通知来源
return res.status(401).send('无效签名');
}
// 更新订单状态
await updateOrderStatus(req.body.merchantTxnId, req.body.status); // 更新业务状态
// 响应 transactionId 确认收到通知(用于通知回执)
res.send(req.body.transactionId); // 必须返回交易ID
});
2
3
4
5
6
7
8
9
10
11
12
说明
返回 transactionId
作为回执是 Onerway 网关规范,用于确认通知已被接收。
错误处理
根据支付结果必须调用 completePayment
结束会话:
// 支付成功
session.completePayment(ApplePaySession.STATUS_SUCCESS);
// 支付失败或异常
session.completePayment(ApplePaySession.STATUS_FAILURE);
2
3
4
5
必须调用 completePayment
无论支付结果如何,都必须调用 session.completePayment()
结束 Apple Pay 会话,否则会话将一直处于等待状态。
安全要点
令牌管理:
- 绝不记录或存储 Apple Pay 支付令牌
- 所有支付通信必须使用 HTTPS
- 始终验证 Webhook 签名
数据传输:
- 确保所有 API 调用使用 HTTPS
- 验证支付令牌的完整性
- 保护敏感用户信息
数据一致性与防重放:
- 校验订单号、金额、币种与商户侧订单一致,防止前端参数被篡改
- 实施请求幂等(如使用
merchantTxnId
去重)并限制令牌使用时效,防止重放
详细实践参见 故障排查。
最佳实践
性能要求
- 设置合理的超时时间(10-15 秒)
- 监控支付成功率和响应时间
- 优化网络请求处理
安全规范
- 始终调用
completePayment()
结束会话 - 验证支付令牌格式和完整性
- 记录审计日志但避免敏感信息泄露
路由与集成检查
提示:本文中的后端路由(如 /api/apple-pay/process-payment
)为示例路径,商户可按自身服务设计调整,但需与前端保持一致。
集成检查清单
- 测试成功支付流程
- 测试失败支付场景
- 验证
completePayment()
调用 - 确认
tokenInfo
字段正确传递 - 测试 Webhook 通知处理
- 验证签名验证功能
下一步
完成支付授权后,继续以下流程: