Web的支付:`Payment Request API`的使用与安全。

Web支付:Payment Request API 的使用与安全

大家好!今天我们来深入探讨一个现代Web支付的关键技术——Payment Request API (PRAPI)。它为Web开发者提供了一种标准化、安全且用户友好的方式来处理在线支付。我们将从PRAPI的基本概念、使用方法、安全考量以及一些最佳实践等方面进行讲解。

1. Payment Request API 简介

Payment Request API 允许网站通过浏览器调用用户已配置的支付方式,如信用卡、借记卡、Apple Pay、Google Pay等。它简化了支付流程,减少了用户输入,提高了转化率。与传统的支付集成相比,PRAPI具有以下优势:

  • 标准化: 提供统一的API接口,无需为每种支付方式编写单独的代码。
  • 安全性: 浏览器处理敏感信息,减少网站的安全负担。
  • 用户体验: 利用浏览器存储的支付信息,简化支付流程。
  • 跨平台: 支持多种支付方式和设备。

2. Payment Request API 的基本使用

PRAPI 的核心在于创建一个 PaymentRequest 对象,并通过它来启动支付流程。下面是一个基本的示例:

// 1. 定义支付方法数据
const methodData = [{
  supportedMethods: ['basic-card'],
  data: {
    supportedNetworks: ['visa', 'mastercard', 'amex'],
    supportedTypes: ['credit', 'debit']
  }
}];

// 2. 定义支付详情
const details = {
  total: {
    label: 'Total',
    amount: { currency: 'USD', value: '10.00' }
  }
};

// 3. 定义支付选项(可选)
const options = {
  requestShipping: false,
  requestPayerEmail: true,
  requestPayerName: true
};

// 4. 创建 PaymentRequest 对象
const request = new PaymentRequest(methodData, details, options);

// 5. 监听 paymentmethodchange 事件 (可选)
request.addEventListener('paymentmethodchange', (event) => {
  // 处理支付方式变更
  console.log('Payment method changed:', event.methodDetails);
  // 你可以在这里更新支付详情,例如根据支付方式调整运费
  event.updateWith({
    total: {
      label: 'Total',
      amount: { currency: 'USD', value: '12.00' } // 示例:加上运费
    }
  });
});

// 6. 显示支付界面
request.show()
  .then(paymentResponse => {
    // 7. 处理支付结果
    console.log('Payment completed:', paymentResponse);
    // 发送支付信息到服务器进行验证和授权
    return handlePayment(paymentResponse);
  })
  .then(result => {
    // 8. 完成支付
    paymentResponse.complete('success'); // 或 'fail'
    console.log('Payment successful:', result);
  })
  .catch(error => {
    // 9. 处理错误
    console.error('Payment error:', error);
    if(paymentResponse){
        paymentResponse.complete('fail');
    }
  });

async function handlePayment(paymentResponse) {
    //模拟服务器端处理逻辑
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if(Math.random() > 0.1){
                resolve({transactionId: 'transaction-' + Date.now()});
            } else {
                reject(new Error('Payment failed on server.'));
            }
        }, 1000);
    });
}

代码解释:

  1. methodData 这是一个数组,定义了网站支持的支付方法。supportedMethods 指定了支付方法的标识符,data 包含特定支付方法所需的详细信息。 basic-card 是一种通用的支付方法,用于处理信用卡和借记卡。 supportedNetworks 指定了支持的信用卡网络,supportedTypes 指定了支持的卡类型。

  2. details 包含了支付的详细信息,例如总金额、币种等。 total 对象定义了总金额的标签和金额。

  3. options 允许你请求用户的收货地址、电子邮件地址和姓名。

  4. PaymentRequest 创建 PaymentRequest 对象,它接受 methodDatadetailsoptions 作为参数。

  5. paymentmethodchange事件监听器:这是一个可选的监听器,允许你在用户更改支付方式时执行自定义逻辑。 例如,您可以根据用户选择的支付方式动态更新总金额(例如,加上或减去费用)。 event.updateWith()方法用于更新支付详情。

  6. request.show() 显示浏览器的支付界面。这是一个异步操作,返回一个 Promise。

  7. paymentResponse 如果用户成功完成支付,Promise 将会 resolve,并返回一个 PaymentResponse 对象,其中包含了支付结果。

  8. handlePayment(paymentResponse) 这是一个自定义函数,用于将支付信息发送到服务器进行验证和授权。重要: 不要在客户端直接处理支付,必须在服务器端进行验证和授权。

  9. paymentResponse.complete() 通知浏览器支付是否成功。如果支付成功,调用 paymentResponse.complete('success');如果支付失败,调用 paymentResponse.complete('fail')

  10. 错误处理: 使用 .catch() 捕获支付过程中发生的任何错误。

3. 支付方法的配置

methodData 是 PRAPI 中一个非常重要的部分,它定义了网站支持的支付方法。除了 basic-card,PRAPI 还支持其他支付方法,例如:

  • https://apple.com/apple-pay Apple Pay
  • https://google.com/pay Google Pay
  • urn:example:payment-method 自定义支付方法(需要浏览器或支付应用的支持)

对于每种支付方法,data 对象都需要包含特定的信息。例如,对于 Apple Pay,你需要提供 merchantIdentifier, supportedNetworks, countryCode 等信息。

示例:Apple Pay 集成

const methodData = [{
  supportedMethods: ['https://apple.com/apple-pay'],
  data: {
    version: 3,
    merchantIdentifier: 'merchant.com.example',
    merchantCapabilities: ['supports3DS', 'supportsCredit', 'supportsDebit'],
    supportedNetworks: ['visa', 'mastercard', 'amex', 'discover'],
    countryCode: 'US'
  }
}];

4. Payment Request API 的安全考量

虽然 PRAPI 简化了支付流程,但也需要注意以下安全问题:

  • 中间人攻击: 使用 HTTPS 协议来保护支付信息的传输。
  • 数据篡改: 在服务器端验证所有支付信息,包括金额、币种、订单详情等。
  • 重放攻击: 使用一次性令牌 (nonce) 或时间戳来防止重放攻击。
  • 恶意脚本: 对用户输入进行验证和过滤,防止 XSS 攻击。
  • 服务器端安全: 确保服务器端安全,防止数据泄露和未经授权的访问。

5. 防止中间人攻击 (HTTPS)

确保你的网站使用 HTTPS 协议。HTTPS 使用 SSL/TLS 加密数据传输,防止中间人窃取或篡改支付信息。

6. 服务器端验证

永远不要信任客户端发送的任何数据。在服务器端验证所有支付信息,包括:

  • 金额: 确保金额与订单金额一致。
  • 币种: 确保币种正确。
  • 订单详情: 验证订单详情是否与用户选择的商品或服务一致。
  • 支付状态: 确认支付是否成功。

7. 一次性令牌 (Nonce)

使用一次性令牌 (nonce) 来防止重放攻击。Nonce 是一个随机字符串,每次支付请求都生成一个新的 nonce。在服务器端存储已使用的 nonce,如果收到相同的 nonce,则拒绝该请求。

示例:使用 Nonce 防止重放攻击

客户端:

async function handlePayment(paymentResponse) {
  const nonce = generateNonce(); // 生成随机 nonce
  const paymentData = {
    paymentMethod: paymentResponse.methodName,
    details: paymentResponse.details,
    nonce: nonce
  };

  // 发送到服务器进行验证
  const response = await fetch('/api/process-payment', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(paymentData)
  });

  const result = await response.json();
  return result;
}

function generateNonce() {
  return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
}

服务器端:

const usedNonces = new Set();

app.post('/api/process-payment', (req, res) => {
  const { paymentMethod, details, nonce } = req.body;

  // 验证 nonce 是否已经使用过
  if (usedNonces.has(nonce)) {
    return res.status(400).json({ error: 'Invalid nonce' });
  }

  // 将 nonce 添加到已使用列表
  usedNonces.add(nonce);

  // 处理支付逻辑
  // ...

  // 返回结果
  res.json({ transactionId: 'transaction-' + Date.now() });
});

8. Payment Request API 的最佳实践

  • 提供清晰的支付流程: 向用户展示清晰的支付流程,包括订单详情、金额、支付方式等。
  • 支持多种支付方式: 提供多种支付方式,以满足不同用户的需求。
  • 优化用户体验: 简化支付流程,减少用户输入,提高转化率。
  • 监控支付性能: 监控支付性能,及时发现和解决问题。
  • 遵循支付安全标准: 遵循 PCI DSS 等支付安全标准,确保支付安全。
  • 使用polyfill: 某些老旧浏览器可能不支持PRAPI,使用polyfill可以提供兼容性支持。
  • 测试: 在不同的浏览器和设备上进行测试,确保支付流程正常工作。

9. Payment Request API 与 PCI DSS

PCI DSS (Payment Card Industry Data Security Standard) 是一套安全标准,旨在保护信用卡数据。如果你的网站处理信用卡数据,你需要遵循 PCI DSS 标准。

Payment Request API 可以帮助你减少 PCI DSS 的合规负担,因为浏览器会处理敏感的信用卡数据。但是,你仍然需要确保你的服务器端安全,并遵循 PCI DSS 的其他要求。

10. 使用 Payment Request API 的优势和劣势

特性 优势 劣势
安全性 浏览器处理敏感数据,降低网站安全风险,符合PCI DSS要求。 依赖浏览器支持,某些老旧浏览器可能不支持。
用户体验 简化支付流程,减少用户输入,提高转化率。 需要用户已经配置了支付方式(例如,信用卡保存在浏览器中)。
标准化 提供统一的API接口,无需为每种支付方式编写单独的代码。 不同的支付方式可能需要不同的配置。
跨平台 支持多种支付方式和设备。
开发效率 降低开发成本,减少集成工作量。 需要了解不同支付方式的配置细节。
支付方式支持 支持多种流行的支付方式,如信用卡、Apple Pay、Google Pay等。 可能不支持某些特定的支付方式。

11. Payment Response 对象的结构

PaymentResponse 对象包含了支付结果的详细信息。它的结构如下:

{
  methodName: string, // 支付方法的标识符,例如 'basic-card' 或 'https://apple.com/apple-pay'
  details: object,    // 支付方法的详细信息,例如信用卡信息或 Apple Pay 令牌
  requestId: string,   // 支付请求的 ID
  shippingAddress: object, // 收货地址 (如果请求了收货地址)
  shippingOption: string,  // 收货选项 (如果请求了收货选项)
  payerName: string,     // 付款人姓名 (如果请求了付款人姓名)
  payerEmail: string,    // 付款人电子邮件地址 (如果请求了付款人电子邮件地址)
  payerPhone: string,    // 付款人电话号码 (如果请求了付款人电话号码)
  complete: function(status) // 用于通知浏览器支付是否成功的函数
}

details 对象的内容取决于 methodName 例如,对于 basic-carddetails 对象可能包含以下信息:

{
  cardNumber: string, // 信用卡卡号 (已加密)
  cardholderName: string, // 持卡人姓名
  cardExpiryMonth: string, // 信用卡过期月份
  cardExpiryYear: string,  // 信用卡过期年份
  cardSecurityCode: string // 信用卡安全码 (CVV/CVC) (已加密)
}

注意: 你无法直接访问未加密的信用卡卡号和安全码。浏览器会处理这些敏感信息,并将加密后的数据传递给你。

12. 关于错误处理的补充

catch 块中处理错误时,要区分不同的错误类型,并向用户提供有用的错误信息。例如:

  • AbortError 用户取消了支付流程。
  • SecurityError 支付请求不安全 (例如,未使用 HTTPS)。
  • 其他错误: 支付过程中发生了其他错误,例如网络错误或支付方式不支持。
request.show()
  .then(paymentResponse => {
    // ...
  })
  .catch(error => {
    console.error('Payment error:', error);
    if (error.name === 'AbortError') {
      // 用户取消了支付
      console.log('Payment cancelled by user.');
    } else if (error.name === 'SecurityError') {
      // 安全错误
      console.error('Payment request is not secure. Ensure you are using HTTPS.');
    } else {
      // 其他错误
      console.error('Payment failed:', error.message);
    }
    if(paymentResponse){
        paymentResponse.complete('fail');
    }
  });

13. 总结与回顾

Payment Request API 提供了一种标准化、安全且用户友好的Web支付方式。 通过使用PRAPI,可以简化支付流程,降低开发成本,并提高用户体验。 记住,安全性是第一位的,始终在服务器端验证支付信息,并遵循最佳安全实践。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注