银联支付的测试开发做的很完善,可以下载各个语言的测试包,进行开发测试,但是并没有 nodejs 的,难点就是证书签名还有验签这两个步骤。

其实银联加密方式和支付宝微信不同的地方在于,使用了非对称加密,意思是为了在网络中传输安全,双方约定各自产生一个公钥还有私钥,私钥自己保存,公钥公开给对方(你要发送信息的人都知道)。当需要传输秘密的信息时候,用自己的私钥加密,发给对方,对方收到信息后,为了判定这个是否伪造(是不是确实从你这儿发送给他的),那么拿出你的公钥进行验证,发现是一样的,那么就可以确定这个确实是你发送的。这样做就可以保证信息的安全。

下面是 code :

银联配置文件:config.js

//配置银联支付需要的数据 - 这都是银联测试商户信息,可以上 https://merchant.unionpay.com/portal/login.jsp 去申请测试商户
merId: '777290058136713', //商户id
font_trans_url: 'https://101.231.204.80:5000/gateway/api/frontTransReq.do', //网关跳转至银联平台支付页面地址
sigle_query_url: 'https://101.231.204.80:5000/gateway/api/queryTrans.do', //单笔查询请求地址
sign_cert_dir: __dirname   '/certificates', //签名证书路径
certId: '40220995861346480087409489142384722381',
sign_cert_pwd: '0000000', //签名证书密码
sign_cert_path: __dirname   '/certificates/700000000000001_acp.pfx', //签名用私钥证书
validate_cert_path: __dirname   '/certificates/verify_sign_acp.cer', //验签用银联公钥证书

.pfx 结尾的都是用密码加密过后的私钥

.cer 结尾的都是公钥

银联支付模块 unionPay.js

var validator = require('validator'),
util = require('util'),
_ = require('underscore'),
crypto = require('crypto'),
x509 = require('x509'),
sha1 = require('sha1'),
wopenssl = require('wopenssl'),
config = require('./config'); //加载银联配置
//银联网关支付
var unionPay = {
//创建预订单
/*
* 参数 parms: {out_trade_no: OUT_TRADE_NO, fee: FEE}
* out_trade_no 商户订单号 fee 订单金额,单位分
*/
sdk_front_notice_url: 'http://'  config.domain  '/unionPay/result', //银联网关支付前台通知地址
sdk_back_notic_url: 'http://'  config.domain  '/unionPay/productPay', //银联网关支付后台通知地址
createOrder:
function(parms, callback) {
var errmsg;
var timeStamp = parms.timeStamp;
if(parms.payType==0) {
var back_notic_url = 'http://'  config.domain  '/unionPay/productPay';
} else if(parms.payType==1) {
var back_notic_url = 'http://'  config.domain  '/unionPay/rechargePay';
} else {
var back_notic_url = this.sdk_back_notic_url;
}
var formData = {
'version' : '5.0.0', //版本号
'encoding' : 'utf-8', //编码方式
'txnType' : '01', //交易类型
'txnSubType' : '01', //交易子类
'bizType' : '000201', //业务类型
'frontUrl' : this.sdk_front_notice_url, //前台通知地址
'signMethod' : '01', //签名方法
'channelType' : '08', //渠道类型,07-PC,08-手机
'accessType' : '0', //接入类型
'currencyCode' : '156', //交易币种,境内商户固定156
//TODO 以下信息需要填写
'merId' : config.merId, //商户代码,请改自己的测试商户号,此处默认取demo演示页面传递的参数
'orderId' : parms.out_trade_no, //商户订单号,8-32位数字字母,不能含“-”或“_”,此处默认取demo演示页面传递的参数,可以自行定制规则
'txnTime' : timeStamp, //订单发送时间timestamp,格式为YYYYMMDDhhmmss,取北京时间,此处默认取demo演示页面传递的参数
'txnAmt' : parms.fee, //交易金额,单位分
'backUrl' : back_notic_url, //后台通知地址
'certId' : '' //可不必填写,在SignKeyFromPfx中返回
};
var privateKey;
var certId;
var cert;
SignKeyFromPfx(function(err , result){
if (err) {
errmsg = '证书签名失败';
} else { 
certId = result.certId;
privateKey = result.key;
formData.certId = certId;
if (formData.signature) {
delete formData.signature
}
//----签名开始----
//参数转变为签名串
var unionPay_parms = transForSign(formData);
//摘要
var unionPay_parms_sha1 = sha1(unionPay_parms);
//签名
var signer = crypto.createSign('RSA-SHA1');
signer.update(unionPay_parms_sha1);
var signature_base64 = signer.sign(privateKey, 'base64');
//放入域中
formData.signature = signature_base64;
//加入表单请求银联的地址
formData.action_url = config.font_trans_url;
//console.log(formData);
if (errmsg) {
callback(errmsg);
} else {
callback(null,formData);
}
}
});
},
//签名
sign:
function(parms, callback) {
var errmsg;
var formData = parms;
SignKeyFromPfx(function(err , result){
if (err) {
errmsg = '证书签名失败';
} else { 
certId = result.certId;
privateKey = result.key;
if (formData.signature) {
delete formData.signature
}
//----签名开始----
//参数转变为签名串
var unionPay_parms = transForSign(formData);
//摘要
var unionPay_parms_sha1 = sha1(unionPay_parms);
//签名
var signer = crypto.createSign('RSA-SHA1');
signer.update(unionPay_parms_sha1);
var signature_base64 = signer.sign(privateKey, 'base64');
//放入域中
formData.signature = signature_base64;
//console.log(formData);
if (errmsg) {
callback(errmsg);
} else {
callback(null,formData);
}
}
});
},
//验签
validate:
function(parms,callback) {
var validate_signature = parms.signature;
delete parms.signature;
var formData = parms;
ValidateKeyFromCer(formData,validate_signature,function(err , result){
if (err || !validate_signature || !formData) {
console.log('验签失败');
callback('验签失败');
} else { 
var publicKey = result.key;
if (formData.signature) {
delete formData.signature
}
//----验签开始----
var unionPay_parms = transForSign(formData);
var unionPay_parms_sha1 = sha1(unionPay_parms);
//console.log('待验证签:'   validate_signature);
var verifier = crypto.createVerify('RSA-SHA1');
//console.log('验证签名public key:\n'   publicKey);
//console.log('验证签名src_sign:'   unionPay_parms_sha1);
verifier.update(new Buffer(unionPay_parms_sha1, 'utf-8'));
var is_success = verifier.verify(publicKey, validate_signature, 'base64');
if (is_success) {
callback(null,formData);
} else {
console.log('验签不相等');
callback('验签不相等');
}
}
});
}
};
// 签名串算法--将参数排序,转成键值对格式字符串
function transForSign(params){
var array = []
for (var i in params) {
array.push(''   i   '='   params[i])
}
var stringSignTemp = _.sortBy(array, function (str) {
return str;
});
return stringSignTemp.join('&');
};
//通过证书密码获得证书的rsa-privatekey值和证书Id
function SignKeyFromPfx(callback){
if (config.certsData) {
callback(null, config.certsData);
} else {
var certPath = config.sign_cert_path;
var certPwd = config.sign_cert_pwd;
var certDir = config.sign_cert_dir;
var p12 = wopenssl.pkcs12.extract(certPath, certPwd);
//console.log(p12.certificate); //p12.certificate和p12.rsa
var certs = wopenssl.x509.parseCert(p12.certificate);
//因为不知道怎么将十六进制证书id:certs.serial变成十进制证书id,因为这是个很大的整形biglong
var certsData = {};
certsData.certId = config.certId;
certsData.key = p12.rsa;
certsData.ca = certs;
//存入config
config.certsData = certsData;
callback(null,certsData); //{key: String, certId: String, ca: Array}
}
};
//获得验签证书的rsa-publickey值
function ValidateKeyFromCer(formData, signature, callback){
if (config.validCertsData) {
callback(null, config.validCertsData);
} else {
var validateCertPath = config.validate_cert_path;
var certs = wopenssl.x509.parseCert(validateCertPath);
//console.log(certs);
var fs = require('fs');
var CERTIFICATE = fs.readFileSync(validateCertPath);
console.log(CERTIFICATE);
var publicKey = CERTIFICATE.toString('ascii');
var validCertsData = {};
validCertsData.key = publicKey;
validCertsData.cert = CERTIFICATE;
config.validCertsData = validCertsData;
if (publicKey) {
callback(null,validCertsData);
} else {
msg = '验签失败';
callback(msg);
}
}
};
//转化时间格式函数
function format(){
//时间格式化
var format = 'yyyyMMddhhmmss';
date = new Date();
var o = {
'M ' : date.getMonth()   1, //month
'd ' : date.getDate(), //day
'h ' : date.getHours(), //hour
'm ' : date.getMinutes(), //minute
's ' : date.getSeconds(), //second
'q ' : Math.floor((date.getMonth()   3) / 3), //quarter
'S' : date.getMilliseconds() //millisecond
};
if (/(y )/.test(format))
format = format.replace(RegExp.$1, (date.getFullYear()   '').substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp('('   k   ')').test(format))
format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00'   o[k]).substr((''   o[k]).length));
return format;
};
module.exports = unionPay;

其实最重要的是签名还有验签部分,对证书 .pfx 和 .cer 的处理,其中的 createOrder 方法只是方便使用返回的请求表单内容。

在测试完成后,生产环境的配置还是不太一样。当银联商户申请成功后,银联会发一份邮件,上面有商户号,你的私钥证书 .pfx 需要你自己根据他的邮件提示去下载的,附件内的 “证书下载,安装” 文件有详细的说明教程。还有就是配置中的请求地址 https://101.231.204.80:5000需要换为 https://gateway.95516.com ,生产环境中除非是从商户提交审核的域名发起的请求,否则一律会报错 亲爱的用户,您本次交易可能存在风险.

以上所述是小编给大家介绍的NodeJS整合银联网关支付DEMO,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对Devmax网站的支持!

NodeJS整合银联网关支付(DEMO)的更多相关文章

  1. zen_cart实现支付前生成订单的方法

    这篇文章主要介绍了zen_cart实现支付前生成订单的方法,结合实例形式详细分析了zen_cart支付前生成订单的具体步骤与相关实现技巧,需要的朋友可以参考下

  2. nodejs npm package.json中文文档

    这篇文章主要介绍了nodejs npm package.json中文文档,本文档中描述的很多行为都受npm-config(7)的影响,需要的朋友可以参考下

  3. php版银联支付接口开发简明教程

    这篇文章主要介绍了php版银联支付接口开发的方法,结合实例形式分析了php银联支付接口开发的具体流程与相关操作技巧,需要的朋友可以参考下

  4. 浅析Nodejs npm常用命令

    这篇文章主要介绍了浅析Nodejs npm常用命令的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下

  5. nodejs 使用nodejs-websocket模块实现点对点实时通讯

    这篇文章主要介绍了nodejs 使用nodejs-websocket模块实现点对点实时通讯的实例代码,代码简单易懂,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下

  6. nodeJs链接Mysql做增删改查的简单操作

    本篇文章主要介绍了nodeJs链接Mysql做增删改查的简单操作,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  7. Nodejs中使用captchapng模块生成图片验证码

    本篇文章主要介绍了Nodejs中使用captchapng模块实现图片验证码,非常具有实用价值,需要的朋友可以参考下

  8. nodejs 图片预览和上传的示例代码

    本篇文章主要介绍了nodejs 图片预览和上传的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  9. nodejs中函数的调用实例详解

    本文通过实例代码给大家介绍了nodejs函数的调用,代码简单易懂,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下

  10. NodeJS使用formidable实现文件上传

    这篇文章主要为大家详细介绍了NodeJS使用formidable实现文件上传的相关方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

随机推荐

  1. Error: Cannot find module ‘node:util‘问题解决

    控制台 安装 Vue-Cli 最后一步出现 Error: Cannot find module 'node:util' 问题解决方案1.问题C:\Windows\System32>cnpm install -g @vue/cli@4.0.3internal/modules/cjs/loader.js:638 throw err; &nbs

  2. yarn的安装和使用(全网最详细)

    一、yarn的简介:Yarn是facebook发布的一款取代npm的包管理工具。二、yarn的特点:速度超快。Yarn 缓存了每个下载过的包,所以再次使用时无需重复下载。 同时利用并行下载以最大化资源利用率,因此安装速度更快。超级安全。在执行代码之前,Yarn 会通过算法校验每个安装包的完整性。超级可靠。使用详细、简洁的锁文件格式和明确的安装算法,Yarn 能够保证在不同系统上无差异的工作。三、y

  3. 前端环境 本机可切换node多版本 问题源头是node使用的高版本

    前言投降投降 重头再来 重装环境 也就分分钟的事 偏要折腾 这下好了1天了 还没折腾出来问题的源头是node 使用的高版本 方案那就用 本机可切换多版本最终问题是因为nodejs的版本太高,导致的node-sass不兼容问题,我的node是v16.14.0的版本,项目中用了"node-sass": "^4.7.2"版本,无法匹配当前的node版本根据文章的提

  4. nodejs模块学习之connect解析

    这篇文章主要介绍了nodejs模块学习之connect解析,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  5. nodejs npm package.json中文文档

    这篇文章主要介绍了nodejs npm package.json中文文档,本文档中描述的很多行为都受npm-config(7)的影响,需要的朋友可以参考下

  6. 详解koa2学习中使用 async 、await、promise解决异步的问题

    这篇文章主要介绍了详解koa2学习中使用 async 、await、promise解决异步的问题,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  7. Node.js编写爬虫的基本思路及抓取百度图片的实例分享

    这篇文章主要介绍了Node.js编写爬虫的基本思路及抓取百度图片的实例分享,其中作者提到了需要特别注意GBK转码的转码问题,需要的朋友可以参考下

  8. CentOS 8.2服务器上安装最新版Node.js的方法

    这篇文章主要介绍了CentOS 8.2服务器上安装最新版Node.js的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  9. node.js三个步骤实现一个服务器及Express包使用

    这篇文章主要介绍了node.js三个步骤实现一个服务器及Express包使用,文章通过新建一个文件展开全文内容,具有一定的参考价值,需要的小伙伴可以参考一下

  10. node下使用UglifyJS压缩合并JS文件的方法

    下面小编就为大家分享一篇node下使用UglifyJS压缩合并JS文件的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

返回
顶部