Onerway
线上支付

Web SDK 接入

服务端创建支付,使用 paymentId 初始化 Web SDK,并处理自定义按钮、钱包、外跳与最终结果确认。

Web SDK 接入分为两部分:服务端创建支付并保存订单与 paymentId 的映射,浏览器加载 SDK 并使用该 paymentId 创建 Checkout。secret、请求签名、客户身份映射和最终支付状态确认始终保留在服务端。

开始前,先按接入准备获取 API 凭证,并将服务端的公网出口 IP 加入对应环境的白名单。每个请求都需按请求签名生成 sign。上线前,参考沙盒测试在沙盒环境验证集成中使用的全部场景。

Web SDK 使用 Onerway 的 Google Pay 能力,无需报备网站;网站报备仅适用于 API 直连接入。

接入流程

  1. 服务端调用创建 SDK 交易,保存返回的 paymentId。
  2. 浏览器加载与交易环境匹配的 CDN 脚本。
  3. 使用 paymentId 创建 Checkout,先订阅事件,再挂载 Payment Element。
  4. Card、本地支付和其他自定义支付按钮调用 confirmPayment();Apple Pay、Google Pay 等 SDK 自有按钮只监听 payment_result。
  5. 客户端根据事件更新页面;服务端通过查询支付记录或支付 Webhook 确认最终状态。

在服务端创建支付

普通 Web SDK v4 支付应通过创建 SDK 交易传入 productType=ALL、subProductType=DIRECT 和 txnType=SALE。标准的一次性扣款示例省略了 billingInformation、shippingInformation、paymentMode 和 osType。

创建交易时,billingInformation 与 shippingInformation 均为可选。信息已具备时即可传入;初始下单省略且业务流程需要时,可以在确认支付前通过更新 SDK 订单补充。传入对象后,其内部字段原有的必填条件仍然生效。更新订单时,服务端应复用原下单的 merchantTxnId,并在客户确认支付前等待更新成功响应。

当前 Web SDK 的字段 txnOrderMsg只包含 returnUrl、products、appId、customerPlatform、periodValue 和 notifyUrl;每个字段是否必填及其适用条件以 API Reference 为准。商户服务端不要采集或传入浏览器、设备和持卡人 IP 字段,这些信息由 Web SDK 采集。

Web SDK 支付通常省略 paymentMode,无需区分桌面和移动浏览器。传入非 WEB 值时,必须同时传入 osType。

服务端只把创建支付响应中的 paymentId 传给浏览器。transactionId 可用于服务端订单关联,但不能替代 paymentId 初始化 SDK。响应仍会返回 redirectUrl,当前 Web SDK 初始化时不消费该字段。

加载 SDK

CDN 与 environment 必须匹配:

环境CDNenvironment
Sandboxhttps://sandbox-checkout-sdk.onerway.com/v4/latest/onerway.jssandbox
Productionhttps://checkout-sdk.onerway.com/v4/latest/onerway.jsproduction,默认值

v4/latest 是 Production 正式推荐的长期地址。即使 environment 默认为 production,也建议显式传入,避免复制配置时混用环境。

<script src="https://checkout-sdk.onerway.com/v4/latest/onerway.js"></script>

<div id="onerway_checkout"></div>
<button id="pay_button" type="button">Pay now</button>

初始化、订阅事件并挂载

const checkout = await Onerway.createCheckout(paymentId, {
  environment: 'production',
  locale: 'en'
})

const paymentElement = checkout.createPaymentElement()

paymentElement.on('ready', (event) => {
  console.log(event.availablePaymentMethods)
})

paymentElement.on('loaderror', (event) => {
  console.error(event.error.code, event.error.message)
})

checkout.on('payment_result', handlePaymentResult)

paymentElement.mount('#onerway_checkout')

必须先订阅事件,再调用 mount():

  • ready.availablePaymentMethods 是当前订单、服务端配置和浏览器钱包能力共同决定的可用支付方式列表。不要把示例值当成固定枚举;钱包不可用时 SDK 会隐藏相应按钮。
  • loaderror 只表示 Checkout 初始化失败。程序逻辑应判断 event.error.code,event.error.message 仅用于展示。公开的 code 只有两个:checkout_load_failed(首次加载支付方式失败,可让客户重新初始化)和 no_available_payment_methods(过滤后没有可展示的支付方式,检查服务端配置与 paymentMethod)。
  • 表单校验、支付接口失败、钱包取消和 3DS/外跳异常不属于 loaderror,应从 confirmPayment() 返回值或 payment_result 处理。

配置 Payment Element

config 的子字段均为可选,但商户应根据自己的支付方式、表单和品牌要求决定是否传入,不能假定所有业务都使用默认展示。配置在 paymentElement.mount() 时读取;修改后需要重新创建并挂载 Payment Element 才能生效。

字段类型默认值作用
paymentMethodString[]不传支付方式显示白名单,只保留服务端实际返回且存在于数组中的方式
showBillingAddressBooleantrue是否展示并校验账单地址表单
displayCardholdernameBooleantrue是否展示并校验持卡人姓名输入框
walletButtonsObject见下文配置 Apple Pay 和 Google Pay 官方按钮支持的外观
checkoutThemeStringlightSDK 主题预设;当前公开主题只有 light
variablesObject{}覆盖主题变量,建议优先使用
stylesObject{}按 CSS selector 覆盖 SDK 样式,优先级最高
customCssURLStringSDK 默认 CSS替换 SDK 基础样式表来源;variables 和 styles 仍然生效

例如,商户可以只展示当前业务接受的方式、隐藏 SDK 账单地址表单,并对 SDK 可控区域应用品牌样式:

const checkout = await Onerway.createCheckout(paymentId, {
  environment: 'production',
  locale: 'en',
  config: {
    paymentMethod: ['CARD', 'DOKU_VA', 'GooglePay'],
    showBillingAddress: false,
    displayCardholdername: true,
    checkoutTheme: 'light',
    variables: {
      colorPrimary: '#2563eb',
      colorText: '#1a202c',
      borderRadius: '8px'
    },
    walletButtons: {
      googlePay: { type: 'pay', color: 'black', height: '44px' },
      applePay: { type: 'pay', color: 'black', height: '44px' }
    }
  }
})

限制支付方式

paymentMethod 只负责前端显示过滤,不会为商户开通支付方式:

传值行为
不传展示服务端返回且当前设备可用的全部方式
['CARD', 'FPX', 'GooglePay']只展示服务端也返回的 CARD、FPX 和 GooglePay
[]不展示任何方式,并触发 loaderror;event.error.code 为 no_available_payment_methods

数组值必须与服务端返回的支付方式标识完全一致。数组顺序不控制展示顺序;实际顺序仍由服务端支付方式配置和 SDK 钱包区域决定。是否成功加载某种方式,最终以 ready.availablePaymentMethods 为准。

配置表单显示

  • showBillingAddress: false 会隐藏账单地址并停止相应的前端校验,但不会修改 Create transaction 或 Update order 的服务端字段约束。需要账单信息的业务应在确认支付前自行采集并更新订单。
  • displayCardholdername: false 会隐藏持卡人姓名并停止相应的前端校验。

配置钱包按钮

字段默认值支持值或规则
googlePay.typepaybook, buy, checkout, donate, order, pay, plain, subscribe
googlePay.colorblackblack, white
applePay.typepayadd-money, book, buy, check-out, continue, contribute, donate, order, plain, reload, rent, subscribe, support, tip, top-up, pay
applePay.colorblackblack, white, white-outline
googlePay.width / applePay.width100%CSS 尺寸字符串
googlePay.height / applePay.height44pxCSS 尺寸字符串
googlePay.radius / applePay.radius8pxCSS 尺寸字符串;Apple / Google 平台可能限制最终外观

这些选项只调整 SDK 官方钱包按钮支持的外观,不会使当前设备不支持的钱包变为可用,也不能用 variables、styles 或自定义 CSS 强制覆盖平台控制的按钮外观。Apple Pay 的域名验证等接入准备见支付方式。

配置主题与样式

  • checkoutTheme 当前只支持 light。
  • variables 是推荐的品牌定制方式。支持的常用变量包括 containerBackground、cardBackground、inputBackground、inputBrandBackground、aggregateHeaderBackground、dialogBackground、colorText、colorPrimary、colorDanger、fontFamily、fontSizeBase 和 borderRadius。fontSizeBase 支持 12px–24px,borderRadius 支持 0px–24px。
  • styles 接收 CSS selector 到 CSS 属性对象的映射,例如 { '.onerway-checkout__input': { color: '#1a202c' } },用于变量无法覆盖的局部样式。
  • customCssURL 用于整体替换 SDK 基础样式表;之后仍可通过 variables 和 styles 覆盖。

主题和样式仅作用于 SDK 可控区域。历史配置 showPayButton 和 payButtonText 已不再支持并会被 SDK 忽略。

语言(Locale)

不传 locale 时,SDK 使用浏览器语言。显式传入的值或浏览器语言不受当前支付方式支持时,SDK 默认回退到英语 en。

普通支付方式支持以下 locale 取值(按字母序):

代码语言代码语言
ar阿拉伯语de德语
en英语es西班牙语
fi芬兰语fr法语
it意大利语ja日语
ko韩语nl荷兰语
no挪威语pl波兰语
pt葡萄牙语ru俄语
sv瑞典语th泰语
zh-cn简体中文zh-tw繁体中文

钱包使用自己的 locale 枚举和大小写,两个钱包的支持范围只有以下差异:

支持范围Locale
两个钱包都支持ar, ca, cs, da, de, el, en, es, fi, fr, hr, id, it, ja, ko, ms, nl, no, pl, pt, ru, sk, sv, th, tr, uk, zh
仅 Google Paybg, et, sl, sr
仅 Apple Payhe, hi, hu, ro, vi, zh-TW

钱包遇到不支持的 locale 时同样回退到英语 en。例如,普通支付方式的简体中文为 zh-cn,钱包为 zh;普通支付方式的繁体中文为 zh-tw,钱包为 zh-TW。不要自行转换其他未列出的值。

确认支付

Card、本地支付与自定义按钮

这些支付方式必须由商户按钮调用 confirmPayment():

document.querySelector('#pay_button').addEventListener('click', async () => {
  const result = await checkout.confirmPayment()
  handleConfirmResult(result)
})

不要重复创建 Checkout 或在重试时重新下单。可重试状态下,保留同一个 paymentId、Checkout 和 Payment Element。

Apple Pay、Google Pay 等 SDK 自有按钮

SDK 自有钱包按钮不能调用 confirmPayment()。用户点击 SDK 渲染的官方按钮后,只通过 payment_result 接收客户端结果:

checkout.on('payment_result', (result) => {
  if (result.reason?.type === 'canceled') {
    // 用户关闭钱包;恢复页面交互,不要把它映射为支付失败。
    return
  }

  renderClientResult(result)
})

钱包是否展示还取决于服务端配置、浏览器、设备和钱包能力。

在 App 内嵌 WebView 中使用 Google Pay

Google Pay 是否可用由能力检测决定:条件不满足时 ready.availablePaymentMethods 不会包含 GooglePay,SDK 不渲染其按钮,属预期行为而非故障。

  • Android WebView 官方支持 Google Pay,但需宿主 App 配合:Android WebView 137+、Google Play services 25.18.30+,集成 androidx.webkit:webkit:1.14.0、声明 Chromium payment intent actions、启用 Payment Request API,并向 Google 发布 App integration;使用自定义 User-Agent 时需附加 GOOGLE_PAY_SUPPORTED。详见 Google 官方 WebView 指南。
  • iOS 内嵌 WebView 不支持 Google Pay。
  • 宿主 App 不具备上述条件时,可将支付流程外跳系统浏览器完成,并确保支付后引导用户返回原 App。

paymentStatus 与 nextAction

只有 paymentStatus === 'R' 时才会返回 nextAction:

nextAction.typeSDK 行为商户处理
PresentToShopperSDK 在当前页展示二维码、本地支付页等承接界面;此时还没有最终支付结果。保留当前 Checkout,等待 payment_result,不要再次调用 confirmPayment()。
RedirectShopperSDK 即将跳转到外部支付页面;此时还没有最终支付结果。3DS 使用此流程。等待 Onerway 回跳到下单时的 returnUrl,再由服务端使用已保存的 paymentId 调用查询支付记录。

R 只表示支付流程需要继续,不代表成功或失败。nextAction 不会在 paymentStatus !== 'R' 时返回。

paymentStatus 的完整取值与定义见响应字段 paymentStatus。客户端状态只用于更新页面,不得据此发货、充值或记账:

paymentStatus商户处理
I、U、P、A非最终状态。订单保持待处理,不履约。
R进入承接或外跳流程,读取 nextAction.type 并按上表处理。
O允许继续支付或重试。保留同一个 paymentId、Checkout 和 Payment Element,不重新下单。
S、N客户端收到的结果,仍需服务端确认后再完成订单或按订单规则展示。

没有形成支付状态的客户端异常通过 reason 描述:

reason.type含义
validation_error本地表单校验失败,未形成支付状态
sdk_errorSDK 本地状态、配置或调用问题
api_error支付接口或后端业务失败;reason.code 是后端原始 respCode,参见响应码
canceled客户主动取消当前交互;reason.code 为 presenter_closed(关闭 SDK 承接的二维码或本地支付弹窗)、cvv_closed(关闭 Google Pay 授权后的二次 CVV 验证弹窗)或 wallet_canceled(取消 Apple Pay 或 Google Pay 官方授权窗)

客户取消时可能没有 paymentStatus,只有 reason.type === 'canceled';取消前端流程不等于最终支付失败,恢复页面交互即可。

确认最终支付结果

confirmPayment() 返回值、payment_result 和 returnUrl 都只能驱动客户端页面流转,最终结果以 Webhook 为准:普通支付见支付结果通知,客户自选保存卡见保存支付方式结果通知,订阅见订阅扣款通知,预授权见预授权、请款与撤销通知。验签、应答与重试、幂等去重、状态判断与查询补偿的通用规则见 Webhook 通知。

Web SDK 接入需额外注意:服务端应保存商户订单与 paymentId 的映射;外跳回到 returnUrl 后不要相信 URL 中携带的支付状态,先把页面交互恢复为“正在确认”,未收到 Webhook 时由服务端使用 paymentId 调用查询支付记录补偿并返回业务结果。不要在浏览器日志、持久化数据或埋点中保存未脱敏的 rawResult、请求响应或支付数据。

保存卡与订阅

让客户主动选择是否保存卡

保持字段 subProductType =DIRECT 并传入稳定的字段 merchantCustId。SDK 会向客户展示保存卡选项,该选项不会默认选中;后续支付复用同一个 merchantCustId,SDK 会回显该客户的已保存卡,并在内部完成选卡和支付,客户端无需为该流程获取 tokenId。

Web SDK 接入需额外注意:subProductType=TOKEN 属于旧版 Web SDK 保存卡流程,不用于当前 Web SDK 的客户自选保存卡流程。merchantCustId 的取值要求、保存支付方式结果通知与已保存 token 的查询、删除见保存支付方式。

创建固定计划的初始订阅

初始订阅支付使用 subProductType=SUBSCRIBE。服务端应维护允许购买的计划映射,并传入稳定、可读的字段 subscription.productName;计费方式由字段 subscription.selfExecute 决定。托管卡订阅可同时传入字段 subscription.bindCard =true,在订阅成功时保存客户卡,此时会收到两条独立通知。

托管订阅与自主管理订阅的选择、合约凭证、生命周期通知、续费与升降级、订阅并绑卡的双通知处理见订阅支付。

预授权

传入字段 txnType =AUTH(subProductType 保持 DIRECT)时,本次支付是预授权:冻结客户卡上的订单金额,不立即扣款;SDK 集成流程与普通支付一致。预授权成功后保存响应中的 transactionId 与 paymentId,后续请款或撤销由服务端调用预授权请款或撤销完成。

适用范围、请款与撤销的生命周期、通知、边界与状态判断见预授权与请款。

分账

分账是平台模式下的能力:平台商户以收款子商户的 merchantNo 创建支付,并传入字段 paymentMethodOptions 在其中的 share 设置 profitShare=true,该笔支付才可参与分账;SDK 集成流程与普通支付一致。同时设置 profitShareRate 时,SALE 或 CAPTURE 成功后由 Onerway 自动分账;不设置时由服务端通过 API 发起分账。需要接收自动分账及自动分账回退通知时,同时设置 profitShareNotifyUrl。paymentMethodOptions 按接口要求以 JSON 字符串提交。

自动分账与通过 API 发起分账的选择、结果通知、查询与分账回退见分账。