SecurionPay custom integration 无法设置金额和货币
支付老李
结论
amount 和 currency 不能直接通过 SecurionpayCheckout.open() 修改。这两个参数位于已签名的 checkoutRequest 中。需要由服务器使用 SecurionPay 私钥生成新的签名请求,再将其交给浏览器。
示例代码中的长字符串并非普通配置,而是一个内容固定的签名请求,其中包含:
{
"charge": {
"amount": 499,
"currency": "EUR"
}
}
只要重复使用这个字符串,Checkout 就会一直显示 4.99 EUR。
正确的处理流程
处理流程如下:
- 浏览器向服务器提交商品或订单标识。
- ASP.NET 后端根据订单数据计算应付金额和货币。
- 后端使用 SecurionPay 私钥生成签名后的
checkoutRequest。 - 页面将签名结果传给
SecurionpayCheckout.open()。 - 支付成功后,页面将返回结果发送到服务器,由服务器验证并完成订单。
私钥只能保存在服务器端,不能写入 HTML 或 JavaScript。浏览器中只能使用 pk_test_... 形式的公钥。
ASP.NET Web Forms 示例
以下示例使用 SecurionPay 的 .NET SDK。不同版本的 SDK 在命名空间或属性写法上可能稍有差异,请以项目实际安装的版本为准。核心步骤是在服务端调用 SignCheckoutRequest。
后端代码
using System;
using System.Web.Script.Serialization;
// using SecurionPay;
// using SecurionPay.Request;
public partial class Payment : System.Web.UI.Page
{
protected string SignedCheckoutRequestJson;
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
return;
// 实际项目中应从数据库读取订单金额,不要相信客户端传入的金额。
int amount = 499;
string currency = "EUR";
var gateway = new SecurionPayGateway(
Environment.GetEnvironmentVariable("SECURIONPAY_SECRET_KEY")
);
var checkoutRequest = new CheckoutRequest
{
Charge = new ChargeRequest
{
Amount = amount,
Currency = currency
}
};
string signedRequest =
gateway.SignCheckoutRequest(checkoutRequest);
// 安全地生成 JavaScript 字符串,避免引号等字符破坏脚本。
SignedCheckoutRequestJson =
new JavaScriptSerializer().Serialize(signedRequest);
}
}
金额通常以货币的最小单位表示。例如:
499 EUR cents = 4.99 EUR
1000 USD cents = 10.00 USD
有些货币没有两位小数,需要按照支付接口针对该币种规定的最小单位处理,不能默认所有货币都要乘以 100。
Web Forms 页面
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://securionpay.com/checkout.js"></script>
<button id="payment-button" type="button">Payment</button>
<script>
$(function () {
SecurionpayCheckout.key = 'pk_test_your_public_key';
SecurionpayCheckout.success = function (result) {
// 将 result 发送到服务器,由服务器验证并更新订单状态。
console.log(result);
};
SecurionpayCheckout.error = function (errorMessage) {
console.error(errorMessage);
};
$('#payment-button').on('click', function () {
SecurionpayCheckout.open({
checkoutRequest: <%= SignedCheckoutRequestJson %>,
name: 'Your Store',
description: 'Order payment'
});
});
});
</script>
这里的 type="button" 很重要。如果按钮位于 Web Forms 的 <form runat="server"> 中,又没有指定该属性,就可能触发页面回发。
根据订单动态生成请求
如果各个订单的金额或币种不同,就要为每个订单生成新的签名请求:
Order order = OrderRepository.GetById(orderId);
var checkoutRequest = new CheckoutRequest
{
Charge = new ChargeRequest
{
Amount = order.AmountInMinorUnits,
Currency = order.Currency
}
};
string signedRequest =
gateway.SignCheckoutRequest(checkoutRequest);
不要使用下面的写法:
SecurionpayCheckout.open({
amount: 999,
currency: 'USD'
});
Checkout 忽略这些字段是正常的,因为实际生效的是签名请求中的 charge.amount 和 charge.currency。
注意事项
- 不要将
sk_test_...或生产环境私钥写入 JavaScript、ASPX 页面、隐藏字段或前端请求。 - 不要让客户端直接决定最终金额。客户端可以提交订单号、商品编号或数量,但服务器必须重新计算金额。
- 金额、货币或其他受保护参数发生变化后,都需要重新生成
checkoutRequest。 - 测试公钥应搭配测试私钥,生产公钥应搭配生产私钥。
- Checkout 的成功回调只表示前端收到了成功结果。订单入账前,服务器仍需核验支付数据。如果当前 SecurionPay 接口提供 webhook,还应以服务端通知和查询结果为准。
- 文档中硬编码的
checkoutRequest仅适合演示,不能作为动态订单的通用请求反复使用。
备注:内容仅供参考。
