payment.js 9.79 KB
/**
 * 各种支付的入口
 *
 * @author: xiaoxiao.hao<xiaoxiao.hao@yoho.cn>
 * @date: 16/7/22
 */

'use strict';

const Promise = require('bluebird');
const PayData = require('../models/pay');
const Alipay = require('./pay/alipay');
const Wechat = require('./pay/wechat');
const Unionpay = require('./pay/unionpay');
const Alibank = require('./pay/alibank');
const Tenpay = require('./pay/tenpay');
const Chinabank = require('./pay/chinabank');
const common = require('./pay/common');
const co = Promise.coroutine;
const logger = global.yoho.logger;
const md5 = require('md5');
const paySign = require('./pay/sign');
const helpers = global.yoho.helpers;
const _ = require('lodash');

module.exports = class extends global.yoho.BaseModel {
    constructor(ctx) {
        super(ctx);
    }

    pay(user, order, payType, protocol) {
        let that = this;

        return co(function*() {
            let result = {
                code: 400,
                message: '获取支付方式信息失败'
            };
            let paymentPars = payType.split('_');
            let payInfo;
            let bankCode = '';

            if (paymentPars.length !== 2) {
                return result;
            }

            if (!order.order_code) {
                result.message = '没有找到该订单';
                return result;
            }

            if (order.is_cancel && order.is_cancel === 'Y') {
                result.message = '该订单已经取消';
                return result;
            }

            if (order.pay_expire && common.getPayExpireMin(order.pay_expire) <= 0) {
                result.message = '当前订单不可支付';// 该订单已超过2个小时
                return result;
            }

            let method = parseInt(paymentPars[0], 10);
            let payModel = new PayData(that.ctx);

            if (method === payModel.payments.wechat) {
                // 如果是微信支付,不需要调用获取支付方式详情接口
                result = yield new Wechat(that.ctx).pay(user, order, {id: method});
            } else if (method === payModel.payments.unionpay) {
                // 不需要调用获取支付方式详情接口
                result = yield new Unionpay(that.ctx).pay(user, order, {id: method, protocol: protocol});
            } else {
                payInfo = yield payModel.getPaymentInfo(method);

                if (!payInfo.payParams) {
                    return result;
                }

                switch (payInfo.id) {
                    case payModel.payments.alipay:
                        result = new Alipay(that.ctx).pay(user, order, payInfo, protocol);
                        break;
                    case payModel.payments.tenpay:
                        result = new Tenpay(that.ctx).pay(user, order, payInfo, protocol);
                        break;
                    case payModel.payments.chinabank:
                        result = new Chinabank(that.ctx).pay(user, order, payInfo, protocol);
                        break;
                    case payModel.payments.alibank:
                        bankCode = paymentPars[1];
                        payInfo.bankCode = bankCode;// 设置默认银行
                        result = new Alibank(that.ctx).pay(user, order, payInfo, protocol);
                        break;
                    default:
                        break;
                }
            }

            logger.info(`pay to url, params = ${JSON.stringify(result)}`);

            if (result.code === 200) {
                let updateInfo = yield that.beforePay(user, order, method, bankCode);

                if (updateInfo && updateInfo.code !== 200) {
                    return updateInfo;
                }
            }

            return result;
        })();
    }

    beforePay(user, order, method, bankCode) {
        let payModel = new PayData(this.ctx);

        return Promise.all([
            payModel.updateOrderPayment(order.order_code, method, user.uid),
            payModel.getBankByOrder(order.order_code)
        ]).then(result => {
            let paymentRecord = result[0];
            let bankRecord = result[1];

            if (!paymentRecord || paymentRecord.code !== 200) {
                let message = paymentRecord && paymentRecord.message ? paymentRecord.message : '系统繁忙,请稍后再试';

                return {code: 400, message: message};
            }

            if (bankRecord && bankRecord.bankCode) {
                return payModel.updateOrderPayBank(order.order_code, method, bankCode);
            } else {
                return payModel.setOrderPayBank(order.order_code, method, bankCode);
            }

        }).catch(e => {
            logger.error('update order pay info error.', e);

            return Promise.resolve({
                code: 400,
                message: '更新订单支付信息失败'
            });
        });
    }

    _validate(query, payId, user) {
        let payModel = new PayData(this.ctx);
        let that = this;

        return co(function*() {
            let payInfo = yield payModel.getPaymentInfo(payId);
            let payResult = {};
            let lpayResult = {};
            let payName = '';
            let isPcpayNotify = false;// 回调地址,java未做,走前端逻辑

            if (payId === payModel.payments.alipay) {
                payResult = new Alipay(that.ctx).notify(query, payInfo);
            } else if (payId === payModel.payments.alibank) {
                payResult = new Alibank(that.ctx).notify(query, payInfo);
            } else if (payId === payModel.payments.unionpay) {
                payResult = new Unionpay(that.ctx).notify(query);
            } else if (payId === payModel.payments.tenpay) {
                payResult = new Tenpay(that.ctx).notify(query, payInfo);
                isPcpayNotify = true;
            } else if (payId === payModel.payments.chinabank) {
                payResult = new Chinabank(that.ctx).notify(query, payInfo);
                isPcpayNotify = true;
            }

            payResult.bankName = payName = (payResult.bankName || payInfo.payName || '');
            payResult.bankCode = (payResult.bankCode || '');

            // 记录日志
            logger.info(`\r\n\r\n pay back confirmreq = ${JSON.stringify({
                query: query,
                payId: payId,
                user: user,
                payResult: payResult
            })}`);

            if (payResult && payResult.payResult === 200) {
                if (payResult.orderCode) {
                    logger.info('pay back confirm');
                    payModel.sendPayConfirm(payResult.orderCode, payId, user.uid);
                }

                lpayResult = yield payModel.procOrderData(payResult, user.uid, payId);

                if (isPcpayNotify && lpayResult.code === 200 && lpayResult.data.order.payment_status === 'N') {
                    // 更新订单状态
                    yield payModel.pcpayNotify(Object.assign({payId: payId}, payResult));
                }
            } else {
                lpayResult = {
                    code: 500,
                    message: '支付失败'
                };
            }

            lpayResult.payName = payName;

            return lpayResult;
        })();
    }

    afterPay(query, payId, user) {
        let _this = this;
        let payModel = new PayData(this.ctx);

        return co(function*() {
            let payResult = {code: 500, message: '支付失败'};
            let sign = query.sign || '';

            switch (payId) {
                case payModel.payments.wechat: // 微信支付不须要验证,但前端必须校验sign
                    delete query.sign;
                    if (md5(paySign.raw(Object.assign({tradeStatus: 'Y'}, query))) === sign) {
                        payResult = yield payModel.procOrderData(query, user.uid, payId);
                    }
                    payResult.payName = '微信';
                    break;
                default:// 须要验证的支付方式
                    payResult = _this._validate(query, payId, user);
                    break;
            }

            return payResult;
        })();
    }

    getWxSign(uid, code) {
        let payModel = new PayData(this.ctx);

        return payModel.orderDetail(uid, code).then(result => {
            let data = {};

            result = (result.code === 200 ? result.data : {});

            if (result.is_cancel === 'Y') {
                return {
                    code: 401,
                    message: '订单已取消'
                };
            }

            if (result.payment_status === 'Y') {
                let payParams = {
                    orderCode: code,
                    tradeStatus: 'Y',
                    totalFee: _.round(parseFloat(result.payment_amount), 2)
                };

                data = {
                    code: 200,
                    data: {
                        href: helpers.urlFormat('/shopping/newpay/callback/wechat', Object.assign(payParams, {
                            sign: md5(paySign.raw(payParams))
                        }))
                    }
                };
            }

            return data;
        });
    }

    // 异步通知地址
    notifyPay(query, payId) {
        let payModel = new PayData(this.ctx);

        return co(function*() {
            let d = '';
            let payInfo = yield payModel.getPaymentInfo(payId);

            if (payId === payModel.payments.tenpay) {
                let payResult = Tenpay.notify(query, payInfo);

                if (payResult.payResult === 200) {
                    // 更新订单状态
                    yield payModel.pcpayNotify(Object.assign({payId: payId}, payResult));
                    return 'success';
                }

                return 'fail';
            }

            return d;
        })();
    }
};