NodeJS集成SimplePay(OTP)支付网关签名生成不符问题
问题根源与解决方案
问题出在你对原始JSON字符串的处理上:你将原始JSON解析为对象后重新序列化,这会导致生成的字符串与网关预期的原始POST请求body存在细微差异(比如属性顺序、隐式格式调整),而签名是严格基于原始字节序列的哈希运算,因此结果必然不符。
修正后的代码
const crypto = require('crypto'); const bodyAsText = `{"salt":"c1ca1d0e9fc2323b3dda7cf145e36f5e","merchant":"PUBLICTESTHUF","orderRef":"101010516348232058105","currency":"HUF","customerEmail":"sdk_test@otpmobil.com","language":"HU","sdkVersion":"SimplePayV2.1_Payment_PHP_SDK_2.0.7_190701:dd236896400d7463677a82a47f53e36e","methods":["CARD"],"total":"25","timeout":"2021-10-30T12:30:11+00:00","url":"https://sdk.simplepay.hu/back.php"}`; const merchantKey = "FxDa5w314kLlNseq2sKuVwaqZshZT5d6"; // 直接使用原始JSON字符串生成签名,跳过解析再序列化步骤 const signature = crypto.createHmac("sha384", merchantKey) .update(bodyAsText) .digest("base64"); console.log("Your signature is: ", signature);
关键说明
官方要求对POST请求的完整JSON body进行哈希,这意味着必须使用发送给网关的原始字符串,而非解析后重新生成的JSON:
- PHP SDK直接使用原始请求体进行签名,而你的NodeJS代码中
JSON.parse+JSON.stringify的操作,可能改变字符串的属性顺序(尽管JSON规范不要求属性顺序,但签名基于字节序列,顺序不同哈希结果必然不同)。 - 即使表面看起来字符串内容一致,隐式的格式调整(如转义处理、空格)也会导致哈希结果偏差。
真实项目中的规范做法
在实际开发中,要确保请求body和签名使用的是同一个序列化后的字符串:
const crypto = require('crypto'); // 定义请求参数对象(属性顺序需与官方示例一致) const payload = { salt: "c1ca1d0e9fc2323b3dda7cf145e36f5e", merchant: "PUBLICTESTHUF", orderRef: "101010516348232058105", currency: "HUF", customerEmail: "sdk_test@otpmobil.com", language: "HU", sdkVersion: "SimplePayV2.1_Payment_PHP_SDK_2.0.7_190701:dd236896400d7463677a82a47f53e36e", methods: ["CARD"], total: "25", timeout: "2021-10-30T12:30:11+00:00", url: "https://sdk.simplepay.hu/back.php" }; // 生成固定格式的JSON字符串(无空格,保留属性顺序) const bodyAsText = JSON.stringify(payload); // 用该字符串生成签名 const signature = crypto.createHmac("sha384", merchantKey) .update(bodyAsText) .digest("base64"); // 发送请求时使用同一个bodyAsText // fetch('网关接口地址', { // method: 'POST', // body: bodyAsText, // headers: { // 'Content-Type': 'application/json', // 'Signature': signature // } // });
内容的提问来源于stack exchange,提问作者David Lacfi
相关产品推荐
相关产品推荐

