PAYATHON 2026

Angular form 按钮无法提交并通过 POST 重定向

支付阿杰

结论

支付网关要求通过 POST 携带数据并跳转页面时,应使用浏览器原生 <form> 提交,不能用 Angular 的 HttpClient.post() 代替。

HttpClient.post() 发起的是 AJAX/XHR 请求。即使服务器返回重定向,浏览器也只会在请求内部处理响应,不会将当前页面导航到支付页面。Angular 的 NgForm、formGroup 和 (ngSubmit) 还可能拦截原生提交,因此外部支付表单应避开 Angular 的表单提交机制。

推荐写法:使用原生表单提交

假设 LiqPay 接口接收 data 和 signature 两个字段,可以这样写:

<form
  ngNoForm
  method="POST"
  action="https://www.liqpay.ua/api/3/checkout"
  accept-charset="UTF-8"
>
  <input type="hidden" name="data" [value]="data">
  <input type="hidden" name="signature" [value]="signature">

  <button type="submit">付款</button>
</form>

这段代码需要注意以下几点:

  • method="POST" 表示通过 POST 提交数据。
  • action="..." 用于指定支付网关地址。
  • 浏览器会根据 name="data" 和 name="signature" 生成 POST 参数。控件只有 value 而没有 name 时,不会被提交。
  • type="submit" 让按钮执行表单提交。
  • ngNoForm 告诉 Angular 不要把该表单转换成 NgForm,由浏览器按原生方式提交并跳转。

不要在这个表单上同时使用 (ngSubmit)、[formGroup],也不要给提交按钮绑定 (click) 事件,否则可能重新进入 Angular 的表单处理流程。

如果数据生成后需要立即跳转

可以在代码中创建并提交一个临时原生表单:

redirectToLiqPay(): void {
  const form = document.createElement('form');

  form.method = 'POST';
  form.action = 'https://www.liqpay.ua/api/3/checkout';
  form.acceptCharset = 'UTF-8';

  const dataInput = document.createElement('input');
  dataInput.type = 'hidden';
  dataInput.name = 'data';
  dataInput.value = this.data;

  const signatureInput = document.createElement('input');
  signatureInput.type = 'hidden';
  signatureInput.name = 'signature';
  signatureInput.value = this.signature;

  form.appendChild(dataInput);
  form.appendChild(signatureInput);
  document.body.appendChild(form);

  form.submit();
}

然后在模板中用普通按钮调用:

<button type="button" (click)="redirectToLiqPay()">
  付款
</button>

如果需要在新窗口中打开支付页面,可以在提交前设置:

form.target = '_blank';

是否允许在新窗口支付,应以支付平台的接入要求为准。

为什么原来的 HttpClient.post() 不会跳转

下面的代码只会在后台请求接口响应:

this._http.post(url, body, options);

它不是浏览器页面提交,所以不会将当前页面导航到 url。原代码还存在以下问题:

  1. Angular HttpClient 返回的 Observable 通常需要订阅,请求才会实际执行:
this._http.post(url, body, options).subscribe(...);
  1. 请求体应该使用表单值,而不是 FormGroup 对象本身:
this.payForm.value
  1. 声明 Content-Type: application/json 并不表示支付网关支持 JSON。传统支付跳转接口通常接收浏览器表单编码的数据,具体格式应以该接口当前的接入文档为准。

  2. 前端向第三方域名发起 AJAX 请求时,还可能受到 CORS 限制。浏览器原生表单导航不需要第三方开放 AJAX CORS。

即使补上 subscribe(),这种方式也只适合调用 API,无法实现 POST 后进入支付页面。

注意事项

signature 等支付签名通常应由后端生成。不要将商户私钥、签名密钥或其他机密写入 Angular 前端代码,因为浏览器中的代码和数据都能被用户查看或修改。

更稳妥的流程是:Angular 先向自己的后端请求支付参数,由后端生成 data 和 signature,前端收到参数后,再通过原生表单提交到支付网关。

备注:内容仅供参考。