wxPay.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /* eslint-disable eqeqeq */
  2. 'use strict';
  3. const Service = require('egg').Service;
  4. const WxPay = require('wechatpay-node-v3');
  5. const fs = require('fs');
  6. const path = require('path');
  7. // 微信支付操作类
  8. class WxPayService extends Service {
  9. /**
  10. * 发起商家转账到零钱
  11. * @return {Promise<void>}
  12. */
  13. async transfer(data) {
  14. const that = this;
  15. const userInfo = await that.app.model.Users.findOne({
  16. where: { user_id: data.user_id },
  17. attributes: [ 'openid' ],
  18. raw: true,
  19. });
  20. const keys = [ 'wx_pay_serial_no', 'wechat_account_appid', 'wechat_pay_key', 'wx_pay_mchid' ];
  21. const config = await that.service.configs.getConfigMoreValue(keys);
  22. // 2023/1/11 微信支付配置初始化
  23. const wxPay = new WxPay({
  24. appid: config.wechat_account_appid, // 公众号appid
  25. mchid: config.wx_pay_mchid, // 微信支付商户号
  26. serial_no: config.wx_pay_serial_no, // 微信支付证书序列号
  27. key: config.wechat_pay_key, // APIv3密钥
  28. // publicKey: fs.readFileSync('./apiclient_cert.pem'), // 公钥
  29. publicKey: fs.readFileSync(path.join(__dirname, './apiclient_cert.pem'), 'utf8'), // 公钥
  30. // privateKey: fs.readFileSync('./apiclient_key.pem'), // 秘钥
  31. privateKey: fs.readFileSync(path.join(__dirname, './apiclient_key.pem'), 'utf8'), // 秘钥
  32. });
  33. const certificates = await wxPay.get_certificates(config.wechat_pay_key);
  34. // 取最后一个
  35. const certificate = certificates.pop();
  36. // 当前时间戳作为商家批次单号
  37. const timestamp = parseInt(+new Date() + '')
  38. .toString();
  39. // 2023/1/12 构造转账参数
  40. const cashAmount = Math.floor(data.cash_amount * 100);
  41. const bodyData = {
  42. out_batch_no: 'cash' + timestamp,
  43. batch_name: '佣金提现',
  44. batch_remark: data.remark ? data.remark : '佣金提现',
  45. total_amount: cashAmount,
  46. total_num: 1,
  47. wx_serial_no: certificate.serial_no, // 当你需要传user_name时 需要传当前参数
  48. transfer_detail_list: [
  49. {
  50. out_detail_no: 'cashd' + timestamp,
  51. transfer_amount: cashAmount,
  52. transfer_remark: data.remark ? data.remark : '佣金提现',
  53. openid: userInfo.openid,
  54. // user_name: wxPay.publicEncrypt('name', Buffer.from(certificate.publicKey)), // 真实姓名转换
  55. },
  56. ],
  57. };
  58. const res = await wxPay.batches_transfer(bodyData);
  59. if (res.status == 200) {
  60. // 2023/2/1 成功发起转账
  61. // 2023/2/1 提现记录
  62. const logData = {
  63. out_batch_no: bodyData.out_batch_no,
  64. out_detail_no: 'cashd' + timestamp,
  65. batch_id: res.data.batch_id,
  66. detail_status: 'INIT',
  67. batch_name: '佣金提现',
  68. batch_remark: data.remark ? data.remark : '佣金提现',
  69. user_id: data.user_id,
  70. cash: data.cash_amount,
  71. type: 2, // 提现中
  72. create_time: that.app.szjcomo.date('Y-m-d H:i:s'),
  73. };
  74. await that.service.cash.addCashLog(logData);
  75. // 2023/2/1 佣金变化记录(提现转帐中)
  76. const commParams = {
  77. user_id: data.user_id, type: 2, commission: -data.cash_amount, log_desc: '提现转帐中...',
  78. out_batch_no: logData.out_batch_no, out_detail_no: logData.out_detail_no,
  79. };
  80. await that.service.commission.addCommissionChangeLog(commParams);
  81. setTimeout(() => {
  82. // 请等待批次处理完成后再查询明细单据 大约9s左右后可以查询转账结果
  83. that.getTransferResult(logData);
  84. }, 12000);
  85. } else {
  86. that.logger.error('用户提现失败:status %s ,errorCode:%s ,msg:%s', res.status, res.error.code, res.error.message);
  87. }
  88. return res;
  89. }
  90. /**
  91. * 查询提现状态 并 更新状态
  92. * @return {Promise<void>}
  93. */
  94. async getTransferResult(param) {
  95. const that = this;
  96. const keys = [ 'wx_pay_serial_no', 'wechat_account_appid', 'wechat_pay_key', 'wx_pay_mchid' ];
  97. const config = await that.service.configs.getConfigMoreValue(keys);
  98. // 2023/1/11 微信支付配置初始化
  99. const wxPay = new WxPay({
  100. appid: config.wechat_account_appid, // 公众号appid
  101. mchid: config.wx_pay_mchid, // 微信支付商户号
  102. serial_no: config.wx_pay_serial_no, // 微信支付证书序列号
  103. key: config.wechat_pay_key, // APIv3密钥
  104. // publicKey: fs.readFileSync('./apiclient_cert.pem'), // 公钥
  105. publicKey: fs.readFileSync(path.join(__dirname, './apiclient_cert.pem'), 'utf8'), // 公钥
  106. // privateKey: fs.readFileSync('./apiclient_key.pem'), // 秘钥
  107. privateKey: fs.readFileSync(path.join(__dirname, './apiclient_key.pem'), 'utf8'), // 秘钥
  108. });
  109. // 商家明细单号查询明细单API
  110. const res = await wxPay.query_batches_transfer_detail({
  111. out_batch_no: param.out_batch_no,
  112. out_detail_no: param.out_detail_no,
  113. });
  114. if (res.status == 200) {
  115. let cashType;
  116. let commissionType;
  117. let log_desc;
  118. if (res.data.detail_status == 'SUCCESS') {
  119. cashType = 1;
  120. commissionType = 0;
  121. log_desc = '提现成功';
  122. // 2023/2/2 更新分佣账户余额
  123. const updateBean = await that.app.comoBean.instance({
  124. commission: that.app.Sequelize.literal('commission - ' + param.cash),
  125. update_time: that.app.szjcomo.date('Y-m-d H:i:s'),
  126. }, { where: { user_id: param.user_id } });
  127. await that.app.comoBean.update(updateBean, that.app.model.Users, '用户分佣金额更新失败,请稍候重试');
  128. } else if (res.data.detail_status == 'FAIL') {
  129. cashType = 0;
  130. commissionType = -1;
  131. log_desc = '提现失败';
  132. } else {
  133. cashType = 2;
  134. commissionType = 2;
  135. log_desc = '提现转账中...';
  136. }
  137. // 2023/2/1 更新提现记录
  138. const updateBean = await that.app.comoBean.instance({
  139. type: cashType, // 2023/2/1 提现转账状态
  140. detail_id: res.data.detail_id,
  141. detail_status: res.data.detail_status,
  142. fail_reason: res.data.fail_reason,
  143. update_time: that.app.szjcomo.date('Y-m-d H:i:s'),
  144. }, {
  145. where: {
  146. out_batch_no: param.out_batch_no,
  147. out_detail_no: param.out_detail_no,
  148. },
  149. });
  150. await that.service.base.update(updateBean, that.app.model.UsersCashLogs, '更新提现记录失败');
  151. // 2023/2/1 更新佣金变化状态记录
  152. const commBean = await that.app.comoBean.instance({
  153. type: commissionType,
  154. log_desc,
  155. }, {
  156. where: {
  157. out_batch_no: param.out_batch_no,
  158. out_detail_no: param.out_detail_no,
  159. },
  160. });
  161. await that.service.base.update(commBean, that.app.model.UsersCommissionLogs, '更新佣金变化失败');
  162. } else {
  163. that.logger.error('提现状态查询失败:status %s ,msg:400 PARAM_ERROR参数错误;404 NOT_FOUND记录不存在;429 FREQUENCY_LIMITED频率超限',
  164. res.status);
  165. }
  166. return res;
  167. }
  168. }
  169. module.exports = WxPayService;