先吐为敬!
最近心血来潮研究nodejs如何完成微信支付功能,结果网上一搜索,一大堆“代码拷贝党”、“留一手”、“缺斤少两”、“不说人话”、“自己都没跑通还出来发blog”、“各种缺少依赖包”、“各种注释都没有”、“自己都不知道在写什么”的程序大神纷纷为了增加自己博客一个帖子的名额而发布了各种千奇百怪的文章,强哥饱受煎熬,浪费了流量和时间居然没有一个教程能够跑通
叔可忍婶不可忍,我必须要发布一篇真正能够用nodejs跑通微信支付功能的干货文章,由于时间关系,错别字大家就谅解,反正程序能够跑通,跑不通你来找我!!!
注意,前情提示:
本代码基于《Node.js(nodejs)对本地JSON文件进行增、删、改、查操作(轻车熟路)》
传送门https://blog.csdn.net/qq_37860634/article/details/103781523
首先要去package.json加入依赖包
"dependencies": {
"crypto": "^1.0.1",
"express": "^4.16.3",
"request": "^2.85.0",
"xmlreader": "^0.2.3"
},
在/api/文件夹下面创建wxpay.js
代码内容
/**做微信支付开发之前一定要修改的配置,各位看官看清楚了◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆*/
const appid = "wx你挚爱的强哥";//公众号开发者ID[必填,公众号后台去拿https://mp.weixin.qq.com/ 开发-基本配置-公众号开发信息-开发者ID(AppID)]
const appsecret = "a071e61你挚爱的强哥9efc7a";//公众号开发者密码[必填,公众号后台去拿https://mp.weixin.qq.com/ 开发-基本配置-公众号开发信息-开发者密码(AppSecret)]
const mchid = "16你挚爱的强哥3";//微信商户号[必填,微信商户平台去拿https://pay.weixin.qq.com/ 个人信息-账号信息-登录账号]
const mchkey = "你挚爱的强哥32位秘钥";//微信支付安全密钥[必填, 在微信商户平台-账户中心-API安全-API密钥-安装操作证书(首次设置才需要)-设置API密钥(注意这个密钥自己保存好勿外传!!!)]
const notify_url = "http://你挚爱的强哥.com";//异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
/**◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆*/
//下面的代码几乎不用修改----------------------------------------------------------------
//如果运行过程提示你“商户号该产品权限未开通,请前往商户平台>产品中心检查后重试”,请你去这里https://pay.weixin.qq.com/index.php/public/product/detail?pid=32&productType=0开通微信H5支付(这个一般审核要几个工作日,如果你的项目很急,建议早点来开通审核)
const $g = global.SG.$g, fs = global.SG.fs, router = global.SG.router, request = global.SG.request, xmlreader = global.SG.xmlreader;
module.exports = global.SG.router;
/*微信支付干货,微信统一下单帮助文档
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1*/
//强哥为你量身打造的微信支付方法工具包
let sgWxpay = {
signType: "MD5",//签名类型,默认为MD5,支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致
raw(args) {
let keys = Object.keys(args);
keys = keys.sort();
let newArgs = {};
keys.forEach(key => {
newArgs[key] = args[key];
});
let string = "";
for (let k in newArgs) {
string += "&" + k + "=" + newArgs[k];
}
string = string.substr(1);
return string;
},
//把金额转为分
getmoney(money) {
return parseFloat(money) * 100;
},
// 随机字符串产生函数
createNonceStr() {
return Math.random().toString(36).substr(2, 15);
},
// 时间戳产生函数
createTimeStamp() {
return parseInt(new Date().getTime() / 1000) + "";
},
//签名加密算法
paysignjsapi(appid, body, mch_id, nonce_str, notify_url, out_trade_no, spbill_create_ip, total_fee, trade_type, mchkey) {
let ret = {
appid: appid,
mch_id: mch_id,
nonce_str: nonce_str,
body: body,
notify_url: notify_url,
out_trade_no: out_trade_no,
spbill_create_ip: spbill_create_ip,
total_fee: total_fee,
trade_type: trade_type
};
let string = this.raw(ret);
let key = mchkey;
string = string + "&key=" + key;
return global.SG.crypto.createHash(this.signType).update(string, "utf8").digest("hex").toUpperCase();
},
//签名加密算法,第二次的签名
paysignjsapifinal(appid, mch_id, prepayid, noncestr, timestamp, mchkey) {
let ret = {
appid: appid,
partnerid: mch_id,
prepayid: prepayid,
package: "Sign=WXPay",
noncestr: noncestr,
timestamp: timestamp
};
let string = this.raw(ret), key = mchkey;
string = string + "&key=" + key;
return global.SG.crypto.createHash(this.signType).update(string, "utf8").digest("hex").toUpperCase();
},
getXMLNodeValue(xml) {
// let tmp = xml.split("<"+node_name+">");
// console.log('tmp',tmp);
// let _tmp = tmp[1].split(""+node_name+">");
// console.log('_tmp',_tmp);
// return _tmp[0];
xmlreader.read(xml, (errors, res) => {
if (null !== errors) {
console.log(errors);
return;
}
console.log("长度===", res.xml.prepay_id.text().length);
let prepay_id = res.xml.prepay_id.text();
console.log("解析后的prepay_id==", prepay_id);
return prepay_id;
});
}
};
//微信支付(all方法支持POST、GET、PUT、PATCH、DELETE传参方式)
router.all("/demo/wx/wxpay", (req, res) => {
//首先拿到前端传过来的参数
let out_trade_no = req.body.out_trade_no || req.query.out_trade_no;//商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|* 且在同一个商户号下唯一。详见商户订单号
let body = req.body.body || req.query.body;//商品简单描述,该字段请按照规范传递,具体请见参数规定https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_2
let total_fee = req.body.total_fee || req.query.total_fee;//准备付款多少钱呀大哥?金额单位分(但是你的前端请传元为单位,因为这里强哥帮你转成了分)
console.log($g.date.nowtime() + "\n", `商户系统内部订单号:${out_trade_no}\n商品描述:${body}\n标价金额:${total_fee}\n${appid}\n${appsecret}\n${mchid}\n${mchkey}`);
let msg = null;
out_trade_no || (msg = "强哥对你说:你为什么不提供商品订单号?");
body || (msg = "强哥对你说:你为什么不提供商品描述?");
total_fee || (msg = "强哥对你说:你为什么不提供商品订单号?");
if (msg) return $g.json.res(req, res, msg, req.body || req.query, false);
//首先生成签名sign
let mch_id = mchid;
let nonce_str = sgWxpay.createNonceStr();
let timestamp = sgWxpay.createTimeStamp();
total_fee = sgWxpay.getmoney(total_fee);//强哥为你转换为分
let spbill_create_ip = req.connection.remoteAddress;
let trade_type = "APP";
let sign = sgWxpay.paysignjsapi(appid, body, mch_id, nonce_str, notify_url, out_trade_no, spbill_create_ip, total_fee, trade_type, mchkey);
// console.log("sign:", sign);
//组装xml数据
body = "";
body += "" + appid + " ";//appid
body += "";
body += "" + mch_id + " ";//商户号
body += "" + nonce_str + " "; //随机字符串,不长于32位。
body += "" + notify_url + " ";
body += "" + out_trade_no + " ";
body += "" + spbill_create_ip + " ";
body += "" + total_fee + " ";
body += "" + trade_type + " ";
body += "" + sign + " ";
body += " ";
const url = "https://api.mch.weixin.qq.com/pay/unifiedorder";//下单地址
// const url = "https://api2.mch.weixin.qq.com/pay/unifiedorder";//冗灾备用域名,正常使用主域名调用,备域名需有流量,保证业务能实时切换。当域名出现请求超时、读写超时,自动换备域名重试。
request.post({url, body}, (err, r, body) => {
if (!err && r.statusCode == 200) {
console.log("body:", body);
xmlreader.read(body.toString("utf-8"), (err, r) => {
if (err) return $g.json.res(req, res, "微信下单报错", err, false);
let prepay_id = r.xml.prepay_id;
if (!prepay_id) return $g.json.res(req, res, r.xml.return_msg.text(), r.xml, false);
prepay_id = prepay_id.prepay_id.text();
console.log("解析后的prepay_id", prepay_id);
let finalsign = sgWxpay.paysignjsapifinal(appid, mch_id, prepay_id, nonce_str, timestamp, mchkey);//将预支付订单和其他信息一起签名后返回给前端
$g.json.res(req, res, "微信下单成功", {
// mchid: mchid,//商户ID(唤起支付貌似不需要这个)
// prepayId: prepay_id,
appId: appid,//商户注册具有支付权限的公众号成功后即可获得
timeStamp: timestamp,//当前的时间,其他详见时间戳规则https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_2
nonceStr: nonce_str,//随机字符串,不长于32位。推荐随机数生成算法https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
package: "prepay_id=sgWxPay",//统一下单接口返回的prepay_id参数值,提交格式如:prepay_id=***
signType: sgWxpay.signType,//签名类型,默认为MD5,支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致
paySign: finalsign//签名,详见签名生成算法https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
}, true);
});
}
});
});
在index.js最后一行加入
app.use(API_PATH, require(`.${API_PATH}/demo/wxpay`));//微信支付
运行
node index
打完收工!