|
@@ -0,0 +1,93 @@
|
|
|
+'use strict';
|
|
|
+const { CrudService } = require('naf-framework-mongoose/lib/service');
|
|
|
+const { BusinessError, ErrorCode } = require('naf-core').Error;
|
|
|
+const _ = require('lodash');
|
|
|
+const assert = require('assert');
|
|
|
+const random = require('string-random');
|
|
|
+const moment = require('moment');
|
|
|
+const crypto = require('crypto');
|
|
|
+const xml2js = require('xml2js');
|
|
|
+const fs = require('fs');
|
|
|
+// wxpay
|
|
|
+class WxpayService extends CrudService {
|
|
|
+ constructor(ctx) {
|
|
|
+ super(ctx, 'wxpay');
|
|
|
+ // this.model = this.ctx.model.Wxpay;
|
|
|
+ this.appInfo = this.app.config.appInfo;
|
|
|
+ this.redis = this.app.redis;
|
|
|
+ }
|
|
|
+
|
|
|
+ async toAuth({ code, id }) {
|
|
|
+ const token = await this.redis.get(id);
|
|
|
+ if (token) return;
|
|
|
+ const url = ({ id, secret }) => `https://api.weixin.qq.com/sns/jscode2session?appid=${id}&secret=${secret}&js_code=${code}&grant_type=authorization_code`;
|
|
|
+ const res = await this.ctx.curl(url(this.appInfo), {
|
|
|
+ method: 'get',
|
|
|
+ dataType: 'json',
|
|
|
+ });
|
|
|
+
|
|
|
+ const info = JSON.stringify(res.data);
|
|
|
+ await this.redis.set(id, info, 'EX', 3600);
|
|
|
+ }
|
|
|
+
|
|
|
+ async cash({ id, name, money }) {
|
|
|
+ const token = await this.redis.get(id);
|
|
|
+ if (!token) throw new BusinessError(ErrorCode.SERVICE_FAULT, '未找到用户的openid,请重新登陆小程序!');
|
|
|
+ const { openid } = JSON.parse(token);
|
|
|
+ if (!this.appInfo) throw new BusinessError(ErrorCode.FILE_FAULT, '未设置小程序相关设置');
|
|
|
+ // check_name:校验真名,暂不校验
|
|
|
+ const object = { amount: money, check_name: 'NO_CHECK', desc: '提现', mch_appid: this.appInfo.id, mchid: this.appInfo.store, nonce_str: random(16), openid, partner_trade_no: `cash${moment().format('YYYYMMDDHHmmss')}${random(12)}`, re_user_name: name };
|
|
|
+ const clientIp = _.get(this.ctx.request, 'header.x-real-ip');
|
|
|
+ if (clientIp) object.spbill_create_ip = clientIp;
|
|
|
+ const sign = this.turnToSign(object);
|
|
|
+ const xml = ` <xml>
|
|
|
+ <mch_appid>${this.appInfo.id}</mch_appid>
|
|
|
+ <mchid>${this.appInfo.store}</mchid>
|
|
|
+ <nonce_str>${object.nonce_str}</nonce_str>
|
|
|
+ <partner_trade_no>${object.partner_trade_no}</partner_trade_no>
|
|
|
+ <openid>${openid}</openid>
|
|
|
+ <check_name>NO_CHECK</check_name>
|
|
|
+ <re_user_name>${name}</re_user_name>
|
|
|
+ <amount>${money}</amount>
|
|
|
+ <desc>提现</desc>
|
|
|
+ ${clientIp ? '<spbill_create_ip></spbill_create_ip>' : ''}
|
|
|
+ <sign>${sign}</sign>
|
|
|
+ </xml>`;
|
|
|
+ const url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers';
|
|
|
+ const res = await this.ctx.curl(url, {
|
|
|
+ method: 'post',
|
|
|
+ data: xml,
|
|
|
+ dataType: 'xml',
|
|
|
+ key: fs.readFileSync('cert/apiclient_key.pem'),
|
|
|
+ cert: fs.readFileSync('cert/apiclient_cert.pem'),
|
|
|
+ });
|
|
|
+ const data = res.data;
|
|
|
+ const parse = new xml2js.Parser({ trim: true, explicitArray: false, explicitRoot: false });
|
|
|
+ const result = await parse.parseStringPromise(data);
|
|
|
+ // TODO 添加错误记录
|
|
|
+ const { return_code, return_msg, result_code, err_code, err_code_des } = result;
|
|
|
+ // 请求是否成功
|
|
|
+ if (return_code !== 'SUCCESS') throw new BusinessError(ErrorCode.SERVICE_FAULT, `${return_code}:${return_msg}`);
|
|
|
+ // 请求失败具体问题
|
|
|
+ if (result_code === 'FAIL') throw new BusinessError(ErrorCode.SERVICE_FAULT, `${err_code}:${err_code_des}`);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ turnToSign(object) {
|
|
|
+ const _arr = [];
|
|
|
+ for (const key in object) {
|
|
|
+ _arr.push(key + '=' + object[key]);
|
|
|
+ }
|
|
|
+ const str = `${_arr.join('&')}&key=${this.appInfo.storeKey}`;
|
|
|
+ return crypto.createHash('md5').update(str, 'utf8').digest('hex')
|
|
|
+ .toUpperCase();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ NoticeCode(code) {
|
|
|
+ const arr = [ 'NOTENOUGH', 'SYSTEMERROR', 'NAME_MISMATCH', 'SIGN_ERROR', 'FREQ_LIMIT', 'MONEY_LIMIT', 'CA_ERROR', 'V2_ACCOUNT_SIMPLE_BAN', 'PARAM_IS_NOT_UTF8', 'SENDNUM_LIMIT' ];
|
|
|
+ return arr;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+module.exports = WxpayService;
|