乐趣区

你可能还不知道的 Web 支付流程标准化

PaymentRequest API 是一种跨浏览器的标准 API,主要的目的是以浏览器充当中介,尽可能标准化支付通信的流程。????
整个流程主要是创建 PaymentRequest,将购买货物的详细信息传递给浏览器,在 UI 层面显示支付的 UI,用户填入支付信息或从缓存中一键填充并确认支付。
他的最大的优势是信用卡、收货地址等支付信息都统一存储在浏览器,如果网站都能够使用该 API,那么就不再需要重复填写支付信息。
⚠️ 使用 PaymentRequest 非常简单,但由于兼容性问题,目前不要在生产环境中使用,API 层面亦会有可能改动。
创建 PaymentRequest 实例
第一步是通过调用 PaymentRequest 构造函数创建一个 PaymentRequest 对象
构造函数需要三个参数分别是 methodData、details 和可选的 options
methodData 支付方式
首先需要设定支付方式,传入卖家支持的支付手段,如 visa 或 mastercard 等信用卡或其他方式:
methodData 是一个数组,数组中每一项应为一个对象,对象内包含两个属性:

supportedMethods 付款方式识别符

data 额外信息

supportedNetworks 支付网络

supportedMethods 需要填写 付款方式识别符,一般填写 basic-card,也可填写 url 的识别符如:

如果是 google pay url 付款识别符,那么调用的时候长这样:

这里以 basic-card 为例,那么 data 属性则需要填写一些额外的信息,如果是 basic-card 方式,那么还可以选择配置 supportedNetworks,这个选项指定了 card networks
card networks 是一个数组,如支持 visa、mastercard 等
[‘visa’, ‘mastercard’, ‘amex’, ‘jcb’, ‘diners’, ‘discover’, ‘mir’, ‘unionpay’]
什么是 card networks 见这篇文章:https://www.cardinalcommerce….

根据规范,我们编写以下代码 ????
const methodData = [{
supportedMethods: ‘basic-card’,
data: {
supportedNetworks: [‘visa’, ‘mastercard’, ‘amex’, ‘jcb’, ‘diners’, ‘discover’, ‘mir’, ‘unionpay’]
}
}]
在调用的时候长这样:

另外,Apple Pay 也支持该特性,详细见文档:https://webkit.org/blog/8182/…

details 交易详情
details 保存的是交易详情,主要有以下字段:

total 需要支付的总额

id 交易 ID,如果不填写浏览器自动生成

displayItems 主要是一些货品信息、税费、运费等详细清单

shippingOptions 则是运输相关的选项,有事件监听如果不能送达,则应在 UI 层面给用户提示

modifier 主要是针对用户付款方式,修改交易详情,比方说针对某种支付手段给予优惠,展示不同金额等

additionalDisplayItems 额外需要展示的新增订单项目

data 额外信息

total 修改后的总价

total
这一字段需要填写支付总额,API 不会自动计算,需要计算后填入 total 字段需要满足 PaymentItem 规范

也就是说至少需要一个 label 字符串和一个 PaymentCurrencyAmount 金额,另外还有一个可选项 pending 用来表示是否为最终金额,默认为 false:
PaymentCurrencyAmount 则需要两个属性,一个是 currency 一个是 value,两个都是字符串:
const details = {
total: {
label: ‘ 合计总金额 ???? ‘,
amount: {currency: ‘CNY’, value: ‘100.00’}
}
}
增加 details 对象,编写代码如上所示
效果如下:

id
id 则代表了交易 ID,可以自定义,字符串格式:
const details = {
id: ‘Order-Funny-ID-000-001’,
total: {
displayItems
订单详情,每个单个条目最终是否展示取决于浏览器。这是一个数组,数组内每一个对象都是一个 PaymentItem,因此数组内每一项的规范参照 total:
const details = {
id: ‘Order-Funny-ID-000-001’,
displayItems: [{
label: ‘ 西瓜 ????’,
amount: {currency: ‘CNY’, value: ‘20.00’}
}, {
label: ‘ 草莓 ????’,
amount: {currency: ‘CNY’, value: ‘90.00’}
}, {
label: ‘VIP 会员 ????’,
amount: {currency: ‘CNY’, value: ‘-10.00’},
pending: true
}],
total: {
label: ‘ 合计总金额 ???? ‘,
amount: {currency: ‘CNY’, value: ‘100.00’}
}
}
上面代码执行效果长这样:

shippingOptions
根据规范 shippingOptions 应该有三个必选参数,id、label、amount(同样,amount 必须符合 PaymentCurrencyAmount),selected 默认为 false,是可选参数:

const details = {
id: ‘Order-Funny-ID-000-001’,
displayItems: [{
}],
total: {
},
shippingOptions: [{
id: ‘ 标准快递 ’,
label: ‘???? 免费普通快递 1 天全国范围内 ’,
amount: {
currency: ‘CNY’,
value: ‘0.00’
},
selected: true
}, {
id: ‘ 东风快递 ’,
label: ‘???? 超快速递 3 小时全球范围内 ’,
amount: {
currency: ‘CNY’,
value: ‘100.00’
}
}]
}

const options = {
requestShipping: true // 别忘记这里需要设置为 true
}

let request = new PaymentRequest(
methodData, // 支付方式
details, // 交易信息
options // 其他额外信息
)
按上述规范配置 shippingOptions,并监听配送选项改变或地址改变动态调整收费价格标准

// 监听配送选项改变,动态修改收费标准
request.onshippingoptionchange = function (e) {
console.log(‘ 快递选项改变,重新计算价格 ’)

e.updateWith(Promise.resolve(updateDetail(details, request.shippingOption))) // 接收 promise
}
// 监听地址选项改变,动态修改收费标准
request.onshippingaddresschange = function (e) {
console.log(‘ 地址选项改变,重新计算价格 ’)

e.updateWith(Promise.resolve(updateDetail(details, request.shippingOption))) // 接收 promise
}

function updateDetail(details, shippingOpts) {
console.log({details, shippingOpts}) // shippingOpts 代表选择的快递 id
// fetch 后台数据
// 各种判断
// 修改 details 最后 return 出去
// if (shippingOpts) {} else {}
// 这里仅作演示没修改数据
return details
}
modifier
用于修改账单,这里以 visa 卡为例,使用此类型信用卡会在账单中增加一条 additionalDisplayItems,并通过 total 修改账单总额
modifier 需要一个 supportedMethods 为必选参数:

modifiers: [
{
additionalDisplayItems: [{
label: ‘visa 手续费 ’,
amount: {currency: ‘CNY’, value: ‘10.00’}
}],
supportedMethods: “basic-card”,
total: {
label: “Total due”,
amount: {currency: “USD”, value: “110.00”},
},
data: {
supportedNetworks: [‘visa’],
},
},
]
效果如下:

options
主要有六个参数可供配置,分别是 requestPayerName、requestPayerEmail、requestPayerPhone、requestShipping、requestBillingAddress、shippingType,前五项默认为 false,最后一项可设置为 shipping、delivery、pickup,三者根据语境选择合适的 UI 层面对“快递”的描述,这三个单词在中国大陆分别代表送货、递送和取货
request 实例的属性和方法
上文提到的 shippingaddresschange 和 shippingoptionchange 就是该实例的两个事件,除此之外还有 paymentmethodchange 和 merchantvalidation。除事件之外,实例的四个属性分别是 id、shippingAddress、shippingOption、shippingType 可以访问到这个支付请求的相关配置。该实例的三个方法比较重要,用来鉴定是否能够发起支付的 canMakePayment、调起 UI 支付界面的 show 以及放弃支付的 abort。
canMakePayment
检测是否支持此种支付方式,在使用之前必须先要判断浏览器是否支持 canMakePayment 本身:
if (request.canMakePayment) {

} else {
+ ;(() => { console.log(‘ 浏览器不支持 canMakePayment 特性 ’) })()
}
然后调用方法检测支付:
if (request.canMakePayment) {
+ request.canMakePayment().then(res => {

+ }).catch(console.error)
} else {
; (() => { console.log(‘ 浏览器不支持 canMakePayment 特性 ’) })()
}
根据检测结果判断是否进一步调用 show,发起支付:
if (request.canMakePayment) {
request.canMakePayment().then(res => {
+ if (res) {
+ request.show() // true 如果支持
+ } else {
+ console.log(‘ 未能发起支付 ’)
+ }
}).catch(console.error)
} else {
; (() => { console.log(‘ 浏览器不支持 canMakePayment 特性 ’) })()
}
show
最终获取支付成功的 response 是通过 show 方法返回的,最后确认无误后,调用 response 的 complete 方法,并传入 success、fail 或 unknow 字段来结束支付:
if (request.canMakePayment) {
request.canMakePayment().then(res => {
if (res) {
+ request.show().then(response => {
+ console.log(response)
+ setTimeout(() => { // 模拟发送支付信息到服务端,并调用 response 的 complete 方法完成支付
+ response.complete()
+ }, 2000)
})
} else {
console.log(‘ 未能发起支付 ’)
}
}).catch(console.error)
} else {
; (() => { console.log(‘ 浏览器不支持 canMakePayment 特性 ’) })()
}
此外 response 还有 retry 方法,可以在遇到支付 response 出现错误的时候重新发起支付
关于 response 的属性和方法见如下截图:

附上用于测试的信用卡卡号
最后附上用于测试的信用卡卡号,日期随便填,CVC 随便填
Test Credit Card Account Numbers http://www.blogjava.net/sealy…
最后代码在此:https://jsfiddle.net/aL5gczm3/

退出移动版