Google Pay 集成
Google Pay 使客户能够使用存储在 Google 账户中的支付信息进行快速、简单的结账。通过 Onerway 集成 Google Pay,您可以为全球数亿 Google 用户提供无缝的支付体验。
快速开始(TL;DR)
用下面 4 步可以跑通最短链路(先完成一笔支付,再逐步完善异常处理与安全策略):
- 获取 Google Pay 配置:调用
POST /v1/txn/consultPaymentMethod,找到paymentMethod = "GooglePay"的那条记录,取出gatewayName、gatewayMerchantId、subCardTypes(前端初始化需要)。 - 前端发起支付:加载 Google Pay JS SDK,使用上一步配置构建
tokenizationSpecification(Google Pay SDK 的令牌化配置),调用loadPaymentData(),并从paymentData.paymentMethodData.tokenizationData.token取到paymentToken(Google Pay 返回的加密 token)。 - 后端下单:调用
POST /v1/txn/doTransaction完成扣款。- Onerway 解密模式:传
tokenInfo.provider = "GooglePay"、tokenInfo.tokenId = <paymentToken>。 - 商户解密模式:不传
tokenInfo,改为在cardInfo中传解密后的字段(见下方示例)。
- Onerway 解密模式:传
- 处理结果与回调:
data.status = "S":成功。data.status = "R":需要重定向(常见于PAN_ONLY/ CVV 补充信息)。- 最终状态以 Webhook 通知为准。
集成概述
通过 Onerway 集成 Google Pay 包含三个主要阶段:
- 配置获取 → 查询 Onerway API 获取 Google Pay 设置
- Google Pay 初始化 → 设置 SDK 并显示支付按钮
- 支付处理 → 处理支付令牌并通过 Onerway 完成交易
前置要求
在实施 Google Pay 集成之前,请确保您具备:
- HTTPS 网站:Google Pay 要求安全的 HTTPS 连接
- 支持的浏览器:Chrome、Firefox、Safari、Edge、Opera 或 UC Browser
- Onerway 商户账户:已启用 Google Pay 的有效账户
- Google 账户:用于测试,需在 Google 账户中添加支付方式
- 后端集成:服务器能够调用 Onerway API
集成流程
前端页面准备
在进入详细集成步骤前,先完成基础的页面准备(结构与 Apple Pay 文档保持一致,替换为 Google Pay 逻辑)。
1. 添加 Google Pay 按钮
通过 Google Pay SDK 动态创建按钮,预留容器:
<div id="container"></div> <!-- Google Pay 按钮容器 -->
<div id="status"></div> <!-- 状态提示区域 -->2
2. 检查设备支持并显示按钮
const paymentsClient = new google.payments.api.PaymentsClient({ environment: 'TEST' })
paymentsClient.isReadyToPay(getGoogleIsReadyToPayRequest())
.then((response) => {
if (response.result) {
const button = paymentsClient.createButton({ onClick: onGooglePaymentButtonClicked })
document.getElementById('container').appendChild(button)
} else {
document.getElementById('status').textContent = '当前设备或账户不支持 Google Pay'
}
})2
3
4
5
6
7
8
9
10
11
3. 创建支付请求
点击按钮时构造 PaymentDataRequest 并发起 loadPaymentData():
function onGooglePaymentButtonClicked() {
const request = getGooglePaymentDataRequest() // 内含 gateway/gatewayMerchantId/allowedCardNetworks
paymentsClient.loadPaymentData(request)
.then((paymentData) => processPayment(paymentData)) // 提取 token 并提交后端
.catch(() => { document.getElementById('status').textContent = '用户取消或支付失败' })
}2
3
4
5
6
完整可运行示例见:前端完整示例(HTML)。
官方示例与按钮自定义
- 交互式演示:Google 提供了官方演示页面,可测试不同场景。
- 按钮样式:完整按钮属性和自定义请参阅 Google Pay 按钮文档。
1. 获取 Onerway 配置
在初始化 Google Pay 之前,调用支付方式查询接口获取 gateway、gatewayMerchantId 和 allowedCardNetworks。
查询 API 请求
{
"appId": "1727880846378401792",
"country": "US", // 客户所在国家
"merchantNo": "800209", // 您的商户号
"orderAmount": "29.99",
"orderCurrency": "USD",
"paymentMode": "WEB", // WEB、APP 或 WAP
"sign": "..."
}2
3
4
5
6
7
8
9
{
"respCode": "20000",
"respMsg": "Success",
"data": [
{
"productType": "LPMS",
"paymentMethod": "GooglePay",
"countryCode": "US",
"gatewayName": "ronghan", // 用作 gateway
"gatewayMerchantId": "BCR2DN6TY7DM5TDU", // 用作 gatewayMerchantId
"merchantId": "800096",
"subCardTypes": ["MASTERCARD", "VISA"] // 用作 allowedCardNetworks
}
]
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
查看支付方式查询 API 获取完整文档。
后端实现
调用支付方式查询接口,筛选 Google Pay 配置并返回给前端:
// 商户后端 → Onerway: POST /v1/txn/consultPaymentMethod
config = consultPaymentMethod({ /* ... */ })
googlePay = config.find(paymentMethod == "GooglePay")
// 返回前端
return {
gateway: googlePay.gatewayName, // 网关标识,用于 tokenizationSpecification
gatewayMerchantId: googlePay.gatewayMerchantId, // 网关商户 ID
allowedCardNetworks: googlePay.subCardTypes // 支持的卡组织
}2
3
4
5
6
7
8
9
10
前端使用
- 从你的后端获取上面的配置。
- 在
tokenizationSpecification.parameters中放gateway/gatewayMerchantId。 - 在
allowedPaymentMethods.parameters.allowedCardNetworks中放allowedCardNetworks。 - 其他支付金额、币种、商户信息按你的业务填充。
2. 支付授权
前端实现
在 loadPaymentData() 成功后,获取 Google Pay 返回的 token 并发送给后端处理:
function processPayment(paymentData) {
// 1. 从 Google Pay 事件中获取 payment token
const paymentToken = paymentData.paymentMethodData.tokenizationData.token
// 2. 发送到你的后端进行支付处理
await fetch('/api/google-pay/process-payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ paymentToken })
})
}2
3
4
5
6
7
8
9
10
11
后端实现
// 商户后端 → Onerway: POST /v1/txn/doTransaction
result = processPayment({
tokenInfo: {
provider: "GooglePay",
tokenId: paymentToken // 来自前端
},
/* ... */
})
// 返回前端
if (result.respCode != "20000") {
return { status: "F" } // 请求失败
} else if (result.data.status == "S") {
return { status: "S", transactionId: result.data.transactionId } // 成功
} else if (result.data.status == "R") {
return { status: "R", redirectUrl: result.data.redirectUrl } // PAN_ONLY 需重定向
} else {
return { status: "P" } // 处理中(等待 webhook)
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
查看完整 API 参数和响应定义
请求参数
| 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 解密模式(二选一)
每笔交易只选一种模式:
- Onerway 解密模式(推荐):把原始 Google Pay wallet token 放到
tokenInfo里,由 Onerway 完成校验与解密(provider="GooglePay",tokenId=<paymentToken>)。 - 商户解密模式:你自行校验/解密 Google Pay token,再把解密后的字段放到
cardInfo(例如:cryptogram、eci、wallet.*)。该模式 不要 传tokenInfo。
安全与合规
商户解密模式意味着你的后端会直接处理解密后的敏感数据与 3DS 相关字段。请确保满足 PCI DSS 以及内部安全要求。
商户解密模式:解密后的 token 示例
以下示例(来自 Google Pay 文档)展示了解密后 PAN_ONLY 与 CRYPTOGRAM_3DS 两种 authMethod 的差异:
// PAN_ONLY
{
"paymentMethod": "CARD",
"paymentMethodDetails": {
"authMethod": "PAN_ONLY",
"pan": "1111222233334444",
"expirationMonth": 10,
"expirationYear": 2025
},
"gatewayMerchantId": "some-merchant-id",
"messageId": "some-message-id",
"messageExpiration": "1759309000000"
}
// CRYPTOGRAM_3DS
{
"paymentMethod": "CARD",
"paymentMethodDetails": {
"authMethod": "CRYPTOGRAM_3DS",
"pan": "1111222233334444",
"expirationMonth": 10,
"expirationYear": 2025,
"cryptogram": "AAAAAA...",
"eciIndicator": "eci indicator"
},
"messageId": "some-message-id",
"messageExpiration": "1759309000000"
}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
响应
| 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 |
paymentId | String | Payment intent ID |
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 |
paymentStatus | String | Payment intent status Refer to PaymentStatusEnum |
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 |
PAN_ONLY 重定向处理
当 Google Pay 使用 PAN_ONLY 身份验证时,Onerway 可能会返回 status=R 和 redirectUrl 以进行额外的 CVV 收集。
前端重定向逻辑
// 1. 把 Google Pay token 交给商户后端
const result = await fetch('/api/google-pay/process-payment', {
method: 'POST',
body: JSON.stringify({ paymentToken, orderAmount, orderCurrency })
})
const data = await result.json()
// 2. 若 Onerway 要求重定向(PAN_ONLY),立即跳转
if (data.redirectUrl) {
window.location.href = data.redirectUrl
// 最终状态以 webhook 通知为准
} else {
// 按商户后端返回结果展示成功/处理中
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
PAN_ONLY 流程
当收到 redirectUrl 时:
- 立即:将用户重定向到 Onerway 的 CVV 收集页面
- 用户操作:客户在 Onerway 页面输入 CVV
- 返回重定向:Onerway 将用户重定向回您的
returnUrl - 异步通知:Onerway 通过 webhook 发送最终状态
- 显示结果:根据 webhook 数据或查询交易状态显示最终结果
身份验证方法
- CRYPTOGRAM_3DS(推荐):使用设备令牌和密文立即授权
- PAN_ONLY:需要通过重定向收集 CVV,为客户增加额外步骤
商户自行收集 CVV(可选)
如果您希望提供更流畅的用户体验,可以选择自行收集 CVV,而不使用 Onerway 的重定向页面。
适用场景:
- 希望提供无缝支付体验,避免页面跳转
- 需要完全自定义 CVV 输入界面
- 具备较高的技术开发能力
实施要求:
- 使用 PAN_ONLY 检查接口判断是否需要 CVV
- 在前端设计并实现 CVV 输入 UI
- 在支付请求的
cardInfo字段中传递cvv - 遵守 CVV 安全处理规范(不存储、不记录日志、使用 HTTPS)
完整的 API 文档和集成要点请参考:Google Pay PAN_ONLY 检查与自定义 CVV 收集
3. Webhook 通知
你必须把 webhook 作为最终状态来源:
- 验签
- 幂等(按
merchantTxnId去重) - 回执:只返回
transactionId
详见:交易通知
API 使用示例
以下是完整的 Google Pay 支付流程示例,包括请求、不同场景的响应和 Webhook 通知。
{
"merchantNo": "800209", // 商户号
"merchantTxnId": "TXN-2025-000001", // 商户唯一交易ID
"merchantTxnTime": "2025-06-25 20:18:57", // 商户交易时间
"orderAmount": "1.00", // 订单金额
"orderCurrency": "USD", // 订单货币
"productType": "CARD", // 产品类型
"subProductType": "DIRECT", // 子产品类型
"txnType": "SALE", // 交易类型:销售
"tokenInfo": "{\"provider\":\"GooglePay\",\"tokenId\":\"<GOOGLE_PAY_TOKEN>\"}", // Google Pay token
"txnOrderMsg": "{\"returnUrl\":\"https://merchant.example.com/return\",\"notifyUrl\":\"https://merchant.example.com/webhook\"}", // URLs
"billingInformation": "{\"country\":\"US\"}", // 必填
"shippingInformation": "{\"country\":\"US\"}", // 必填
"sign": "<SIGN>" // 签名
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"merchantNo": "800209", // 商户号
"merchantTxnId": "TXN-2025-000001", // 商户唯一交易ID
"merchantTxnTime": "2025-06-25 20:18:57", // 商户交易时间
"orderAmount": "1.00", // 订单金额
"orderCurrency": "USD", // 订单货币
"productType": "CARD", // 产品类型
"subProductType": "DIRECT", // 子产品类型
"txnType": "SALE", // 交易类型:销售
"txnOrderMsg": "{\"returnUrl\":\"https://merchant.example.com/return\",\"notifyUrl\":\"https://merchant.example.com/webhook\"}", // URLs
"cardInfo": "{\"cardNumber\":\"1111222233334444\",\"month\":\"10\",\"year\":\"2025\",\"cryptogram\":\"AAAAAA...\",\"eci\":\"05\",\"wallet\":{\"type\":\"GooglePay\",\"googlePay\":{\"authMethod\":\"CRYPTOGRAM_3DS\"}}}", // 商户解密模式(不传 tokenInfo)
"billingInformation": "{\"country\":\"US\"}", // 必填
"shippingInformation": "{\"country\":\"US\"}", // 必填
"sign": "<SIGN>" // 签名
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"respCode": "20000", // 响应码:成功
"respMsg": "Success", // 响应消息
"data": {
"transactionId": null, // 交易ID(重定向时为null)
"responseTime": null, // 响应时间
"txnTime": null, // 交易时间
"txnTimeZone": null, // 交易时区
"orderAmount": "1.00", // 订单金额
"orderCurrency": "USD", // 订单货币
"txnAmount": null, // 交易金额
"txnCurrency": null, // 交易货币
"status": "R", // 状态:需要重定向
"redirectUrl": "https://sandbox-checkout.onerway.com/additional-information?name=%7B%22lastName%22%3A%22Pfannerstill%22%2C%22firstName%22%3A%22Frederik%22%7D&returnUrl=https%3A%2F%2Fdocs.onerway.com%2F&key=64089c441df14ef8ba30489b8f0f17ae", // 重定向URL
"contractId": null, // 合同ID
"tokenId": null, // 令牌ID
"eci": null, // ECI值
"periodValue": null, // 周期值
"codeForm": null, // 代码形式
"presentContext": null, // 展示上下文
"actionType": "RedirectURL", // 操作类型:重定向URL
"subscriptionManageUrl": null, // 订阅管理URL
"sign": "e5fbaf27b2fc6cbbc09dbb7de05fef2eb3e95895ee8a53dc4c85535d930d1e68" // 响应签名
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"respCode": "20000", // 响应码:成功
"respMsg": "Success", // 响应消息
"data": {
"transactionId": "1993514205793619968", // Onerway交易ID
"responseTime": "2025-11-26 10:56:42", // 响应时间
"txnTime": "2025-11-26 10:56:39", // 交易时间
"txnTimeZone": "+08:00", // 交易时区
"orderAmount": "1.00", // 订单金额
"orderCurrency": "USD", // 订单货币
"txnAmount": "1.00", // 交易金额
"txnCurrency": "USD", // 交易货币
"status": "S", // 状态:成功
"redirectUrl": null, // 无需重定向
"contractId": null, // 合同ID
"tokenId": null, // 令牌ID
"eci": "05", // ECI值
"periodValue": null, // 周期值
"codeForm": null, // 代码形式
"presentContext": null, // 展示上下文
"actionType": null, // 无需额外操作
"subscriptionManageUrl": null, // 订阅管理URL
"sign": "abc123..." // 响应签名
}
}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": "1993514205793619968", // Onerway交易ID
"txnType": "SALE", // 交易类型:销售
"merchantNo": "800209", // 商户号
"merchantTxnId": "3dd112e6-cb7f-42e2-b9bd-605485b466c8", // 商户交易ID
"responseTime": "2025-11-26 10:56:42", // 响应时间
"txnTime": "2025-11-26 10:56:39", // 交易时间
"txnTimeZone": "+08:00", // 交易时区
"orderAmount": "1.00", // 订单金额
"orderCurrency": "USD", // 订单货币
"status": "S", // 最终状态:成功
"cardBinCountry": "US", // 卡BIN国家
"paymentMethod": "VISA", // 支付方式
"channelRequestId": "8002091993514208116998145", // 渠道请求ID
"reason": "{\"respCode\":\"20000\",\"respMsg\":\"Success\"}", // 原因详情
"sign": "99de5c609e9e2715ed1f8297012a6dfe91677600a060d535b268e685772a606a" // Webhook签名
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
前端完整示例(HTML)
HTML 完整实现
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Google Pay 集成(示例)</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; }
</style>
</head>
<body>
<div id="container"></div>
<div id="status"></div>
<script>
const statusEl = document.getElementById('status')
/**
* 定义 Google Pay API 版本。
*
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects#PaymentDataRequest|apiVersion in PaymentDataRequest}
*/
const baseRequest = { apiVersion: 2, apiVersionMinor: 0 }
/**
* 你的网站与支付网关支持的卡认证方式。
*
* @see {@link https://developers.google.com/pay/api/web/reference/request-objects#CardParameters|CardParameters}
*/
const allowedCardAuthMethods = ['PAN_ONLY', 'CRYPTOGRAM_3DS']
function getTokenizationSpecification() {
return {
type: 'PAYMENT_GATEWAY',
parameters: {
// TODO: 替换为实际的 Onerway 配置
gateway: '<GATEWAY_FROM_ONERWAY>',
gatewayMerchantId: '<GATEWAY_MERCHANT_ID_FROM_ONERWAY>'
}
}
}
/**
* 你的网站与支付网关支持的卡组织。
* TODO: 替换为 Onerway consultPaymentMethod 返回的 `subCardTypes`。
*/
const allowedCardNetworks = ['AMEX', 'DISCOVER', 'JCB', 'MASTERCARD', 'VISA']
function getBaseCardPaymentMethod() {
return {
type: 'CARD',
parameters: {
allowedAuthMethods: allowedCardAuthMethods,
allowedCardNetworks: allowedCardNetworks
}
}
}
function getCardPaymentMethod() {
return Object.assign({}, getBaseCardPaymentMethod(), {
tokenizationSpecification: getTokenizationSpecification()
})
}
function getGoogleIsReadyToPayRequest() {
return Object.assign({}, baseRequest, {
allowedPaymentMethods: [getBaseCardPaymentMethod()]
})
}
let paymentsClient = null
let paymentToken = null
function setStatus(message) {
statusEl.textContent = message
}
function getGooglePaymentsClient() {
if (paymentsClient === null) {
paymentsClient = new google.payments.api.PaymentsClient({ environment: 'TEST' })
}
return paymentsClient
}
function getGoogleTransactionInfo() {
return {
countryCode: 'US',
currencyCode: 'USD',
totalPriceStatus: 'FINAL',
totalPrice: '1.00'
}
}
function getGooglePaymentDataRequest() {
const paymentDataRequest = Object.assign({}, baseRequest)
paymentDataRequest.allowedPaymentMethods = [getCardPaymentMethod()]
paymentDataRequest.transactionInfo = getGoogleTransactionInfo()
paymentDataRequest.merchantInfo = {
merchantName: '示例商户 Example Merchant'
}
// 可选:让 Google Pay 弹窗采集 email / shipping 信息(按你的 checkout 体验决定)。
paymentDataRequest.emailRequired = true
paymentDataRequest.shippingAddressRequired = true
paymentDataRequest.shippingAddressParameters = {
phoneNumberRequired: true
}
return paymentDataRequest
}
function onGooglePayLoaded() {
const paymentsClient = getGooglePaymentsClient()
paymentsClient
.isReadyToPay(getGoogleIsReadyToPayRequest())
.then(function (response) {
if (response.result) {
addGooglePayButton()
setStatus('已准备好,可以尝试 Google Pay')
} else {
setStatus('当前设备或账户不支持 Google Pay')
}
})
.catch(function (err) {
console.error(err)
setStatus('初始化失败,详情见控制台')
})
}
function addGooglePayButton() {
const paymentsClient = getGooglePaymentsClient()
const button = paymentsClient.createButton({
onClick: onGooglePaymentButtonClicked,
allowedPaymentMethods: [getBaseCardPaymentMethod()]
})
document.getElementById('container').appendChild(button)
}
function onGooglePaymentButtonClicked() {
const paymentDataRequest = getGooglePaymentDataRequest()
paymentDataRequest.transactionInfo = getGoogleTransactionInfo()
const paymentsClient = getGooglePaymentsClient()
paymentsClient
.loadPaymentData(paymentDataRequest)
.then(function (paymentData) {
processPayment(paymentData)
})
.catch(function (err) {
console.error(err)
setStatus('用户取消或支付失败,详情见控制台')
})
}
function processPayment(paymentData) {
// 生产环境禁止记录或存储 `paymentToken`。
paymentToken = paymentData.paymentMethodData.tokenizationData.token
setStatus(
'TODO:把 token 交给后端调用 Onerway /v1/txn/doTransaction。\n' +
'paymentToken=<hidden>'
)
// TODO: 替换为实际的后端 API 调用
// 请求结构请参考 "Token 解密模式" 与 "API 使用示例"。
}
</script>
<script async
src="https://pay.google.com/gp/p/js/pay.js"
onload="onGooglePayLoaded()"></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
前端拿到 `paymentToken` 后:
1)组装并调用 `POST /v1/txn/doTransaction`
- Onerway 解密模式:`tokenInfo.provider="GooglePay"`,`tokenInfo.tokenId=<paymentToken>`
- 商户解密模式:不传 `tokenInfo`;在 `cardInfo` 里传解密后的字段(见 TxnCardInfoWallet)
- `billingInformation`、`shippingInformation` 为必填(建议来自你的 checkout 数据)
2)处理响应
- `respCode != "20000"`:失败
- `data.status = "S"`:成功
- `data.status = "R"` 且有 `redirectUrl`:PAN_ONLY 重定向;最终状态以 webhook 为准2
3
4
5
6
7
8
9
10
11
错误处理
常见错误
- 按钮不显示:未加载
pay.js/ 非 HTTPS / 账户无可用卡 → 检查isReadyToPay()返回值 - 配置获取失败:后端未返回
gateway/gatewayMerchantId/allowedCardNetworks→ 检查consultPaymentMethod调用 - PAN_ONLY 未处理:
doTransaction返回redirectUrl但未跳转 → 前端收到redirectUrl立即跳转
安全最佳实践
- 不要在客户端解密 token(server-only)。
- 不要记录或存储 Google Pay payment tokens。
- webhook 必须验签,并强制幂等。
- 校验订单号/金额/币种与商户订单一致。
实现最佳实践
- 缓存 Onerway 配置以加快按钮响应(配置变化时刷新)。
- webhook 作为最终状态来源。
- 全链路测试 PAN_ONLY 重定向与 CRYPTOGRAM_3DS 直接成功两条路径。
集成清单
- 后端配置接口返回
gateway/gatewayMerchantId/allowedCardNetworks - 已实现
loadPaymentData()并提取paymentToken - 后端调用
doTransaction且tokenInfo结构正确(provider="GooglePay",tokenId=<paymentToken>) - PAN_ONLY 返回
redirectUrl时前端立即跳转 - webhook 验签 + 幂等已实现
相关文档
Onerway 文档
- 支付方式查询 API - 获取 Google Pay 配置
- 交易通知 - 处理 Webhook 通知
- Google Pay PAN_ONLY 检查与自定义 CVV 收集 - 自定义选项:商户自行收集 CVV
Google Pay 官方资源
需要帮助?
有关 Google Pay 集成的技术支持或问题,请联系 Onerway 支持。