Payment Request API
一、为什么需要 Payment Request API
1.1 传统支付流程的问题
传统 Web 支付流程需要用户填写大量信息:卡号、有效期、CVV、账单地址等。每次购物都要重复填写,体验差,且表单容易出错。
1.2 Payment Request API 的解决方案
Payment Request API 允许浏览器存储用户的支付信息,用户只需几次点击即可完成支付:
- 浏览器存储支付凭证
- 用户选择支付方式
- 商家只获取必要的支付信息
- 标准化、安全的支付流程
二、基本流程
2.1 完整支付流程
// 1. 检查浏览器支持
if (!window.PaymentRequest) {
console.log('浏览器不支持 Payment Request API');
return;
}
// 2. 创建 PaymentRequest
const supportedMethods = [{
supportedMethods: 'basic-card',
data: {
supportedNetworks: ['visa', 'mastercard', 'amex'],
supportedTypes: ['credit', 'debit']
}
}];
const paymentDetails = {
total: {
label: '总金额',
amount: { currency: 'CNY', value: '299.00' }
},
displayItems: [
{
label: '商品 A',
amount: { currency: 'CNY', value: '199.00' }
},
{
label: '运费',
amount: { currency: 'CNY', value: '100.00' }
}
]
};
const request = new PaymentRequest(supportedMethods, paymentDetails);
// 3. 检查是否可以支付
async function checkCanMake() {
const canMake = await request.canMakePayment();
if (canMake) {
console.log('可以发起支付');
} else {
console.log('无法支付');
}
}
// 4. 显示支付界面
async function showPayment() {
try {
const response = await request.show();
console.log('支付响应:', response);
// 处理支付结果
await response.complete('success');
// 发送 paymentMethodData 到支付网关
await sendToPaymentGateway(response.details);
} catch (err) {
if (err.name === 'AbortError') {
console.log('用户取消支付');
} else {
console.error('支付错误:', err);
}
}
}
三、PaymentRequest 构造参数
3.1 PaymentMethodData
const supportedMethods = [
// 基础卡片支持
{
supportedMethods: 'basic-card',
data: {
supportedNetworks: ['visa', 'mastercard', 'unionpay'],
supportedTypes: ['credit', 'debit', 'prepaid']
}
},
// Apple Pay(iOS Safari)
{
supportedMethods: 'https://apple.com/apple-pay',
data: {
version: 3,
merchantIdentifier: 'merchant.com.example',
merchantCapabilities: ['supports3DS', 'supportsCredit', 'supportsDebit'],
supportedNetworks: ['amex', 'discover', 'masterCard', 'visa'],
countryCode: 'CN'
}
},
// Google Pay
{
supportedMethods: 'https://google.com/pay',
data: {
environment: 'TEST',
apiVersion: 2,
merchantInfo: {
merchantName: '示例商家'
},
allowedPaymentMethods: [{
type: 'CARD',
parameters: {
allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'],
allowedCardNetworks: ['VISA', 'MASTERCARD']
}
}]
}
}
];
3.2 PaymentDetails
const paymentDetails = {
// 金额(必需)
total: {
label: '总金额', // 人类可读标签
amount: {
currency: 'CNY',
value: '299.00' // 必须是字符串
}
},
// 显示项目(可选)
displayItems: [
{
label: '商品:无线蓝牙耳机',
amount: { currency: 'CNY', value: '299.00' }
},
{
label: '运费',
amount: { currency: 'CNY', value: '0.00' }
}
],
// shipping 选项(可选)
shippingOptions: [
{
id: 'standard',
label: '标准配送',
amount: { currency: 'CNY', value: '0.00' },
selected: true
},
{
id: 'express',
label: '快速配送',
amount: { currency: 'CNY', value: '20.00' }
}
],
// modifiers(可选)- 针对特定支付方式的调整
modifiers: [
{
supportedMethods: 'basic-card',
total: {
label: '总金额(含折扣)',
amount: { currency: 'CNY', value: '279.00' }
},
additionalDisplayItems: [
{
label: '会员折扣',
amount: { currency: 'CNY', value: '-20.00' }
}
]
}
]
};
3.3 PaymentOptions
const paymentOptions = {
requestPayerName: true, // 请求付款人姓名
requestPayerEmail: true, // 请求付款人邮箱
requestPayerPhone: true, // 请求付款人电话
requestShipping: true, // 请求收货地址
shippingType: 'delivery' // 配送类型:'shipping' | 'delivery' | 'pickup'
};
四、canMakePayment 和 show
4.1 canMakePayment
检查用户是否有可用的支付方式:
const request = new PaymentRequest(supportedMethods, paymentDetails);
// 方法1:直接调用
async function checkPayment() {
const canMake = await request.canMakePayment();
if (!canMake) {
// 回退到传统表单支付
showTraditionalPaymentForm();
return;
}
showPaymentButton();
}
// 方法2:使用 PaymentRequest.canMakePayment 静态方法
async function checkSupport() {
const result = await PaymentRequest.canMakePayment({
supportedMethods: 'basic-card'
});
console.log('支持 basic-card:', result);
}
4.2 show
显示支付界面:
async function processPayment() {
const request = new PaymentRequest(supportedMethods, paymentDetails, paymentOptions);
try {
// 显示支付 UI
const paymentResponse = await request.show();
// 获取支付数据
const { methodName, details } = paymentResponse;
console.log('支付方式:', methodName);
console.log('支付详情:', details);
// 验证支付(发送到服务器)
const result = await verifyPayment(details);
if (result.success) {
// 完成支付
await paymentResponse.complete('success');
showSuccessMessage();
} else {
// 支付失败
await paymentResponse.complete('fail');
showErrorMessage(result.error);
}
} catch (err) {
if (err.name === 'AbortError') {
// 用户取消
console.log('用户取消支付');
} else {
console.error('支付错误:', err);
}
}
}
4.3 abort
取消支付请求:
async function userCancelPayment() {
const request = new PaymentRequest(supportedMethods, paymentDetails);
// 显示支付 UI
const paymentResponse = await request.show();
// 用户点击取消按钮
await paymentResponse.abort();
console.log('支付已取消');
}
五、Payment Handler API
5.1 什么是 Payment Handler
Payment Handler 允许第三方支付应用(如各银行的支付插件)处理支付:
// 注册 Payment Handler
async function registerPaymentHandler() {
if (!('PaymentHandler' in window)) {
return;
}
await window.PaymentHandler.registerPaymentHandler({
supportedMethods: ['https://example.com/pay']
});
}
// 处理支付请求
self.addEventListener('paymentrequest', event => {
console.log('收到支付请求:', event.paymentRequest);
// 显示支付应用 UI
showPaymentAppUI(event.paymentRequest);
});
六、实际应用示例
6.1 完整的购物结账流程
class PaymentProcessor {
constructor() {
this.supportedMethods = this.getSupportedMethods();
}
getSupportedMethods() {
return [
{
supportedMethods: 'basic-card',
data: {
supportedNetworks: ['visa', 'mastercard', 'unionpay'],
supportedTypes: ['credit', 'debit']
}
}
];
}
async canMakePayment() {
if (!window.PaymentRequest) {
return false;
}
const request = new PaymentRequest(
this.supportedMethods,
this.getPaymentDetails(),
{ requestShipping: true }
);
return await request.canMakePayment();
}
getPaymentDetails() {
return {
total: {
label: '订单总金额',
amount: { currency: 'CNY', value: this.getCartTotal() }
},
displayItems: this.getCartItems()
};
}
getCartTotal() {
// 计算购物车总金额
return '299.00';
}
getCartItems() {
// 返回购物车商品列表
return [
{ label: '商品 A', amount: { currency: 'CNY', value: '199.00' } },
{ label: '运费', amount: { currency: 'CNY', value: '100.00' } }
];
}
async requestPayment() {
const request = new PaymentRequest(
this.supportedMethods,
this.getPaymentDetails(),
{
requestPayerName: true,
requestPayerEmail: true,
requestPayerPhone: true,
requestShipping: true,
shippingType: 'delivery'
}
);
request.addEventListener('shippingoptionchange', event => {
// 运费选项变化
const selectedOption = request.shippingOption;
this.updateShipping(selectedOption);
event.updateWith(this.getPaymentDetails());
});
request.addEventListener('shippingaddresschange', event => {
// 收货地址变化
const address = request.shippingAddress;
this.validateAddress(address).then(isValid => {
if (!isValid) {
event.updateWith({
total: this.getPaymentDetails().total,
error: '不支持的配送地址'
});
} else {
event.updateWith(this.getPaymentDetails());
}
});
});
try {
const response = await request.show();
await this.processPayment(response);
await response.complete('success');
return { success: true };
} catch (err) {
if (err.name === 'AbortError') {
return { success: false, error: 'cancelled' };
}
return { success: false, error: err.message };
}
}
async processPayment(response) {
// 发送支付数据到服务器
const result = await fetch('/api/pay', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(response.details)
});
return result.json();
}
}
七、浏览器支持
7.1 支持情况
Payment Request API 的支持情况:
- Chrome:完全支持
- Safari:完全支持
- Firefox:部分支持
- Edge:完全支持
7.2 特性检测
function isPaymentRequestSupported() {
return 'PaymentRequest' in window;
}
function isBasicCardSupported() {
return PaymentRequest.isTypeSupported('basic-card');
}