Node.js实现支付宝移动支付签名与验签完整流程

作者:慢半拍时间:2026-10-22 00:36:54浏览:211

本文详细解析在Node.js环境下实现支付宝移动支付的完整流程,涵盖从生成带签名的订单到接收并验证支付通知的全过程。重点讲解RSA-SHA1签名算法的应用、私钥与公钥的配置位置、以及生成订单字符串时双引号处理的常见坑点,帮助开发者顺利完成移动支付对接。

Node.js支付宝移动支付流程概述

感谢 http://www.jianshu.com/p/8513e995ff3a?utm_campaign=hugo&utm_medium=reader_share&utm_content=note&utm_source=weibo 提供的参考文章,若非此文指引,开发过程或许还需耗费数日。代码实现主要借鉴其思路,但由于支付宝移动支付官方文档描述不够清晰,且缺乏Node.js的SDK与示例代码,开发过程颇为艰难。经过一下午的摸索,在支付宝技术人员的协助下,最终完成了移动支付全流程,特此记录开发中遇到的难点及整体流程梳理。

Node.js支付宝移动支付流程概述

移动支付核心业务逻辑

支付宝提供的业务流程图逻辑清晰,核心步骤如下:

  1. 用户向服务器发起付款请求。
  2. 服务器生成包含签名的订单数据,并发送给客户端。
  3. 客户端利用该订单数据调用App SDK发起支付请求。
  4. SDK引导用户进入支付宝支付界面完成支付操作。
  5. 支付完成后,支付宝向前端返回支付结果,同时向服务器发送支付通知。
  6. 服务器接收通知,并验证该结果是否由支付宝官方发出。

客户端与服务器端职责分工

App客户端的操作相对简单,主要步骤包括:

  1. 向服务器请求获取订单信息。
  2. 获取订单后,向支付宝SDK发起支付请求。
  3. 交易结束后,返回成功或失败的状态结果。

服务器端签名与验签实现细节

服务器端处理逻辑较为复杂(注意:服务端需存储应用私钥用于签名,并存储支付宝公钥用于验签):

1. 接收客户端请求后,生成带签名的订单并返回给客户端,具体步骤如下:

1) 将相关配置数据整理为数组,并将数组数据拼接为有序字符串。


//将支付宝发来的数据生成有序数列
function getVerifyParams(params) {
 var sPara = [];
 if(!params) return null;
 for(var key in params) {
 if((!params[key]) || key == "sign" || key == "sign_type") {
 continue;
 };
 sPara.push([key, params[key]]);
 }
 sPara = sPara.sort();
 var prestr = '';
 for(var i2 = 0; i2 < sPara.length; i2++) {
 var obj = sPara[i2];
 if(i2 == sPara.length - 1) {
 prestr = prestr + obj[0] + '=' + obj[1] + '';
 } else {
 prestr = prestr + obj[0] + '=' + obj[1] + '&';
 }
 }
 return prestr;
}

2) 对该支付字符串进行RSA-SHA1算法处理,得到签名结果后,使用服务端私钥进行签名。


//验签
function veriySign(params) {
 try {
 var publicPem = fs.readFileSync('./rsa_public_key.pem');
 var publicKey = publicPem.toString();
 var prestr = getVerifyParams(params);
 var sign = params['sign'] ? params['sign'] : "";
 var verify = crypto.createVerify('RSA-SHA1');
 verify.update(prestr);
 return verify.verify(publicKey, sign, 'base64')

 } catch(err) {
 console.log('veriSign err', err)
 }
}

3) 将有序字符串、生成的签名以及签名方法组合,形成最终订单,并返回给客户端。


//发送订单号
 sendAlipay: function(req, res) {
 var code = ""
 for(var i = 0; i < 4; i++) {
 code += Math.floor(Math.random() * 10);
 }
 //订单号暂时由时间戳与四位随机码生成
 AlipayConfig.out_trade_no = Date.now().toString() + code;
 var myParam = getParams(AlipayConfig);
 var mySign = getSign(AlipayConfig)
 var last = myParam + '&sign="' + mySign + '"&sign_type="RSA"';
 console.log(last)
 return res.send(last)
 }

2. 前半部分工作完成后,若前端支付成功,支付宝会向预留的回调接口发送POST请求,以验证用户是否支付成功。

1) 将支付宝发送过来的数据生成有序字符串。


//将支付宝发来的数据生成有序数列
function getVerifyParams(params) {
 var sPara = [];
 if(!params) return null;
 for(var key in params) {
 if((!params[key]) || key == "sign" || key == "sign_type") {
 continue;
 };
 sPara.push([key, params[key]]);
 }
 sPara = sPara.sort();
 var prestr = '';
 for(var i2 = 0; i2 < sPara.length; i2++) {
 var obj = sPara[i2];
 if(i2 == sPara.length - 1) {
 prestr = prestr + obj[0] + '=' + obj[1] + '';
 } else {
 prestr = prestr + obj[0] + '=' + obj[1] + '&';
 }
 }
 return prestr;
}

2) 对获取的数据进行Hash处理,并根据公钥验证签名的有效性,返回true或false。


//验签
function veriySign(params) {
 try {
 var publicPem = fs.readFileSync('./rsa_public_key.pem');
 var publicKey = publicPem.toString();
 var prestr = getVerifyParams(params);
 var sign = params['sign'] ? params['sign'] : "";
 var verify = crypto.createVerify('RSA-SHA1');
 verify.update(prestr);
 return verify.verify(publicKey, sign, 'base64')

 } catch(err) {
 console.log('veriSign err', err)
 }
}

3) 若验签成功,需生成支付宝通知URL,以验证该通知是否确实由支付宝发出(支付宝的验证机制较为繁琐,令人头疼)。若验证通过且有数据返回,则确认为支付宝发出的有效通知,本次交易有效。


//回调验签
 getAlipay: function(req, res) {
 console.log(req.body)
 var params = req.body
 var mysign = veriySign(params);
 //验证支付宝签名mysign为true表示签名正确
 console.log(mysign)
 try {
 //验签成功
 if(mysign) {
 if(params['notify_id']) {
 var partner = AlipayConfig.partner;
 //生成验证支付宝通知的url
 var url = 'https://mapi.alipay.com/gateway.do?service=notify_verify&' + 'partner=' + partner + '&notify_id=' + params['notify_id'];
 console.log('url:' + url)
 //验证是否是支付宝发来的通知
 https.get(url, function(text) {
 //有数据表示是由支付宝发来的通知
 if(text) {
 //交易成功
 console.log('success')
 } else {
 //交易失败
 console.log('err')
 }
 })
 }
 }
 } catch(err) {
 console.log(err);
 }
 }

开发注意事项与常见坑点

至此,整个流程运行完毕。代码细节可参考原博客,此处主要将部分逻辑改为Sails框架写法,重点记录本次开发中遇到的几个坑及注意事项。

1. 由于移动支付文档描述不够明确,私钥实际上应上传至账户信息的mapi网关产品密钥中:

Node.js支付宝移动支付流程概述

而非上传至应用密钥中。

2. 移动支付仅支持RSA(SHA1)算法。

3. ./ 路径在Sails框架中用于获取根目录下的密钥文件(对Sails的路径机制尚存疑惑)。

4. 生成订单时的有序字符串格式中,body字段值包含双引号,例如body="测试";但在验签生成的有序字符串中,不应包含双引号。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持本站!

作者最新文章