'use strict'; const { CrudService } = require('naf-framework-mongoose-free/lib/service'); const { BusinessError, ErrorCode } = require('naf-core').Error; 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) { super(ctx, 'payorder'); this.model = this.ctx.model.Business.PayOrder; this.payService = this.ctx.service.wxpay; this.lessonStudentModel = this.ctx.model.Business.LessonStudent; this.lessonStudentService = this.ctx.service.business.lessonStudent; this.tempLessonApplyModel = this.ctx.model.Apply.TempLessonApply; this.userModel = this.ctx.model.User.User; this.studentModel = this.ctx.model.User.Student; 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(); } /** * 处理支付回调 * @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(); } } async afterUpdate(filter, body, data) { await this.syncData(data); return data; } /** * 支付同步 * @param {Object} data 支付单数据 */ async syncData(data) { const pay_for = _.get(data, 'pay_for'); // 不知道该去同步哪个表的支付状态,不处理 if (!pay_for) return; const { from_id: _id, status: is_pay, money, _id: pay_id, config } = data; if (pay_for === 'lessonStudent') { // 因为上课产生的支付,去找lessonStudent,修改指定学生的支付状态 await this.lessonStudentModel.updateOne({ _id }, { is_pay, pay_id }); // 检查下各种记录 await this.makeRecord(data); } else if (pay_for === 'tempLessonApply') { // 私教课临时上课,需要到临时申请那找到相关数据 const tempApply = await this.tempLessonApplyModel.findById(_id); if (!tempApply) return; tempApply.pay_id = pay_id; await tempApply.save(); // 修改完申请,再创建 lessonStudent if (is_pay === '1') { const { lesson_id, student_id, school_id } = tempApply; const obj = { lesson_id, student_id, school_id, is_pay, pay_id, config, money }; await this.lessonStudentService.create(obj); } await this.makeRecord(data); } else if (pay_for === 'Bill' && is_pay !== '0') { // 充值记录,找到充值记录,没有就生成 const billData = await this.billModel.findOne({ pay_id }); if (billData) { // 有就修改 billData.is_pay = is_pay; await billData.save(); } else { const obj = _.pick(data, [ 'school_id', 'payer_id', 'payer_role' ]); await this.billModel.create({ ...obj, pay_for: 'Bill', pay_id, is_pay, time: moment().format('YYYY-MM-DD HH:mm:ss'), type: '1' }); } } } // 检查记录 async makeRecord(data) { const { config, _id: pay_id, status: is_pay, pay_for, from_id } = data; let billData; let wxBill; // 检查是否有本次微信支付部分的账单 wxBill = await this.billModel.findOne({ pay_id }); if (!wxBill) { // 没有微信支付账单,需要创建 const obj = _.pick(data, [ 'school_id', 'payer_id', 'payer_role', 'pay_for', 'from_id', 'time', 'money' ]); obj.type = '-1'; obj.pay_id = data._id; obj.is_pay = is_pay; wxBill = await this.billModel.create(obj); } // 使用余额的处理 if (_.get(config, 'useSurplus')) { const { bill } = config; // 使用了余额,但是余额记录不是直接生成的,需要检查下bill是否为ObjectId // 如果是ObjectId,说明余额记录已经生成.无需操作 if (_.isObject(bill)) { // 是数据,生成账单记录 billData = await this.billModel.create({ ...bill, is_pay, pay_for, from_id, time: moment().format('YYYY-MM-DD HH:mm:ss') }); if (billData) data.config.bill = ObjectId(billData._id).toString(); await this.model.updateOne({ _id: data._id }, data); // 创建账单后,扣余额 const { type, payer_role, payer_id, money, school_id } = billData; if (type === '-2') { let relation; 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 }); relation.money = relation.money - money; await relation.save(); } } } } /** * 重新支付 * @param {String} body 参数体 * @property {String} id 数据id */ async toRePay({ id }) { const data = await this.model.findById(id); if (!data) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到支付的信息'); // 找到这个订单,然后获取到订单号 const { order_no } = data; // 利用订单号去查询微信那边,该订单是否还存在 const wxOrder = await this.payService.search(order_no); let wxSign; if (_.get(wxOrder, 'trade_state') === 'NOTPAY') { // 未支付就用原数据,去支付 wxSign = await this.payService.create(data); } else if (_.get(wxOrder, 'trade_state') === 'SUCCESS') { throw new BusinessError(ErrorCode.SERVICE_FAULT, '订单已支付,无需重复支付'); } else if (_.get(wxOrder, 'trade_state') === 'USERPAYING') { throw new BusinessError(ErrorCode.SERVICE_FAULT, '订单支付中'); } else { await this.payService.close(order_no); // 其他情况就关闭订单.直接新订单 const newNo = this.getOrderNo(); data.order_no = newNo; await data.save(); wxSign = await this.payService.create(data); } return { pay_id: id, wxSign }; } /** * 申请退款 * @param {Object} body 参数体 * @property {String} id 支付单的数据id */ async toRefund({ id }) { // 查询支付 const data = await this.model.findById(id); if (!data) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到要退款的支付数据'); const wxOrderReq = await this.payService.search(data.order_no); if (wxOrderReq) { if (!wxOrderReq) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未查询到微信支付订单'); const wxRefundReq = await this.payService.refund(data.order_no, '退赛'); if (wxRefundReq) { if (wxRefundReq.status !== 'REFUND') throw new BusinessError(ErrorCode.SERVICE_FAULT, '退款失败'); // 退款成功 data.status = '-3'; await data.save(); await this.syncData(data); return 'ok'; } } throw new BusinessError(ErrorCode.SERVICE_FAULT, '查询微信支付订单失败'); } /** * 关闭订单 * @param {String} id 支付id * @return {Object} 关闭订单结果 */ async closeOrder(id) { const data = await this.model.findById(id); if (!data) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到要退款的支付数据'); const res = await this.payService.close(data.order_no); return res; } getOrderNo() { return `ONCAPP${moment().format('YYYYMMDDHHmmss')}-${_.random(1, 999999)}`; } // 二维码扫码 async toPayNative(body) { body = await this.beforeCreate(body); const data = await this.model.create(body); const qrcode = await this.payService.createNative({ ...body, notice_url: '/newCourt/v2/api/payOrder/nativeReturn' }); return qrcode; } // 二维码扫码回调 async nativeReturn(body) { const result = _.get(body, 'result'); if (!result) throw new BusinessError(ErrorCode.BADPARAM, '参数错误'); const { out_trade_no: order_no, trade_state, payer } = result; const obj = {}; if (trade_state === 'SUCCESS') obj.status = '1'; else if (trade_state === 'REFUND') obj.status = '-3'; const openid = _.get(payer, 'openid'); if (openid) { obj.openid = openid; // 找payer_id的信息,学员,教练 const user = await this.userModel.findOne({ openid }); if (user) { const { _id: user_id } = user; let roleUser = await this.studentModel.findOne({ user_id }); if (!roleUser) { roleUser = await this.coachModel.findOne({ user_id }); if (roleUser) { obj.payer_role = 'Coach'; obj.payer_id = roleUser._id; } } else { obj.payer_role = 'Student'; obj.payer_id = roleUser._id; } } } await this.model.updateOne({ order_no }, obj); const data = await this.model.findOne({ order_no }); await this.syncData(data); } } module.exports = PayOrderService;