wechat.js 4.69 KB
/*
 * @Author: Targaryen
 * @Date: 2017-01-03 17:42:41
 * @Last Modified by: Targaryen
 * @Last Modified time: 2017-01-05 13:52:47
 */

'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 tools = {

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

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

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

        return buff;
    },

    /**
     * 生成指定长度的随机字符串
     */
    createRandomString(len) {
        let strlen = len || 32;
        let originString = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        let resultString = '';

        for (let i = 0; i < strlen; i++) {
            let position = parseInt(100 * Math.random(), 0) % 62;

            resultString += originString.slice(position - 1, position);
        }

        return resultString;
    },

    /**
     * 构造获取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,
            device_info: 'WEB',
            nonce_str: tools.createRandomString(),
            body: '有货订单号:' + params.orderCode,
            out_trade_no: 'YOHOBuy_' + params.orderCode,
            total_fee: params.totalFee,
            trade_type: 'JSAPI',
            openid: params.openId,
            sign_type: 'MD5',
        };

        let signStr = md5(sign.raw(unifiedParams) + WxPayConfig.KEY).toUpperCase();

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

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

            return Promise.resolve({ redirectUrl: redirectUrl });
        } else {
            return tools.getOpenidFromMp(code).then(openid => {
                return { openid: openid };
            });
        }
    },

    /**
     * 异步拉起微信支付相关处理
     */
    pay(user, order, openId) {
        co(function* () {
            let unifiedOrderResult = yield tools.unifiedorder({
                orderCode: order.order_code,
                totalFee: parseFloat(order.payment_amount),
                openId: openId
            });

            if (unifiedOrderResult) {
                let nonceStr = tools.createRandomString();

                return {
                    appId: unifiedOrderResult.appid,
                    timeStamp: Date.parse(new Date()),
                    nonceStr: nonceStr,
                    package: 'prepay_id=' + unifiedOrderResult.prepay_id,
                    signType: 'MD5',
                    paySign: '' // TODO 签名算法
                };
            } else {
                return {};
            }
        })();
    }
};

module.exports = Wechat;