Google Pay
Google Pay 让用户用 Google 账户中保存的卡快速完成支付,支持 Chrome、Safari、Firefox、Edge 等主流浏览器。收银台与 Web SDK 由 Onerway 渲染 Google Pay 按钮并处理 token;API 直连由商户自行加载 Google Pay JS SDK 取得加密 payment token,再提交给 Onerway。
接入方式选择
| 接入方式 | 按钮与 token | 商户需要做的 |
|---|---|---|
| 收银台 | 收银台渲染按钮,token 不经过商户系统 | 下单传 productType=ALL,或传 lpmsInfo.lpmsType=GooglePay 锁定 Google Pay |
| Web SDK | SDK 在商户页面内渲染按钮,外观见配置钱包按钮;token 不经过商户系统 | 结果通过 payment_result 接收,见 SDK 自有按钮;App 内嵌 WebView 的限制见 WebView 说明 |
| API 直连 | 商户加载 Google Pay JS SDK 并取得 token | 本页「API 直连接入」全部步骤 |
三种接入方式都要求网站 HTTPS 且 Onerway 商户账号已开通 Google Pay;浏览器与设备支持范围以 Google Pay Web 文档为准,沙盒测试用 Google 账号加入测试卡群组,见测试卡。
接入准备
API 直连接入前,先确定 token 的认证方式如何处理、由谁解密,以及商户标识的来源。
token 认证方式
authMethod 由用户的卡在 Google 侧的状态决定,商户不能指定;Onerway 要求 allowedAuthMethods 同时声明以下两个取值,不能只接受其中之一:
CRYPTOGRAM_3DS:用户在设备上完成过 token 化的卡,token 含动态密文与 3DS 凭证,可直接授权。PAN_ONLY:只保存在 Google 账户、未在设备上 token 化的卡,token 不含 3DS 凭证,扣款前需要补采 CVC,处理方式见「PAN_ONLY token 的两条路径」。
两个取值都会在真实流量中出现,PAN_ONLY 不声明就无法向这部分用户收款。PAN_ONLY 也不意味着商户要接触卡号:Onerway 代解密模式下 token 以 Onerway 密钥加密,卡号不进商户系统;标准路径的 CVC 由 Onerway 页面采集,商户无需 PCI DSS 资质。只有选择商户自解密或商户自行采集 CVC 时,才涉及 PCI DSS。
解密模式
| 模式 | 前端 tokenization | 提交给 Onerway | 前提 |
|---|---|---|---|
| Onerway 代解密(推荐) | PAYMENT_GATEWAY,网关参数取查询可用支付方式的返回值 | tokenInfo,token 原样透传 | 无 |
| 商户自解密 | DIRECT,ECv2 加商户在自己的 Google Pay & Wallet Console 注册的公钥 | 不传 tokenInfo,解密结果放入 cardInfo | 须满足 PCI DSS |
商户自解密时,解密结果按下表放入 cardInfo:
| 解密后的 Google 字段 | cardInfo 字段 |
|---|---|
pan | cardNumber |
expirationMonth、expirationYear | month、year |
authMethod | wallet.googlePay.authMethod |
cryptogram、eciIndicator(仅 CRYPTOGRAM_3DS) | cryptogram、eci |
商户标识
Google 规定 environment 为 PRODUCTION 时 merchantInfo.merchantId 必填(TEST 可省略),取值取决于域名报备方式,二选一:
- 由 Onerway 在其 Google Pay & Wallet Console 档案下报备商户域名:用查询可用支付方式返回的
merchantId。 - 商户自行注册 Google Pay & Wallet Console 并登记域名:用自己的商户标识。商户自解密只能选这种方式。
向 Onerway 技术支持确认使用的账号后,完成上线前网站报备。
上线前网站报备
本要求仅适用于 API 直连;收银台和 Web SDK 使用 Onerway 的 Google Pay 能力,无需报备网站。
接受生产环境支付前,需完成网站报备并获得 Google 审批通过,沙盒测试成功不能替代。按使用的 Google Pay 账号,参照 Google 官方发布集成指南完成:
| Google Pay 账号 | 报备主体 | 商户需要做的 |
|---|---|---|
| 商户自有账号 | 商户 | 在自己的 Google Pay & Wallet Console 添加网站,提供域名与集成截图并提交审批。 |
| Onerway 账号 | Onerway | 将域名和下述五张截图提交给 Onerway 技术支持,由 Onerway 代为报备并通知结果。 |
域名即实际调用 Google Pay API 的网站。账号只决定 merchantInfo.merchantId 的取值:使用自有账号不要求自行解密 token;由 Onerway 解密时,gatewayName 和 gatewayMerchantId 仍取查询可用支付方式的返回值。
Onerway 代报备所需的五张截图
按购买流程的五个阶段各提供一张截图:
| 截图 | 需要展示的内容 |
|---|---|
| 商品选择页(Item selection) | 用户浏览商品或服务。 |
| 购买前页面(Pre-purchase screen) | 用户准备进行购买。 |
| 支付方式页面(Payment method screen) | 用户选择 Google Pay 作为支付方式。 |
| Google Pay 支付面板(Google Pay API payment screen) | Google Pay 面板显示用户已保存的支付信息。 |
| 购买完成页面(Post-purchase screen) | 用户成功购买后显示的页面。 |
Android 无法截取 Google Pay 支付面板时,可用另一台设备拍照;仅该项也接受错误信息的截图或照片。
审批通过后切换生产环境
Google 审批通过后,配置报备账号对应的 merchantInfo.merchantId,创建 PaymentsClient 时将 environment 设为 PRODUCTION,并改用 Onerway 生产环境 API 地址和凭证创建交易。
API 直连接入
获取 Onerway 配置
服务端调用查询可用支付方式,取 paymentMethod=GooglePay 的记录,把配置返回前端。配置可缓存,变更时刷新。
| 查询返回字段 | Google Pay 参数 |
|---|---|
gatewayName | tokenizationSpecification 的 gateway |
gatewayMerchantId | tokenizationSpecification 的 gatewayMerchantId |
subCardTypes | allowedCardNetworks,已是 Google 要求的全大写,原样透传 |
merchantId | merchantInfo.merchantId(按「商户标识」一节二选一) |
countryCode | transactionInfo.countryCode |
初始化并展示按钮
加载 Google Pay JS SDK,创建 PaymentsClient,用 isReadyToPay() 做能力检测,通过后 createButton() 渲染官方按钮。
<script async src="https://pay.google.com/gp/p/js/pay.js" onload="onGooglePayLoaded()"></script>
const paymentsClient = new google.payments.api.PaymentsClient({ environment: 'TEST' }) // 生产改为 'PRODUCTION'
发起支付
点击按钮时构造 PaymentDataRequest,调用 loadPaymentData(),从返回结果的 tokenizationData.token 取出 token 交给服务端。请求至少包含 apiVersion: 2、含 tokenization 参数的 allowedPaymentMethods、transactionInfo(totalPriceStatus 取 FINAL,以及 totalPrice、currencyCode、countryCode)与 merchantInfo。可按需开启 emailRequired、shippingAddressRequired 让 Google Pay 面板采集联系信息。
创建交易
服务端调用创建直连交易(productType=CARD、subProductType=DIRECT、txnType=SALE),按解密模式提交 tokenInfo 或 cardInfo;完整报文见该接口的「Onerway 代解密 Google Pay 支付」「商户自解密 Google Pay CRYPTOGRAM_3DS 支付」示例。
处理同步响应
respCode 不为 20000 为失败;data.status=S 为成功;data.status=R 且 actionType=RedirectURL 表示 token 为 PAN_ONLY、需要补采 CVC,前端立即把用户跳转到 redirectUrl(Onerway 页面),用户输入后回跳 txnOrderMsg.returnUrl;此时响应中的 transactionId 为 null,只能以 merchantTxnId 关联订单。其余状态为处理中,等待通知。
确认最终结果
最终状态以支付结果通知为准,钱包交易的通知带 walletTypeName=GooglePay;校验通知中的金额、币种与商户订单一致。验签、应答与幂等处理见 Webhook 通知。
PAN_ONLY token 的两条路径
| 对比项 | 标准路径 | 商户自行采集 CVC |
|---|---|---|
| CVC 采集 | Onerway 页面 | 商户自建输入框 |
| 用户体验 | 一次跳转与回跳 | 无跳转 |
| 实现 | 只需处理 status=R 跳转 | 多一次检查接口调用与 CVC 输入界面 |
| CVC 安全责任 | Onerway | 商户:不存储、不落日志、HTTPS 传输、用后即清 |
两条路径都无需额外开通。
商户自行采集 CVC 的流程:
- 取得 token 后先调用检查 Google Pay PAN_ONLY token。
checkResult=false:向用户采集 CVC,创建直连交易时传同一个tokenInfo,并在cardInfo.cvv提交 CVC。提交PAN_ONLYtoken 时,若未传cardInfo.cvv,交易响应返回data.status=R和data.redirectUrl;商户需引导用户跳转至data.redirectUrl,在 Onerway 托管页面输入 CVC。是否在创建交易前调用检查接口不影响此行为。checkResult=true:通过同一个tokenInfo创建直连交易,无需提交cardInfo。- 检查与下单必须使用同一个 token 与同一个
merchantTxnId;检查接口失败时回退到标准路径。 - CVC 输入框按卡组织要求的位数实时校验(多数卡组织 3 位,American Express 4 位)、以密码类型显示,并向用户说明为何需要输入。
前端示例
配置从服务端取得后填入,processPayment 把 token 交给服务端。
<div id="container"></div>
<script>
// 以下配置来自服务端调用查询可用支付方式返回的 GooglePay 记录
const onerwayConfig = {
gateway: '<gatewayName>',
gatewayMerchantId: '<gatewayMerchantId>',
allowedCardNetworks: ['MASTERCARD', 'VISA'],
merchantId: '<merchantId>',
countryCode: 'US'
}
const baseRequest = { apiVersion: 2, apiVersionMinor: 0 }
const baseCardPaymentMethod = {
type: 'CARD',
parameters: { allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'], allowedCardNetworks: onerwayConfig.allowedCardNetworks }
}
const cardPaymentMethod = {
...baseCardPaymentMethod,
tokenizationSpecification: {
type: 'PAYMENT_GATEWAY',
parameters: { gateway: onerwayConfig.gateway, gatewayMerchantId: onerwayConfig.gatewayMerchantId }
}
}
let paymentsClient = null
function getPaymentsClient() {
if (!paymentsClient) {
paymentsClient = new google.payments.api.PaymentsClient({ environment: 'TEST' }) // 生产改为 'PRODUCTION'
}
return paymentsClient
}
function getPaymentDataRequest() {
return {
...baseRequest,
allowedPaymentMethods: [cardPaymentMethod],
transactionInfo: { countryCode: onerwayConfig.countryCode, currencyCode: 'USD', totalPriceStatus: 'FINAL', totalPrice: '99.99' },
merchantInfo: { merchantId: onerwayConfig.merchantId, merchantName: 'Example Store' }
}
}
function onGooglePayLoaded() {
getPaymentsClient()
.isReadyToPay({ ...baseRequest, allowedPaymentMethods: [baseCardPaymentMethod] })
.then((res) => {
if (!res.result) return
const button = getPaymentsClient().createButton({ onClick: onButtonClicked, allowedPaymentMethods: [baseCardPaymentMethod] })
document.getElementById('container').appendChild(button)
})
}
function onButtonClicked() {
getPaymentsClient()
.loadPaymentData(getPaymentDataRequest())
.then(processPayment)
.catch(() => {
// 用户关闭了 Google Pay 面板或支付失败,恢复页面交互
})
}
async function processPayment(paymentData) {
// 不要记录或存储 token
const paymentToken = paymentData.paymentMethodData.tokenizationData.token
// 服务端调用创建直连交易(tokenInfo.provider=GooglePay),把 respCode 非 20000 映射为 status 'F',返回 { status, redirectUrl }
const res = await fetch('/api/google-pay/process-payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ paymentToken })
}).then(r => r.json())
if (res.status === 'R' && res.redirectUrl) {
window.location.href = res.redirectUrl // PAN_ONLY:跳转 Onerway 页面采集 CVC,最终状态以通知为准
} else if (res.status === 'S') {
// 同步成功,仍以支付结果通知为准
} else if (res.status === 'F') {
// 失败,提示用户更换支付方式
} else {
// 处理中,等待支付结果通知
}
}
</script>
<script async src="https://pay.google.com/gp/p/js/pay.js" onload="onGooglePayLoaded()"></script>
Google 官方资源:Web 集成文档、交互式演示、品牌指南。
钱包订阅
Google Pay 可用于订阅:初始订阅时传 subProductType=SUBSCRIBE 与 subscription,selfExecute=1(Onerway 托管)与 selfExecute=2(商户自行发起每期扣款)均支持。收银台路径以 lpmsInfo.lpmsType=GooglePay 锁定 Google Pay;API 直连路径以 tokenInfo 提交钱包加密 token 发起初始订阅。初始订阅成功后,订阅扣款通知返回 contractId 与订阅 tokenId,后续续费与升降级与卡订阅相同,见订阅支付。
与卡订阅的差异:钱包加密 token 是一次性的,不能保存复用,订阅期间的扣款凭证只有订阅 token;钱包订阅不产生保存支付方式结果通知,也不返回卡 token。
常见问题
| 现象 | 常见原因 | 处理 |
|---|---|---|
| 按钮不显示 | 未加载 pay.js、非 HTTPS、账户无可用卡、App 内嵌 WebView 不满足条件 | 检查 isReadyToPay() 返回值;WebView 见 Web SDK 接入 |
| 配置获取失败 | 服务端未返回 gatewayName、gatewayMerchantId 或 subCardTypes | 检查查询可用支付方式的调用与筛选 |
| 生产环境弹窗报商户未验证 | merchantInfo.merchantId 缺失或与域名报备方式不匹配 | 按「商户标识」一节确认取值来源 |
| PAN_ONLY 未处理 | 创建直连交易返回 redirectUrl 但前端未跳转 | 收到 redirectUrl 立即跳转,或改用商户自行采集 CVC 路径 |
上线前检查
- API 直连:已完成网站报备并获得 Google 审批通过。
- 不在客户端解密 token,不记录、不存储 token。
- 已用真实卡验证
PAN_ONLY跳转与CRYPTOGRAM_3DS直接成功两条路径。 - 采用商户自行采集 CVC 路径时,已验证检查接口失败时的回退。
- Webhook 验签、幂等与金额校验已实现。