銀聯支付的測試開發做的很完善,可以下載各個語言的測試包,進行開發測試,但是并沒有 nodejs 的,難點就是證書簽名還有驗簽這兩個步驟。
其實銀聯加密方式和支付寶微信不同的地方在于,使用了非對稱加密,意思是為了在網絡中傳輸安全,雙方約定各自產生一個公鑰還有私鑰,私鑰自己保存,公鑰公開給對方(你要發送信息的人都知道)。當需要傳輸秘密的信息時候,用自己的私鑰加密,發給對方,對方收到信息后,為了判定這個是否偽造(是不是確實從你這兒發送給他的),那么拿出你的公鑰進行驗證,發現是一樣的,那么就可以確定這個確實是你發送的。這樣做就可以保證信息的安全。
下面是 code :
銀聯配置文件:config.js
//配置銀聯支付需要的數據 - 這都是銀聯測試商戶信息,可以上 https://merchant.unionpay.com/portal/login.jsp 去申請測試商戶merId: '777290058136713', //商戶idfont_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值和證書Idfunction 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.rsavar certs = wopenssl.x509.parseCert(p12.certificate);//因為不知道怎么將十六進制證書id:certs.serial變成十進制證書id,因為這是個很大的整形biglongvar certsData = {};certsData.certId = config.certId;certsData.key = p12.rsa;certsData.ca = certs;//存入configconfig.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,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!
新聞熱點
疑難解答