├── .babelrc ├── lib.zip ├── .gitignore ├── config ├── wechat.json └── wechatWeb.json ├── src ├── index.js ├── urls.js ├── WechatPaymentForWeb.js ├── utils.js └── WechatPayment.js ├── tests ├── utils.test.js ├── WechatPaymentForWeb.test.js └── WechatPayment.test.js ├── lib ├── urls.js ├── index.js ├── WechatPaymentForWeb.js ├── utils.js └── WechatPayment.js ├── gulpfile.js ├── package.json └── README.md /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0"] 3 | } 4 | -------------------------------------------------------------------------------- /lib.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/boyaq/wechat-payment-node/HEAD/lib.zip -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | mochawesome-reports 4 | real_tests 5 | config/realWechat.json 6 | config/wechatForWeb.json 7 | 8 | -------------------------------------------------------------------------------- /config/wechat.json: -------------------------------------------------------------------------------- 1 | { 2 | "partnerKey": "微信商户平台API密钥", 3 | "appId": "微信APP id", 4 | "mchId": "微信商户平台商户ID", 5 | "notifyUrl": "/order/notify-from-wechat", 6 | "tradeType": "APP/JSAPI/NATIVE" 7 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * wechat payment node for app and jsapi payment 3 | * Copyright(c) 2013-2017 Alim Boyaq 4 | * MIT Licensed 5 | */ 6 | 7 | import WechatPayment from './WechatPayment'; 8 | import WechatPaymentForWeb from './WechatPaymentForWeb'; 9 | 10 | 11 | 12 | export { 13 | WechatPaymentForWeb, 14 | } 15 | 16 | export default WechatPayment; -------------------------------------------------------------------------------- /config/wechatWeb.json: -------------------------------------------------------------------------------- 1 | { 2 | "partnerKey": "微信商户平台API密钥", 3 | "appId": "微信APP id", 4 | "mchId": "微信商户平台商户ID", 5 | "notifyUrl": "/order/notify-from-wechat", 6 | "openId": "", 7 | "accessToken": "", //普通型AccessToken 来自 https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET 8 | "ticket": "", // 来自 https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=iNlD17-Acv3jxNffBtqBbbUeIFZ5f-X-9uajnvZT6abTj6rpPCeVelk5kSc_SCaB-EySrtWCMrOWhwjm3Jl0xU7V06pLLwx584iIBEMQRYcXONaAHALCB&type=JSAPI 9 | "tradeType": "JSAPI", 10 | "currentUrl": "" //不需要转移 当前调起接口的url 11 | } -------------------------------------------------------------------------------- /src/urls.js: -------------------------------------------------------------------------------- 1 | export default { 2 | UNIFIED_ORDER: 'https://api.mch.weixin.qq.com/pay/unifiedorder', 3 | ORDER_QUERY: 'https://api.mch.weixin.qq.com/pay/orderquery', 4 | CLOSE_ORDER: 'https://api.mch.weixin.qq.com/pay/closeorder', 5 | REFUND: 'https://api.mch.weixin.qq.com/secapi/pay/refund', 6 | REFUND_QUERY: 'https://api.mch.weixin.qq.com/pay/refundquery', 7 | TRANSFERS: 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers', 8 | TRNSFER_INFO: 'https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo', 9 | TICKET: 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=:access_token&type=jsapi', 10 | } -------------------------------------------------------------------------------- /tests/utils.test.js: -------------------------------------------------------------------------------- 1 | import utils from '../src/utils'; 2 | import {expect} from 'chai'; 3 | 4 | describe('utils:工具方法测试', function() { 5 | 6 | describe('sign 函数测试', function() { 7 | var object1 = { key1: 'test', key2: 'test' }; 8 | var object2 = { key2: 'test', key1: 'test' }; 9 | var sign1 = utils.sign(object1); 10 | var sign2 = utils.sign(object2); 11 | 12 | it('sign 成功', function() { 13 | expect(sign1).to.be.equal(sign2); 14 | }); 15 | }); 16 | 17 | describe('createNonceStr 随机字符串', function() { 18 | it('createNonceStr 随机字符串生成成功', function() { 19 | expect(utils.createNonceStr(24).length).to.be.equal(24); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /lib/urls.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = { 7 | UNIFIED_ORDER: 'https://api.mch.weixin.qq.com/pay/unifiedorder', 8 | ORDER_QUERY: 'https://api.mch.weixin.qq.com/pay/orderquery', 9 | CLOSE_ORDER: 'https://api.mch.weixin.qq.com/pay/closeorder', 10 | REFUND: 'https://api.mch.weixin.qq.com/secapi/pay/refund', 11 | REFUND_QUERY: 'https://api.mch.weixin.qq.com/pay/refundquery', 12 | TRANSFERS: 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers', 13 | TRNSFER_INFO: 'https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo', 14 | TICKET: 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=:access_token&type=jsapi' 15 | }; -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.WechatPaymentForWeb = undefined; 7 | 8 | var _WechatPayment = require('./WechatPayment'); 9 | 10 | var _WechatPayment2 = _interopRequireDefault(_WechatPayment); 11 | 12 | var _WechatPaymentForWeb = require('./WechatPaymentForWeb'); 13 | 14 | var _WechatPaymentForWeb2 = _interopRequireDefault(_WechatPaymentForWeb); 15 | 16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 17 | 18 | /*! 19 | * wechat payment node for app and jsapi payment 20 | * Copyright(c) 2013-2017 Alim Boyaq 21 | * MIT Licensed 22 | */ 23 | 24 | exports.WechatPaymentForWeb = _WechatPaymentForWeb2.default; 25 | exports.default = _WechatPayment2.default; -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const sourcemaps = require('gulp-sourcemaps'); 3 | const babel = require('gulp-babel'); 4 | const concat = require('gulp-concat'); 5 | const mocha = require('gulp-mocha'); 6 | const watch = require('gulp-watch'); 7 | 8 | require('babel-core/register'); 9 | require('babel-polyfill'); 10 | 11 | var jsFiles = ['src/*.js', '*.js', 'tests/*.js']; 12 | 13 | gulp.task('build', () => { 14 | return gulp.src('src/*') 15 | .pipe(babel({ 16 | plugins: ['transform-runtime'], 17 | presets: ['es2015'] 18 | })) 19 | .pipe(gulp.dest('lib')); 20 | }); 21 | 22 | 23 | gulp.task('watch', ['build'], () => { 24 | return watch(jsFiles, { ignoreInitial: false }, () => { 25 | gulp.src('tests/*.test.js', { read: false }) 26 | // gulp-mocha needs filepaths so you can't have any plugins before it 27 | .pipe(mocha()) 28 | }); 29 | }); 30 | 31 | gulp.task('test', () => { 32 | gulp.src('tests/*.test.js', { read: false }) 33 | // gulp-mocha needs filepaths so you can't have any plugins before it 34 | .pipe(mocha()) 35 | }); 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wechat-payment-node", 3 | "version": "2.7.0", 4 | "description": "微信支付 for nodejs,创建统一支付订单,查询订单,关闭订单,申请退款,查询退款详情,企业付款,即转账到个人微信钱包,查询企业付款详情 Payment API of Weixin (WeChat) for APP and also JSAPI", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "watch": "gulp watch", 8 | "test": "gulp test", 9 | "build": "gulp build" 10 | }, 11 | "dependencies": { 12 | "babel-runtime": "^6.23.0", 13 | "jssha": "^2.2.0", 14 | "md5": "^2.1.0", 15 | "request": "^2.54.0", 16 | "xml2js": "^0.4.6" 17 | }, 18 | "keywords": [ 19 | "wechat", 20 | "weixin", 21 | "payment", 22 | "node", 23 | "微信支付", 24 | "weixin node", 25 | "wechat node", 26 | "weixin nodejs", 27 | "wechat nodejs", 28 | "nodejs", 29 | "wechat payment", 30 | "weixin zhifu", 31 | "weixin payment" 32 | ], 33 | "author": "Alim Boyaq", 34 | "contributors": [ 35 | { 36 | "name": "Alim Boyaq", 37 | "email": "boyaq@otkur.biz" 38 | }, 39 | { 40 | "name": "MirGhojam", 41 | "email": "mirshat@otkur.biz" 42 | } 43 | ], 44 | "license": "MIT", 45 | "devDependencies": { 46 | "babel-cli": "^6.24.0", 47 | "babel-core": "^6.24.0", 48 | "babel-plugin-transform-runtime": "^6.23.0", 49 | "babel-polyfill": "^6.26.0", 50 | "babel-preset-es2015": "^6.24.0", 51 | "babel-preset-stage-0": "^6.22.0", 52 | "chai": "^3.5.0", 53 | "gulp": "^3.9.1", 54 | "gulp-babel": "^6.1.2", 55 | "gulp-concat": "^2.6.0", 56 | "gulp-mocha": "^3.0.1", 57 | "gulp-sourcemaps": "^2.2.0", 58 | "gulp-uglify": "^2.0.0", 59 | "gulp-watch": "^4.3.10", 60 | "mocha": "^2.4.5", 61 | "mochawesome": "^1.3.4" 62 | }, 63 | "files": [ 64 | "lib" 65 | ], 66 | "repository": { 67 | "type": "git", 68 | "url": "https://github.com/OtkurBiz/wechat-payment-node" 69 | }, 70 | "bugs": "https://github.com/OtkurBiz/wechat-payment-node/issues", 71 | "homepage": "https://github.com/OtkurBiz/wechat-payment-node" 72 | } -------------------------------------------------------------------------------- /tests/WechatPaymentForWeb.test.js: -------------------------------------------------------------------------------- 1 | import WechatPaymentForWeb from '../lib/WechatPaymentForWeb'; 2 | import { expect } from 'chai'; 3 | import fs from 'fs'; 4 | 5 | var wechatConfig = require('../config/wechatForWeb.json'); 6 | 7 | describe('Wechat payment for web 测试', function () { 8 | describe('Wechat Payment for web 构造函数', function () { 9 | let options = { 10 | //appid: wechatConfig.appId, 11 | mch_id: wechatConfig.mchId, 12 | apiKey: wechatConfig.partnerKey, //微信商户平台API密钥 13 | //pfx: fs.readFileSync('./apiclient_cert.p12'), //微信商户平台证书 (optional,部分API需要使用) 14 | } 15 | 16 | it('应该返回没有app id错误', function () { 17 | var fcn = function () { new WechatPaymentForWeb(options) }; 18 | expect(fcn).to.throw("Seems that app id or merchant id is not set, please provide wechat app id and merchant id."); 19 | }); 20 | 21 | it('成功构造wechat payment for web instance', function () { 22 | options['appid'] = wechatConfig.appId; 23 | let wechatPaymentInstance = new WechatPaymentForWeb(options); 24 | expect(wechatPaymentInstance.options).to.not.be.empty; 25 | }); 26 | }); 27 | 28 | var myResult = ''; 29 | 30 | describe('公众号支付统一下单接口: wxPayment.createUnifiedOrder', function () { 31 | let options = { 32 | appid: wechatConfig.appId, 33 | mch_id: wechatConfig.mchId, 34 | openid: wechatConfig.openid, 35 | apiKey: wechatConfig.partnerKey, //微信商户平台API密钥 36 | //pfx: fs.readFileSync('./apiclient_cert.p12'), //微信商户平台证书 (optional,部分API需要使用) 37 | } 38 | 39 | 40 | let wechatPaymentInstance = new WechatPaymentForWeb(options); 41 | it('公众号统一下单', function () { 42 | 43 | wechatPaymentInstance.createUnifiedOrder({ 44 | body: '支付测试', 45 | out_trade_no: '20140703' + Math.random().toString().substr(2, 10), 46 | total_fee: 1, 47 | spbill_create_ip: '192.168.2.210', 48 | notify_url: wechatConfig.notifyUrl, 49 | trade_type: wechatConfig.tradeType, 50 | }).then(result => { 51 | console.log(result, 'result for unified order'); 52 | expect(result.return_code).to.be.equal('SUCCESS'); 53 | }).catch(error => { 54 | console.log(error); 55 | }); 56 | 57 | }); 58 | }); 59 | 60 | describe('生成payment设置: wxPayment.configForPayment', function () { 61 | let options = { 62 | appid: wechatConfig.appId, 63 | mch_id: wechatConfig.mchId, 64 | url: wechatConfig.currentUrl, 65 | ticket: wechatConfig.ticket, 66 | apiKey: wechatConfig.partnerKey, //微信商户平台API密钥 67 | //pfx: fs.readFileSync('./apiclient_cert.p12'), //微信商户平台证书 (optional,部分API需要使用) 68 | } 69 | console.log(wechatConfig.ticket, 'tiket'); 70 | 71 | 72 | 73 | it('公众号支付生成配置', function () { 74 | let wechatPaymentInstance = new WechatPaymentForWeb(options); 75 | let prepayId = 'wx201612132002348597b1e4c50601226291'; 76 | let config = wechatPaymentInstance.configForPayment(prepayId); 77 | console.log(config, 'payment config'); 78 | }); 79 | }); 80 | 81 | 82 | }); 83 | -------------------------------------------------------------------------------- /src/WechatPaymentForWeb.js: -------------------------------------------------------------------------------- 1 | import utils from './utils'; 2 | import urls from './urls'; 3 | 4 | import request from 'request'; 5 | 6 | import jsSHA from 'jssha'; 7 | 8 | export default class WechatPaymentForWeb { 9 | 10 | constructor(options) { 11 | if (!options.appid || !options.mch_id) { 12 | throw new Error("Seems that app id or merchant id is not set, please provide wechat app id and merchant id."); 13 | //throw new Error('haha'); 14 | } 15 | this.options = options; 16 | } 17 | 18 | /** 19 | * create wechat unified order 20 | * @params order object 21 | * 22 | * spec: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1 23 | */ 24 | 25 | createUnifiedOrder(order) { 26 | return new Promise((resolve, reject) => { 27 | order.nonce_str = order.nonce_str || utils.createNonceStr(); 28 | order.appid = this.options.appid; 29 | order.mch_id = this.options.mch_id; 30 | order.openid = this.options.openid; 31 | order.sign = utils.sign(order, this.options.apiKey); 32 | let requestParam = { 33 | url: urls.UNIFIED_ORDER, 34 | method: 'POST', 35 | body: utils.buildXML(order) 36 | }; 37 | request(requestParam, (err, response, body) => { 38 | if (err) { 39 | reject(err); 40 | } 41 | utils.parseXML(body) 42 | .then(result => { 43 | resolve(result); 44 | }) 45 | .catch(err => { 46 | reject(err); 47 | }); 48 | }); 49 | }); 50 | } 51 | 52 | /** 53 | * config for payment 54 | * 55 | * @param prepayId from prepay id 56 | */ 57 | configForPayment(prepayId, nonceStr, timeStamp) { 58 | let configData = { 59 | appId: this.options.appid, 60 | nonceStr: nonceStr, 61 | package: "prepay_id=" + prepayId, 62 | signType: "MD5", 63 | timeStamp: timeStamp 64 | } 65 | configData.paySign = utils.sign(configData, this.options.apiKey); 66 | return configData; 67 | } 68 | 69 | 70 | configSignature(url, nonceStr, jsApiTicket) { 71 | let configData = { 72 | jsapi_ticket: jsApiTicket, 73 | nonceStr: nonceStr, 74 | timestamp: parseInt(new Date().getTime() / 1000).toString(), 75 | url: url 76 | } 77 | let string = utils.buildQueryStringWithoutEncode(configData); 78 | let shaObj = new jsSHA("SHA-1", 'TEXT'); 79 | shaObj.update(string); 80 | 81 | return { 82 | signature: shaObj.getHash('HEX'), 83 | timestamp: configData.timestamp 84 | }; 85 | 86 | } 87 | 88 | static wxCallback(fn, apiKey) { 89 | return (req, res, next) => { 90 | var _this = this; 91 | res.success = function () { res.end(utils.buildXML({ xml: { return_code: 'SUCCESS' } })); }; 92 | res.fail = function () { res.end(utils.buildXML({ xml: { return_code: 'FAIL' } })); }; 93 | console.log("hahah"); 94 | utils.pipe(req, function (err, data) { 95 | var xml = data.toString('utf8'); 96 | utils.parseXML(xml).then(notification => { 97 | 98 | let dataForSign = Object.assign({}, notification); 99 | delete dataForSign.sign; 100 | let signValue = utils.sign(dataForSign, apiKey); 101 | if (signValue != notification.sign) { 102 | fn.apply(_this, [null, req, res, next]); 103 | } else { 104 | req.wxmessage = notification; 105 | fn.apply(_this, [notification, req, res, next]); 106 | } 107 | 108 | }).catch(err => { 109 | console.log(err); 110 | next(err) 111 | }) 112 | }); 113 | } 114 | } 115 | 116 | } -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | import xml2js from 'xml2js'; 2 | import MD5 from 'md5'; 3 | import jsSHA from 'jssha'; 4 | import crypto from 'crypto'; 5 | import { Buffer } from 'safe-buffer'; 6 | import fs from 'fs'; 7 | 8 | export default class utils { 9 | static sign(object, key) { 10 | var querystring = utils.createQueryString(object); 11 | if (key) querystring += "&key=" + key; 12 | return MD5(querystring).toUpperCase(); 13 | } 14 | 15 | 16 | static shaSign(object) { 17 | var querystring = utils.createQueryString(object); 18 | 19 | let shaObj = new jsSHA("SHA-1", 'TEXT'); 20 | shaObj.update(querystring); 21 | return shaObj.getHash('HEX'); 22 | } 23 | 24 | static createNonceStr(length) { 25 | length = length || 24; 26 | if (length > 32) length = 32; 27 | let randomStr = Math.random().toString(36); 28 | let randomStr2 = Math.random().toString(36); 29 | let nonceStr = (randomStr + randomStr2).substr(0, length); 30 | return nonceStr; 31 | } 32 | 33 | static createTimestamp() { 34 | return parseInt(new Date().getTime() / 1000) + ''; 35 | } 36 | 37 | static createQueryString(options) { 38 | return Object.keys(options).filter(function (key) { 39 | return options[key] !== undefined && options[key] !== '' && ['pfx', 'apiKey', 'sign', 'key'].indexOf(key) < 0; 40 | }).sort().map(function (key) { 41 | return key + '=' + options[key]; 42 | }).join("&"); 43 | } 44 | 45 | static buildXML(json) { 46 | var builder = new xml2js.Builder(); 47 | return builder.buildObject(json); 48 | } 49 | 50 | static parseXML(xml, fn) { 51 | return new Promise((resolve, reject) => { 52 | let parser = new xml2js.Parser({ trim: true, explicitArray: false, explicitRoot: false }); 53 | parser.parseString(xml, (err, result) => { 54 | if (err) { 55 | reject(err); 56 | } else { 57 | resolve(result); 58 | } 59 | 60 | }); 61 | }); 62 | 63 | 64 | } 65 | 66 | static parseRaw() { 67 | return function (req, res, next) { 68 | var buffer = []; 69 | req.on('data', function (trunk) { 70 | buffer.push(trunk); 71 | }); 72 | req.on('end', function () { 73 | req.rawbody = Buffer.concat(buffer).toString('utf8'); 74 | next(); 75 | }); 76 | req.on('error', function (err) { 77 | next(err); 78 | }); 79 | } 80 | } 81 | 82 | static pipe(stream, fn) { 83 | var buffers = []; 84 | stream.on('data', function (trunk) { 85 | buffers.push(trunk); 86 | }); 87 | stream.on('end', function () { 88 | fn(null, Buffer.concat(buffers)); 89 | }); 90 | stream.once('error', fn); 91 | } 92 | 93 | 94 | 95 | static buildQueryStringWithoutEncode(obj) { 96 | return obj ? Object.keys(obj).sort().map(function (key) { 97 | var val = obj[key]; 98 | key = key.toLowerCase(); 99 | if (Array.isArray(val)) { 100 | return val.sort().map(function (val2) { 101 | return key + '=' + val2; 102 | }).join('&'); 103 | } 104 | return key + '=' + val; 105 | }).join('&') : ''; 106 | } 107 | 108 | static decryptByAes256Cbc(encryptdata, cryptkey) { 109 | encryptdata = new Buffer(encryptdata, 'base64').toString('hex'); 110 | var dec, decipher; 111 | decipher = crypto.createDecipheriv('aes-256-ecb', MD5(cryptkey).toLowerCase(), ''); 112 | dec = decipher.update(encryptdata, 'hex', 'utf8'); 113 | dec += decipher.final('utf8'); 114 | return dec; 115 | } 116 | 117 | } -------------------------------------------------------------------------------- /tests/WechatPayment.test.js: -------------------------------------------------------------------------------- 1 | import WechatPayment from '../src/WechatPayment'; 2 | import { expect } from 'chai'; 3 | import fs from 'fs'; 4 | 5 | var wechatConfig = require('../config/realWechat.json'); 6 | 7 | describe('Wechat payment 测试', function () { 8 | 9 | describe('Wechat Payment 构造函数', function () { 10 | let options = { 11 | //appid: wechatConfig.appId, 12 | mch_id: wechatConfig.mchId, 13 | apiKey: wechatConfig.partnerKey, //微信商户平台API密钥 14 | //pfx: fs.readFileSync('./apiclient_cert.p12'), //微信商户平台证书 (optional,部分API需要使用) 15 | } 16 | it('应该返回没有app id错误', function () { 17 | var fcn = function () { new WechatPayment(options) }; 18 | expect(fcn).to.throw("Seems that app id or merchant id is not set, please provide wechat app id and merchant id."); 19 | }); 20 | 21 | it('成功构造wechat payment instance', function () { 22 | options['appid'] = wechatConfig.appId; 23 | let wechatPaymentInstance = new WechatPayment(options); 24 | expect(wechatPaymentInstance.options).to.not.be.empty; 25 | }); 26 | }); 27 | 28 | var myResult = ''; 29 | 30 | describe('统一下单接口: wxPayment.createUnifiedOrder', function () { 31 | let options = { 32 | appid: wechatConfig.appId, 33 | mch_id: wechatConfig.mchId, 34 | apiKey: wechatConfig.partnerKey, //微信商户平台API密钥 35 | //pfx: fs.readFileSync('./apiclient_cert.p12'), //微信商户平台证书 (optional,部分API需要使用) 36 | } 37 | 38 | 39 | let wechatPaymentInstance = new WechatPayment(options); 40 | it('统一下单', function () { 41 | wechatPaymentInstance.createUnifiedOrder({ 42 | body: '支付测试', 43 | out_trade_no: '20140703' + Math.random().toString().substr(2, 10), 44 | total_fee: 1, 45 | spbill_create_ip: '192.168.2.210', 46 | notify_url: wechatConfig.notifyUrl, 47 | trade_type: wechatConfig.tradeType, 48 | }) 49 | .then(result => { 50 | console.log(result, 'result'); 51 | myResult = result; 52 | expect(result.return_code).to.be.equal('SUCCESS'); 53 | 54 | }) 55 | .catch(err => { 56 | console.log(err); 57 | }) 58 | }); 59 | }); 60 | 61 | describe('生成payment设置: wxPayment.configForPayment', function () { 62 | let options = { 63 | appid: wechatConfig.appId, 64 | mch_id: wechatConfig.mchId, 65 | apiKey: wechatConfig.partnerKey, //微信商户平台API密钥 66 | //pfx: fs.readFileSync('./apiclient_cert.p12'), //微信商户平台证书 (optional,部分API需要使用) 67 | } 68 | let wechatPaymentInstance = new WechatPayment(options); 69 | it('生成配置', function () { 70 | let prepayId = 'wx201612132002348597b1e4c50601226291'; 71 | let config = wechatPaymentInstance.configForPayment(prepayId); 72 | console.log(config, 'payment config'); 73 | }); 74 | }); 75 | 76 | describe('退款通知: wxPayment.checkNotification', function () { 77 | let xml = `SUCCESS`; 78 | it('解码成功', async function () { 79 | try { 80 | let res = await WechatPayment.checkNotification(xml, wechatConfig.partnerKey, 'refund'); 81 | expect(res).to.be.an('object'); 82 | expect(res).to.have.property('out_trade_no'); 83 | 84 | } catch (error) { 85 | console.log(error); 86 | } 87 | }); 88 | }); 89 | 90 | 91 | }); 92 | -------------------------------------------------------------------------------- /lib/WechatPaymentForWeb.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _assign = require('babel-runtime/core-js/object/assign'); 8 | 9 | var _assign2 = _interopRequireDefault(_assign); 10 | 11 | var _promise = require('babel-runtime/core-js/promise'); 12 | 13 | var _promise2 = _interopRequireDefault(_promise); 14 | 15 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 16 | 17 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 18 | 19 | var _createClass2 = require('babel-runtime/helpers/createClass'); 20 | 21 | var _createClass3 = _interopRequireDefault(_createClass2); 22 | 23 | var _utils = require('./utils'); 24 | 25 | var _utils2 = _interopRequireDefault(_utils); 26 | 27 | var _urls = require('./urls'); 28 | 29 | var _urls2 = _interopRequireDefault(_urls); 30 | 31 | var _request = require('request'); 32 | 33 | var _request2 = _interopRequireDefault(_request); 34 | 35 | var _jssha = require('jssha'); 36 | 37 | var _jssha2 = _interopRequireDefault(_jssha); 38 | 39 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 40 | 41 | var WechatPaymentForWeb = function () { 42 | function WechatPaymentForWeb(options) { 43 | (0, _classCallCheck3.default)(this, WechatPaymentForWeb); 44 | 45 | if (!options.appid || !options.mch_id) { 46 | throw new Error("Seems that app id or merchant id is not set, please provide wechat app id and merchant id."); 47 | //throw new Error('haha'); 48 | } 49 | this.options = options; 50 | } 51 | 52 | /** 53 | * create wechat unified order 54 | * @params order object 55 | * 56 | * spec: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1 57 | */ 58 | 59 | (0, _createClass3.default)(WechatPaymentForWeb, [{ 60 | key: 'createUnifiedOrder', 61 | value: function createUnifiedOrder(order) { 62 | var _this2 = this; 63 | 64 | return new _promise2.default(function (resolve, reject) { 65 | order.nonce_str = order.nonce_str || _utils2.default.createNonceStr(); 66 | order.appid = _this2.options.appid; 67 | order.mch_id = _this2.options.mch_id; 68 | order.openid = _this2.options.openid; 69 | order.sign = _utils2.default.sign(order, _this2.options.apiKey); 70 | var requestParam = { 71 | url: _urls2.default.UNIFIED_ORDER, 72 | method: 'POST', 73 | body: _utils2.default.buildXML(order) 74 | }; 75 | (0, _request2.default)(requestParam, function (err, response, body) { 76 | if (err) { 77 | reject(err); 78 | } 79 | _utils2.default.parseXML(body).then(function (result) { 80 | resolve(result); 81 | }).catch(function (err) { 82 | reject(err); 83 | }); 84 | }); 85 | }); 86 | } 87 | 88 | /** 89 | * config for payment 90 | * 91 | * @param prepayId from prepay id 92 | */ 93 | 94 | }, { 95 | key: 'configForPayment', 96 | value: function configForPayment(prepayId, nonceStr, timeStamp) { 97 | var configData = { 98 | appId: this.options.appid, 99 | nonceStr: nonceStr, 100 | package: "prepay_id=" + prepayId, 101 | signType: "MD5", 102 | timeStamp: timeStamp 103 | }; 104 | configData.paySign = _utils2.default.sign(configData, this.options.apiKey); 105 | return configData; 106 | } 107 | }, { 108 | key: 'configSignature', 109 | value: function configSignature(url, nonceStr, jsApiTicket) { 110 | var configData = { 111 | jsapi_ticket: jsApiTicket, 112 | nonceStr: nonceStr, 113 | timestamp: parseInt(new Date().getTime() / 1000) + '', 114 | url: url 115 | }; 116 | var string = _utils2.default.buildQueryStringWithoutEncode(configData); 117 | var shaObj = new _jssha2.default("SHA-1", 'TEXT'); 118 | shaObj.update(string); 119 | 120 | return { 121 | signature: shaObj.getHash('HEX'), 122 | timestamp: configData.timestamp 123 | }; 124 | } 125 | }], [{ 126 | key: 'wxCallback', 127 | value: function wxCallback(fn, apiKey) { 128 | var _this3 = this; 129 | 130 | return function (req, res, next) { 131 | var _this = _this3; 132 | res.success = function () { 133 | res.end(_utils2.default.buildXML({ xml: { return_code: 'SUCCESS' } })); 134 | }; 135 | res.fail = function () { 136 | res.end(_utils2.default.buildXML({ xml: { return_code: 'FAIL' } })); 137 | }; 138 | console.log("hahah"); 139 | _utils2.default.pipe(req, function (err, data) { 140 | var xml = data.toString('utf8'); 141 | _utils2.default.parseXML(xml).then(function (notification) { 142 | 143 | var dataForSign = (0, _assign2.default)({}, notification); 144 | delete dataForSign.sign; 145 | var signValue = _utils2.default.sign(dataForSign, apiKey); 146 | if (signValue != notification.sign) { 147 | fn.apply(_this, [null, req, res, next]); 148 | } else { 149 | req.wxmessage = notification; 150 | fn.apply(_this, [notification, req, res, next]); 151 | } 152 | }).catch(function (err) { 153 | console.log(err); 154 | next(err); 155 | }); 156 | }); 157 | }; 158 | } 159 | }]); 160 | return WechatPaymentForWeb; 161 | }(); 162 | 163 | exports.default = WechatPaymentForWeb; -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _promise = require('babel-runtime/core-js/promise'); 8 | 9 | var _promise2 = _interopRequireDefault(_promise); 10 | 11 | var _keys = require('babel-runtime/core-js/object/keys'); 12 | 13 | var _keys2 = _interopRequireDefault(_keys); 14 | 15 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 16 | 17 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 18 | 19 | var _createClass2 = require('babel-runtime/helpers/createClass'); 20 | 21 | var _createClass3 = _interopRequireDefault(_createClass2); 22 | 23 | var _xml2js = require('xml2js'); 24 | 25 | var _xml2js2 = _interopRequireDefault(_xml2js); 26 | 27 | var _md = require('md5'); 28 | 29 | var _md2 = _interopRequireDefault(_md); 30 | 31 | var _jssha = require('jssha'); 32 | 33 | var _jssha2 = _interopRequireDefault(_jssha); 34 | 35 | var _crypto = require('crypto'); 36 | 37 | var _crypto2 = _interopRequireDefault(_crypto); 38 | 39 | var _safeBuffer = require('safe-buffer'); 40 | 41 | var _fs = require('fs'); 42 | 43 | var _fs2 = _interopRequireDefault(_fs); 44 | 45 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 46 | 47 | var utils = function () { 48 | function utils() { 49 | (0, _classCallCheck3.default)(this, utils); 50 | } 51 | 52 | (0, _createClass3.default)(utils, null, [{ 53 | key: 'sign', 54 | value: function sign(object, key) { 55 | var querystring = utils.createQueryString(object); 56 | if (key) querystring += "&key=" + key; 57 | return (0, _md2.default)(querystring).toUpperCase(); 58 | } 59 | }, { 60 | key: 'shaSign', 61 | value: function shaSign(object) { 62 | var querystring = utils.createQueryString(object); 63 | 64 | var shaObj = new _jssha2.default("SHA-1", 'TEXT'); 65 | shaObj.update(querystring); 66 | return shaObj.getHash('HEX'); 67 | } 68 | }, { 69 | key: 'createNonceStr', 70 | value: function createNonceStr(length) { 71 | length = length || 24; 72 | if (length > 32) length = 32; 73 | var randomStr = Math.random().toString(36); 74 | var randomStr2 = Math.random().toString(36); 75 | var nonceStr = (randomStr + randomStr2).substr(0, length); 76 | return nonceStr; 77 | } 78 | }, { 79 | key: 'createTimestamp', 80 | value: function createTimestamp() { 81 | return parseInt(new Date().getTime() / 1000) + ''; 82 | } 83 | }, { 84 | key: 'createQueryString', 85 | value: function createQueryString(options) { 86 | return (0, _keys2.default)(options).filter(function (key) { 87 | return options[key] !== undefined && options[key] !== '' && ['pfx', 'apiKey', 'sign', 'key'].indexOf(key) < 0; 88 | }).sort().map(function (key) { 89 | return key + '=' + options[key]; 90 | }).join("&"); 91 | } 92 | }, { 93 | key: 'buildXML', 94 | value: function buildXML(json) { 95 | var builder = new _xml2js2.default.Builder(); 96 | return builder.buildObject(json); 97 | } 98 | }, { 99 | key: 'parseXML', 100 | value: function parseXML(xml, fn) { 101 | return new _promise2.default(function (resolve, reject) { 102 | var parser = new _xml2js2.default.Parser({ trim: true, explicitArray: false, explicitRoot: false }); 103 | parser.parseString(xml, function (err, result) { 104 | if (err) { 105 | reject(err); 106 | } else { 107 | resolve(result); 108 | } 109 | }); 110 | }); 111 | } 112 | }, { 113 | key: 'parseRaw', 114 | value: function parseRaw() { 115 | return function (req, res, next) { 116 | var buffer = []; 117 | req.on('data', function (trunk) { 118 | buffer.push(trunk); 119 | }); 120 | req.on('end', function () { 121 | req.rawbody = _safeBuffer.Buffer.concat(buffer).toString('utf8'); 122 | next(); 123 | }); 124 | req.on('error', function (err) { 125 | next(err); 126 | }); 127 | }; 128 | } 129 | }, { 130 | key: 'pipe', 131 | value: function pipe(stream, fn) { 132 | var buffers = []; 133 | stream.on('data', function (trunk) { 134 | buffers.push(trunk); 135 | }); 136 | stream.on('end', function () { 137 | fn(null, _safeBuffer.Buffer.concat(buffers)); 138 | }); 139 | stream.once('error', fn); 140 | } 141 | }, { 142 | key: 'buildQueryStringWithoutEncode', 143 | value: function buildQueryStringWithoutEncode(obj) { 144 | return obj ? (0, _keys2.default)(obj).sort().map(function (key) { 145 | var val = obj[key]; 146 | key = key.toLowerCase(); 147 | if (Array.isArray(val)) { 148 | return val.sort().map(function (val2) { 149 | return key + '=' + val2; 150 | }).join('&'); 151 | } 152 | return key + '=' + val; 153 | }).join('&') : ''; 154 | } 155 | }, { 156 | key: 'decryptByAes256Cbc', 157 | value: function decryptByAes256Cbc(encryptdata, cryptkey) { 158 | encryptdata = new _safeBuffer.Buffer(encryptdata, 'base64').toString('hex'); 159 | var dec, decipher; 160 | decipher = _crypto2.default.createDecipheriv('aes-256-ecb', (0, _md2.default)(cryptkey).toLowerCase(), ''); 161 | dec = decipher.update(encryptdata, 'hex', 'utf8'); 162 | dec += decipher.final('utf8'); 163 | return dec; 164 | } 165 | }]); 166 | return utils; 167 | }(); 168 | 169 | exports.default = utils; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wechat Payment (Node.js) 2 | Wechat Payment API for Node.js 3 | 4 | 微信支付 API for Node.js 5 | 6 | Please Reference Wechat payment docs carefully before using this package. 7 | Wormhole:[wechat payment docs](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=1_1), [enterprice version (transfers、withdraw)](https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_1) 8 | 9 | 使用之前一定要读懂微信支付的文档, 传送门:[微信支付](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=1_1), [企业付款 (转账、提现)](https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_1) 10 | 11 | 12 | 包含以下API: 13 | * 创建统一支付订单 14 | * 查询订单 15 | * 关闭订单 16 | * 申请退款 17 | * 查询退款详情 18 | * 企业付款,即转账到个人微信钱包 19 | * 查询企业付款详情 20 | 21 | ## Usage 22 | 23 | ### Initialize 24 | 配置,初始化; 在调用其他接口前必须先初始化! 25 | 26 | ```javascript 27 | import WechatPayment from 'wechat-payment-node'; 28 | let options = { 29 | appid: 'your app id', 30 | mch_id: 'your merchant id', 31 | apiKey: 'your app key (partner key)', //微信商户平台API密钥 32 | notify_url: 'your notify url', 33 | trade_type: 'APP', //APP, JSAPI, NATIVE etc. 34 | pfx: fs.readFileSync('./apiclient_cert.p12'), //微信商户平台证书 (optional,部分API需要使用) 35 | }; 36 | let wechatPaymentInstance = new WechatPayment(options); 37 | ``` 38 | 39 | ### Create Unified Order 40 | 创建统一支付订单, [SPEC](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1) 41 | 42 | ```javascript 43 | let orderData = { 44 | body: '支付测试', // 商品或支付单简要描述 45 | out_trade_no: 'order1', // 商户系统内部的订单号,32个字符内、可包含字母 46 | total_fee: 100, 47 | spbill_create_ip: '192.168.2.1', 48 | notify_url: 'http://wxpayment_notify_url', 49 | trade_type: 'JSAPI' 50 | }; 51 | 52 | wechatPaymentInstance.createUnifiedOrder(orderData) 53 | .then(result=>{ 54 | console.log(result); 55 | }) 56 | .catch(err=>{ 57 | console.log(err); 58 | }); 59 | ``` 60 | 61 | result 示例: 62 | ```javascript 63 | { 64 | return_code: 'SUCCESS', 65 | return_msg: 'OK', 66 | appid: 'your app id', 67 | mch_id: 'your merchantt id', 68 | nonce_str: 'GPAwjlqZmkl04dCH', 69 | sign: 'E9970AC7C2F2890BD6CA5C36F78B6921', 70 | result_code: 'SUCCESS', 71 | prepay_id: 'wx20160701104002e0af0b0dbf0031984704', 72 | trade_type: 'JSAPI' 73 | } 74 | ``` 75 | 76 | 77 | ### WeChat Callbacks 78 | 接收微信的回调, 对应上面的`notify_url` 79 | 80 | ```javascript 81 | WechatPayment.wxCallback(fn, apiKey, type) 82 | ```` 83 | 此方法有三个参数,第一个是匿名函数,第二个是商户partnerKey, 第三个是类型。 目前只支持两种通知,一是支付通知(值为:'payment', 也是默认值),二是退款通知(值为: 'refund')。暂时不支持扫码付款通知。 84 | 85 | ```javascript 86 | // 支付结果异步通知 87 | router.use('/wechat/payment/notify', WechatPayment.wxCallback(function(notification, req, res, next){ 88 | // 处理商户业务逻辑 89 | 90 | // res.success() 向微信返回处理成功信息,res.fail()返回失败信息。 91 | res.success(); 92 | }), apiKey); 93 | ``` 94 | 95 | 注:此方法是静态方法,直接调用,不用实例化。WechatPayment.wxCallback() 96 | 97 | 98 | 99 | ### Query Order 100 | 查询订单, [SPEC](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2) 101 | 102 | ```javascript 103 | wechatPaymentInstance.queryOrder({ 104 | out_trade_no: 'order1' 105 | }) 106 | .then(result=>{ 107 | console.log(result); 108 | }) 109 | .catch(err=>{ 110 | console.log(err); 111 | }) 112 | 113 | ``` 114 | or 115 | 116 | ```javascript 117 | wechatPaymentInstance.queryOrder({ 118 | transaction_id: '12222' 119 | }) 120 | .then(result=>{ 121 | console.log(result); 122 | }) 123 | .catch(err=>{ 124 | console.log(err); 125 | }) 126 | ``` 127 | 128 | `out_trade_no` 和 `transaction_id` 二选一 129 | 130 | 131 | result 示例: 132 | ```javascript 133 | { 134 | return_code: 'SUCCESS', 135 | return_msg: 'OK', 136 | appid: 'your app id', 137 | mch_id: 'your merchant id', 138 | nonce_str: 'P6IFNTlKWVKtlOH4', 139 | sign: 'B3348E5BCB4FC7C7C5D1D7445023A9A0', 140 | result_code: 'SUCCESS', 141 | out_trade_no: 'ordertest003', 142 | trade_state: 'NOTPAY', 143 | trade_state_desc: '订单未支付' 144 | } 145 | ``` 146 | 147 | 148 | ### Close Order 149 | 关闭订单, [SPEC](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_3) 150 | 151 | ```javascript 152 | wechatPaymentInstance.closeOrder({ 153 | out_trade_no: 'order1' 154 | }) 155 | .then(result=>{ 156 | console.log(result); 157 | }) 158 | .catch(err=>{ 159 | console.log(err); 160 | }) 161 | ``` 162 | 163 | result 示例: 164 | ```javascript 165 | { 166 | return_code: 'SUCCESS', 167 | return_msg: 'OK', 168 | appid: 'your wechat id', 169 | mch_id: 'your merchant id', 170 | nonce_str: 'qlJrwWF8E0M5tHyh', 171 | sign: '5E44DB01443506897D8CC7AA1C67BB0F', 172 | result_code: 'SUCCESS' 173 | } 174 | ``` 175 | 176 | ### Refund 177 | 申请退款, [SPEC](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4) 178 | 179 | ```javascript 180 | let orderData = { 181 | out_trade_no: 'order1', 182 | out_refund_no: 'refund1', 183 | total_fee: 100, 184 | refund_fee: 100, 185 | op_user_id: '1234567890', 186 | }; 187 | wechatPaymentInstance.refund({ 188 | out_trade_no: 'order1' 189 | }) 190 | .then(result=>{ 191 | console.log(result); 192 | }) 193 | .catch(err=>{ 194 | console.log(err); 195 | }); 196 | ``` 197 | or 198 | 199 | ```javascript 200 | let orderData = { 201 | transaction_id: '1', 202 | out_refund_no: 'refund1', 203 | total_fee: 100, 204 | refund_fee: 100, 205 | op_user_id: '1234567890', 206 | }; 207 | wechatPaymentInstance.refund({ 208 | out_trade_no: 'order1' 209 | }) 210 | .then(result=>{ 211 | console.log(result); 212 | }) 213 | .catch(err=>{ 214 | console.log(err); 215 | }); 216 | ``` 217 | 218 | `out_trade_no` 和 `transaction_id` 二选一 219 | 220 | ```javascript 221 | { 222 | return_code: 'SUCCESS', 223 | return_msg: 'OK', 224 | appid: 'your app id', 225 | mch_id: 'your merchant id', 226 | nonce_str: 'DFYEiS63BBNbwr9Y', 227 | sign: '9826704D76452E728B19C26BB7A81666', 228 | result_code: 'SUCCESS', 229 | transaction_id: '4001812001201606288028146396', 230 | out_trade_no: '5772427d816dfa0055f5b7c9', 231 | out_refund_no: 'refund5772427d816dfa0055f5b7c9', 232 | refund_id: '2001812001201607010302081102', 233 | refund_channel: '', 234 | refund_fee: '1', 235 | coupon_refund_fee: '0', 236 | total_fee: '1', 237 | cash_fee: '1', 238 | coupon_refund_count: '0', 239 | cash_refund_fee: '1' 240 | } 241 | ``` 242 | 243 | 244 | ### Query Refund 245 | 查询退款详情, [SPEC](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_5) 246 | 247 | ```javascript 248 | wechatPaymentInstance.queryRefund({ 249 | out_trade_no: 'order1' 250 | }) 251 | .then(result=>{ 252 | console.log(result); 253 | }) 254 | .catch(err=>{ 255 | console.log(err); 256 | }); 257 | ``` 258 | `out_trade_no` 也可以使用以下代替: `transaction_id`, `out_refund_no`, `refund_id` 259 | 260 | ```javascript 261 | { 262 | appid: 'your app id', 263 | cash_fee: '1', 264 | mch_id: '1329105201', 265 | nonce_str: '9cMaFrdWbYMaYyZi', 266 | out_refund_no_0: 'refund5772427d816dfa0055f5b7c9', 267 | out_trade_no: '5772427d816dfa0055f5b7c9', 268 | refund_channel_0: 'ORIGINAL', 269 | refund_count: '1', 270 | refund_fee: '1', 271 | refund_fee_0: '1', 272 | refund_id_0: '2001812001201607010302081102', 273 | refund_recv_accout_0: '支付用户的零钱', 274 | refund_status_0: 'SUCCESS', 275 | result_code: 'SUCCESS', 276 | return_code: 'SUCCESS', 277 | return_msg: 'OK', 278 | sign: 'A444F580BDD2D8306187D9AF14091B5C', 279 | total_fee: '1', 280 | transaction_id: '4001812001201606288028146396' 281 | } 282 | ``` 283 | 284 | 285 | ### Transfers 286 | 企业付款是基于微信支付商户平台的资金管理能力,为了协助商户方便地实现企业向个人付款,针对部分有开发能力的商户,提供通过API完成企业付款的功能。[SPEC](https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2) 287 | 288 | 接口调用规则: 289 | ◆ 给同一个实名用户付款,单笔单日限额2W/2W 290 | ◆ 给同一个非实名用户付款,单笔单日限额2000/2000 291 | ◆ 一个商户同一日付款总额限额100W 292 | ◆ 单笔最小金额默认为1元 293 | ◆ 每个用户每天最多可付款10次,可以在商户平台--API安全进行设置 294 | ◆ 给同一个用户付款时间间隔不得低于15秒 295 | 296 | 297 | ```javascript 298 | let orderData = { 299 | partner_trade_no: 'transferOrder1', //商户订单号,需保持唯一性 300 | openid: 'open id', 301 | check_name: 'OPTION_CHECK', 302 | re_user_name: 'Mr Ma', 303 | amount: 100, 304 | desc: '红包', 305 | spbill_create_ip: '192.168.0.1' 306 | } 307 | wechatPaymentInstance.transfers(orderData) 308 | .then(result=>{ 309 | console.log(result); 310 | }) 311 | .catch(err=>{ 312 | console.log(err); 313 | }); 314 | ``` 315 | 316 | result 示例: 317 | ```javascript 318 | { 319 | return_code: 'SUCCESS', 320 | return_msg: '', 321 | mch_appid: 'app id', 322 | mchid: 'merchant id', 323 | device_info: '', 324 | nonce_str: 'x5pzs6q95pkchaorbsn6o8ic', 325 | result_code: 'SUCCESS', 326 | partner_trade_no: 'testTransferOrder10', 327 | payment_no: '1000018301201607010763072544', 328 | payment_time: '2016-07-01 10:44:55' 329 | } 330 | ``` 331 | 332 | ### Query Transfer Info 333 | 查询付款详情, [SPEC](https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_3) 334 | 335 | ```javascript 336 | wechatPaymentInstance.transfers({ 337 | partner_trade_no: 'transferOrder1' //商户订单号,需保持唯一性 338 | }) 339 | .then(result=>{ 340 | console.log(result); 341 | }) 342 | .catch(err=>{ 343 | console.log(err); 344 | }); 345 | ``` 346 | 347 | result 示例: 348 | ```javascript 349 | { 350 | return_code: 'SUCCESS', 351 | result_code: 'SUCCESS', 352 | partner_trade_no: 'testTransferOrder10', 353 | mch_id: 'merchant id', 354 | detail_id: '1000018301201607010763072544', 355 | status: 'SUCCESS', 356 | openid: 'open id', 357 | payment_amount: '100', 358 | transfer_time: '2016-07-01 10:44:55', 359 | desc: '红包', 360 | return_msg: '获取成功' 361 | } 362 | ``` 363 | 364 | [更多问题请提交ISSUE](https://github.com/OtkurBiz/wechat-payment-node/issues) 365 | -------------------------------------------------------------------------------- /src/WechatPayment.js: -------------------------------------------------------------------------------- 1 | import utils from './utils'; 2 | import urls from './urls'; 3 | import request from 'request'; 4 | 5 | export default class WechatPayment { 6 | 7 | constructor(options) { 8 | if (!options.appid || !options.mch_id) { 9 | throw new Error("Seems that app id or merchant id is not set, please provide wechat app id and merchant id."); 10 | //throw new Error('haha'); 11 | } 12 | this.options = options; 13 | } 14 | 15 | /** 16 | * create wechat unified order 17 | * @params order object 18 | * 19 | * spec: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1 20 | */ 21 | 22 | createUnifiedOrder(order) { 23 | return new Promise((resolve, reject) => { 24 | order.nonce_str = order.nonce_str || utils.createNonceStr(); 25 | order.appid = this.options.appid; 26 | order.mch_id = this.options.mch_id; 27 | order.sign = utils.sign(order, this.options.apiKey); 28 | let requestParam = { 29 | url: urls.UNIFIED_ORDER, 30 | method: 'POST', 31 | body: utils.buildXML(order) 32 | }; 33 | request(requestParam, (err, response, body) => { 34 | if (err) { 35 | reject(err); 36 | } 37 | utils.parseXML(body) 38 | .then(result => { 39 | resolve(result); 40 | }) 41 | .catch(err => { 42 | reject(err); 43 | }); 44 | }); 45 | }); 46 | } 47 | 48 | /** 49 | * config for payment 50 | * 51 | * @param prepayId from prepay id 52 | */ 53 | 54 | configForPayment(prepayId) { 55 | let configData = { 56 | appid: this.options.appid, 57 | partnerid: this.options.mch_id, 58 | prepayid: prepayId, 59 | package: 'Sign=WXPay', 60 | noncestr: utils.createNonceStr(), 61 | timestamp: parseInt(new Date().getTime() / 1000).toString(), 62 | } 63 | configData.sign = utils.sign(configData, this.options.apiKey); 64 | return configData; 65 | } 66 | 67 | /** 68 | * query 69 | * 70 | * 71 | * spec: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2 72 | */ 73 | queryOrder(query) { 74 | return new Promise((resolve, reject) => { 75 | if (!(query.transaction_id || query.out_trade_no)) { 76 | reject(new Error('缺少参数')); 77 | } 78 | 79 | query.nonce_str = query.nonce_str || utils.createNonceStr(); 80 | query.appid = this.options.appid; 81 | query.mch_id = this.options.mch_id; 82 | query.sign = utils.sign(query, this.options.apiKey); 83 | 84 | request({ 85 | url: urls.ORDER_QUERY, 86 | method: "POST", 87 | body: utils.buildXML({ xml: query }) 88 | }, function (err, res, body) { 89 | utils.parseXML(body) 90 | .then(result => { 91 | resolve(result); 92 | }) 93 | .catch(err => { 94 | reject(err); 95 | }); 96 | }); 97 | }); 98 | 99 | } 100 | 101 | closeOrder(order) { 102 | return new Promise((resolve, reject) => { 103 | if (!order.out_trade_no) { 104 | reject(new Error('缺少参数')); 105 | } 106 | 107 | order.nonce_str = order.nonce_str || utils.createNonceStr(); 108 | order.appid = this.options.appid; 109 | order.mch_id = this.options.mch_id; 110 | order.sign = utils.sign(order, this.options.apiKey); 111 | 112 | request({ 113 | url: urls.CLOSE_ORDER, 114 | method: "POST", 115 | body: utils.buildXML({ xml: order }) 116 | }, function (err, res, body) { 117 | utils.parseXML(body) 118 | .then(result => { 119 | resolve(result); 120 | }) 121 | .catch(err => { 122 | reject(err); 123 | }); 124 | }); 125 | }); 126 | 127 | } 128 | 129 | refund(order) { 130 | return new Promise((resolve, reject) => { 131 | if (!order.transaction_id && !order.out_trade_no || !order.out_refund_no) { 132 | reject(new Error('缺少参数')); 133 | } 134 | 135 | order.nonce_str = order.nonce_str || utils.createNonceStr(); 136 | order.appid = this.options.appid; 137 | order.mch_id = this.options.mch_id; 138 | order.op_user_id = order.op_user_id || order.mch_id; 139 | order.sign = utils.sign(order, this.options.apiKey); 140 | 141 | request({ 142 | url: urls.REFUND, 143 | method: "POST", 144 | body: utils.buildXML({ xml: order }), 145 | agentOptions: { 146 | pfx: this.options.pfx, 147 | passphrase: this.options.mch_id 148 | } 149 | }, function (err, response, body) { 150 | utils.parseXML(body) 151 | .then(result => { 152 | resolve(result); 153 | }) 154 | .catch(err => { 155 | reject(err); 156 | }); 157 | }); 158 | }); 159 | } 160 | 161 | queryRefund(order) { 162 | return new Promise((resolve, reject) => { 163 | if (!(order.transaction_id || order.out_trade_no || order.out_refund_no || order.refund_id)) { 164 | reject(new Error('缺少参数')); 165 | } 166 | 167 | order.nonce_str = order.nonce_str || utils.createNonceStr(); 168 | order.appid = this.options.appid; 169 | order.mch_id = this.options.mch_id; 170 | order.sign = utils.sign(order, this.options.apiKey); 171 | 172 | request({ 173 | url: urls.REFUND_QUERY, 174 | method: "POST", 175 | body: utils.buildXML({ xml: order }) 176 | }, function (err, response, body) { 177 | utils.parseXML(body) 178 | .then(result => { 179 | resolve(result); 180 | }) 181 | .catch(err => { 182 | reject(err); 183 | }); 184 | }); 185 | }); 186 | } 187 | 188 | 189 | transfers(order) { 190 | return new Promise((resolve, reject) => { 191 | if (!(order.partner_trade_no && order.openid && order.check_name && order.amount && order.desc && order.spbill_create_ip)) { 192 | reject(new Error('缺少参数')); 193 | } 194 | 195 | order.nonce_str = order.nonce_str || utils.createNonceStr(); 196 | order.mch_appid = this.options.appid; 197 | order.mchid = this.options.mch_id; 198 | order.sign = utils.sign(order, this.options.apiKey); 199 | 200 | request({ 201 | url: urls.TRANSFERS, 202 | method: "POST", 203 | body: utils.buildXML(order), 204 | agentOptions: { 205 | pfx: this.options.pfx, 206 | passphrase: this.options.mch_id 207 | } 208 | }, function (err, response, body) { 209 | if (err) return reject(err); 210 | utils.parseXML.parseXML(body).then(function (result) { 211 | if (!result || result.result_code === 'FAIL') reject(result); 212 | else resolve(result); 213 | }).catch(reject); 214 | }); 215 | }); 216 | } 217 | 218 | 219 | queryTransferInfo(order, fn) { 220 | return new Promise((resolve, reject) => { 221 | if (!order.partner_trade_no) { 222 | reject(new Error('缺少参数')); 223 | } 224 | 225 | order.nonce_str = order.nonce_str || utils.createNonceStr(); 226 | order.appid = this.options.appid; 227 | order.mch_id = this.options.mch_id; 228 | order.sign = utils.sign(order, this.options.apiKey); 229 | 230 | request({ 231 | url: urls.TRNSFER_INFO, 232 | method: "POST", 233 | body: utils.buildXML({ xml: order }), 234 | agentOptions: { 235 | pfx: this.options.pfx, 236 | passphrase: this.options.mch_id 237 | } 238 | }, function (err, response, body) { 239 | utils.parseXML(body, fn); 240 | }); 241 | }); 242 | } 243 | 244 | 245 | getBrandWCPayRequestParams(order) { 246 | return new Promise((resolve, reject) => { 247 | order.trade_type = "JSAPI"; 248 | this.createUnifiedOrder(order) 249 | .then(data => { 250 | let reqparam = { 251 | appId: this.options.appid, 252 | timeStamp: utils.createTimestamp(), 253 | nonceStr: data.nonce_str, 254 | package: "prepay_id=" + data.prepay_id, 255 | signType: "MD5" 256 | }; 257 | reqparam.paySign = utils.sign(reqparam, this.options.apiKey); 258 | resolve(reqparam); 259 | }).catch(err => { 260 | reject(err); 261 | }); 262 | }); 263 | 264 | 265 | } 266 | 267 | createMerchantPrepayUrl(param) { 268 | param.time_stamp = param.time_stamp || utils.createTimestamp(); 269 | param.nonce_str = param.nonce_str || utils.createNonceStr(); 270 | param.appid = this.options.appid; 271 | param.mch_id = this.options.mch_id; 272 | param.sign = utils.sign(param, this.options.apiKey); 273 | 274 | var query = Object.keys(param).filter(function (key) { 275 | return ['sign', 'mch_id', 'product_id', 'appid', 'time_stamp', 'nonce_str'].indexOf(key) >= 0; 276 | }).map(function (key) { 277 | return key + "=" + encodeURIComponent(param[key]); 278 | }).join('&'); 279 | 280 | return "weixin://wxpay/bizpayurl?" + query; 281 | } 282 | 283 | 284 | static async checkNotification(xml, apiKey, type = 'payment') { 285 | try { 286 | if (type == 'payment') { 287 | let notification = await utils.parseXML(xml); 288 | let dataForSign = Object.assign({}, notification); 289 | delete dataForSign.sign; 290 | let signValue = utils.sign(dataForSign, apiKey); 291 | if (signValue != notification.sign) { 292 | return null; 293 | } else { 294 | return notification; 295 | } 296 | } else { 297 | let notification = await utils.parseXML(xml); 298 | let decryptedData = utils.decryptByAes256Cbc(notification.req_info, apiKey); 299 | let finalData = await utils.parseXML(decryptedData); 300 | return finalData; 301 | } 302 | } catch (error) { 303 | return error; 304 | } 305 | // fn.apply(_this, [notification, req, res, next]); 306 | } 307 | 308 | 309 | static wxCallback(fn, apiKey, type = 'payment') { 310 | return (req, res, next) => { 311 | var _this = this; 312 | res.success = function () { res.end(utils.buildXML({ xml: { return_code: 'SUCCESS' } })); }; 313 | res.fail = function () { res.end(utils.buildXML({ xml: { return_code: 'FAIL' } })); }; 314 | utils.pipe(req, async function (err, data) { 315 | var xml = data.toString('utf8'); 316 | let notification = await WechatPayment.checkNotification(xml, apiKey, type); 317 | fn.apply(_this, [notification, req, res, next]); 318 | }); 319 | } 320 | } 321 | 322 | 323 | 324 | } 325 | -------------------------------------------------------------------------------- /lib/WechatPayment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _regenerator = require('babel-runtime/regenerator'); 8 | 9 | var _regenerator2 = _interopRequireDefault(_regenerator); 10 | 11 | var _assign = require('babel-runtime/core-js/object/assign'); 12 | 13 | var _assign2 = _interopRequireDefault(_assign); 14 | 15 | var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator'); 16 | 17 | var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2); 18 | 19 | var _keys = require('babel-runtime/core-js/object/keys'); 20 | 21 | var _keys2 = _interopRequireDefault(_keys); 22 | 23 | var _promise = require('babel-runtime/core-js/promise'); 24 | 25 | var _promise2 = _interopRequireDefault(_promise); 26 | 27 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); 28 | 29 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 30 | 31 | var _createClass2 = require('babel-runtime/helpers/createClass'); 32 | 33 | var _createClass3 = _interopRequireDefault(_createClass2); 34 | 35 | var _utils = require('./utils'); 36 | 37 | var _utils2 = _interopRequireDefault(_utils); 38 | 39 | var _urls = require('./urls'); 40 | 41 | var _urls2 = _interopRequireDefault(_urls); 42 | 43 | var _request = require('request'); 44 | 45 | var _request2 = _interopRequireDefault(_request); 46 | 47 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 48 | 49 | var WechatPayment = function () { 50 | function WechatPayment(options) { 51 | (0, _classCallCheck3.default)(this, WechatPayment); 52 | 53 | if (!options.appid || !options.mch_id) { 54 | throw new Error("Seems that app id or merchant id is not set, please provide wechat app id and merchant id."); 55 | //throw new Error('haha'); 56 | } 57 | this.options = options; 58 | } 59 | 60 | /** 61 | * create wechat unified order 62 | * @params order object 63 | * 64 | * spec: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1 65 | */ 66 | 67 | (0, _createClass3.default)(WechatPayment, [{ 68 | key: 'createUnifiedOrder', 69 | value: function createUnifiedOrder(order) { 70 | var _this2 = this; 71 | 72 | return new _promise2.default(function (resolve, reject) { 73 | order.nonce_str = order.nonce_str || _utils2.default.createNonceStr(); 74 | order.appid = _this2.options.appid; 75 | order.mch_id = _this2.options.mch_id; 76 | order.sign = _utils2.default.sign(order, _this2.options.apiKey); 77 | var requestParam = { 78 | url: _urls2.default.UNIFIED_ORDER, 79 | method: 'POST', 80 | body: _utils2.default.buildXML(order) 81 | }; 82 | (0, _request2.default)(requestParam, function (err, response, body) { 83 | if (err) { 84 | reject(err); 85 | } 86 | _utils2.default.parseXML(body).then(function (result) { 87 | resolve(result); 88 | }).catch(function (err) { 89 | reject(err); 90 | }); 91 | }); 92 | }); 93 | } 94 | 95 | /** 96 | * config for payment 97 | * 98 | * @param prepayId from prepay id 99 | */ 100 | 101 | }, { 102 | key: 'configForPayment', 103 | value: function configForPayment(prepayId) { 104 | var configData = { 105 | appid: this.options.appid, 106 | partnerid: this.options.mch_id, 107 | prepayid: prepayId, 108 | package: 'Sign=WXPay', 109 | noncestr: _utils2.default.createNonceStr(), 110 | timestamp: parseInt(new Date().getTime() / 1000).toString() 111 | }; 112 | configData.sign = _utils2.default.sign(configData, this.options.apiKey); 113 | return configData; 114 | } 115 | 116 | /** 117 | * query 118 | * 119 | * 120 | * spec: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2 121 | */ 122 | 123 | }, { 124 | key: 'queryOrder', 125 | value: function queryOrder(query) { 126 | var _this3 = this; 127 | 128 | return new _promise2.default(function (resolve, reject) { 129 | if (!(query.transaction_id || query.out_trade_no)) { 130 | reject(new Error('缺少参数')); 131 | } 132 | 133 | query.nonce_str = query.nonce_str || _utils2.default.createNonceStr(); 134 | query.appid = _this3.options.appid; 135 | query.mch_id = _this3.options.mch_id; 136 | query.sign = _utils2.default.sign(query, _this3.options.apiKey); 137 | 138 | (0, _request2.default)({ 139 | url: _urls2.default.ORDER_QUERY, 140 | method: "POST", 141 | body: _utils2.default.buildXML({ xml: query }) 142 | }, function (err, res, body) { 143 | _utils2.default.parseXML(body).then(function (result) { 144 | resolve(result); 145 | }).catch(function (err) { 146 | reject(err); 147 | }); 148 | }); 149 | }); 150 | } 151 | }, { 152 | key: 'closeOrder', 153 | value: function closeOrder(order) { 154 | var _this4 = this; 155 | 156 | return new _promise2.default(function (resolve, reject) { 157 | if (!order.out_trade_no) { 158 | reject(new Error('缺少参数')); 159 | } 160 | 161 | order.nonce_str = order.nonce_str || _utils2.default.createNonceStr(); 162 | order.appid = _this4.options.appid; 163 | order.mch_id = _this4.options.mch_id; 164 | order.sign = _utils2.default.sign(order, _this4.options.apiKey); 165 | 166 | (0, _request2.default)({ 167 | url: _urls2.default.CLOSE_ORDER, 168 | method: "POST", 169 | body: _utils2.default.buildXML({ xml: order }) 170 | }, function (err, res, body) { 171 | _utils2.default.parseXML(body).then(function (result) { 172 | resolve(result); 173 | }).catch(function (err) { 174 | reject(err); 175 | }); 176 | }); 177 | }); 178 | } 179 | }, { 180 | key: 'refund', 181 | value: function refund(order) { 182 | var _this5 = this; 183 | 184 | return new _promise2.default(function (resolve, reject) { 185 | if (!order.transaction_id && !order.out_trade_no || !order.out_refund_no) { 186 | reject(new Error('缺少参数')); 187 | } 188 | 189 | order.nonce_str = order.nonce_str || _utils2.default.createNonceStr(); 190 | order.appid = _this5.options.appid; 191 | order.mch_id = _this5.options.mch_id; 192 | order.op_user_id = order.op_user_id || order.mch_id; 193 | order.sign = _utils2.default.sign(order, _this5.options.apiKey); 194 | 195 | (0, _request2.default)({ 196 | url: _urls2.default.REFUND, 197 | method: "POST", 198 | body: _utils2.default.buildXML({ xml: order }), 199 | agentOptions: { 200 | pfx: _this5.options.pfx, 201 | passphrase: _this5.options.mch_id 202 | } 203 | }, function (err, response, body) { 204 | _utils2.default.parseXML(body).then(function (result) { 205 | resolve(result); 206 | }).catch(function (err) { 207 | reject(err); 208 | }); 209 | }); 210 | }); 211 | } 212 | }, { 213 | key: 'queryRefund', 214 | value: function queryRefund(order) { 215 | var _this6 = this; 216 | 217 | return new _promise2.default(function (resolve, reject) { 218 | if (!(order.transaction_id || order.out_trade_no || order.out_refund_no || order.refund_id)) { 219 | reject(new Error('缺少参数')); 220 | } 221 | 222 | order.nonce_str = order.nonce_str || _utils2.default.createNonceStr(); 223 | order.appid = _this6.options.appid; 224 | order.mch_id = _this6.options.mch_id; 225 | order.sign = _utils2.default.sign(order, _this6.options.apiKey); 226 | 227 | (0, _request2.default)({ 228 | url: _urls2.default.REFUND_QUERY, 229 | method: "POST", 230 | body: _utils2.default.buildXML({ xml: order }) 231 | }, function (err, response, body) { 232 | _utils2.default.parseXML(body).then(function (result) { 233 | resolve(result); 234 | }).catch(function (err) { 235 | reject(err); 236 | }); 237 | }); 238 | }); 239 | } 240 | }, { 241 | key: 'transfers', 242 | value: function transfers(order) { 243 | var _this7 = this; 244 | 245 | return new _promise2.default(function (resolve, reject) { 246 | if (!(order.partner_trade_no && order.openid && order.check_name && order.amount && order.desc && order.spbill_create_ip)) { 247 | reject(new Error('缺少参数')); 248 | } 249 | 250 | order.nonce_str = order.nonce_str || _utils2.default.createNonceStr(); 251 | order.mch_appid = _this7.options.appid; 252 | order.mchid = _this7.options.mch_id; 253 | order.sign = _utils2.default.sign(order, _this7.options.apiKey); 254 | 255 | (0, _request2.default)({ 256 | url: _urls2.default.TRANSFERS, 257 | method: "POST", 258 | body: _utils2.default.buildXML(order), 259 | agentOptions: { 260 | pfx: _this7.options.pfx, 261 | passphrase: _this7.options.mch_id 262 | } 263 | }, function (err, response, body) { 264 | if (err) return reject(err); 265 | _utils2.default.parseXML.parseXML(body).then(function (result) { 266 | if (!result || result.result_code === 'FAIL') reject(result);else resolve(result); 267 | }).catch(reject); 268 | }); 269 | }); 270 | } 271 | }, { 272 | key: 'queryTransferInfo', 273 | value: function queryTransferInfo(order, fn) { 274 | var _this8 = this; 275 | 276 | return new _promise2.default(function (resolve, reject) { 277 | if (!order.partner_trade_no) { 278 | reject(new Error('缺少参数')); 279 | } 280 | 281 | order.nonce_str = order.nonce_str || _utils2.default.createNonceStr(); 282 | order.appid = _this8.options.appid; 283 | order.mch_id = _this8.options.mch_id; 284 | order.sign = _utils2.default.sign(order, _this8.options.apiKey); 285 | 286 | (0, _request2.default)({ 287 | url: _urls2.default.TRNSFER_INFO, 288 | method: "POST", 289 | body: _utils2.default.buildXML({ xml: order }), 290 | agentOptions: { 291 | pfx: _this8.options.pfx, 292 | passphrase: _this8.options.mch_id 293 | } 294 | }, function (err, response, body) { 295 | _utils2.default.parseXML(body, fn); 296 | }); 297 | }); 298 | } 299 | }, { 300 | key: 'getBrandWCPayRequestParams', 301 | value: function getBrandWCPayRequestParams(order) { 302 | var _this9 = this; 303 | 304 | return new _promise2.default(function (resolve, reject) { 305 | order.trade_type = "JSAPI"; 306 | _this9.createUnifiedOrder(order).then(function (data) { 307 | var reqparam = { 308 | appId: _this9.options.appid, 309 | timeStamp: _utils2.default.createTimestamp(), 310 | nonceStr: data.nonce_str, 311 | package: "prepay_id=" + data.prepay_id, 312 | signType: "MD5" 313 | }; 314 | reqparam.paySign = _utils2.default.sign(reqparam, _this9.options.apiKey); 315 | resolve(reqparam); 316 | }).catch(function (err) { 317 | reject(err); 318 | }); 319 | }); 320 | } 321 | }, { 322 | key: 'createMerchantPrepayUrl', 323 | value: function createMerchantPrepayUrl(param) { 324 | param.time_stamp = param.time_stamp || _utils2.default.createTimestamp(); 325 | param.nonce_str = param.nonce_str || _utils2.default.createNonceStr(); 326 | param.appid = this.options.appid; 327 | param.mch_id = this.options.mch_id; 328 | param.sign = _utils2.default.sign(param, this.options.apiKey); 329 | 330 | var query = (0, _keys2.default)(param).filter(function (key) { 331 | return ['sign', 'mch_id', 'product_id', 'appid', 'time_stamp', 'nonce_str'].indexOf(key) >= 0; 332 | }).map(function (key) { 333 | return key + "=" + encodeURIComponent(param[key]); 334 | }).join('&'); 335 | 336 | return "weixin://wxpay/bizpayurl?" + query; 337 | } 338 | }], [{ 339 | key: 'checkNotification', 340 | value: function () { 341 | var _ref = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee(xml, apiKey) { 342 | var type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'payment'; 343 | 344 | var notification, dataForSign, signValue, _notification, decryptedData, finalData; 345 | 346 | return _regenerator2.default.wrap(function _callee$(_context) { 347 | while (1) { 348 | switch (_context.prev = _context.next) { 349 | case 0: 350 | _context.prev = 0; 351 | 352 | if (!(type == 'payment')) { 353 | _context.next = 15; 354 | break; 355 | } 356 | 357 | _context.next = 4; 358 | return _utils2.default.parseXML(xml); 359 | 360 | case 4: 361 | notification = _context.sent; 362 | dataForSign = (0, _assign2.default)({}, notification); 363 | 364 | delete dataForSign.sign; 365 | signValue = _utils2.default.sign(dataForSign, apiKey); 366 | 367 | if (!(signValue != notification.sign)) { 368 | _context.next = 12; 369 | break; 370 | } 371 | 372 | return _context.abrupt('return', null); 373 | 374 | case 12: 375 | return _context.abrupt('return', notification); 376 | 377 | case 13: 378 | _context.next = 23; 379 | break; 380 | 381 | case 15: 382 | _context.next = 17; 383 | return _utils2.default.parseXML(xml); 384 | 385 | case 17: 386 | _notification = _context.sent; 387 | decryptedData = _utils2.default.decryptByAes256Cbc(_notification.req_info, apiKey); 388 | _context.next = 21; 389 | return _utils2.default.parseXML(decryptedData); 390 | 391 | case 21: 392 | finalData = _context.sent; 393 | return _context.abrupt('return', finalData); 394 | 395 | case 23: 396 | _context.next = 28; 397 | break; 398 | 399 | case 25: 400 | _context.prev = 25; 401 | _context.t0 = _context['catch'](0); 402 | return _context.abrupt('return', _context.t0); 403 | 404 | case 28: 405 | case 'end': 406 | return _context.stop(); 407 | } 408 | } 409 | }, _callee, this, [[0, 25]]); 410 | })); 411 | 412 | function checkNotification(_x2, _x3) { 413 | return _ref.apply(this, arguments); 414 | } 415 | 416 | return checkNotification; 417 | }() 418 | }, { 419 | key: 'wxCallback', 420 | value: function wxCallback(fn, apiKey) { 421 | var _this10 = this; 422 | 423 | var type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'payment'; 424 | 425 | return function (req, res, next) { 426 | var _this = _this10; 427 | res.success = function () { 428 | res.end(_utils2.default.buildXML({ xml: { return_code: 'SUCCESS' } })); 429 | }; 430 | res.fail = function () { 431 | res.end(_utils2.default.buildXML({ xml: { return_code: 'FAIL' } })); 432 | }; 433 | _utils2.default.pipe(req, function () { 434 | var _ref2 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee2(err, data) { 435 | var xml, notification; 436 | return _regenerator2.default.wrap(function _callee2$(_context2) { 437 | while (1) { 438 | switch (_context2.prev = _context2.next) { 439 | case 0: 440 | xml = data.toString('utf8'); 441 | _context2.next = 3; 442 | return WechatPayment.checkNotification(xml, apiKey, type); 443 | 444 | case 3: 445 | notification = _context2.sent; 446 | 447 | fn.apply(_this, [notification, req, res, next]); 448 | 449 | case 5: 450 | case 'end': 451 | return _context2.stop(); 452 | } 453 | } 454 | }, _callee2, this); 455 | })); 456 | 457 | return function (_x5, _x6) { 458 | return _ref2.apply(this, arguments); 459 | }; 460 | }()); 461 | }; 462 | } 463 | }]); 464 | return WechatPayment; 465 | }(); 466 | 467 | exports.default = WechatPayment; --------------------------------------------------------------------------------