BusinessPayService.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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 BusinessPayService 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. partner_id: data.partner_id,
  71. cash: data.cash_amount,
  72. type: 2, // 提现中
  73. create_time: that.app.szjcomo.date('Y-m-d H:i:s'),
  74. };
  75. await that.service.cash.addCashLog(logData);
  76. // 2023/2/1 餐币变化记录(提现转帐中)
  77. const logParams = {
  78. user_id: data.user_id, type: 2, account: -data.cash_amount, log_desc: '提现转帐中...',
  79. out_batch_no: logData.out_batch_no, out_detail_no: logData.out_detail_no,
  80. partner_id: data.partner_id,
  81. };
  82. await that.service.diningCoin.addDiningCoinChangeLog(logParams);
  83. setTimeout(() => {
  84. // 请等待批次处理完成后再查询明细单据 大约9s左右后可以查询转账结果
  85. that.getTransferResult(logData);
  86. }, 12000);
  87. } else {
  88. that.logger.error('商铺发起提现失败:status %s ,errorCode:%s ,msg:%s', res.status, res.error.code, res.error.message);
  89. }
  90. return res;
  91. }
  92. /**
  93. * 查询提现状态 并 更新状态
  94. * @return {Promise<void>}
  95. */
  96. async getTransferResult(param) {
  97. const that = this;
  98. const keys = [ 'wx_pay_serial_no', 'wechat_account_appid', 'wechat_pay_key', 'wx_pay_mchid' ];
  99. const config = await that.service.configs.getConfigMoreValue(keys);
  100. // 2023/1/11 微信支付配置初始化
  101. const wxPay = new WxPay({
  102. appid: config.wechat_account_appid, // 公众号appid
  103. mchid: config.wx_pay_mchid, // 微信支付商户号
  104. serial_no: config.wx_pay_serial_no, // 微信支付证书序列号
  105. key: config.wechat_pay_key, // APIv3密钥
  106. // publicKey: fs.readFileSync('./apiclient_cert.pem'), // 公钥
  107. publicKey: fs.readFileSync(path.join(__dirname, './apiclient_cert.pem'), 'utf8'), // 公钥
  108. // privateKey: fs.readFileSync('./apiclient_key.pem'), // 秘钥
  109. privateKey: fs.readFileSync(path.join(__dirname, './apiclient_key.pem'), 'utf8'), // 秘钥
  110. });
  111. // 商家明细单号查询明细单API
  112. const res = await wxPay.query_batches_transfer_detail({
  113. out_batch_no: param.out_batch_no,
  114. out_detail_no: param.out_detail_no,
  115. });
  116. if (res.status == 200) {
  117. let cashType;
  118. let coinLogType;
  119. let log_desc;
  120. if (res.data.detail_status == 'SUCCESS') {
  121. cashType = 1;
  122. coinLogType = 0;
  123. log_desc = '提现成功';
  124. // 2023/2/2 更新餐币钱包账户余额
  125. const updateBean = await that.app.comoBean.instance({
  126. account: that.app.Sequelize.literal('account - ' + param.cash),
  127. update_time: that.app.szjcomo.date('Y-m-d H:i:s'),
  128. }, { where: { user_id: param.user_id, partner_id: param.partner_id, expired: false } });
  129. await that.service.base.update(updateBean, that.app.model.DinnerCoins, '商家餐饮币余额更新失败,请重试');
  130. } else if (res.data.detail_status == 'FAIL') {
  131. cashType = 0;
  132. coinLogType = -1;
  133. log_desc = '提现失败';
  134. } else {
  135. cashType = 2;
  136. coinLogType = 2;
  137. log_desc = '提现转账中...';
  138. }
  139. // 2023/2/1 更新提现记录
  140. const updateBean = await that.app.comoBean.instance({
  141. type: cashType, // 2023/2/1 提现转账状态
  142. detail_id: res.data.detail_id,
  143. detail_status: res.data.detail_status,
  144. fail_reason: res.data.fail_reason,
  145. update_time: that.app.szjcomo.date('Y-m-d H:i:s'),
  146. }, {
  147. where: {
  148. out_batch_no: param.out_batch_no,
  149. out_detail_no: param.out_detail_no,
  150. },
  151. });
  152. await that.service.base.update(updateBean, that.app.model.UsersCashLogs, '更新提现记录失败');
  153. // 2023/2/1 更新餐币变化状态记录
  154. const commBean = await that.app.comoBean.instance({
  155. type: coinLogType,
  156. log_desc,
  157. }, {
  158. where: {
  159. out_batch_no: param.out_batch_no,
  160. out_detail_no: param.out_detail_no,
  161. },
  162. });
  163. await that.service.base.update(commBean, that.app.model.DinnerCoinLogs, '更新餐币变化失败');
  164. } else {
  165. that.logger.error('提现状态查询失败:status %s ,msg:400 PARAM_ERROR参数错误;404 NOT_FOUND记录不存在;429 FREQUENCY_LIMITED频率超限',
  166. res.status);
  167. }
  168. return res;
  169. }
  170. }
  171. module.exports = BusinessPayService;