如何提取支付网关POST请求发送的form-data表单交易数据
问题原因
- 你当前的Angular代码逻辑完全不匹配支付网关的回调流程:支付网关是将form-data格式的交易数据以POST形式发送到你自己的服务端回调地址,而非发送到浏览器端的Angular应用。你写的代码是主动向
https://xyz.app/test地址发送POST请求,该地址实际返回的是站点HTML首页代码(通常是接口路由未正确配置、服务端把所有未匹配路由都重定向到前端单页应用入口导致),Angular HttpClient默认会将响应当JSON格式解析,遇到HTML开头的<字符就会抛出你看到的Unexpected token < in JSON at position 0解析错误。 - 你存在严重的流程安全误区:支付验签用到的商户密钥、AES解密、哈希校验逻辑绝对不能放在前端Angular代码中实现。前端代码可被任意用户反编译、抓包获取敏感密钥,会直接导致支付回调被恶意伪造,造成资金损失。
正确实现方案
第一步:服务端实现回调接收与验签
使用服务端语言(可直接使用官方提供的PHP代码,或按照相同逻辑用你服务端使用的其他语言复现)部署专门的回调接口,接收支付网关的POST请求,完成验签逻辑:
<?php function verifychecksum_eFromStr($str, $key, $checksumvalue) { $sadad_hash = decrypt_e($checksumvalue, $key); $salt = substr($sadad_hash, -4); $finalString = $str . "|" . $salt; $website_hash = hash("sha256", $finalString); $website_hash .= $salt; $validFlag = "FALSE"; if ($website_hash == $sadad_hash) { $validFlag = "TRUE"; } else { $validFlag = "FALSE"; } return $validFlag; } function decrypt_e($crypt, $ky) { $ky = html_entity_decode($ky); $iv = "@@@@&&&&####$$$$"; $data = openssl_decrypt($crypt, "AES-128-CBC", $ky, 0, $iv); return $data; } $merchantId = '1234657'; //替换为你的商户ID $secretKey = 'XXXXXXXXXXXXXXX'; //替换为你的商户密钥 //提取checksumhash字段后从参数中移除,避免影响验签 $checksum_response = $_POST['checksumhash']; unset($_POST['checksumhash']); $data_repsonse = array(); $data_repsonse['postData'] = $_POST; //存储移除checksumhash后的所有POST参数 $data_repsonse['secretKey'] = $secretKey; $key = $secretKey . $merchantId; if (verifychecksum_eFromStr(json_encode($data_repsonse), $key, $checksum_response) === "TRUE") { //验签通过,在此处处理交易状态更新、订单状态修改等业务逻辑 //可进一步校验POST参数中的响应码、交易状态字段判断交易成功或失败 echo 'Checksum TRUE'; }else{ //验签失败,说明请求并非来自支付网关,直接丢弃即可 echo 'Checksum False'; } ?>
验签通过后,将交易状态、订单号、支付金额等核心数据和你系统内的订单做关联存储,供前端查询。
第二步:前端Angular逻辑调整
前端不需要直接接收支付网关的回调请求,只需要在用户支付完成跳回站点页面时,从URL中提取订单标识,调用你自己服务端提供的订单状态查询接口,拿到已经过服务端验签的交易数据做页面展示即可。
如果你确实需要在Angular中请求非JSON格式的接口,需要手动指定响应类型为文本,避免强制JSON解析报错,示例代码:
this.http .post('https://xyz.app/test', { title: 'Testing...', }, { responseType: 'text' // 告知HttpClient直接返回文本内容,不做JSON解析 }) .subscribe((data) => { console.log(data); });
重要提醒:所有涉及商户密钥、加解密、验签的逻辑必须放在服务端完成,绝对不能暴露在前端代码中。
内容的提问来源于stack exchange,提问作者Nabil Mehtab
相关产品推荐
相关产品推荐

