From d905550f7c516f0daa5fc1c9f3100657074ec4c8 Mon Sep 17 00:00:00 2001 From: jipan Date: Mon, 14 Dec 2015 17:05:41 +0800 Subject: [PATCH 1/2] =?UTF-8?q?add=20=E9=80=80=E6=AC=BE=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 24 +++++++- lib/wxpay.js | 170 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 133 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index 5d43628..5490fda 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,21 @@ wxpay.closeOrder({ out_trade_no:"xxxxxx"}, function(err, result){ console.log(result); }); ``` +退款接口 +```js +let params = { + appid: 'xxxxxxxx', + mch_id: '1234567890', + op_user_id: '商户号即可', + out_refund_no: '20140703'+Math.random().toString().substr(2, 10), + total_fee: '1', //原支付金额 + refund_fee: '1', //退款金额 + transaction_id: '微信订单号' } + +wxpay.refund(params, function(err, result){ + console.log('refund', arguments); +}); +``` ### 原生支付 (NATIVE) @@ -107,7 +122,14 @@ WeixinJSBridge.invoke( 商户服务端处理微信的回调(express为例) ```js var router = express.Router(); -var wxpay = require('weixin-pay'); +var WXPay = require('weixin-pay'); + +var wxpay = WXPay({ + appid: 'xxxxxxxx', + mch_id: '1234567890', + partner_key: 'xxxxxxxxxxxxxxxxx', //微信商户平台API密钥 + pfx: fs.readFileSync('./wxpay_cert.p12'), //微信商户平台证书 +}); // 原生支付回调 router.use('/wxpay/native/callback', wxpay.useWXCallback(function(msg, req, res, next){ diff --git a/lib/wxpay.js b/lib/wxpay.js index d0a49ed..3d2685d 100644 --- a/lib/wxpay.js +++ b/lib/wxpay.js @@ -1,4 +1,3 @@ - var util = require('./util'); var request = require('request'); var md5 = require('MD5'); @@ -6,58 +5,59 @@ var md5 = require('MD5'); exports = module.exports = WXPay; function WXPay() { - - if (!(this instanceof WXPay)) { + + if (! (this instanceof WXPay)) { return new WXPay(arguments[0]); }; this.options = arguments[0]; - this.wxpayID = { appid:this.options.appid, mch_id:this.options.mch_id }; + this.wxpayID = { + appid: this.options.appid, + mch_id: this.options.mch_id + }; }; -WXPay.mix = function(){ - +WXPay.mix = function() { + switch (arguments.length) { - case 1: - var obj = arguments[0]; - for (var key in obj) { - if (WXPay.prototype.hasOwnProperty(key)) { - throw new Error('Prototype method exist. method: '+ key); - } - WXPay.prototype[key] = obj[key]; - } - break; - case 2: - var key = arguments[0].toString(), fn = arguments[1]; + case 1: + var obj = arguments[0]; + for (var key in obj) { if (WXPay.prototype.hasOwnProperty(key)) { - throw new Error('Prototype method exist. method: '+ key); + throw new Error('Prototype method exist. method: ' + key); } - WXPay.prototype[key] = fn; - break; + WXPay.prototype[key] = obj[key]; + } + break; + case 2: + var key = arguments[0].toString(), + fn = arguments[1]; + if (WXPay.prototype.hasOwnProperty(key)) { + throw new Error('Prototype method exist. method: ' + key); + } + WXPay.prototype[key] = fn; + break; } }; - -WXPay.mix('option', function(option){ - for( var k in option ) { +WXPay.mix('option', function(option) { + for (var k in option) { this.options[k] = option[k]; } }); +WXPay.mix('sign', function(param) { -WXPay.mix('sign', function(param){ - - var querystring = Object.keys(param).filter(function(key){ - return param[key] !== undefined && param[key] !== '' && ['pfx', 'partner_key', 'sign', 'key'].indexOf(key)<0; - }).sort().map(function(key){ + var querystring = Object.keys(param).filter(function(key) { + return param[key] !== undefined && param[key] !== '' && ['pfx', 'partner_key', 'sign', 'key'].indexOf(key) < 0; + }).sort().map(function(key) { return key + '=' + param[key]; }).join("&") + "&key=" + this.options.partner_key; return md5(querystring).toUpperCase(); }); - -WXPay.mix('createUnifiedOrder', function(opts, fn){ +WXPay.mix('createUnifiedOrder', function(opts, fn) { opts.nonce_str = opts.nonce_str || util.generateNonceString(); util.mix(opts, this.wxpayID); @@ -71,21 +71,22 @@ WXPay.mix('createUnifiedOrder', function(opts, fn){ pfx: this.options.pfx, passphrase: this.options.mch_id } - }, function(err, response, body){ + }, + function(err, response, body) { util.parseXML(body, fn); }); }); -WXPay.mix('getBrandWCPayRequestParams', function(order, fn){ +WXPay.mix('getBrandWCPayRequestParams', function(order, fn) { order.trade_type = "JSAPI"; var _this = this; - this.createUnifiedOrder(order, function(err, data){ + this.createUnifiedOrder(order, function(err, data) { var reqparam = { appId: _this.options.appid, - timeStamp: Math.floor(Date.now()/1000)+"", + timeStamp: Math.floor(Date.now() / 1000) + "", nonceStr: data.nonce_str, - package: "prepay_id="+data.prepay_id, + package: "prepay_id=" + data.prepay_id, signType: "MD5" }; reqparam.paySign = _this.sign(reqparam); @@ -93,45 +94,58 @@ WXPay.mix('getBrandWCPayRequestParams', function(order, fn){ }); }); -WXPay.mix('createMerchantPrepayUrl', function(param){ +WXPay.mix('createMerchantPrepayUrl', function(param) { - param.time_stamp = param.time_stamp || Math.floor(Date.now()/1000); + param.time_stamp = param.time_stamp || Math.floor(Date.now() / 1000); param.nonce_str = param.nonce_str || util.generateNonceString(); util.mix(param, this.wxpayID); param.sign = this.sign(param); - var query = Object.keys(param).filter(function(key){ - return ['sign', 'mch_id', 'product_id', 'appid', 'time_stamp', 'nonce_str'].indexOf(key)>=0; - }).map(function(key){ + var query = Object.keys(param).filter(function(key) { + return ['sign', 'mch_id', 'product_id', 'appid', 'time_stamp', 'nonce_str'].indexOf(key) >= 0; + }).map(function(key) { return key + "=" + encodeURIComponent(param[key]); }).join('&'); return "weixin://wxpay/bizpayurl?" + query; }); +WXPay.mix('useWXCallback', function(fn) { -WXPay.mix('useWXCallback', function(fn){ - - return function(req, res, next){ + return function(req, res, next) { var _this = this; - res.success = function(){ res.end(util.buildXML({ xml:{ return_code:'SUCCESS' } })); }; - res.fail = function(){ res.end(util.buildXML({ xml:{ return_code:'FAIL' } })); }; + res.success = function() { + res.end(util.buildXML({ + xml: { + return_code: 'SUCCESS' + } + })); + }; + res.fail = function() { + res.end(util.buildXML({ + xml: { + return_code: 'FAIL' + } + })); + }; - util.pipe(req, function(err, data){ + util.pipe(req, function(err, data) { var xml = data.toString('utf8'); - util.parseXML(xml, function(err, msg){ + util.parseXML(xml, function(err, msg) { req.wxmessage = msg; fn.apply(_this, [msg, req, res, next]); }); }); }; }); - -WXPay.mix('queryOrder', function(query, fn){ - - if (!(query.transaction_id || query.out_trade_no)) { - fn(null, { return_code: 'FAIL', return_msg:'缺少参数' }); +WXPay.mix('queryOrder', function(query, fn) { + + if (! (query.transaction_id || query.out_trade_no)) { + fn(null, { + return_code: 'FAIL', + return_msg: '缺少参数' + }); } query.nonce_str = query.nonce_str || util.generateNonceString(); @@ -141,17 +155,22 @@ WXPay.mix('queryOrder', function(query, fn){ request({ url: "https://api.mch.weixin.qq.com/pay/orderquery", method: "POST", - body: util.buildXML({xml: query}) - }, function(err, res, body){ + body: util.buildXML({ + xml: query + }) + }, + function(err, res, body) { util.parseXML(body, fn); }); }); - -WXPay.mix('closeOrder', function(order, fn){ +WXPay.mix('closeOrder', function(order, fn) { if (!order.out_trade_no) { - fn(null, { return_code:"FAIL", return_msg:"缺少参数" }); + fn(null, { + return_code: "FAIL", + return_msg: "缺少参数" + }); } order.nonce_str = order.nonce_str || util.generateNonceString(); @@ -161,8 +180,39 @@ WXPay.mix('closeOrder', function(order, fn){ request({ url: "https://api.mch.weixin.qq.com/pay/closeorder", method: "POST", - body: util.buildXML({xml:order}) - }, function(err, res, body){ + body: util.buildXML({ + xml: order + }) + }, + function(err, res, body) { util.parseXML(body, fn); }); -}); \ No newline at end of file +}); + +WXPay.mix('refund', function(order, fn) { + if (! (order.transaction_id || order.out_refund_no)) { + fn(null, { + return_code: 'FAIL', + return_msg: '缺少参数' + }); + } + + order.nonce_str = order.nonce_str || util.generateNonceString(); + util.mix(order, this.wxpayID); + order.sign = this.sign(order); + + request({ + url: "https://api.mch.weixin.qq.com/secapi/pay/refund", + method: "POST", + body: util.buildXML({ + xml: order + }), + agentOptions: { + pfx: this.options.pfx, + passphrase: this.options.mch_id + } + }, + function(err, response, body) { + util.parseXML(body, fn); + }); +}); From 7abd0e605aac256c401819fe456f6b1d54acb680 Mon Sep 17 00:00:00 2001 From: jipan Date: Mon, 14 Dec 2015 17:40:56 +0800 Subject: [PATCH 2/2] =?UTF-8?q?modify=20=E9=80=80=E6=AC=BE=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/wxpay.js | 158 +++++++++++++++++++++------------------------------ 1 file changed, 66 insertions(+), 92 deletions(-) diff --git a/lib/wxpay.js b/lib/wxpay.js index 3d2685d..969ac13 100644 --- a/lib/wxpay.js +++ b/lib/wxpay.js @@ -1,3 +1,4 @@ + var util = require('./util'); var request = require('request'); var md5 = require('MD5'); @@ -5,59 +6,58 @@ var md5 = require('MD5'); exports = module.exports = WXPay; function WXPay() { - - if (! (this instanceof WXPay)) { + + if (!(this instanceof WXPay)) { return new WXPay(arguments[0]); }; this.options = arguments[0]; - this.wxpayID = { - appid: this.options.appid, - mch_id: this.options.mch_id - }; + this.wxpayID = { appid:this.options.appid, mch_id:this.options.mch_id }; }; -WXPay.mix = function() { - +WXPay.mix = function(){ + switch (arguments.length) { - case 1: - var obj = arguments[0]; - for (var key in obj) { + case 1: + var obj = arguments[0]; + for (var key in obj) { + if (WXPay.prototype.hasOwnProperty(key)) { + throw new Error('Prototype method exist. method: '+ key); + } + WXPay.prototype[key] = obj[key]; + } + break; + case 2: + var key = arguments[0].toString(), fn = arguments[1]; if (WXPay.prototype.hasOwnProperty(key)) { - throw new Error('Prototype method exist. method: ' + key); + throw new Error('Prototype method exist. method: '+ key); } - WXPay.prototype[key] = obj[key]; - } - break; - case 2: - var key = arguments[0].toString(), - fn = arguments[1]; - if (WXPay.prototype.hasOwnProperty(key)) { - throw new Error('Prototype method exist. method: ' + key); - } - WXPay.prototype[key] = fn; - break; + WXPay.prototype[key] = fn; + break; } }; -WXPay.mix('option', function(option) { - for (var k in option) { + +WXPay.mix('option', function(option){ + for( var k in option ) { this.options[k] = option[k]; } }); -WXPay.mix('sign', function(param) { - var querystring = Object.keys(param).filter(function(key) { - return param[key] !== undefined && param[key] !== '' && ['pfx', 'partner_key', 'sign', 'key'].indexOf(key) < 0; - }).sort().map(function(key) { +WXPay.mix('sign', function(param){ + + var querystring = Object.keys(param).filter(function(key){ + return param[key] !== undefined && param[key] !== '' && ['pfx', 'partner_key', 'sign', 'key'].indexOf(key)<0; + }).sort().map(function(key){ return key + '=' + param[key]; }).join("&") + "&key=" + this.options.partner_key; return md5(querystring).toUpperCase(); }); -WXPay.mix('createUnifiedOrder', function(opts, fn) { + +WXPay.mix('createUnifiedOrder', function(opts, fn){ opts.nonce_str = opts.nonce_str || util.generateNonceString(); util.mix(opts, this.wxpayID); @@ -71,22 +71,21 @@ WXPay.mix('createUnifiedOrder', function(opts, fn) { pfx: this.options.pfx, passphrase: this.options.mch_id } - }, - function(err, response, body) { + }, function(err, response, body){ util.parseXML(body, fn); }); }); -WXPay.mix('getBrandWCPayRequestParams', function(order, fn) { +WXPay.mix('getBrandWCPayRequestParams', function(order, fn){ order.trade_type = "JSAPI"; var _this = this; - this.createUnifiedOrder(order, function(err, data) { + this.createUnifiedOrder(order, function(err, data){ var reqparam = { appId: _this.options.appid, - timeStamp: Math.floor(Date.now() / 1000) + "", + timeStamp: Math.floor(Date.now()/1000)+"", nonceStr: data.nonce_str, - package: "prepay_id=" + data.prepay_id, + package: "prepay_id="+data.prepay_id, signType: "MD5" }; reqparam.paySign = _this.sign(reqparam); @@ -94,58 +93,45 @@ WXPay.mix('getBrandWCPayRequestParams', function(order, fn) { }); }); -WXPay.mix('createMerchantPrepayUrl', function(param) { +WXPay.mix('createMerchantPrepayUrl', function(param){ - param.time_stamp = param.time_stamp || Math.floor(Date.now() / 1000); + param.time_stamp = param.time_stamp || Math.floor(Date.now()/1000); param.nonce_str = param.nonce_str || util.generateNonceString(); util.mix(param, this.wxpayID); param.sign = this.sign(param); - var query = Object.keys(param).filter(function(key) { - return ['sign', 'mch_id', 'product_id', 'appid', 'time_stamp', 'nonce_str'].indexOf(key) >= 0; - }).map(function(key) { + var query = Object.keys(param).filter(function(key){ + return ['sign', 'mch_id', 'product_id', 'appid', 'time_stamp', 'nonce_str'].indexOf(key)>=0; + }).map(function(key){ return key + "=" + encodeURIComponent(param[key]); }).join('&'); return "weixin://wxpay/bizpayurl?" + query; }); -WXPay.mix('useWXCallback', function(fn) { - return function(req, res, next) { +WXPay.mix('useWXCallback', function(fn){ + + return function(req, res, next){ var _this = this; - res.success = function() { - res.end(util.buildXML({ - xml: { - return_code: 'SUCCESS' - } - })); - }; - res.fail = function() { - res.end(util.buildXML({ - xml: { - return_code: 'FAIL' - } - })); - }; + res.success = function(){ res.end(util.buildXML({ xml:{ return_code:'SUCCESS' } })); }; + res.fail = function(){ res.end(util.buildXML({ xml:{ return_code:'FAIL' } })); }; - util.pipe(req, function(err, data) { + util.pipe(req, function(err, data){ var xml = data.toString('utf8'); - util.parseXML(xml, function(err, msg) { + util.parseXML(xml, function(err, msg){ req.wxmessage = msg; fn.apply(_this, [msg, req, res, next]); }); }); }; }); + -WXPay.mix('queryOrder', function(query, fn) { - - if (! (query.transaction_id || query.out_trade_no)) { - fn(null, { - return_code: 'FAIL', - return_msg: '缺少参数' - }); +WXPay.mix('queryOrder', function(query, fn){ + + if (!(query.transaction_id || query.out_trade_no)) { + fn(null, { return_code: 'FAIL', return_msg:'缺少参数' }); } query.nonce_str = query.nonce_str || util.generateNonceString(); @@ -155,22 +141,17 @@ WXPay.mix('queryOrder', function(query, fn) { request({ url: "https://api.mch.weixin.qq.com/pay/orderquery", method: "POST", - body: util.buildXML({ - xml: query - }) - }, - function(err, res, body) { + body: util.buildXML({xml: query}) + }, function(err, res, body){ util.parseXML(body, fn); }); }); -WXPay.mix('closeOrder', function(order, fn) { + +WXPay.mix('closeOrder', function(order, fn){ if (!order.out_trade_no) { - fn(null, { - return_code: "FAIL", - return_msg: "缺少参数" - }); + fn(null, { return_code:"FAIL", return_msg:"缺少参数" }); } order.nonce_str = order.nonce_str || util.generateNonceString(); @@ -180,21 +161,16 @@ WXPay.mix('closeOrder', function(order, fn) { request({ url: "https://api.mch.weixin.qq.com/pay/closeorder", method: "POST", - body: util.buildXML({ - xml: order - }) - }, - function(err, res, body) { + body: util.buildXML({xml:order}) + }, function(err, res, body){ util.parseXML(body, fn); }); }); -WXPay.mix('refund', function(order, fn) { - if (! (order.transaction_id || order.out_refund_no)) { - fn(null, { - return_code: 'FAIL', - return_msg: '缺少参数' - }); + +WXPay.mix('refund',function(order, fn){ + if (!(order.transaction_id || order.out_refund_no)) { + fn(null, { return_code: 'FAIL', return_msg:'缺少参数' }); } order.nonce_str = order.nonce_str || util.generateNonceString(); @@ -204,15 +180,13 @@ WXPay.mix('refund', function(order, fn) { request({ url: "https://api.mch.weixin.qq.com/secapi/pay/refund", method: "POST", - body: util.buildXML({ - xml: order - }), + body: util.buildXML({xml: order}), agentOptions: { pfx: this.options.pfx, passphrase: this.options.mch_id } - }, - function(err, response, body) { + }, function(err, response, body){ util.parseXML(body, fn); }); }); +