PAYATHON 2026

SecurionPay custom integration 无法设置金额和货币

支付老李

结论

amount 和 currency 不能直接通过 SecurionpayCheckout.open() 修改。这两个参数位于已签名的 checkoutRequest 中。需要由服务器使用 SecurionPay 私钥生成新的签名请求,再将其交给浏览器。

示例代码中的长字符串并非普通配置,而是一个内容固定的签名请求,其中包含:

{
  "charge": {
    "amount": 499,
    "currency": "EUR"
  }
}

只要重复使用这个字符串,Checkout 就会一直显示 4.99 EUR。

正确的处理流程

处理流程如下:

  1. 浏览器向服务器提交商品或订单标识。
  2. ASP.NET 后端根据订单数据计算应付金额和货币。
  3. 后端使用 SecurionPay 私钥生成签名后的 checkoutRequest。
  4. 页面将签名结果传给 SecurionpayCheckout.open()。
  5. 支付成功后,页面将返回结果发送到服务器,由服务器验证并完成订单。

私钥只能保存在服务器端,不能写入 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 仅适合演示,不能作为动态订单的通用请求反复使用。

备注:内容仅供参考。