前提条件
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地址了,不然会报错