pay.js 11.2 KB
/**
 * 订单支付
 * @author: jiangfeng<jeff.jiang@yoho.cn>
 * @date: 2016/07/18
 */

'use strict';

const payApi = require('./pay-api');
const Promise = require('bluebird');
const co = Promise.coroutine;
const payHelpersBank = require('./../helpers/bank');
const logger = global.yoho.logger;
const _ = require('lodash');
const helpers = global.yoho.helpers;
const FRAUD_CONTENT_CODE = '78d0fb6c97d691863286edcb4d8abfa9';
const payments = {
    alipay: 2, // 支付宝
    wechat: 21, // 微信支付
    alibank: 12, // 支付宝支联
    unionpayweb: 25, // 银联支付
    chinabank: 4, // 网银在线
    tenpay: 1, // 财付通
    delivery: 3 // 货到付款和0元订单
};

/**
 * [更新订单支付方式]
 * @param  {[type]} code    [订单号]
 * @param  {[type]} payment [支付方式]
 * @param  {[type]} uid     [用户uid]
 * @return {[type]}         [{}]
 */
const updateOrderPayment = (code, payment, uid) => {
    return payApi.updateOrderPayment(code, payment, uid);
};

/**
 * [订单详情]
 * @param  {[type]} uid       [用户uid]
 * @param  {[type]} orderCode [订单号]
 * @return {[type]}           [{}]
 */
const orderDetail = (uid, orderCode)=>{
    return payApi.orderDetail(uid, orderCode);
};

/**
 * 获取在线支付方式
 * @returns {*|Promise.<TResult>}
 */
const getOnlinePayProvider = () => {
    return payApi.getPayProvider().then(result => {
        let payPattern = [], nav = [];

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

        // 处理支付宝等平台数据
        _.each(result.data, val => {
            // 去除icon为空的
            if (_.isEmpty(val.payIcon)) {
                return true;
            }

            nav.push({
                id: val.id,
                name: val.payName,
                value: val.id + '_platform',
                img: val.payIcon
            });
        });

        payPattern.push({
            name: '支付宝等平台',
            children: nav
        });

        // 处理使用银行卡数据
        nav = [];
        _.each(payHelpersBank.getList(), (val, key) => {
            nav.push({
                id: payments.alibank,
                name: val.name,
                value: payments.alibank + '_' + key,
                img: val.ico
            });
        });
        payPattern.push({
            name: '使用银行卡',
            children: nav
        });

        return payPattern;
    });
};

/**
 * 获取订单详情
 * @param uid
 * @param code
 */
const getOrderInfo = (uid, code) => {
    return orderDetail(uid, code).then(data => {
        return data && data.code === 200 && data.data || {};
    });
};

/**
 * 获取订单支付银行信息
 * @param id
 */
const getBankByOrder = (id) => {
    return payApi.getBankByOrder(id).then(data => {
        return data && data.code === 200 && data.data || {};
    });
};

/**
 * 获取支付页数据
 * @param uid
 * @param code
 */
const getPayInfo = (uid, code) => {
    return Promise.all([
        getOnlinePayProvider(),
        getOrderInfo(uid, code),
        getBankByOrder(code),
        payApi.getOrderCountByUid(uid)
    ]).then(payment => {
        let paymentId = parseInt(payment[2].payment || 0, 10);
        let orderInfo = payment[1] || {};
        let findIndex;
        let payDefault;

        // 渲染选择的默认支付方式
        if (paymentId === payments.alibank && payment[0][1] && payment[2]) { // 银行支付
            findIndex = _.findIndex(payment[0][1].children, {value: payments.alibank + '_' + payment[2].bankCode});
            findIndex = findIndex > 0 ? findIndex : 0;
            payment[0][1].selected = true;
            payment[0][1].children[findIndex].selected = true;

            payDefault = payment[0][1].children[findIndex];
        } else if (payment[0] && payment[0][0]) { // 支付宝等平台
            findIndex = _.findIndex(payment[0][0].children, {id: paymentId});
            findIndex = findIndex > 0 ? findIndex : 0;
            payment[0][0].selected = true;
            payment[0][0].children[findIndex].selected = true;

            payDefault = payment[0][0].children[findIndex];
        }

        // 订单处理
        if (orderInfo.attribute === 3) {
            orderInfo.deliveryTimes = '自动发货 - 在您支付成功后,系统将立即为您发放二维码,您可以在您的订单中查看。';
        } else {
            orderInfo.deliveryTimes = orderInfo.delivery_time || '';
        }

        // 用于订单统计
        orderInfo.uid = uid;
        orderInfo.orderCount = 0;
        if (payment[3].code === 200 && payment[3].data) {
            orderInfo.orderCount = payment[3].data.count || 0;
        }
        orderInfo.isOldUser = orderInfo.orderCount > 1;
        orderInfo.ordersGoodsNums = orderInfo.order_goods.length;
        let sknList = [];
        let skuList = [];
        let goodsData = [];
        let pinyouGoodsArr = [];

        _.each(orderInfo.order_goods, function(el) {
            sknList.push(el.product_skn);
            skuList.push(el.product_sku);
            goodsData.push({
                goods_name: el.product_name,
                product_sku: el.product_sku,
                price: el.goods_price,
                number: el.buy_number
            });
            pinyouGoodsArr.push(`${el.product_skn},${el.buy_number}`);
        });
        orderInfo.sknList = sknList;
        orderInfo.skuList = skuList;
        orderInfo.goodsData = goodsData;
        orderInfo.goodsDatas = JSON.stringify(goodsData);

        // 品友统计需要的商品数据
        orderInfo.pinyouGoods = pinyouGoodsArr.join(';');

        return {
            pay: payment[0],
            order: orderInfo,
            payDefault: payDefault
        };
    });
};

/**
 * 获取支付方式的相关参数, (密钥等信息)
 * @param id
 */
const getPaymentInfo = (id) => {
    return co(function *() {
        let data = yield payApi.getPaymentInfo(id);

        return data && data.code === 200 && data.data || {};
    })();
};

/**
 * 设置订单支付银行
 * @param code
 * @param payment
 * @param bankCode
 */
const setOrderPayBank = (code, payment, bankCode) => {
    return co(function *() {
        return yield payApi.setOrderPayBank(code, payment, bankCode);
    })();
};

/**
 * 更新订单支付银行
 * @param code
 * @param payment
 * @param bankCode
 */
const updateOrderPayBank = (code, payment, bankCode) => {
    return co(function *() {
        return yield payApi.updateOrderPayBank(code, payment, bankCode);
    })();
};

/**
 * 支付确认
 * @param code
 * @param payment
 * @param uid
 */
const sendPayConfirm = (code, payment, uid) => {
    return co(function *() {
        return yield payApi.sendPayConfirm(code, payment, uid);
    })();
};

/**
 * [埋点数据公共处理]
 * @param  {[type]} orderInfo [订单详情]
 * @param  {[type]} payId [支付方式id]
 * @param  {[type]} payResult [支付成功状态]
 * @return {[type]}           [{}]
 */
const _point = (orderInfo, payId, payResult) => {
    let sknArr = [];
    let skuArr = [];

    _.each(orderInfo.order_goods, function(el) {
        sknArr.push(el.product_skn);
        skuArr.push(el.product_sku);
    });

    return {
        order_code: orderInfo.order_code,
        payResult: payResult, // 1支付成功,2支付失败
        amount: orderInfo.payment_amount,
        payStyle: payId, // 支付方式id
        proSkn: sknArr.join(','),
        proSku: skuArr.join(',')
    };
};

/**
 * [公共支付成功数据处理]
 * @param  {[type]} orderInfo [订单信息]
 * @param  {[type]} fraudData [资源位数据]
 * @param  {[type]} payId [支付方式ID]
 * @return {[type]}           [{}]
 */
const _execOrderData = (orderInfo, fraudData, payId) => {
    let fraud = _.result(_.find(fraudData, {template_name: 'text'}), 'data') || {};

    return {
        paymentTypes: (orderInfo.payment_type === 1 ? '在线支付' : '货到付款'),
        multiPackage: orderInfo.is_multi_package === 'Y',
        ordersUrl: helpers.urlFormat('/home/orders'),
        vipUrl: helpers.urlFormat('/help/detail', {id: 9}),
        returnGoodsUrl: helpers.urlFormat('/help/detail', {id: 49}),
        yohoCoinUrl: helpers.urlFormat('/help/detail', {id: 105, contId: 233}),
        detailUrl: helpers.urlFormat('/home/orders/detail', {
            orderCode: orderInfo.order_code
        }),
        packageTitle: orderInfo.package_title || '商品需要品牌商调货',
        fraudTip: fraud.text || '',
        criteo: {// criteo统计代码有关数据
            order_code: orderInfo.order_code,
            items: orderInfo.order_goods
        },
        point: _point(orderInfo, payId, 1)
    };
};

/**
 * 支付成功,前端回调时,处理订单信息
 * @param payResult
 * @param uid
 */
const procOrderData = (payResult, uid, payId) => {
    return co(function *() {
        let orderCode = payResult.orderCode;
        let result = {code: 400, message: ''};

        if (!orderCode) {
            result.message = '订单不存在';
            return result;
        }

        return Promise.all([orderDetail(uid, orderCode), payApi.getResourceData(FRAUD_CONTENT_CODE)]).then(procData => {
            let orderInfo = procData[0].code === 200 && procData[0].data;

            if (!orderInfo) {
                result.message = '订单不存在!';
                return result;
            }

            if (orderInfo.is_cancel === 'Y') {
                logger.warn('front pay success but order is cancel.', {payResult: payResult, order: orderInfo});
                return {code: 417, message: '支付成功,但订单已取消,需联系客服!', point: _point(orderInfo, payId, 2)};
            }

            if (orderInfo.payment_status === 'N') {
                logger.warn('front pay success but may be notify fail');
            }

            if (_.round(parseFloat(orderInfo.payment_amount), 2) !== _.round(parseFloat(payResult.totalFee), 2)) {
                logger.warn('front pay success but the amount is not same.', {payResult: payResult, order: orderInfo});
                return {
                    code: 415,
                    message: '支付金额与订单金额不一致,订单状态更新失败!',
                    point: _point(orderInfo, payId, 2)
                };
            }

            return {
                code: 200,
                message: '支付成功,请等待发货',
                data: {
                    order: Object.assign(
                        orderInfo,
                        _execOrderData(orderInfo, procData[1] && procData[1].data || {}, payId)
                    )
                }
            };

        });
    })();
};

/**
 * [0元订单或者货到付款-成功处理]
 * @param  {[type]} orderInfo [订单详情]
 * @param  {[type]} payId [支付方式ID]
 * @return {[type]}           [{}]
 */
const deliveryData = (orderInfo, payId) => {
    return payApi.getResourceData(FRAUD_CONTENT_CODE).then(procData => {
        return Object.assign(orderInfo, _execOrderData(orderInfo, procData && procData.data || {}, payId));
    });
};

module.exports = {
    orderDetail,
    getPayInfo,
    getPaymentInfo,
    getBankByOrder,
    setOrderPayBank,
    updateOrderPayBank,
    sendPayConfirm,
    procOrderData,
    updateOrderPayment,
    payments,
    deliveryData
};