小程序发微信红包后端Nodejs实现

时间:2024-03-07 21:36:06

前提条件

1、有一个微信开放平台 https://open.weixin.qq.com/

2、有一个微信公众平台 https://mp.weixin.qq.com  并且开通微信支付

3、有一个微信小程序 https://developers.weixin.qq.com/miniprogram/dev/quickstart/basic/release.html 登陆地址同 2 微信公众平台

申请过程就不详细说了,准备好各种企业的资质证书,运营执照,企业版 需要对公账户打款验证 

小程序要绑定到公众号,公众号和小程序都要绑定到开放平台

说一下为什么要有这三个平台账号,原生平台app 拉起微信小程序,授权登陆以后取得登录码,授权登录官方文档 https://developers.weixin.qq.com/miniprogram/dev/api/wx.login.html

把这个登录码发送到后端,后端通过这个登录码+小程序的appid + secret 拿到用户的unionid(小程序 和 公众号 都要绑定了开放平台才会有unionid,同一开放平台下的小程序和公众号unionid相同) 

获取unionid官方文档 https://developers.weixin.qq.com/miniprogram/dev/api/code2Session.html

通过这个unionid来获取到玩家在公众号的openid,微信支付发送红包到玩家账号上就是通过这个公众号的openid

后端一定要配置一个https的服务器 不然发微信红包的时候 只会收到微信服务器返回 302 not fond

Node 环境https的配置 https://blog.csdn.net/wufaliang003/article/details/77478720

下面直接上代码了

辅助函数部分

let crypto = require("crypto");

exports.MgetMD5 = function(str){
    let hash = crypto.createHash(\'md5\');
    let md5Value = hash.update(str).digest("hex");
    return md5Value.toUpperCase()
}

exports.MgetSHA256 = function(str,key){
    let hmac = crypto.createHmac(\'sha256\',key);
    let shaValue = hmac.update(str).digest("hex");
    return shaValue.toUpperCase();
}

let charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklnmopqrstuvwxyz0123456789";
//返回随机字符串
exports.MgetRandomStr = function(length){
    if (length){
        length = Math.round(length);
    }
    else{
        length = Math.round(Math.random()*20 + 12)
    }
    let range = charSet.length;
    let result = \'\';
    let position = 0;
    for(let i = 0; i < length; i++)
    {   position = Math.floor(Math.random()*range);
        result += charSet[position];
    }
    return result;
}


//返回随机数字字符串
exports.MgetRandomNumber = function(length){
    let numbers = \'0123456789\';
    length = length ? length : 10;
    let range = numbers.length;
    let result = \'\';
    let position = 0;
    for(let i = 0; i < length; i++)
    {   position = Math.floor(Math.random()*range);
        result += numbers[position];
    }
    return result;    
}

//返回给定宽度的数字字符串
exports.MgetFixedNumber = function(number,width){
    width = width ? width : 2;
    let fixedNumber = number.toString();
    for(; fixedNumber.length < width ; ){
        fixedNumber = "0" + fixedNumber;
    }
    return fixedNumber;
}

//返回 对象属性排序后的所有键和值拼接在在一起的字符串
exports.MgetSortedParameter = function(parameterObject){
    let attributes = [];
    parameterObject = parameterObject ? parameterObject:{};
    for(let attr in parameterObject){
        attributes.push(attr);
    }

    attributes.sort();
    let paramStr = "";
    attributes.forEach(element=>{
        paramStr += element + "=" + parameterObject[element] + "&"
    });

    return paramStr;
}

发送红包部分

//构造订单号
function getOrderID(){
    let currentDate = new Date();
    let year = currentDate.getFullYear();
    
    let month = currentDate.getMonth()
    month = month >= 9 ? month + 1 : \'0\'+(month + 1);
    
    let day = currentDate.getDate()
    day = mutils.MgetFixedNumber(day);
    
    //mch_id 是微信支付分配的商户号
    let orderid = mch_id + year + month + day + mutils.MgetRandomNumber();

    return orderid;
}

//发红包
function makeHongBao(openid,res){
    //设置参数模型
    let ParamModel = {};
    ParamModel["nonce_str"] = mutils.MgetRandomStr();//随机字符串,不长于32位
    ParamModel["wxappid"] = gzh_appid;//公众账号appid
    ParamModel["mch_id"] = mch_id;//商户号 微信支付分配的商户号 申请中
    ParamModel["mch_billno"] = getOrderID();//商户订单号 字母加数字构成 长度最多28位且必须唯一
    ParamModel["send_name"] = "商户名称";//商户名称 红包发送者名称
    ParamModel["re_openid"] = "otWaQ0ztsh9p6X4ZQVMzNa17sTpQ"//公众号下 用户openid
    ParamModel["total_amount"] = 100;//付款金额,单位分
    ParamModel["total_num"] = 1;//红包发放总个数
    ParamModel["wishing"] = "感谢您使用XXX,祝您旗开得胜!";//红包祝福语 最大长度128位
    ParamModel["client_ip"] = "125.120.226.214";//调用接口的机器Ip地址
    ParamModel["act_name"] = "分享送红包";//活动名称
    ParamModel["remark"] = "分享越多送得越多";//备注信息
    ParamModel["scene_id"] = "PRODUCT_5";// 非必须   PRODUCT_6 渠道分润  红包金额大于200或者小于1元时必传 PRODUCT_1
    //ParamModel["notify_way"] ="JSAPI";//通知用户形式 ------------- 小程序红包参数?
    //ParamModel["risk_info"] = "posttime%3d123123412%26clientversion%3d234134%26mobile%3d122344545%26deviceid%3dIOS";//活动信息 非必须
    let attrStr = mutils.MgetSortedParameter(ParamModel);
    attrStr += "key=" + mch_key//key为商户平台设置的密钥key 
    ParamModel["sign"] = mutils.MgetMD5(attrStr);

    //构造POST参数xml
    let contents = \'<xml>\';
    for( let attr in ParamModel){
        contents += makeXMLNode(attr,ParamModel[attr],false);
    } 
    contents += \'</xml>\';
    //配置请求选项
    let options = {
        host:\'api.mch.weixin.qq.com\',
        port:443,
        path:\'/mmpaymkttransfers/sendredpack\',
        key:fs.readFileSync(__dirname + \'/cert/apiclient_key.pem\'),
        cert:fs.readFileSync(__dirname + \'/cert/apiclient_cert.pem\'),
        method:\'POST\'
    }

    options.agent  =  new https.Agent(options);
    //发送Post请求
    var request = https.request(options, function(response){
        response.setEncoding(\'utf8\');
        response.on(\'data\',function(data){
            console.log("data:",data);
            res.end("wait to do !");
        });
    });

    request.write(contents);
    request.end();
    request.on(\'error\',function(err){
        console.log(err)
    })
}
function makeXMLNode(nodeName,Value,bCTata){
    if (bCTata){
        return \'<\' + nodeName + \'><![CDATA[\' + Value + \']]></\' + nodeName + \'>\'
    }

    return \'<\' + nodeName + \'>\' + Value + \'</\' + nodeName + \'>\'
}

 

如何获取到公众号的openid

1、后台通过公众号的的appid 和 secret(需要自己设置) 获取到access_token 官方文档 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183

2、批量获取到关注公众号的的用户信息 注意一次最多获取一百条  官方文档 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140839

3、把前面提到的unionid 拿来依次和用户信息里面的unionid比对 如果相同说明找到正确的用户了就取它的openid ,就可以根据这个openid发红包了

写得感觉有点乱,主要是参考思路,过程比较坑 + 各种扫二维码 输验证码

node 服务器如果是在外网有固定ip地址,listen函数就不要填ip地址了,不然会报错