UNPKG

egg-old-pay

Version:

sandan store pay inclued wechat alipay vista

586 lines (518 loc) 16.4 kB
const urllib = require('urllib'); const util = require('./util'); const replyData = msg => util.buildXML(msg ? { return_code: 'FAIL', return_msg: msg } : { return_code: 'SUCCESS' }); class Payment { constructor({ appid, mchid, partnerKey, pfx, notify_url, refund_url, spbill_create_ip, sandbox, accounts } = {}, debug = false) { if (!appid) throw new Error('appid fail'); if (!mchid) throw new Error('mchid fail'); if (!partnerKey) throw new Error('partnerKey fail'); this.accounts = accounts; this.appid = appid; this.mchid = mchid; this.partnerKey = partnerKey; // this.pfx = pfx; this.pfx = require('fs').readFileSync(pfx); this.notify_url = notify_url; this.refund_url = refund_url; this.spbill_create_ip = spbill_create_ip || '127.0.0.1'; this.prefix = 'https://api.mch.weixin.qq.com' + (sandbox ? '/sandboxnew' : ''); this.urls = { pappayapply: '/pay/pappayapply', querycontract: '/papay/querycontract', micropay: '/pay/micropay', reverse: '/secapi/pay/reverse', unifiedorder: '/pay/unifiedorder', orderquery: '/pay/orderquery', closeorder: '/pay/closeorder', refund: '/secapi/pay/refund', refundquery: '/pay/refundquery', downloadbill: '/pay/downloadbill', downloadfundflow: '/pay/downloadfundflow', send_coupon: '/mmpaymkttransfers/send_coupon', query_coupon_stock: '/mmpaymkttransfers/query_coupon_stock', querycouponsinfo: '/mmpaymkttransfers/querycouponsinfo', transfers: '/mmpaymkttransfers/promotion/transfers', gettransferinfo: '/mmpaymkttransfers/gettransferinfo', sendredpack: '/mmpaymkttransfers/sendredpack', sendgroupredpack: '/mmpaymkttransfers/sendgroupredpack', gethbinfo: '/mmpaymkttransfers/gethbinfo', getsignkey: '/sandboxnew/pay/getsignkey', getWxpayfaceAuthinfo: '/face/get_wxpayface_authinfo', facepay: '/pay/facepay' }; this.debug = debug; } get(config) { if (config) { const { appid, mchid, partnerKey, pfx, notify_url, refund_url, spbill_create_ip, sandbox } = config; this.appid = appid; this.mchid = mchid; this.partnerKey = partnerKey; this.pfx = require('fs').readFileSync(pfx); this.notify_url = notify_url; this.refund_url = refund_url; this.spbill_create_ip = spbill_create_ip || '127.0.0.1'; this.prefix = 'https://api.mch.weixin.qq.com' + (sandbox ? '/sandboxnew' : ''); } return this; } log(...args) { console.log(...args); } static init(...args) { return new Payment(...args); } static async sandbox(config, debug) { let { sandbox_signkey } = await Payment.init(config).getSignkey(); return new Payment({ ...config, partnerKey: sandbox_signkey, sandbox: true }, debug); } async _parse(xml, type) { let json = await util.parseXML(xml); switch (type) { case 'middleware_nativePay': break; default: if (json.return_code !== 'SUCCESS') throw new Error(json.return_msg || 'XMLDataError'); } switch (type) { case 'middleware_refund': case 'middleware_nativePay': case 'getsignkey': case 'getWxpayfaceAuthinfo': break; default: if (json.result_code !== 'SUCCESS') throw new Error(json.err_code || 'XMLDataError'); } switch (type) { case 'middleware_contract': case 'contract': //免密签约 break; case 'getsignkey': break; case 'middleware_refund': // if (json.appid !== this.appid) throw new Error('appid不匹配'); // if (json.mch_id !== this.mchid) throw new Error('mch_id不匹配'); let key = util.md5(this.partnerKey).toLowerCase(); let info = util.decrypt(json.req_info, key); json.req_info = await util.parseXML(info); break; case 'transfers': // if (json.mchid !== this.mchid) throw new Error('mchid不匹配'); break; case 'sendredpack': case 'sendgroupredpack': // if (json.wxappid !== this.appid) throw new Error('wxappid不匹配'); // if (json.mch_id !== this.mchid) throw new Error('mchid不匹配'); break; case 'gethbinfo': case 'gettransferinfo': // if (json.mch_id !== this.mchid) throw new Error('mchid不匹配'); break; default: // if (json.appid !== this.appid) throw new Error('appid不匹配'); // if (json.mch_id !== this.mchid) throw new Error('mch_id不匹配'); // if (json.sign !== this._getSign(json, json.sign_type)) throw new Error('sign签名错误'); } return json; } async _parseBill(xml, format = false) { if (util.checkXML(xml)) { let json = await util.parseXML(xml); throw new Error(json.err_code || json.return_msg || 'XMLDataError'); } if (!format) return xml; let arr = xml.trim().split(/\r?\n/).filter(item => item.trim()); let total_data = arr.pop().substr(1).split(',`'); let total_title = arr.pop().split(','); let list_title = arr.shift().split(','); let list_data = arr.map(item => item.substr(1).split(',`')); return { total_title, total_data, list_title, list_data }; } _getSign(params, type = 'MD5') { let str = util.toQueryString(params) + '&key=' + this.partnerKey; console.log('_getSign', str, util.md5(str).toUpperCase()) switch (type) { case 'MD5': return util.md5(str).toUpperCase(); break; case 'HMAC-SHA256': return util.sha256(str, this.partnerKey).toUpperCase(); break; default: throw new Error('signType Error'); } } async _request(params, type, cert = false, prefix = '') { // 安全签名 params.sign = this._getSign(params, params.sign_type); // 创建请求参数 let pkg = { method: 'POST', dataType: 'text', data: util.buildXML(params) }; if (cert) { pkg.pfx = this.pfx; pkg.passphrase = this.mchid; } this.log('---post data:---\r\n%s\r\n', pkg.data); let tmpPrefix = this.prefix; if (prefix) { tmpPrefix = prefix } let { status, data } = await urllib.request(tmpPrefix + this.urls[type], pkg); if (status !== 200) throw new Error('request fail'); this.log('---receive data:---\r\n%s\r\n', data); return ['downloadbill', 'downloadfundflow'].indexOf(type) < 0 ? this._parse(data, type) : data; } // Express中间件 middlewareForExpress(type = 'pay') { return async (req, res, next) => { res.reply = msg => { res.header('Content-Type', 'application/xml; charset=utf-8'); res.send(replyData(msg)); } try { if (typeof req.body !== 'string') throw new Error('XMLDataError'); req.weixin = await this._parse(req.body, 'middleware_' + type); } catch (err) { return res.reply(err.message); } next(); } } // Koa中间件 middleware(type = 'pay') { return async (ctx, next) => { ctx.reply = msg => { ctx.type = 'application/xml; charset=utf-8'; ctx.body = replyData(msg); } try { if (typeof ctx.request.body !== 'string') throw new Error('XMLDataError'); ctx.request.weixin = await this._parse(ctx.request.body, 'middleware_' + type); } catch (err) { return ctx.reply(err.message); } await next(); }; } // 获取沙盒密钥 getSignkey() { let pkg = { mch_id: this.mchid, nonce_str: util.generate() }; return this._request(pkg, 'getsignkey'); } // 获取JS支付参数(自动下单) async getPayParams(params) { params.trade_type = params.trade_type || 'JSAPI'; let order = await this.unifiedOrder(params); return this.getPayParamsByPrepay(order); } // 获取JS支付参数(通过预支付会话标志) getPayParamsByPrepay(params) { let pkg = { appId: this.appid, timeStamp: '' + (Date.now() / 1000 | 0), nonceStr: util.generate(), package: 'prepay_id=' + params.prepay_id, signType: params.signType || 'MD5' }; pkg.paySign = this._getSign(pkg, pkg.signType); pkg.timestamp = pkg.timeStamp; return pkg; } // 获取APP支付参数(自动下单) async getAppParams(params) { params.trade_type = params.trade_type || 'APP'; let order = await this.unifiedOrder(params); return this.getAppParamsByPrepay(order); } // 获取APP支付参数(通过预支付会话标志) getAppParamsByPrepay(params, signType) { let pkg = { appid: this.appid, partnerid: this.mchid, prepayid: params.prepay_id, package: 'Sign=WXPay', noncestr: util.generate(), timestamp: '' + (Date.now() / 1000 | 0) }; pkg.sign = this._getSign(pkg, signType); return pkg; } //人脸支付微信授权 getWxpayfaceAuthinfo(params) { let pkg = { ...params, appid: this.appid, mch_id: this.mchid, version: 1, nonce_str: util.generate(), sign_type: params.sign_type || 'MD5' }; return this._request(pkg, 'getWxpayfaceAuthinfo', false, 'https://payapp.weixin.qq.com'); } facepay(params) { let pkg = { ...params, appid: this.appid, mch_id: this.mchid, nonce_str: util.generate(), sign_type: params.sign_type || 'MD5', spbill_create_ip: params.spbill_create_ip || this.spbill_create_ip }; console.log('facepay parm :', pkg) return this._request(pkg, 'facepay'); } // 扫码支付, 生成URL(模式一) getNativeUrl(params) { let pkg = { ...params, appid: this.appid, mch_id: this.mchid, time_stamp: '' + (Date.now() / 1000 | 0), nonce_str: util.generate() }; let url = 'weixin://wxpay/bizpayurl' + '?sign=' + this._getSign(pkg) + '&appid=' + pkg.appid + '&mch_id=' + pkg.mch_id + '&product_id=' + pkg.product_id + '&time_stamp=' + pkg.time_stamp + '&nonce_str=' + pkg.nonce_str; return url; } // 免密支付 pappayapply(params) { let pkg = { ...params, appid: this.appid, mch_id: this.mchid, nonce_str: util.generate(), sign_type: params.sign_type || 'MD5', spbill_create_ip: params.spbill_create_ip || this.spbill_create_ip }; return this._request(pkg, 'pappayapply'); } //查询免密协议 querycontract(params) { console.log('params-----', params) let pkg = { ...params, appid: this.appid, mch_id: this.mchid, nonce_str: util.generate(), sign_type: params.sign_type || 'MD5', spbill_create_ip: params.spbill_create_ip || this.spbill_create_ip }; console.log('pkg-----', pkg) return this._request(pkg, 'querycontract'); } // 刷卡支付 micropay(params) { let pkg = { ...params, appid: this.appid, mch_id: this.mchid, nonce_str: util.generate(), sign_type: params.sign_type || 'MD5', spbill_create_ip: params.spbill_create_ip || this.spbill_create_ip }; return this._request(pkg, 'micropay'); } // 撤销订单 reverse(params) { let pkg = { ...params, appid: this.appid, mch_id: this.mchid, nonce_str: util.generate(), sign_type: params.sign_type || 'MD5' }; return this._request(pkg, 'reverse', true); } // 统一下单 unifiedOrder(params) { let pkg = { ...params, appid: this.appid, mch_id: this.mchid, nonce_str: util.generate(), sign_type: params.sign_type || 'MD5', notify_url: params.notify_url || this.notify_url, spbill_create_ip: params.spbill_create_ip || this.spbill_create_ip, trade_type: params.trade_type || 'JSAPI' }; return this._request(pkg, 'unifiedorder'); } // 订单查询 orderQuery(params) { let pkg = { ...params, appid: this.appid, mch_id: this.mchid, nonce_str: util.generate(), sign_type: params.sign_type || 'MD5' }; return this._request(pkg, 'orderquery'); } // 关闭订单 closeOrder(params) { let pkg = { ...params, appid: this.appid, mch_id: this.mchid, nonce_str: util.generate(), sign_type: params.sign_type || 'MD5' }; return this._request(pkg, 'closeorder'); } // 申请退款 refund(params) { let pkg = { ...params, appid: this.appid, mch_id: this.mchid, nonce_str: util.generate(), sign_type: params.sign_type || 'MD5', op_user_id: params.op_user_id || this.mchid, notify_url: params.notify_url || this.refund_url }; if (!pkg.notify_url) delete pkg.notify_url; return this._request(pkg, 'refund', true); } // 查询退款 refundQuery(params) { let pkg = { ...params, appid: this.appid, mch_id: this.mchid, nonce_str: util.generate(), sign_type: params.sign_type || 'MD5' }; return this._request(pkg, 'refundquery'); } // 下载对帐单 async downloadBill(params, format = false) { let pkg = { ...params, appid: this.appid, mch_id: this.mchid, nonce_str: util.generate(), sign_type: params.sign_type || 'MD5', bill_type: params.bill_type || 'ALL' }; let xml = await this._request(pkg, 'downloadbill'); return this._parseBill(xml, format); } // 下载资金帐单 async downloadFundflow(params, format = false) { let pkg = { ...params, appid: this.appid, mch_id: this.mchid, nonce_str: util.generate(), sign_type: params.sign_type || 'HMAC-SHA256', account_type: params.account_type || 'Basic' }; let xml = await this._request(pkg, 'downloadfundflow', true); return this._parseBill(xml, format); } // 发放代金券 sendCoupon(params) { let pkg = { ...params, appid: this.appid, mch_id: this.mchid, nonce_str: util.generate(), openid_count: params.openid_count || 1 }; return this.request(pkg, 'send_coupon', true); } // 查询代金券批次 queryCouponStock(params) { let pkg = { ...params, appid: this.appid, mch_id: this.mchid, nonce_str: util.generate() }; return this.request(pkg, 'query_coupon_stock'); } // 查询代金券信息 queryCouponInfo(params) { let pkg = { ...params, appid: this.appid, mch_id: this.mchid, nonce_str: util.generate() }; return this.request(pkg, 'querycouponsinfo'); } // 企业付款 transfers(params) { let pkg = { ...params, mch_appid: this.appid, mchid: this.mchid, nonce_str: util.generate(), check_name: params.check_name || 'FORCE_CHECK', spbill_create_ip: params.spbill_create_ip || this.spbill_create_ip }; return this._request(pkg, 'transfers', true); } // 查询企业付款 transfersQuery(params) { let pkg = { ...params, appid: this.appid, mch_id: this.mchid, nonce_str: util.generate() }; return this._request(pkg, 'gettransferinfo', true); } // 发送普通红包 sendRedpack(params) { let pkg = { ...params, wxappid: this.appid, mch_id: this.mchid, nonce_str: util.generate(), client_ip: params.client_ip || this.spbill_create_ip, mch_billno: params.mch_billno || (params.mch_autono ? this.mchid + util.getFullDate() + params.mch_autono : ''), total_num: params.total_num || 1 }; delete pkg.mch_autono; return this._request(pkg, 'sendredpack', true); } // 发送裂变红包 sendGroupRedpack(params) { let pkg = { ...params, wxappid: this.appid, mch_id: this.mchid, nonce_str: util.generate(), mch_billno: params.mch_billno || (params.mch_autono ? this.mchid + util.getFullDate() + params.mch_autono : ''), total_num: params.total_num || 3, amt_type: params.amt_type || 'ALL_RAND' }; delete pkg.mch_autono; return this._request(pkg, 'sendgroupredpack', true); } // 查询红包记录 redpackQuery(params) { let pkg = { ...params, appid: this.appid, mch_id: this.mchid, nonce_str: util.generate(), bill_type: params.bill_type || 'MCHT' }; return this._request(pkg, 'gethbinfo', true); } } module.exports = Payment;