wechat.js 4.9 KB
/**
 * 微信公众号签名获取
 * @author: qiujun <jun.qiu@yoho.cn>
 * @date: 14/11/2017
 */


const config = global.yoho.config;
const crypto = require('crypto');
const request = require('request-promise');
const WechatModel = require('../models/wechat');
const moment = require('moment');


const sha1 = (str) => {
    const generator = crypto.createHash('sha1');

    generator.update(str);
    return generator.digest('hex');
};


// 微信 JS 接口签名校验工具 http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign

let getAccessToken = function(appId, secret, prefix) { // 获取access_token
    return request({
        url: prefix + 'token',
        qs: {
            grant_type: 'client_credential',
            appid: appId,
            secret: secret
        },
        json: true
    }).then(res => {
        return res;
    });

};

let getTicket = function(accessToken, appId, secret, prefix) { // 获取ticket
    return request({
        url: prefix + 'ticket/getticket',
        qs: {
            access_token: accessToken,
            type: 'jsapi'
        },
        json: true
    }).then(res => {
        return res;
    });
};

let calcSigature = function(ticket_res) { // 计算signature
    let appID = ticket_res.appID;
    let ticket = ticket_res.access_ticket;
    let nonceStr = Math.random().toString(36).substr(2, 15);
    let timestamp = Math.floor(Date.now() / 1000) + '';
    let url = ticket_res.url;
    let str = `jsapi_ticket=${ticket}&noncestr=${nonceStr}&timestamp=${timestamp}&url=${url}`;
    let signature = sha1(str);

    return {
        appId: appID,
        nonceStr,
        timestamp,
        url,
        signature
    };
};

let getResult = function(wechat_res) { // 向微信接口获取access_token及ticket并计算signature
    let appID = wechat_res.appID;
    let appSecret = wechat_res.appSecret;
    let prefix = wechat_res.prefix;
    let mpPrefix = wechat_res.mpPrefix;

    return getAccessToken(appID, appSecret, prefix)
        .then(result => { // 先获取accessToken
            let access_token = result.access_token;

            return getTicket(access_token, appID, appSecret, mpPrefix)
                .then(ticket_res => { // 通过accessToken获取ticket
                    return Object.assign({
                        access_token: access_token,
                        appID: appID
                    }, ticket_res);
                });
        });
};


const wechatShare = {

    getSignPackage(req, res, next) {

        const appID = config.wechat.appID;
        const appSecret = config.wechat.appSecret;
        const prefix = config.wechat.prefix;
        const mpPrefix = config.wechat.mpPrefix;
        const url = req.query.pageurl || 'http://www.yoho.cn';

        req.ctx(WechatModel).getAccessKeys()
            .then(keys_res => { // 先查询数据库中是否有access_token并且未过期

                if (keys_res.code === 200) { // code=200说明数据库中查找到数据并且未过期

                    delete keys_res.code;
                    return Object.assign({
                        appID,
                        url
                    }, keys_res);
                } else if (keys_res.code === 201) {
                    // code=201说明数据库中没有数据,或者数据已过期,根据返回的type判断是添加还是更新

                    delete keys_res.code;
                    keys_res = Object.assign({
                        appID,
                        appSecret,
                        prefix,
                        mpPrefix,
                        url
                    }, keys_res);

                    return getResult(keys_res).then(ticket_res => {
                        let appId = ticket_res.appID;
                        let access_token = ticket_res.access_token || '';
                        let access_ticket = ticket_res.ticket || '';
                        let expired = parseInt(ticket_res.expires_in, 10) - 100 || 0;
                        let create_time = moment().format('YYYY-MM-DD HH:mm:ss') || '';
                        let expired_time = moment().add(expired, 'seconds').format('YYYY-MM-DD HH:mm:ss') || '';

                        // 获取到ticket信息之后把相关字段保存到数据库中
                        return req.ctx(WechatModel).
                        saveAccessKeys(access_token, access_ticket, create_time, expired_time, keys_res.type)
                            .then(() => {
                                return {
                                    appID: appId,
                                    access_ticket: access_ticket,
                                    url: url
                                };
                            });
                    });
                }
            })
            .then(result => {
                let calc_res = calcSigature(result);

                res.jsonp(calc_res);
            }).catch(next);
    }
};

module.exports = wechatShare;