pay.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. 'use strict';
  2. const { CrudService } = require('naf-framework-mongoose-free/lib/service');
  3. const { BusinessError, ErrorCode } = require('naf-core').Error;
  4. const _ = require('lodash');
  5. const assert = require('assert');
  6. const Transaction = require('mongoose-transactions');
  7. //
  8. class PayService extends CrudService {
  9. constructor(ctx) {
  10. super(ctx, 'pay');
  11. this.httpUtil = this.ctx.service.util.httpUtil;
  12. this.appConfig = 'pointApp';
  13. this.orderModel = this.ctx.model.Trade.Order;
  14. this.dictDataModel = this.ctx.model.Dev.DictData;
  15. this.payOrderReturnUrl = this.app.config.payReturn.order;
  16. this.wxDomain = _.get(this.app, 'config.httpPrefix.wechat');
  17. this.tran = new Transaction();
  18. }
  19. /**
  20. * 去支付订单
  21. * 1.有支付方式之分; 微信/支付宝
  22. * 2.根据不同方式去请求
  23. * @param {Object} body 请求体
  24. * @param body.order_id 订单id
  25. * @param body.type 支付方式
  26. */
  27. async toPayOrder({ order_id, type = '0' }) {
  28. const payWay = await this.dictDataModel.findOne({ value: type, status: '0' });
  29. if (!payWay) throw new BusinessError(ErrorCode.DATA_INVALID, '该支付方式暂时无法使用');
  30. const order = await this.orderModel.findById(order_id);
  31. if (!order) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到订单数据');
  32. const { no, pay } = order;
  33. // 检查是否有支付信息如果有的话,支付方式是否一致
  34. if (_.isObject(pay) && Object.keys(pay).length > 0) {
  35. const pay_type = _.get(pay, 'pay_type');
  36. // 支付方式不同,则将支付内容刷掉,重新进行数据生成 => 关闭之前的订单
  37. if (pay_type === type) {
  38. // 支付方式相同,则再找有没有支付订单
  39. const pay_no = _.get(pay, 'pay_no');
  40. if (pay_no) {
  41. // 有支付订单.则先查支付订单是否可以继续支付
  42. // TODO:
  43. // 1.当前用户和之前下单支付的用户的openid是不是一个人.是一个人=>3;不是一个人=>2
  44. // 2.需要关闭之前的支付订单并且重新进行支付: 关闭订单.再放到下面
  45. // 3.查询订单是否过期 没过期=>4; 过期=>5
  46. // 4.继续支付:取出数据,return;
  47. // 5.放到下面.重新支付
  48. const openid = _.get(this.ctx, 'user.openid');
  49. const pay_openid = _.get(pay, 'openid');
  50. if (pay_openid === openid) {
  51. // 检查订单是否可以支付
  52. const canPay = await this.search(pay.pay_no);
  53. console.log(canPay);
  54. // if (canPay) {
  55. // // 继续支付
  56. // return;
  57. // }
  58. await this.closeOrder(pay);
  59. } else await this.closeOrder(pay);
  60. }
  61. // 没有的话,就说明数据有问题.按照没有支付信息处理即可
  62. } else await this.closeOrder(pay);
  63. }
  64. // 没有支付信息(要是有支付信息,上面直接return了.漏下来的都是没有的处理方案)
  65. const str = this.ctx.service.util.trade.createNonceStr();
  66. const arr = no.split('-');
  67. // 订单中pay的信息
  68. const payObject = { pay_type: type, pay_no: `${_.last(arr)}-${str}` };
  69. const totalMoney = this.getOrderNeedPay(order);
  70. payObject.pay_money = totalMoney;
  71. let payData;
  72. let res;
  73. // 找到当前用户的openid:这里涉及问题是: 如果自己下单,自己付款.那没有问题;
  74. // 如果是自己下单.如果使用账号密码登录再付款.还用下单的人找到的openid就不能在这个微信号上进行支付了;
  75. // 所以此处是需要用当前用户的openid进行支付,如果之前生成单子.还需要检查当前用户的openid和之前的openid是否一致
  76. // 如果不一致.则需要将之前的订单关闭,重新生成
  77. // 请求微信支付接口的数据
  78. if (type === '0') {
  79. payData = this.getWxPayData(order_id, totalMoney, payObject.pay_no);
  80. payObject.openid = payData.openid;
  81. }
  82. try {
  83. this.tran.update('Order', order_id, { pay: payObject });
  84. await this.tran.run();
  85. } catch (error) {
  86. await this.tran.rollback();
  87. console.error(error);
  88. }
  89. if (type === '0') {
  90. res = await this.create(payData);
  91. res = this.preparToUniAppWxPay(res);
  92. }
  93. return res;
  94. }
  95. /**
  96. * 支付订单回调函数
  97. * @param {Object} body 请求地址参数
  98. * @param body.result 支付回调结果
  99. */
  100. async callBackPayOrder({ result }) {
  101. const { out_trade_no, payer } = result;
  102. // TODO: 没有需要报警,没有订单号就出问题了
  103. if (!out_trade_no) {
  104. console.error('没有支付订单号');
  105. return;
  106. }
  107. const openid = _.get(payer, 'openid');
  108. const query = { 'pay.pay_no': new RegExp(`${out_trade_no}`), 'pay.openid': openid };
  109. let orderData = await this.orderModel.findOne(query);
  110. // TODO: 没找到订单也是有问题的,需要报警
  111. if (!orderData) {
  112. console.error('没有找到订单');
  113. return;
  114. }
  115. orderData = JSON.parse(JSON.stringify(orderData));
  116. const payData = _.get(orderData, 'pay', {});
  117. // 支付结果全都存起来
  118. payData.result = result;
  119. // 修改状态
  120. await this.orderModel.updateOne(query, { pay: payData, status: '1' });
  121. }
  122. /**
  123. * 关闭订单
  124. * @param {Object} pay 支付信息
  125. */
  126. async closeOrder(pay) {
  127. const { pay_type } = pay;
  128. if (pay_type === '0') {
  129. // 微信支付方式
  130. }
  131. }
  132. /**
  133. * 微信支付:整理出uniapp需要的数据
  134. * @param {Object} data 微信接口的数据
  135. */
  136. preparToUniAppWxPay(data) {
  137. const obj = {
  138. // appid: _.get(data, 'appid'),
  139. // prepayid: _.get(data, 'prepay_id'),
  140. nonceStr: _.get(data, 'nonceStr'),
  141. package: `prepay_id=${_.get(data, 'prepay_id')}`,
  142. signType: _.get(data, 'signType'),
  143. timeStamp: _.get(data, 'timestamp'),
  144. paySign: _.get(data, 'paySign'),
  145. };
  146. return obj;
  147. }
  148. /**
  149. * 组织微信支付数据
  150. * @param {String} order_id 订单号
  151. * @param {Number} money 支付金额
  152. * @param {String} no 支付订单号
  153. */
  154. getWxPayData(order_id, money, no) {
  155. const openid = _.get(this.ctx, 'user.openid');
  156. const data = { config: this.appConfig, money, openid, order_no: no, desc: '购物', notice_url: this.payOrderReturnUrl(order_id) };
  157. return data;
  158. }
  159. /**
  160. * 计算订单需支付的金额
  161. * @param {Object} order 要支付的订单数据
  162. */
  163. getOrderNeedPay(order) {
  164. let total = 0;
  165. const { total_detail = {} } = order;
  166. for (const key in total_detail) {
  167. total = _.floor(total_detail[key] + total, 2);
  168. }
  169. return total;
  170. }
  171. /**
  172. * 查询订单
  173. * @param {String} order_no 订单号
  174. */
  175. async search(order_no) {
  176. assert(order_no, '缺少订单号,无法查询订单信息');
  177. const params = { config: this.appConfig, order_no };
  178. const url = `${this.wxDomain}/pay/searchOrderByOrderNo`;
  179. const wxOrderReq = await this.httpUtil.cpost(url, params);
  180. return wxOrderReq;
  181. }
  182. /**
  183. * 创建订单,获取微信支付签名
  184. * @param {Object} data 数据
  185. */
  186. async create(data) {
  187. const { money, openid, order_no, desc, notice_url } = data;
  188. const wxOrderData = { config: this.appConfig, money, openid, order_no, desc, notice_url };
  189. const url = `${this.wxDomain}/pay/payOrder`;
  190. const res = await this.httpUtil.cpost(url, wxOrderData);
  191. if (res) return res;
  192. throw new BusinessError(ErrorCode.SERVICE_FAULT, '微信下单失败!');
  193. }
  194. /**
  195. * 关闭订单
  196. * @param {String} order_no 订单号
  197. */
  198. async close(order_no) {
  199. assert(order_no, '缺少订单号,无法查询订单信息');
  200. const params = { config: this.appConfig, order_no };
  201. const url = `${this.wxDomain}/pay/closeOrder`;
  202. const res = await this.httpUtil.cpost(url, params);
  203. return res || 'ok';
  204. }
  205. /**
  206. * TODO 退款,金额需要指定.可能是部分退款,也可能是全额退款
  207. * @param {String} order_no 订单号
  208. * @param {String} reason 原因
  209. */
  210. async refund(order_no, reason) {
  211. assert(order_no, '缺少订单号,无法查询订单信息');
  212. const url = `${this.wxDomain}/pay/refundOrder`;
  213. const params = { config: this.appConfig, order_no, reason };
  214. const wxRefundReq = await this.httpUtil.cpost(url, params);
  215. return wxRefundReq || 'ok';
  216. }
  217. }
  218. module.exports = PayService;