lrf 2 éve
szülő
commit
028197f2fb

+ 2 - 2
app/controller/business/config/.lessonStudent.js

@@ -1,6 +1,6 @@
 module.exports = {
   create: {
-    requestBody: ['pay_id', 'is_pay', 'lesson_id', 'school_id', 'student_id', 'money', 'is_try', 'try_status', 'is_sign'],
+    requestBody: ['openid', 'pay_id', 'is_pay', 'lesson_id', 'school_id', 'student_id', 'money', 'is_try', 'try_status', 'is_sign'],
   },
   destroy: {
     params: ['!id'],
@@ -8,7 +8,7 @@ module.exports = {
   },
   update: {
     params: ['!id'],
-    requestBody: ['pay_id', 'is_pay', 'lesson_id', 'school_id', 'student_id', 'money', 'is_try', 'try_status', 'is_sign'],
+    requestBody: ['openid', 'pay_id', 'is_pay', 'lesson_id', 'school_id', 'student_id', 'money', 'is_try', 'try_status', 'is_sign'],
   },
   show: {
     parameters: {

+ 3 - 0
app/controller/business/config/.payOrder.js

@@ -50,4 +50,7 @@ module.exports = {
   toPayNative: {
     requestBody: ['!school_id', 'pay_for', 'from_id', 'money', 'time', 'desc', 'config'],
   },
+  payCallBack: {
+    requestBody: ['result'],
+  },
 };

+ 74 - 6
app/service/business/lessonStudent.js

@@ -4,6 +4,7 @@ const { BusinessError, ErrorCode } = require('naf-core').Error;
 const _ = require('lodash');
 const assert = require('assert');
 const moment = require('moment');
+const Transaction = require('mongoose-transactions');
 
 //
 class LessonStudentService extends CrudService {
@@ -15,18 +16,23 @@ class LessonStudentService extends CrudService {
     this.lessonModel = this.ctx.model.Business.Lesson;
     this.rcsModel = this.ctx.model.Relation.RelationCoachSchool;
     this.rssModel = this.ctx.model.Relation.RelationStudentSchool;
+    this.billModel = this.ctx.model.Business.Bill;
+    this.payOrderModel = this.ctx.model.Business.PayOrder;
+    this.studentModel = this.ctx.model.User.Student;
+    this.payService = this.ctx.service.wxpay;
+    this.tran = new Transaction();
   }
 
   async checkCanUse({ school_id, student_id, lesson_id }) {
-    const num = await this.model.count({ school_id, student_id, lesson_id, $or: [{ is_try: '0', is_pay: { $ne: '-3' } }, { is_try: '1' }] });
+    const num = await this.model.count({ school_id, student_id, lesson_id, $or: [{ is_try: '0', is_pay: { $and: [{ $ne: '-3' }, { $ne: '-1' }] } }, { is_try: '1' }] });
     if (num > 0) throw new BusinessError(ErrorCode.DATA_EXISTED, '已存在有效的报名信息');
     return true;
   }
 
   async beforeCreate(body) {
     const { lesson_id, school_id, student_id } = body;
-    // 检查是否已经有数据
-    const data = await this.model.findOne({ lesson_id, school_id, student_id, $or: [{ is_try: '0', is_pay: { $ne: '-3' } }, { is_try: '1' }] });
+    // 检查是否已经有数据: 支付失败和已退款不算
+    const data = await this.model.findOne({ lesson_id, school_id, student_id, $or: [{ is_try: '0', is_pay: { $nin: [ '-1', '-3' ] } }, { is_try: '1' }] });
     // 数据已存在
     if (data) throw new BusinessError(ErrorCode.DATA_EXISTED, '您已报名');
     // 数据不存在
@@ -39,6 +45,70 @@ class LessonStudentService extends CrudService {
     return body;
   }
 
+  async create(data) {
+    // 检查是否能报名上课
+    data = await this.beforeCreate(data);
+    // 能报名上课,就直接创建数据
+    try {
+      // 创建数据
+      const id = this.tran.insert('LessonStudent', data);
+      const { school_id, student_id: payer_id, money, openid } = data;
+      if (!openid) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到用户的微信信息');
+      const payData = {
+        openid,
+        school_id,
+        payer_id,
+        payer_role: 'Student',
+        pay_for: 'LessonStudent',
+        from_id: id,
+        time: moment().format('YYYY-MM-DD HH:mm:ss'),
+        order_no: this.ctx.service.business.payOrder.getOrderNo(),
+        desc: '课程费',
+        money,
+      };
+      // 计算付款单支付金额
+      // 有余额且余额够,就直接扣余额;余额不足,就直接去支付
+      const relation = await this.rssModel.findOne({ student_id: payer_id, school_id });
+      if (!relation) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到在校记录');
+      const { money: surplus } = relation;
+      let billId;
+      if (this.ctx.minus(surplus, money) >= 0) {
+        // 余额够,生成账单
+        const billData = _.pick(payData, [ 'school_id', 'payer_role', 'payer_id', 'pay_for', 'from_id' ]);
+        billData.type = '-2';
+        billData.is_pay = '1';
+        billData.time = moment().format('YYYY-MM-DD HH:mm:ss');
+        billId = this.tran.insert('Bill', billData);
+        // 减少余额
+        const rs = this.ctx.minus(surplus, data.money);
+        this.tran.update('RelationStudentSchool', relation._id, { money: rs });
+        // 生成付款单数据,但是不需要支付
+        payData.status = '1';
+        payData.config = {
+          useSurplus: true,
+          bill: billId,
+          money,
+        };
+      }
+      const pay_id = this.tran.insert('PayOrder', payData);
+      const result = { pay_id };
+      // 如果生成账单id,则说明是全余额付的.修改账单的数据,将pay_id改了
+      if (billId) this.tran.update('Bill', billId, { pay_id });
+      else {
+        // 需要微信支付签名(填充回调函数,回调函数处理)
+        const wxSign = await this.payService.create({ ...payData, notice_url: this.app.config.payReturn });
+        result.wxSign = wxSign;
+      }
+      await this.tran.run();
+      return result;
+    } catch (error) {
+      await this.tran.rollback();
+      throw Error(error);
+    } finally {
+      this.tran.clean();
+    }
+  }
+
   // 计算价格
   async toComputed({ lesson_id, student_id }) {
     // 通过课程id找到该课的所有教练
@@ -142,7 +212,7 @@ class LessonStudentService extends CrudService {
       time: moment().format('YYYY-MM-DD HH:mm:ss'),
       money: data.money,
     };
-      // 创建退回余额的账单
+    // 创建退回余额的账单
     await billModel.create(obj);
     const relation = await this.rssModel.findOne({ student_id, school_id: data.school_id });
     relation.money = _.get(relation, 'money', 0) + _.get(obj, 'money', 0);
@@ -151,8 +221,6 @@ class LessonStudentService extends CrudService {
     data.is_pay = '-3';
     // 学员退课
     await data.save();
-
-
   }
 }
 

+ 48 - 84
app/service/business/payOrder.js

@@ -5,6 +5,7 @@ const _ = require('lodash');
 const assert = require('assert');
 const moment = require('moment');
 const { ObjectId } = require('mongoose').Types;
+const Transaction = require('mongoose-transactions');
 //
 class PayOrderService extends CrudService {
   constructor(ctx) {
@@ -19,82 +20,51 @@ class PayOrderService extends CrudService {
     this.coachModel = this.ctx.model.User.Coach;
     this.rcsModel = this.ctx.model.Relation.RelationCoachSchool;
     this.rssModel = this.ctx.model.Relation.RelationStudentSchool;
-
     this.billModel = this.ctx.model.Business.Bill;
+    this.tran = new Transaction();
   }
-
-  async beforeCreate(data) {
-    if (!_.get(data, 'order_no')) data.order_no = this.getOrderNo();
-    const { data: nd, next } = await this.checkSurplus(data);
-    if (!next) throw new BusinessError(0, '扣除余额成功', nd);
-    data = nd;
-    return data;
-  }
-  // 检查是否扣除余额
-  async checkSurplus(data) {
-    const pay_for = _.get(data, 'pay_for');
-    // 充值不检查余额
-    if (pay_for && pay_for === 'Bill') return { data, next: true };
-    const payer_id = _.get(data, 'payer_id');
-    const payer_role = _.get(data, 'payer_role');
-    const school_id = _.get(data, 'school_id');
-    let billData;
-    let relation;
-    // 没有要支付的人及其角色,那就找不到余额,返回下单
-    if (!(payer_id && payer_role)) return { data, next: true };
-    // 有支付的人,可以查查这个人的余额
-    if (payer_role === 'Student') relation = await this.rssModel.findOne({ student_id: payer_id, school_id });
-    else if (payer_role === 'Coach') relation = await this.rcsModel.findOne({ coach_id: payer_id, school_id });
-    // 没找到关系,那就直接返回正常下单缴费
-    if (!relation) return { data, next: true };
-    // 有关系,找余额
-    const { money: surplus } = relation;
-    const { money } = data;
-    // 没有余额,或者余额没钱,返回正常下单缴费
-    if (surplus && surplus <= 0) return { data, next: true };
-    // 有余额,看余额够不够
-    if (this.ctx.minus(surplus, money) >= 0) {
-      try {
-        // 余额够, 无需生成订单,直接去使用余额缴费
-        const bill = _.pick(data, [ 'school_id', 'payer_id', 'payer_role', 'pay_for', 'from_id' ]);
-        // 本次扣除余额的金额就是本次需要支付的金额
-        bill.money = money;
-        bill.type = '-2';
-        bill.is_pay = '1';
-        billData = await this.billModel.create({ ...bill, time: moment().format('YYYY-MM-DD HH:mm:ss') });
-        // 余额需要扣除本次支付的费用
-        relation.money = surplus - money;
-        await relation.save();
-        // 前端生成报名数据
-        return { data: billData, next: false };
-      } catch (error) {
-        // 抓取数据库创建和修改的异常,
-        // 如果 billData没有值,说明是创建消费记录时发生的错误,不需要做回滚处理;如果有值,那就删除
-        if (billData) {
-          await this.billModel.deleteOne({ _id: billData._id });
-        }
-        // 如果是余额修改错误,上面的回滚删除就足够了.因为保存新余额发生错误,余额不会保存,只要把消费记录删了,就回到原样了
-      }
-    } else if (surplus !== 0) {
-      // 余额不足且不为0,在config里记录部分使用了余额,再进行下单,下单完后,生成余额的扣款
-      const needPay = this.ctx.minus(money, surplus);
-      const bill = _.pick(data, [ 'school_id', 'payer_id', 'payer_role', 'pay_for', 'from_id' ]);
-      bill.money = surplus;
-      bill.type = '-2';
-      if (!data.config) data.config = {};
-      // 添加余额的设置, 等支付成功后,再生成余额的消费记录
-      data.config.useSurplus = true;
-      data.config.bill = bill;
-      data.money = needPay;
+  /**
+   * 处理支付回调
+   * @param {Object} body 参数体
+   */
+  async payCallBack({ result }) {
+    const { out_trade_no, payer } = result;
+    // 没有需要报警,没有订单号就出问题了
+    if (!out_trade_no) {
+      console.error('没有支付订单号');
+      return;
+    }
+    const openid = _.get(payer, 'openid');
+    const query = { order_no: out_trade_no, openid };
+    let payOrder = await this.model.findOne(query);
+    // 没找到订单也是有问题的,需要报警
+    if (!payOrder) {
+      console.error('没有找到付款单');
+      return;
+    }
+    payOrder = JSON.parse(JSON.stringify(payOrder));
+    const config = _.get(payOrder, 'config', {});
+    // 修改信息
+    config.pay = result;
+    const update = { config, status: _.get(result, 'trade_state') === 'SUCCESS' ? '1' : '-1' };
+    try {
+      this.tran.update('PayOrder', payOrder._id, update);
+      // 生成账单
+      const billData = _.pick(payOrder, [ 'school_id', 'payer_id', 'payer_role', 'pay_for', 'from_id' ]);
+      // 充值为1,直接支付为-1
+      if (billData.pay_for && _.lowerCase(billData.pay_for) === 'charge') billData.type = '1';
+      else billData.type = '-1';
+      billData.pay_id = payOrder._id;
+      billData.is_pay = update.status;
+      billData.time = moment().format('YYYY-MM-DD HH:mm:ss');
+      this.tran.insert('Bill', billData);
+      await this.tran.run();
+    } catch (error) {
+      await this.tran.rollback();
+      console.log(error);
+    } finally {
+      this.tran.clean();
     }
-
-    return { data, next: true };
-  }
-
-  async afterCreate(body, data) {
-    // await this.syncData(data);
-    const wxSign = await this.payService.create(data);
-    return { data, wxSign };
   }
 
   async afterUpdate(filter, body, data) {
@@ -157,7 +127,6 @@ class PayOrderService extends CrudService {
       wxBill = await this.billModel.create(obj);
     }
 
-
     // 使用余额的处理
     if (_.get(config, 'useSurplus')) {
       const { bill } = config;
@@ -178,12 +147,6 @@ class PayOrderService extends CrudService {
           await relation.save();
         }
       }
-      // 账单的退款是退款,支付是支付,各自生成数据,不需要修改
-      // else {
-      //   // 已经有数据了, 修改账单.并根据is_pay去做相应的处理
-      //   await this.billModel.updateOne({ _id: data.config.bill }, { is_pay });
-      //   billData = await this.billModel.findById(data.config.bill);
-      // }
     }
   }
 
@@ -208,13 +171,14 @@ class PayOrderService extends CrudService {
     } else if (_.get(wxOrder, 'trade_state') === 'USERPAYING') {
       throw new BusinessError(ErrorCode.SERVICE_FAULT, '订单支付中');
     } else {
-      // 其他情况就当订单被关闭了.直接新订单
-      const order_no = this.getOrderNo();
-      data.order_no = order_no;
+      await this.payService.close(order_no);
+      // 其他情况就关闭订单.直接新订单
+      const newNo = this.getOrderNo();
+      data.order_no = newNo;
       await data.save();
       wxSign = await this.payService.create(data);
     }
-    return { data, wxSign };
+    return { pay_id: id, wxSign };
   }
 
   /**

+ 2 - 2
app/service/wxpay.js

@@ -30,8 +30,8 @@ class WxpayService extends CrudService {
    * @param {Object} data 数据
    */
   async create(data) {
-    const { money, openid, order_no, desc } = data;
-    const wxOrderData = { config: this.appConfig, money, openid, order_no, desc };
+    const { money, openid, order_no, desc, notice_url } = data;
+    const wxOrderData = { config: this.appConfig, money, openid, order_no, desc, notice_url };
     const url = `${this.wxDomain}/pay/payOrder`;
     const res = await this.httpUtil.cpost(url, wxOrderData);
     if (res) return res;

+ 1 - 0
app/z_router/business/payOrder.js

@@ -7,6 +7,7 @@ const rkey = 'payOrder';
 const ckey = 'business.payOrder';
 const keyZh = '支付订单';
 const routes = [
+  { method: 'post', path: `${rkey}/payCallBack`, controller: `${ckey}.payCallBack`, name: `${ckey}payCallBack`, zh: `${keyZh}-正常支付回调` },
   { method: 'post', path: `${rkey}/nativeReturn`, controller: `${ckey}.nativeReturn`, name: `${ckey}nativeReturn`, zh: `${keyZh}-二维码支付回调` },
   { method: 'post', path: `${rkey}/toPayNative`, controller: `${ckey}.toPayNative`, name: `${ckey}toPayNative`, zh: `${keyZh}-二维码支付` },
   { method: 'post', path: `${rkey}/toRePay`, controller: `${ckey}.toRePay`, name: `${ckey}toRePay`, zh: `${keyZh}-重新支付订单` },

+ 2 - 0
config/config.default.js

@@ -122,6 +122,8 @@ module.exports = appInfo => {
   };
   // 路由设置
   config.routePrefix = '/newCourt/v2/api';
+  // 支付回调
+  config.payReturn = '/newCourt/v2/api/payOrder/payCallBack';
 
   // 中间件
   config.requestLog = {