wechat.js 5.69 KB
/*
 * @Author: Targaryen
 * @Date: 2017-01-03 17:42:41
 * @Last Modified by: Targaryen
 * @Last Modified time: 2017-03-01 16:38:39
 */

'use strict';
const config = global.yoho.config;
const WxPayConfig = config.WxPayConfig;
const _ = require('lodash');
const logger = global.yoho.logger;
const rp = require('request-promise');
const Promise = require('bluebird');
const co = Promise.coroutine;
const sign = require('./sign');
const md5 = require('md5');
const moment = require('moment');
const common = require('./common');

/**
 * 微信支付相关工具类
 */
const tools = {

    /**
     * 拼接签名字符串
     */
    toUrlParams(urlObj) {
        let buff = '';

        _.forEach(urlObj, (value, key) => {
            if (key !== 'sign') {
                buff += key + '=' + value + '&';
            }
        });

        buff = _.trimEnd(buff, '&');

        return buff;
    },

    /**
     * 构造获取code的url连接
     */
    createOauthUrlForCode(redirectUrl) {
        let urlObj = {
            appid: WxPayConfig.appId,
            redirect_uri: redirectUrl,
            response_type: 'code',
            scope: 'snsapi_base',
            state: 'STATE#wechat_redirect',
        };

        let bizString = tools.toUrlParams(urlObj);

        return 'https://open.weixin.qq.com/connect/oauth2/authorize?' + bizString;
    },

    /**
     * 构造获取open和access_toke的url地址
     */
    createOauthUrlForOpenid(code) {
        let urlObj = {
            appid: WxPayConfig.appId,
            secret: WxPayConfig.appSecret,
            code: code,
            grant_type: 'authorization_code'
        };

        let bizString = tools.toUrlParams(urlObj);

        return 'https://api.weixin.qq.com/sns/oauth2/access_token?' + bizString;
    },

    /**
     * 通过code从工作平台获取openid机器access_token
     */
    getOpenidFromMp(code) {
        let uri = tools.createOauthUrlForOpenid(code);

        let rpOption = {
            method: 'POST',
            uri: uri,
            body: {
                some: 'payload'
            },
            json: true
        };

        return rp(rpOption).then(resultFromWx => {
            return resultFromWx && resultFromWx.openid;
        }).catch(err => {
            logger.error(err);
        });
    },

    /**
     * 微信统一下单接口
     */
    unifiedOrder(params) {
        let unifiedParams = {
            appid: WxPayConfig.appId,
            mch_id: WxPayConfig.mchId,
            notify_url: WxPayConfig.notifyUrl,
            device_info: 'WEB',
            nonce_str: common.nonceStr(),
            body: '有货订单号:' + params.orderCode,
            out_trade_no: 'YOHOBuy_' + params.orderCode,
            total_fee: params.totalFee * 100,
            trade_type: 'JSAPI',
            time_start: moment().format('YYYYMMDDHHmmss'),
            time_expire: moment().add(2, 'hours').format('YYYYMMDDHHmmss'),
            spbill_create_ip: params.ip,
            openid: params.openId,
            sign_type: 'MD5'
        };

        let signStr = md5(sign.raw(unifiedParams) + '&key=' + WxPayConfig.key).toUpperCase();

        _.assign(unifiedParams, { sign: signStr });

        let xml = common.toXml(unifiedParams);

        let xmlParams = {
            method: 'POST',
            uri: 'https://api.mch.weixin.qq.com/pay/unifiedorder',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            body: xml,
            timeout: 1000
        };

        return co(function* () {
            let xmlResult = yield rp(xmlParams);
            let jsonResult = yield common.xml2Obj(xmlResult);

            return jsonResult && jsonResult.xml;
        })();
    }
};

// TODO 微信支付
const Wechat = {
    /**
     * 支付中心微信支付相关处理
     */
    getOpenid(code, originalUrl) {
        if (!code) {
            let isProduction = process.env.NODE_ENV === 'production';
            let baseUrl = (isProduction ? 'https://m.yohobuy.com' : 'http://m.yohobuy.com') + originalUrl;
            let redirectUrl = tools.createOauthUrlForCode(baseUrl);

            logger.info('payCenter: wechat pay no code');
            return Promise.resolve({ redirectUrl: redirectUrl });
        } else {
            return tools.getOpenidFromMp(code).then(openid => {
                return { openid: openid };
            });
        }
    },

    /**
     * 异步拉起微信支付相关处理
     */
    pay(user, order, openId, ip) {

        return co(function* () {
            let unifiedOrderResult = yield tools.unifiedOrder({
                orderCode: order.order_code,
                totalFee: parseFloat(order.payment_amount),
                openId: openId,
                ip: ip
            });

            if (unifiedOrderResult && unifiedOrderResult.appid && unifiedOrderResult.prepay_id) {
                let nonceStr = common.nonceStr();

                let resParams = {
                    appId: unifiedOrderResult.appid,
                    timeStamp: parseInt(Date.parse(new Date()) / 1000, 10) + '',
                    nonceStr: nonceStr,
                    package: 'prepay_id=' + unifiedOrderResult.prepay_id,
                    signType: 'MD5',
                };

                let paySign = md5(sign.raw(resParams) + '&key=' + WxPayConfig.key).toUpperCase();

                _.assign(resParams, { paySign: paySign });
                return {
                    code: 200,
                    data: {
                        jsApiParameters: resParams
                    }
                };
            } else {
                logger.info('payCenter: wechat pay no unifiedOrderResult');
                return {};
            }

        })();
    }
};

module.exports = Wechat;