UNPKG

yp-wx-payment

Version:
1,742 lines (1,540 loc) 65.2 kB
'use strict'; var _requestPromise = require('request-promise'); var _requestPromise2 = _interopRequireDefault(_requestPromise); var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs); var _querystring = require('querystring'); var _querystring2 = _interopRequireDefault(_querystring); var _promise = require('md5-file/promise'); var _promise2 = _interopRequireDefault(_promise); var _url = require('url'); var _url2 = _interopRequireDefault(_url); var _crypto = require('crypto'); var _crypto2 = _interopRequireDefault(_crypto); var _moment = require('moment'); var _moment2 = _interopRequireDefault(_moment); var _lodash = require('lodash'); var _lodash2 = _interopRequireDefault(_lodash); var _path = require('path'); var _path2 = _interopRequireDefault(_path); var _util = require('./util'); var _util2 = _interopRequireDefault(_util); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * * * @class Payment */ class Payment { constructor({ appid, mchid, partnerKey, pfx, privateKey, certSerialNo, notify_url, spbill_create_ip, sandbox, wxCertSerialNo, wxPublicKey, wechatPayPublicKeyID, wechatPayPublicKey, wxCertTime // 上次获取证书的时间 } = {}, logger = false) { if (!appid) throw new Error('MISSING_APPID'); if (!mchid) throw new Error('MISSING_MCHID'); if (!partnerKey) throw new Error('MISSING_PARTNERKEY'); this.appid = appid; this.mchid = mchid; this.partnerKey = partnerKey; this.pfx = pfx; this.privateKey = privateKey; this.certSerialNo = certSerialNo; this.notify_url = notify_url; this.spbill_create_ip = spbill_create_ip || '127.0.0.1'; this.logger = _lodash2.default.isFunction(logger) ? logger : false; this.sandbox = sandbox; this.wxCertSerialNo = wxCertSerialNo; this.wxPublicKey = wxPublicKey; this.wxCertTime = wxPublicKey && wxCertSerialNo ? (0, _moment2.default)().unix() : 0; this.wechatPayPublicKeyID = wechatPayPublicKeyID; this.wechatPayPublicKey = wechatPayPublicKey; // 默认地址 this.URLS = { v3: 'https://api.mch.weixin.qq.com/', micropay: 'https://api.mch.weixin.qq.com/pay/micropay', reverse: 'https://api.mch.weixin.qq.com/secapi/pay/reverse', unifiedorder: 'https://api.mch.weixin.qq.com/pay/unifiedorder', orderquery: 'https://api.mch.weixin.qq.com/pay/orderquery', closeorder: 'https://api.mch.weixin.qq.com/pay/closeorder', refund: 'https://api.mch.weixin.qq.com/secapi/pay/refund', refundv2: 'https://api.mch.weixin.qq.com/secapi/pay/refundv2', refundquery: 'https://api.mch.weixin.qq.com/pay/refundquery', downloadbill: 'https://api.mch.weixin.qq.com/pay/downloadbill', transfers: 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers', gettransferinfo: 'https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo', sendredpack: 'https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack', sendgroupredpack: 'https://api.mch.weixin.qq.com/mmpaymkttransfers/sendgroupredpack', gethbinfo: 'https://api.mch.weixin.qq.com/mmpaymkttransfers/gethbinfo', facepayAuthInfo: 'https://payapp.weixin.qq.com/face/get_wxpayface_authinfo', facepay: 'https://api.mch.weixin.qq.com/pay/facepay', queryunionid: 'https://api.mch.weixin.qq.com/pay/queryunionid', getcertficates: 'https://api.mch.weixin.qq.com/risk/getcertficates', applymentmicrosubmit: 'https://api.mch.weixin.qq.com/applyment/micro/submit', applymentmicrogetstate: 'https://api.mch.weixin.qq.com/applyment/micro/getstate', applymentmicrosubmitupgrade: 'https://api.mch.weixin.qq.com/applyment/micro/submitupgrade', uploadmedia: 'https://api.mch.weixin.qq.com/secapi/mch/uploadmedia', // 修改结算银行卡 applymentmicromodifyarchives: 'https://api.mch.weixin.qq.com/applyment/micro/modifyarchives', // 查询提现状态 fundqueryautowithdrawbydate: 'https://api.mch.weixin.qq.com/fund/queryautowithdrawbydate', // 重新发起提现 fundreautowithdrawbydate: 'https://api.mch.weixin.qq.com/fund/reautowithdrawbydate', // 修改联系信息 applymentmicromodifycontactinfo: 'https://api.mch.weixin.qq.com/applyment/micro/modifycontactinfo', // 小微商户开发配置查询API secapimchquerysubdevconfig: 'https://api.mch.weixin.qq.com/secapi/mch/querysubdevconfig', // 获取验签秘钥 sandboxnewpaygetsignkey: 'https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey', // 小微商户新增对应APPID关联API secapimchaddsubdevconfig: 'https://api.mch.weixin.qq.com/secapi/mch/addsubdevconfig', // 支付分 payscore_user_service_state: 'https://api.mch.weixin.qq.com/v3/payscore/user-service-state', // 请求单次分账 secapiprofitsharing: 'https://api.mch.weixin.qq.com/secapi/pay/profitsharing', // 完结分账 secapiprofitsharingfinish: 'https://api.mch.weixin.qq.com/secapi/pay/profitsharingfinish', // 添加分账接收方 profitsharingaddreceiver: 'https://api.mch.weixin.qq.com/pay/profitsharingaddreceiver', // 删除分账接收方 profitsharingremovereceiver: 'https://api.mch.weixin.qq.com/pay/profitsharingremovereceiver', // 查询分账结果 profitsharingquery: 'https://api.mch.weixin.qq.com/pay/profitsharingquery', // 企业付款到银行卡 mmpaysptranspay_bank: 'https://api.mch.weixin.qq.com/mmpaysptrans/pay_bank', // 查询企业付款银行卡 mmpaysptransquery_bank: 'https://api.mch.weixin.qq.com/mmpaysptrans/query_bank', // 获取RSA加密公钥 getpublickey: 'https://fraud.mch.weixin.qq.com/risk/getpublickey' }; if (this.sandbox) { this.URLS.micropay = 'https://api.mch.weixin.qq.com/sandboxnew/pay/micropay'; this.URLS.orderquery = 'https://api.mch.weixin.qq.com/sandboxnew/pay/orderquery'; this.URLS.downloadbill = 'https://api.mch.weixin.qq.com/sandboxnew/pay/downloadbill'; this.URLS.unifiedorder = 'https://api.mch.weixin.qq.com/sandboxnew/pay/unifiedorder'; this.URLS.refundquery = 'https://api.mch.weixin.qq.com/sandboxnew/pay/refundquery'; this.URLS.refund = 'https://api.mch.weixin.qq.com/sandboxnew/pay/refund'; this.URLS.refundv2 = 'https://api.mch.weixin.qq.com/sandboxnew/pay/refundv2'; } // 平台证书 https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=19_11#menu1 this.parseBill = async (xml, format) => { if (_util2.default.checkXML(xml)) { const json = await _util2.default.parseXML(xml); throw new Error(json.return_msg || 'XMLDataError_1'); } if (!format) { return xml; } const arr = xml.trim().split(/\r?\n/).filter(item => item.trim()); const total_data = arr.pop().substr(1).split(',`'); const total_title = arr.pop().split(','); const list_title = arr.shift().split(','); const list_data = arr.map(item => item.substr(1).split(',`')); return { total_title, total_data, list_title, list_data }; }; } static init(config) { return new Payment(config); } async parseBody(xml, type, format) { if (this.logger) { this.logger(xml); } if (type === 'downloadbill') return xml; const json = format === 'JSON' ? xml : await _util2.default.parseXML(xml); if (json.return_code !== 'SUCCESS') { const err = new Error(json.return_msg || 'XMLDataError_1'); err.data = json; throw err; } switch (type) { case 'uploadmedia': case 'sandboxnewpaygetsignkey': return json; case 'facepayAuthInfo': break; default: if (json.result_code !== 'SUCCESS') { const err = new Error(json.err_code || 'XMLDataError_2'); err.data = json; throw err; } } switch (type) { case 'facepayAuthInfo': case 'secapimchquerysubdevconfig': case 'secapimchaddsubdevconfig': if (json.sign !== this.getSign(json)) { const err = new Error('ERR_SIGN_INVALID'); err.data = json; throw err; } break; // 小微 case 'fundqueryautowithdrawbydate': case 'fundreautowithdrawbydate': case 'applymentmicrosubmit': case 'applymentmicrogetstate': case 'applymentmicrosubmitupgrade': case 'applymentmicromodifyarchives': case 'applymentmicromodifycontactinfo': case 'secapiprofitsharing': // 分账接口的数据校验 case 'secapiprofitsharingfinish': case 'profitsharingaddreceiver': case 'profitsharingremovereceiver': case 'profitsharingquery': if (json.sign !== this.getHmacSign(json)) { const err = new Error('ERR_SIGN_INVALID'); err.data = json; throw err; } break; case 'transfers': if (json.mchid !== this.mchid) { const err = new Error('ERR_MCH_ID_NOT_MATCH'); err.data = json; throw err; } break; case 'sendredpack': case 'sendgroupredpack': if (json.wxappid !== this.appid) { const err = new Error('ERR_WXAPPID_NOT_MATCH'); err.data = json; throw err; } if (json.mch_id !== this.mchid) { const err = new Error('ERR_MCH_ID_NOT_MATCH'); err.data = json; throw err; } break; case 'gethbinfo': case 'gettransferinfo': case 'getcertficates': case 'getpublickey': case 'mmpaysptransquery_bank': case 'mmpaysptranspay_bank': if (json.mch_id !== this.mchid) { const err = new Error('ERR_MCH_ID_NOT_MATCH'); err.data = json; throw err; } break; default: if (json.appid !== this.appid) { const err = new Error('ERR_APPID_NOT_MATCH'); err.data = json; throw err; } if (json.mch_id !== this.mchid) { const err = new Error('ERR_MCH_ID_NOT_MATCH'); err.data = json; throw err; } if (json.sign !== this.getSign(json)) { const err = new Error('ERR_SIGN_INVALID'); err.data = json; throw err; } } return json; } getSign(params) { const str = `${_util2.default.toQueryString(params)}&key=${this.partnerKey}`; return _util2.default.md5(str).toUpperCase(); } getHmacSign(params, key) { if (!key) { key = this.partnerKey; } const str = `${_util2.default.toQueryString(params)}&key=${key}`; return _util2.default.hmac(str, key).toUpperCase(); } /** * v3 RSA-SHA256 签名 * * @param {*} message * @returns * @memberof Payment */ createSignatureWithRSA(message) { const rsa = _crypto2.default.createSign('RSA-SHA256'); rsa.update(message, 'utf8'); return rsa.sign(this.privateKey, 'base64'); } /** * 上传文件 * @param {*} formData * @param {*} param1 */ async postFile(formData, { type, cert = false } = {}) { // 创建请求参数 const options = { method: 'POST', uri: this.URLS[type], timeout: 60000, // 60秒超时 formData }; if (cert) { options.agentOptions = { pfx: this.pfx, passphrase: this.mchid }; } let body = false; try { body = await (0, _requestPromise2.default)(options); } catch (e) { throw new Error('request fail'); } return this.parseBody(body, type); } async post(params, { type, needs = [], cert = false } = {}) { const miss = needs.filter(key => !key.split('|').some(k => params[k])); if (miss.length) throw new Error(`missing params: ${miss.join(', ')}`); // 安全签名 if (params.sign_type === 'HMAC-SHA256') { params.sign = this.getHmacSign(params); } else { params.sign = this.getSign(params); } if (this.logger) { this.logger(JSON.stringify(params)); } // 创建请求参数 const options = { method: 'POST', uri: this.URLS[type], body: _util2.default.buildXML(params) }; if (cert) { options.agentOptions = { pfx: this.pfx, passphrase: this.mchid }; } let body = false; try { body = await (0, _requestPromise2.default)(options); } catch (e) { throw new Error(`request fail:${e.toString()}`); } return this.parseBody(body, type); } /** * 微信支付API v3 */ // eslint-disable-next-line no-shadow async v3Request(method, path, params, fileBuffer) { // 计算签名 const nonceStr = (0, _moment2.default)().valueOf(); const timestamp = (0, _moment2.default)().unix(); let data = ''; if (method === 'POST' || method === 'PUT' || method === 'PATCH') { data = JSON.stringify(params); } else if (method === 'GET' && params) { path = `${path}?${_querystring2.default.stringify(params)}`; } const message = `${method}\n${path}\n${timestamp}\n${nonceStr}\n${data}\n`; // 签名 const sign = this.createSignatureWithRSA(message); // 创建请求参数 const options = { resolveWithFullResponse: true, method, url: _url2.default.resolve('https://api.mch.weixin.qq.com', path), headers: { 'User-Agent': 'YoPoint', Authorization: `WECHATPAY2-SHA256-RSA2048 mchid="${this.mchid}",serial_no="${this.certSerialNo}",nonce_str="${nonceStr}",timestamp="${timestamp}",signature="${sign}"`, 'Wechatpay-Serial': `${this.wechatPayPublicKeyID || this.wxCertSerialNo}` }, json: true }; if (params && options.method !== 'GET') { options.body = params; } if (params && fileBuffer && (path === '/v3/merchant/media/upload' || path === '/v3/merchant-service/images/upload')) { delete options.body; const fileType = (params.filename || '').split('.')[1]; options.formData = { meta: JSON.stringify(params), file: { value: fileBuffer, options: { filename: params.filename, contentType: `image/${fileType}` } } }; } if (this.logger) { this.logger(JSON.stringify({ path, options })); } const response = await (0, _requestPromise2.default)(options); if (this.logger) { this.logger(JSON.stringify({ response })); } return response.body; } /** * 设置证书的保存状态 */ registerCertficatesV3Handle(getCertficates, setCertficates) { if (!getCertficates && !setCertficates) { this.certficatesStore = {}; } this.getCertficates = getCertficates || async function _getCertficates(mchid) { return this.certficatesStore[mchid]; }; this.setCertficates = setCertficates || async function _setCertficates(mchid, data) { this.certficatesStore[mchid] = data; }; } async ensureCertficatesV3() { let data = await this.getCertficates(this.mchid); if (!data) { const response = await this.v3Request('GET', '/v3/certificates'); [data] = response.data; // 证书解密处理 data = _util2.default.decryptCertificates(this.partnerKey, data); await this.setCertficates(this.mchid, data); } return data; } /** * v3获取证书 * * @returns * @memberof Payment */ async getCertficatesV3() { const response = await this.v3Request('GET', '/v3/certificates'); const certArr = response.data; let [data] = certArr; // 获取最新的证书 certArr.forEach(cert => { if ((0, _moment2.default)(cert.effective_time).unix() > (0, _moment2.default)(data.effective_time).unix()) { data = cert; } }); // 证书解密处理 data = _util2.default.decryptCertificates(this.partnerKey, data); return data; } /** * 支付分-查询用户开启状态 * * @param {*} params * @memberof Payment */ async payScoreUserServiceState(params) { params = Object.assign({}, params, { appid: this.appid }); return this.v3Request('GET', '/v3/payscore/user-service-state', params); } /** * 创建智慧零售订单 * * @param {*} params * @memberof Payment */ async payScoreSmartRetailOrderCreate(params) { // /v3/payscore/smartretail-orders params = Object.assign({ appid: this.appid, service_start_time: 'OnAccept' }, params); return this.v3Request('POST', '/v3/payscore/smartretail-orders', params); } /** * 查询智慧零售订单 * * @param {*} params * @returns * @memberof Payment */ async payScoreSmartRetailOrderQuery(params) { // /v3/payscore/smartretail-orders params = Object.assign({}, params, { appid: this.appid, service_start_time: 'OnAccept' }); return this.v3Request('GET', '/v3/payscore/smartretail-orders', params); } /** * * * @param {*} out_order_no * @param {*} params * @memberof Payment */ async payScoreSmartRetailOrderComplete(out_order_no, params) { // /v3/payscore/smartretail-orders/{out_order_no}/complete params = Object.assign({}, params, { appid: this.appid }); return this.v3Request('POST', `/v3/payscore/smartretail-orders/${out_order_no}/complete`, params); } /** * 撤销智慧零售订单 * * @param {*} out_order_no * @param {*} params * @returns * @memberof Payment */ async payScoreSmartRetailOrderCancel(out_order_no, params) { // /v3/payscore/smartretail-orders/{out_order_no}/cancel params = Object.assign({}, params, { appid: this.appid }); return this.v3Request('POST', `/v3/payscore/smartretail-orders/${out_order_no}/cancel`, params); } /** * 解密回调的resource */ decryptResource(resource) { const key = Buffer.from(this.partnerKey, 'utf8'); const iv = Buffer.from(resource.nonce, 'utf8'); const cipherText = Buffer.from(resource.ciphertext, 'base64'); const authTag = cipherText.slice(cipherText.length - 16); const data = cipherText.slice(0, cipherText.length - 16); const decipher = _crypto2.default.createDecipheriv('aes-256-gcm', key, iv); decipher.setAuthTag(authTag); decipher.setAAD(Buffer.from(resource.associated_data)); const decoded = decipher.update(data, null, 'utf8'); decipher.final(); return decoded; } /** * 获取沙箱测试的签名key * https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=23_1&index=1 * * @returns * @memberof Payment */ async sandboxnewGetSignKey() { const pkg = { mch_id: this.mchid, nonce_str: _util2.default.generate() }; const needs = []; return this.post(pkg, { type: 'sandboxnewpaygetsignkey', needs }); } /** * 上传图片 * https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=19_9 * @param {string} filepath * @memberof Payment */ async uploadMedia(filePath) { // 获取文件的md5 const media_hash = await (0, _promise2.default)(filePath); // 计算签名 const sign = this.getHmacSign({ mch_id: this.mchid, media_hash, sign_type: 'HMAC-SHA256' }); const formData = { sign, mch_id: this.mchid, sign_type: 'HMAC-SHA256', media_hash, media: _fs2.default.createReadStream(filePath) }; return this.postFile(formData, { type: 'uploadmedia', cert: true }); } /** * 企业付款到银行卡 * https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=24_2 * @param {*} params */ async mmPaySptransPayBank(parmas, publicKey) { const pkg = Object.assign({}, parmas, { mch_id: this.mchid, nonce_str: _util2.default.generate() }); // 部分字段需要脱敏 const encryptKeys = ['enc_true_name', 'enc_bank_no']; encryptKeys.forEach(key => { if (pkg[key]) { // do encrypt pkg[key] = _util2.default.encryptRSAPublicKey(pkg[key], publicKey, true); } }); const needs = ['partner_trade_no', 'enc_bank_no', 'enc_true_name', 'bank_code', 'amount']; return this.post(pkg, { type: 'mmpaysptranspay_bank', needs, cert: true }); } /** * 查询企业付款银行卡 * https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=24_3 * @param {*} params */ async mmPaySptransQueryBank(parmas) { const pkg = Object.assign({}, parmas, { mch_id: this.mchid, nonce_str: _util2.default.generate() }); const needs = ['partner_trade_no']; return this.post(pkg, { type: 'mmpaysptransquery_bank', needs, cert: true }); } /** * 获取RSA加密公钥 * https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=24_7&index=4 * @param {*} params */ async getPublicKey(parmas) { const pkg = Object.assign({}, parmas, { mch_id: this.mchid, nonce_str: _util2.default.generate(), sign_type: 'MD5' }); const needs = []; return this.post(pkg, { type: 'getpublickey', needs, cert: true }); } /** * 删除分账方 * https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_4&index=5 * @param {*} params * @memberof Payment */ async profitSharingRemoveReceiver(params) { const pkg = Object.assign({}, params, { mch_id: this.mchid, appid: this.appid, nonce_str: _util2.default.generate(), sign_type: 'HMAC-SHA256' }); if (typeof pkg.receiver === 'object') { pkg.receiver = JSON.stringify(pkg.receiver); } const needs = ['sub_mch_id', 'receiver']; return this.post(pkg, { type: 'profitsharingremovereceiver', needs, cert: true }); } /** * 添加分账接收方 * https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_3&index=4 * * @param {*} params * @memberof Payment */ async profitSharingAddReceiver(params) { const pkg = Object.assign({}, params, { mch_id: this.mchid, appid: this.appid, nonce_str: _util2.default.generate(), sign_type: 'HMAC-SHA256' }); if (typeof pkg.receiver === 'object') { pkg.receiver = JSON.stringify(pkg.receiver); } const needs = ['sub_mch_id', 'receiver']; return this.post(pkg, { type: 'profitsharingaddreceiver', needs, cert: true }); } /** * 查询分账结果 * https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_2&index=3 * @param {*} params * @memberof Payment */ async profitSharingQuery(params) { const pkg = Object.assign({}, params, { mch_id: this.mchid, nonce_str: _util2.default.generate(), sign_type: 'HMAC-SHA256' }); const needs = ['sub_mch_id', 'transaction_id', 'out_order_no']; return this.post(pkg, { type: 'profitsharingquery', needs, cert: true }); } /** * 请求单次分账 * https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_1&index=1 * @param {*} params * @memberof Payment */ async secapiProfitSharing(params) { const pkg = Object.assign({}, params, { mch_id: this.mchid, appid: this.appid, nonce_str: _util2.default.generate(), sign_type: 'HMAC-SHA256' }); if (typeof pkg.receivers === 'object') { pkg.receivers = JSON.stringify(pkg.receivers); } const needs = ['sub_mch_id', 'transaction_id', 'out_order_no', 'receivers']; return this.post(pkg, { type: 'secapiprofitsharing', needs, cert: true }); } /** * 完结分账 * https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_5&index=6 * @param {*} params * @memberof Payment */ async secapiProfitSharingFinish(params) { const pkg = Object.assign({}, params, { mch_id: this.mchid, appid: this.appid, nonce_str: _util2.default.generate(), sign_type: 'HMAC-SHA256' }); const needs = ['sub_mch_id', 'transaction_id', 'out_order_no', 'description']; return this.post(pkg, { type: 'secapiprofitsharingfinish', needs, cert: true }); } /** * 小微商户开发配置查询API * https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=20_4&index=4 * @param {*} params * @memberof Payment */ async secapiMchQuerySubDevConfig(params) { const pkg = Object.assign({}, params, { mch_id: this.mchid, appid: this.appid // nonce_str: util.generate(), // sign_type: 'HMAC-SHA256', }); const needs = ['sub_mch_id']; return this.post(pkg, { type: 'secapimchquerysubdevconfig', needs, cert: true }); } /** * 小微商户新增对应APPID关联API * https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=20_3&index=3 * @param {*} params * @memberof Payment */ async secapiMchAddSubDevConfig(params) { const pkg = Object.assign({}, params, { mch_id: this.mchid, appid: this.appid }); const needs = ['sub_mch_id', 'sub_appid']; return this.post(pkg, { type: 'secapimchaddsubdevconfig', needs, cert: true }); } /** * 重新发起提现 * https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=21_3 * * @param {*} params * @returns * @memberof Payment */ async fundReAutoWithdrawByDate(params) { const pkg = Object.assign({}, params, { mch_id: this.mchid, nonce_str: _util2.default.generate(), sign_type: 'HMAC-SHA256' }); const needs = ['sub_mch_id', 'date']; return this.post(pkg, { type: 'fundreautowithdrawbydate', needs, cert: true }); } /** * 查询提现状态 * https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=21_1 * * @memberof Payment */ async fundQueryAutoWithdrawByDate(params) { const pkg = Object.assign({}, params, { mch_id: this.mchid, nonce_str: _util2.default.generate(), sign_type: 'HMAC-SHA256' }); const needs = ['sub_mch_id', 'date']; return this.post(pkg, { type: 'fundqueryautowithdrawbydate', needs, cert: true }); } /** * 修改联系信息 * https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=21_4 * @param {*} params * @param {*} publicKey * @memberof Payment */ async applymentMicroModifyContactInfo(params, publicKey) { const pkg = Object.assign({}, params, { version: '1.0', mch_id: this.mchid, nonce_str: _util2.default.generate(), sign_type: 'HMAC-SHA256' }); // 部分字段需要脱敏 const encryptKeys = ['mobile_phone', 'email', 'merchant_name']; encryptKeys.forEach(key => { if (pkg[key]) { // do encrypt pkg[key] = _util2.default.encryptRSAPublicKey(pkg[key], publicKey); } }); const needs = ['sub_mch_id', 'cert_sn']; return this.post(pkg, { type: 'applymentmicromodifycontactinfo', needs, cert: true }); } /** * 修改结算银行卡 * https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=21_2 * @param {*} params * @param {*} publicKey * @memberof Payment */ async applymentMicroModifyArchives(params, publicKey) { const pkg = Object.assign({}, params, { version: '1.0', mch_id: this.mchid, nonce_str: _util2.default.generate(), sign_type: 'HMAC-SHA256' }); // 部分字段需要脱敏 const encryptKeys = ['account_number']; encryptKeys.forEach(key => { if (pkg[key]) { // do encrypt pkg[key] = _util2.default.encryptRSAPublicKey(pkg[key], publicKey); } }); const needs = ['sub_mch_id', 'account_number', 'account_bank', 'bank_address_code', 'cert_sn']; return this.post(pkg, { type: 'applymentmicromodifyarchives', needs, cert: true }); } /** * 微小商户-提交升级申请单接口 * https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=28_2&index=2 */ async applymentMicroSubmitUpgrade(params, publicKey) { const pkg = Object.assign({}, params, { version: '1.0', mch_id: this.mchid, nonce_str: _util2.default.generate(), sign_type: 'HMAC-SHA256' }); const needs = ['cert_sn', 'sub_mch_id', 'organization_type', 'business_license_copy', 'business_license_number', 'merchant_name', 'company_address', 'legal_person', 'business_time', 'business_licence_type', 'business', 'business_scene']; // 部分字段需要脱敏 const encryptKeys = ['legal_person']; encryptKeys.forEach(key => { if (pkg[key]) { // do encrypt pkg[key] = _util2.default.encryptRSAPublicKey(pkg[key], publicKey); } }); return this.post(pkg, { type: 'applymentmicrosubmitupgrade', needs, cert: true }); } /** * 微小商户-查询申请状态 * https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=19_3 */ async applymentMicroGetState(params) { const pkg = Object.assign({}, params, { version: '1.0', mch_id: this.mchid, nonce_str: _util2.default.generate(), sign_type: 'HMAC-SHA256' }); const needs = ['applyment_id|business_code']; return this.post(pkg, { type: 'applymentmicrogetstate', needs, cert: true }); } /** * 微小商户-申请入驻 * https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=19_2 */ async applymentMicroSubmit(params, publicKey) { const pkg = Object.assign({}, params, { version: '3.0', mch_id: this.mchid, nonce_str: _util2.default.generate(), sign_type: 'HMAC-SHA256' }); // 部分字段需要脱敏 const encryptKeys = ['id_card_name', 'id_card_number', 'id_card_address', 'account_name', 'account_number', 'contact', 'contact_phone', 'contact_email']; encryptKeys.forEach(key => { if (pkg[key]) { // do encrypt pkg[key] = _util2.default.encryptRSAPublicKey(pkg[key], publicKey); } }); const needs = ['business_code', 'id_card_copy', 'id_card_national', 'id_card_name', 'id_card_number', 'id_card_valid_time', 'account_name', 'account_bank', 'bank_address_code', 'account_number', 'store_name', 'store_address_code', 'store_street', 'store_entrance_pic', 'indoor_pic', 'merchant_shortname', 'service_phone', 'product_desc', 'rate', 'contact', 'contact_phone']; return this.post(pkg, { type: 'applymentmicrosubmit', needs, cert: true }); } /** * 微小商户-获取平台证书 * https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=19_11 * @returns {object} */ async getCertficates() { const pkg = { mch_id: this.mchid, nonce_str: _util2.default.generate(), sign_type: 'HMAC-SHA256' }; const result = await this.post(pkg, { type: 'getcertficates' }); // 处理解密 let [certificates] = JSON.parse(result.certificates).data; // 证书解密处理 certificates = _util2.default.decryptCertificates(this.partnerKey, certificates); return certificates; } facepay(params) { const pkg = Object.assign({}, params, { appid: this.appid, mch_id: this.mchid, nonce_str: _util2.default.generate() }); /** * body = 门店品牌名-城市分店 名-实际商品名称 * out_trade_no */ const needs = ['body', 'out_trade_no', 'total_fee', 'spbill_create_ip', 'openid', 'face_code']; return this.post(pkg, { type: 'facepay', needs }); } // 刷脸支付authinfo facepayAuthInfo(params) { const pkg = Object.assign({}, params, { appid: this.appid, mch_id: this.mchid, sign_type: 'MD5', version: '1', nonce_str: _util2.default.generate() }); const needs = ['store_id', 'store_name', 'device_id', 'rawdata', 'now']; return this.post(pkg, { type: 'facepayAuthInfo', needs }); } // 校验回调参数是否合法 async notifyValidate(data) { return await this.parseBody(data, 'middleware_pay', 'JSON'); } // 获取H5支付参数(自动下单) async getPayParams(params) { params.trade_type = 'JSAPI'; const order = await this.unifiedOrder(params); return this.getPayParamsByPrepay(order); } // 获取小程序支付参数 async getMicroPayParams(params, appId) { params.trade_type = 'JSAPI'; const order = await this.unifiedOrder(params); const pkg = { appId: appId || params.appid, timeStamp: `${parseInt(Date.now() / 1000, 10)}`, nonceStr: _util2.default.generate(), package: `prepay_id=${order.prepay_id}`, signType: 'MD5' }; pkg.paySign = this.getSign(pkg); return pkg; } /** * 支付分-JS调用的参数签名 * 注意签名的apikey 是api的秘钥,非apiV3秘钥,建议两个秘钥设置一样 */ getPayScoreExtraParams(params, key) { params = Object.assign({}, params, { mch_id: this.mchid, timestamp: (0, _moment2.default)().unix().toString(), nonce_str: _util2.default.generate(), sign_type: 'HMAC-SHA256' }); params.sign = this.getHmacSign(params, key); return params; } // 获取H5支付参数(通过预支付会话标志) getPayParamsByPrepay(params) { const pkg = { appId: this.appid, timeStamp: `${parseInt(Date.now() / 1000, 10)}`, nonceStr: _util2.default.generate(), package: `prepay_id=${params.prepay_id}`, signType: 'MD5' }; pkg.paySign = this.getSign(pkg); pkg.timestamp = pkg.timeStamp; return pkg; } // 获取APP支付参数(自动下单) async getAppParams(params) { params.trade_type = 'APP'; const order = await this.unifiedOrder(params); return this.getAppParamsByPrepay(order); } // 获取APP支付参数(通过预支付会话标志) getAppParamsByPrepay(params) { const pkg = { appid: this.appid, partnerid: this.mchid, prepayid: params.prepay_id, package: 'Sign=WXPay', noncestr: _util2.default.generate(), timestamp: `${parseInt(Date.now() / 1000, 10)}` }; pkg.sign = this.getSign(pkg); return pkg; } // 扫码支付, 生成URL(模式一) getNativeUrl(params) { const pkg = Object.assign({}, params, { appid: this.appid, mch_id: this.mchid, time_stamp: `${parseInt(Date.now() / 1000, 10)}`, nonce_str: _util2.default.generate() }); const uri = `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 uri; } // 刷卡支付 micropay(params) { const pkg = Object.assign({}, params, { appid: this.appid, mch_id: this.mchid, nonce_str: _util2.default.generate(), spbill_create_ip: params.spbill_create_ip || this.spbill_create_ip }); const needs = ['body', 'out_trade_no', 'total_fee', 'auth_code']; return this.post(pkg, { type: 'micropay', needs }); } // 撤销订单 reverse(params) { const pkg = Object.assign({}, params, { appid: this.appid, mch_id: this.mchid, nonce_str: _util2.default.generate() }); const needs = ['transaction_id|out_trade_no']; return this.post(pkg, { type: 'reverse', needs, cert: true }); } // 统一下单 unifiedOrder(params) { const pkg = Object.assign({}, params, { appid: this.appid, mch_id: this.mchid, nonce_str: _util2.default.generate(), 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' }); const needs = ['body', 'out_trade_no', 'total_fee', 'notify_url']; if (pkg.trade_type === 'JSAPI') { needs.push('openid|sub_openid'); } else if (pkg.trade_type === 'NATIVE') { needs.push('product_id'); } return this.post(pkg, { type: 'unifiedorder', needs }); } /** * 根据订单查询unionid * * @param {*} params * @memberof Payment * { * "return_code": "SUCCESS", * "return_msg": "OK", * "appid": "wx9563ee2dd1a2f3d4", * "mch_id": "1311269701", * "nonce_str": "Y2BGtYlBtJiCZXxv", * "sign": "44958715C49BC7A2848C2F41475F6183", * "result_code": "SUCCESS", * "unionid": "oz69Q06YUFKw_n678GxSWStZUxiE", * "openid": "ogg_-t9YZAtobkay5Yd_UQcmoMsc" * } */ queryUnionID(params) { const pkg = Object.assign({}, params, { appid: this.appid, mch_id: this.mchid, nonce_str: _util2.default.generate() }); const needs = ['out_trade_no|transaction_id', 'openid']; return this.post(pkg, { type: 'queryunionid', needs }); } // 订单查询 orderQuery(params) { const pkg = Object.assign({}, params, { appid: this.appid, mch_id: this.mchid, nonce_str: _util2.default.generate() }); const needs = ['transaction_id|out_trade_no']; return this.post(pkg, { type: 'orderquery', needs }); } // 关闭订单 closeOrder(params) { const pkg = Object.assign({}, params, { appid: this.appid, mch_id: this.mchid, nonce_str: _util2.default.generate() }); const needs = ['out_trade_no']; return this.post(pkg, { type: 'closeorder', needs }); } /** * 退款接口v2 */ refundV2(params) { const pkg = Object.assign({}, params, { appid: this.appid, mch_id: this.mchid, nonce_str: _util2.default.generate(), op_user_id: params.op_user_id || this.mchid }); const needs = ['transaction_id|out_trade_no', 'out_refund_no', 'total_fee', 'refund_fee', 'op_user_id']; return this.post(pkg, { type: 'refundv2', needs, cert: true }); } // 申请退款 refund(params) { const pkg = Object.assign({}, params, { appid: this.appid, mch_id: this.mchid, nonce_str: _util2.default.generate(), op_user_id: params.op_user_id || this.mchid }); const needs = ['transaction_id|out_trade_no', 'out_refund_no', 'total_fee', 'refund_fee', 'op_user_id']; return this.post(pkg, { type: 'refund', needs, cert: true }); } // 查询退款 refundQuery(params) { const pkg = Object.assign({}, params, { appid: this.appid, mch_id: this.mchid, nonce_str: _util2.default.generate() }); const needs = ['transaction_id|out_trade_no|out_refund_no|refund_id']; return this.post(pkg, { type: 'refundquery', needs }); } // 下载对帐单 async downloadBill(params, format = false) { const pkg = Object.assign({}, params, { appid: this.appid, mch_id: this.mchid, nonce_str: _util2.default.generate(), bill_type: params.bill_type || 'ALL' }); const needs = ['bill_date']; const xml = await this.post(pkg, { type: 'downloadbill', needs }); return this.parseBill(xml, format); } // 企业付款 transfers(params) { const pkg = Object.assign({}, params, { mch_appid: this.appid, mchid: this.mchid, nonce_str: _util2.default.generate(), check_name: params.check_name || 'FORCE_CHECK', spbill_create_ip: params.spbill_create_ip || this.spbill_create_ip }); const needs = ['partner_trade_no', 'openid', 'check_name', 'amount', 'desc']; if (pkg.check_name === 'FORCE_CHECK') needs.push('re_user_name'); return this.post(pkg, { type: 'transfers', needs, cert: true }); } // 查询企业付款 transfersQuery(params) { const pkg = Object.assign({}, params, { appid: this.appid, mch_id: this.mchid, nonce_str: _util2.default.generate() }); const needs = ['partner_trade_no']; return this.post(pkg, { type: 'gettransferinfo', needs, cert: true }); } // 发送普通红包 sendRedpack(params) { const pkg = Object.assign({}, params, { wxappid: this.appid, mch_id: this.mchid, nonce_str: _util2.default.generate(), client_ip: params.client_ip || this.spbill_create_ip, mch_billno: params.mch_billno || (params.mch_autono ? this.mchid + _util2.default.getFullDate() + params.mch_autono : ''), total_num: params.total_num || 1 }); delete pkg.mch_autono; const needs = ['mch_billno', 'send_name', 're_openid', 'total_amount', 'wishing', 'act_name', 'remark']; if (pkg.total_amount >= 200) needs.push('scene_id'); return this.post(pkg, { type: 'sendredpack', needs, cert: true }); } // 发送裂变红包 sendGroupRedpack(params) { const pkg = Object.assign({}, params, { wxappid: this.appid, mch_id: this.mchid, nonce_str: _util2.default.generate(), mch_billno: params.mch_billno || (params.mch_autono ? this.mchid + _util2.default.getFullDate() + params.mch_autono : ''), total_num: params.total_num || 3, amt_type: params.amt_type || 'ALL_RAND' }); delete pkg.mch_autono; const needs = ['mch_billno', 'send_name', 're_openid', 'total_amount', 'wishing', 'act_name', 'remark']; if (pkg.total_amount >= 200) needs.push('scene_id'); return this.post(pkg, { type: 'sendgroupredpack', needs, cert: true }); } // 查询红包记录 redpackQuery(params) { const pkg = Object.assign({}, params, { appid: this.appid, mch_id: this.mchid, nonce_str: _util2.default.generate(), bill_type: params.bill_type || 'MCHT' }); const needs = ['mch_billno']; return this.post(pkg, { type: 'gethbinfo', needs, cert: true }); } // 2020.03.11 支付分新版接口 /** * 创建支付分订单 * * @param {*} params * @memberof Payment */ async payScoreServiceOrderCreate(params) { // post /v3/payscore/serviceorder params = Object.assign({ appid: this.appid, time_range: { start_time: 'OnAccept' } }, params); return this.v3Request('POST', '/v3/payscore/serviceorder', params); } /** * 查询支付分订单 * * @param {*} params * @returns * @memberof Payment */ async payScoreServiceOrderQuery(params) { // get /v3/payscore/serviceorder params = Object.assign({}, params, { appid: this.appid }); return this.v3Request('GET', '/v3/payscore/serviceorder', params); } /** * 取消支付分订单 * * @param {*} out_order_no * @param {*} params * @memberof Payment */ async payScoreServiceOrderCancel(out_order_no, params) { // /v3/payscore/serviceorder/{out_order_no}/cancel params = Object.assign({}, params, { appid: this.appid }); return this.v3Request('POST', `/v3/payscore/serviceorder/${out_order_no}/cancel`, params); } /** * 完结支付分订单 * * @param {*} out_order_no * @param {*} params * @memberof Payment */ async payScoreServiceOrderComplete(out_order_no, params) { // /v3/payscore/serviceorder/{out_order_no}/complete params = Object.assign({}, params, { appid: this.appid }); return this.v3Request('POST', `/v3/payscore/serviceorder/${out_order_no}/complete`, params); } /** * 同步服务订单信息 * @param {*} out_order_no * @param {*} params * @memberof Payment */ async payScoreServiceOrderSync(out_order_no, params) { // /v3/payscore/serviceorder/{out_order_no}/sync params = Object.assign({}, params, { appid: this.appid }); return this.v3Request('POST', `/v3/payscore/serviceorder/${out_order_no}/sync`, params); } /** * 支付分订单收款 * @param {*} out_order_no * @param {*} params * @memberof Payment */ async payScoreServiceOrderPay(out_order_no, params) { // /v3/payscore/serviceorder/{out_order_no}/pay params = Object.assign({}, params, { appid: this.appid }); return this.v3Request('POST', `/v3/payscore/serviceorder/${out_order_no}/pay`, params); } /** * 支付分-商户预授权API * @param {*} params * @memberof Payment */ async payScorePermissions(params) { // /v3/payscore/permissions params = Object.assign({}, params, { appid: this.appid }); return this.v3Request('POST', `/v3/payscore/permissions`, params); } /** * 支付分-查询与用户授权记录(openid)API * @param {*} openid * @param {*} params * @memberof Payment */ async payScorePermissionsSearch(openid, params) { // get /v3/payscore/permissions/openid/${openid} params = Object.assign({}, params, { appid: this.appid }); return this.v3Request('GET', `/v3/payscore/permissions/openid/${openid}`, params); } /** * 设置刷新平台证书版本号 v3加密数据需要使用 */ async setWxCertSerialNoAndWxPublicKey() { if (this.wechatPayPublicKeyID) return; const diffTime = (0, _moment2.default)().unix() - this.wxCertTime; if (this.wxCertSerialNo && this.wxPublicKey && diffTime < 12 * 3600) return; const resp = await this.getCertficatesV3(); if (!resp.serial_no || !resp.certificate) throw new Error('获取微信平台证书失败'); this.wxCertSerialNo = resp.serial_no; this.wxPublicKey = resp.certificate; this.wxCertTime = (0, _moment2.default)().unix(); } /** * 商户进件(特约商户与小微商户)提交申请单 */ async merchantApplyment(params) { await this.setWxCertSerialNoAndWxPublicKey(); if (!_lodash2.default.isPlainObject(params)) params = {}; // 处理加密 if (_lodash2.default.isPlainObject(params.contact_info)) { ['contact_name', 'mobile_phone', 'contact_email', 'contact_id_number', 'contact_id_card_number'].forEach(key => { if (params.contact_info[key]) { params.contact_info[key] = _util2.default.encryptRSAPublicKey(params.contact_info[key], this.wechatPayPublicKey || this.wxPublicKey, true); } }); } if (_lodash2.default.isPlainObject(params.subject_info) && _lodash2.default.isPlainObject(params.subject_info.identity_info)) { if (_lodash2.default.isPlainObject(params.subject_info.identity_info.id_card_info)) { ['id_card_name', 'id_card_number', 'id_card_address'].forEach(key => { if (params.subject_info.identity_info.id_card_info[key]) { params.subject_info.identity_info.id_card_info[key] = _util2.default.encryptRSAPublicKey(params.subject_info.identity_info.id_card_info[key], this.wechatPayPublicKey || this.wxPublicKey, true); } }); } if (_lodash2.default.isPlainObject(params.subject_info.identity_info.id_doc_info)) { ['id_doc_name', 'id_doc_number', 'id_doc_address'].forEach(key => { if (params.subject_info.identity_info.id_doc_info[key]) { params.subject_info.identity_info.id_doc_info[key] = _util2.default.encryptRSAPublicKey(params.subject_info.identity_info.id_doc_info[key], this.wechatPayPublicKey || this.wxPublicKey, true); } }); } } if (_lodash2.default.isPlainObject(params.bank_account_info)) { ['account_name', 'account_number'].forEach(key => { if (params.bank_account_info[key]) { params.bank_account_info[key] = _util2.default.encryptRSAPublicKey(params.bank_account_info[key], this.wechatPayPublicKey || this.wxPublicKey, true); } }); } return this.v3Request('POST', '/v3/applyment4sub/applyment/', params); } /** * 商户进件(特约商户与小微商户)查询申请状态 */ async merchantQueryApplymentByCode(business_code) { return this.v3Request('GET', `/v3/applyment4sub/applyment/business_code/${business_code}`); } /** * 商户进件(特约商户与小微商户)修改结算账号 */ async merchantUpdateSettlment(params, sub_mchid) { await this.setWxCertSerialNoAndWxPublicKey(); if (params.account_number) { params.account_number = _util2.default.encryptRSAPublicKey(params.account_number, this.wechatPayPublicKey || this.wxPublicKey, true); } return this.v3Request('POST', `/v3/apply4sub/sub_merchants/${sub_mchid}/modify-settlement`, params); } /** * 商户进件(特约商户与小微商户)查询结算账号 */ async merchantQuerySettlement(sub_mchid) { return this.v3Request('GET', `/v3/apply4sub/sub_merchants/${sub_mchid}/settlement`); } /** * 商户进件(特约商户与小微商户)上传资料图片 */ async merchantUploadMedia(filePath) { const filename = _path2.default.parse(filePath).base; const fileBuffer = _fs2.default.readFileSync(filePath); const sha256 = _crypto2.default.createHash('sha256').update(fileBuffer).digest('hex'); return this.v3Request('POST', '/v3/merchant/media/upload', { filename, sha256 }, fileBuffer); } // 收付通电商二级商户进件 async ecommerceMerchantApplyment(params) { await this.setWxCertSerialNoAndWxPublicKey(); if (!_lodash2.default.isPlainObject(params)) params = {}; // 处理加密 if (_lodash2.default.isPlainObject(params.id_card_info)) { ['id_card_name', 'id_card_number', 'id_card_address'].forEach(key => { if (params.id_card_info[key]) { params.id_card_info[key] = _util2.default.encryptRSAPublicKey(params.id_card_info[key], this.wechatPayPublicKey, true); } }); } if (_lodash2.default.isPlainObject(params.id_doc_info)) { ['id_doc_name', 'id_doc_number', 'id_doc_address'].forEach(key => { if (params.id_doc_info[key]) { params.id_doc_info[key] = _util2.default.encryptRSAPublicKey(params.id_doc_info[key], this.wechatPayPublicKey || this.wxPublicKey, true); } }); } if (_lodash2.default.isPlainObject(params.account_info)) { ['account_name', 'account_number'].forEach(key => { if (params.account_info[key]) { params.account_info[key] = _util2.default.encryptRSAPublicKey(params.account_info[key], this.wechatPayPublicKey || this.wxPublicKey, true); } }); } if (_lodash2.default.isPlainObject(params.contact_info)) { ['contact_name', 'mobile_phone', 'contact_email', 'contact_id_number', 'contact_id_card_number'].forEach(key => { if (params.contact_info[key]) { params.contact_info[key] = _util2.default.encryptRSAPublicKey(params.contact_info[key], this.wechatPayPublicKey || this.wxPublicKey, true); } }); } if (_lodash2.default.isArray(params.ubo_info_list) && params.ubo_info_list.length > 0) { params.ubo_info_list.map(ubo_info => { ['ubo_id_doc_name', 'ubo_id_doc_number', 'ubo_id_doc_address'].forEach(key => { if (ubo_info[key]) { ubo_info[key] = _util2.default.encryptRSAPublicKey(ubo_info[key], this.wechatPayPublicKey || this.wxPublicKey, true); } }); return ubo_info; }); } return this.v3Request('POST', '/v3/ecommerce/applyments/', params); } // 收付通电商二级进件状态查询 async ecommerceMerchantQueryStatus(applymentID) { return this.v3Request('GET', `/v3/ecommerce/applyments/${applymentID}`); } async ecommerceMerchantQueryStatusByOutRequestNo(outRequestNo) { return this.v3Request('GET',