UNPKG

cn-pay

Version:

a alipay wechat pay lib

113 lines (105 loc) 3.44 kB
'use strict'; const { createHash } = require('crypto'); const { parseString, Builder } = require('xml2js'); const { parse } = require('url'); const { stringify } = require('querystring'); const { request } = require('https'); const { GatewayException, InvalidSignException } = require('../error'); class Util { static createNonceStr(length = 15) { return Math.random().toString(36).substr(2, length); } static request(url, data, key, pfx, passphrase) { url = 'https://api.mch.weixin.qq.com' + url; data = Util.buildObject(data); return new Promise((resolve, reject) => { const { hostname, path } = parse(url); const options = { hostname, path, pfx, passphrase, protocol: 'https:', method: 'POST', headers: { 'Content-Type': 'application/xml; charset=utf-8', }, }; const req = request(options, res => { res.setEncoding('utf8'); let data = ''; res.on('data', chunk => { data += chunk; }); res.on('end', () => { Util.parseXML(data) .then(data => { if (data.return_code !== 'SUCCESS' || (data.result_code !== 'SUCCESS' && data.err_code !== 'USERPAYING')) { return reject(new GatewayException(data.err_code_des || data.return_msg || '', data)); } const sign = data.sign; if (Util.generateSign(data, key) !== sign) { return reject(new InvalidSignException('验签失败', data)); } return resolve(data); }) .catch(error => { reject(error); }); }); }); req.on('error', error => { reject(error); }); req.write(data); req.end(); }); } // 把js对象转换成xml字符串 static buildObject(obj) { return new Builder({ rootName: 'xml', cdata: true, headless: true, }).buildObject(obj); } // 把xml字符串转换成js对象 static parseXML(xmlString) { return new Promise((resolve, reject) => { parseString(xmlString, { trim: true }, (error, result) => { if (error) return reject(error); result = result.xml; // 内容会被解析一个数组,应该取下标0 Object.keys(result).forEach(key => (result[key] = result[key][0])); resolve(result); }); }); } // 转换成md5 static createMD5(source) { return createHash('md5').update(source).digest('hex'); } // 签名 static generateSign(payload, key) { if (payload.sign) { delete payload.sign; } const dispose = {}; // 第一步: 按 key 值进行自然排序 Object.keys(payload).sort().forEach(key => { // 去除空值 #1 if (payload[key]) { dispose[key] = payload[key]; } }); // 第二步: 把对象转换为类似HTTP Query 参数 并去除URL转义符号 let str = decodeURIComponent(stringify(dispose, '&', '=')); // 第三步: 加入私钥字段进行MD5转码并大写 str += '&key=' + key; return Util.createMD5(str).toUpperCase(); } // static getSignkey(payload) { // return Util.httpPost('/pay/getsignkey', payload, true) // } } module.exports = Util;