PAYATHON 2026

Blazor 如何通过 POST 请求跳转到第三方支付页面

支付阿杰

结论

可以通过 POST 跳转到第三方支付页面,但 NavigationManager.NavigateTo() 只能执行普通页面导航,发起的是 GET 请求,无法携带 POST body。

浏览器通常通过 HTML 表单实现:

  1. 创建一个指向支付网关的 <form>。
  2. 将参数写入隐藏字段。
  3. 提交表单,由浏览器加载支付网关返回的 HTML 页面。

需要注意,POST 只能防止参数出现在 URL、浏览器历史记录和常规访问日志中,并不能向用户隐藏参数。用户仍然可以通过开发者工具查看表单字段和网络请求。商户密钥、签名私钥等敏感信息绝不能发送到 Blazor WebAssembly 客户端。

使用 HTML 表单提交

如果由用户点击按钮进入支付页面,可以直接在 Blazor 组件中使用普通表单:

<form action="https://sandbox.merchant.razer.com/RMS/pay/MerchantID/"
      method="post">
    <input type="hidden" name="Param1" value="@Param1" />
    <input type="hidden" name="param2" value="@Param2" />
    <input type="hidden" name="returnurl" value="@ReturnUrl" />
    <input type="hidden" name="cancelurl" value="@CancelUrl" />

    <button type="submit">前往支付</button>
</form>

@code {
    private string Param1 = "data";
    private string Param2 = "data";
    private string ReturnUrl = "https://example.com/payment/return";
    private string CancelUrl = "https://example.com/payment/cancel";
}

提交表单后,浏览器会向 action 指定的地址发送 POST 请求,并将网关返回的 HTML 显示为新页面。

浏览器表单默认使用以下编码格式:

application/x-www-form-urlencoded

只有在支付网关明确要求 multipart/form-data 时,才需要添加:

<form action="https://sandbox.merchant.razer.com/RMS/pay/MerchantID/"
      method="post"
      enctype="multipart/form-data">

不要只凭 Postman 中测试成功的配置判断编码格式,应以支付网关的接口文档为准。

自动提交并跳转

如果不想让用户再次点击按钮,可以通过 JavaScript 自动提交表单。

Blazor 组件:

@inject IJSRuntime JS

<form id="paymentForm"
      action="https://sandbox.merchant.razer.com/RMS/pay/MerchantID/"
      method="post">
    <input type="hidden" name="Param1" value="@Param1" />
    <input type="hidden" name="param2" value="@Param2" />
    <input type="hidden" name="returnurl" value="@ReturnUrl" />
    <input type="hidden" name="cancelurl" value="@CancelUrl" />
</form>

<p>正在跳转到支付页面……</p>

@code {
    private bool _submitted;

    private string Param1 = "data";
    private string Param2 = "data";
    private string ReturnUrl = "https://example.com/payment/return";
    private string CancelUrl = "https://example.com/payment/cancel";

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender && !_submitted)
        {
            _submitted = true;
            await JS.InvokeVoidAsync("submitPaymentForm", "paymentForm");
        }
    }
}

在 wwwroot/js/site.js 中添加:

window.submitPaymentForm = function (formId) {
    const form = document.getElementById(formId);

    if (!form) {
        throw new Error(`Form '${formId}' was not found.`);
    }

    HTMLFormElement.prototype.submit.call(form);
};

确认该脚本已经在 wwwroot/index.html 中加载:

<script src="js/site.js"></script>

使用 HTMLFormElement.prototype.submit.call(form),可以防止表单中名为 submit 的字段覆盖原生 submit() 方法。

不要用 HttpClient 模拟页面跳转

下面的代码虽然能发送 POST 请求,却不会让浏览器进入支付页面:

var response = await HttpClient.PostAsync(paymentUrl, content);
var html = await response.Content.ReadAsStringAsync();

这段代码只是让 Blazor 应用获取响应内容,浏览器地址不会切换到第三方页面。跨域请求还可能受到 CORS 限制,支付页面使用的 Cookie、重定向、脚本和相对资源也未必能在应用中正常运行。

如果网关要求浏览器通过 POST 进入支付页面,应提交表单,而不是调用 HttpClient.PostAsync()。

敏感参数应由服务器处理

Hosted Blazor WebAssembly 的客户端代码和数据都在用户浏览器中运行。以下内容不能安全地存放在组件、隐藏字段或客户端配置中:

  • Merchant secret
  • API secret
  • 私钥
  • 用于生成签名的密钥
  • 可被重复使用的访问凭证

type="hidden" 只是让字段不直接显示在页面上,并不能保密。用户仍可通过 HTML、JavaScript、浏览器开发者工具或网络面板查看字段内容。

更安全的处理流程如下:

Blazor 客户端
    → 请求自己的 ASP.NET Core 后端创建支付
    → 后端校验订单金额和用户身份
    → 后端使用商户密钥生成签名或调用支付网关
    → 后端返回一次性支付 URL、session/token 或允许公开的表单字段
    → 浏览器跳转或提交支付表单

例如,客户端只需将内部订单编号发送给自己的后端:

var response = await HttpClient.PostAsJsonAsync(
    "/api/payments/create",
    new { OrderId = orderId });

var result = await response.Content
    .ReadFromJsonAsync<CreatePaymentResult>();

后端必须根据数据库中的订单重新确定金额、币种和收款信息,不能信任客户端提交的金额。确认这些信息后,再按照网关协议生成签名或创建支付会话。

如果网关返回一次性支付地址,可以直接跳转:

NavigationManager.NavigateTo(result.PaymentUrl, forceLoad: true);

如果网关要求浏览器提交一组签名字段,这些字段应由后端生成,再由客户端通过表单提交。即使签名本身可以公开,用于计算签名的密钥也必须始终保存在服务器端。

其他注意事项

  • 参数名称、大小写、字符编码和签名顺序必须严格遵循支付网关文档。
  • returnurl 通常只负责支付完成后的页面跳转,不能单独作为支付成功的可信依据。
  • 订单的最终状态应由后端通过支付网关回调、Webhook 或服务端查询接口确认。
  • 返回地址和取消地址应使用 HTTPS,并由服务端限制为可信域名,避免开放重定向问题。
  • 如果站点配置了 Content Security Policy,需要确认 form-action 允许向支付网关域名提交表单。
  • 不要在前端日志、异常信息或分析系统中记录完整的支付参数。
  • 前端应防止重复点击和重复提交,后端则要保证创建支付订单的操作具有幂等性。

页面需要通过 POST 跳转时,应提交 HTML 表单。所有需要保密或用于决定支付金额的逻辑,都应放在 ASP.NET Core 后端完成。

备注:内容仅供参考。