node 微信支付
基于node 实现微信支付功能
需要了解的网站:微信支付
流程图:
1.
1.我的路由:
const Koa = require('koa')
const app = new Koa()
const path = require('path')
const route = require('koa-route');
const static = require('koa-static');
const keyBody = require('koa-body') // routes
const { oauth } = require('./routes/accredit/oauth');
const { token } = require('./routes/accredit/token');
const { order } = require('./routes/order/order');
const { payComplete } = require('./routes/order/payComplete');
const rootPath = path.join(__dirname + '/View')
const _static = static(rootPath)
// 中间件
const logger = async(ctx, next) => {
const rt_start = Date.now()
await next()
const rt_end = Date.now()
ctx.set('X-Response-Time', `${rt_end - rt_start}ms`);
console.log(ctx.request.method, ctx.url, `${rt_end - rt_start}ms`)
} app.use(_static) // 静态资源
app.use(keyBody()) // req body数据获取 (非参数序列化)
app.use(logger) // 日志 // page route
app.use(route.get('/oauth', oauth)); //授权
app.use(route.get('/token', token)); //获取openid
app.use(route.get('/order', order)); //下订单 1
app.use(route.post('/payComplete', payComplete)); //支付成功回复通知 3 app.listen(8088, (err) => {
if (err) { console.error(err) }
console.log('Listening At:', 8088)
})
2. 通过访问 /order (微信支付) 下单
var request = require('request');
var wxpay = require('./wxpay');
var config = require('./../config');
var axios = require('axios')
/* 订单 */
const order = async(ctx, next) => {
const { request: req, response: res } = ctx
// 发送 统一下单参数
var orderInfo = {
attach: req.query.attach, // 深圳分店 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。
body: req.query.num, //腾讯充值中心-QQ会员充值 商品简单描述, 该字段请按照规范传递,具体请见参数规定
mch_id: config.mch_id, //微信支付分配的商户号
openid: req.query.openid, //trade_type=JSAPI时(即公众号支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识。
total_fee: req.query.money, //payFee, 订单总金额,单位为分,详见支付金额
notify_url: config.notify_url, //异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
out_trade_no: req.query.OrderNum, //商户订单号 out_trade_no
ip: '127.0.0.1', // 127.0.0.1, spbill_create_ip 终端IP APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP
};
//存储订单//
// 参数成功回调
var result = await wxpay.order(orderInfo).then(function(data) {
console.log('支付成功 返回下单参数', JSON.stringify(data));
// 成功返回 下单参数
return JSON.stringify(data);
});
ctx.type = 'json';
ctx.body = result;
};
module.exports = { order };
注意:这里我存储订单是为了微信支付成功后获取支付时的订单金额,以便判断订单的安全。
1.1 下单成功后(前端页面调用)
//弹框确认支付
jsApiCall: function() {
var self = this;
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId": this.user.appId, //公众号名称,由商户传入
"timeStamp": this.user.timeStamp, //时间戳,自1970年以来的秒数
"nonceStr": this.user.nonceStr, //随机串
"package": this.user.package,
"signType": this.user.signType, //微信签名方式:
"paySign": this.user.paySign //微信签名
},
function(res) { //判断支付返回的参数是否支付成功并跳转
// WeixinJSBridge.log(res.err_msg);//alert(res.err_code+res.err_desc+res.err_msg);
if (res.err_msg == "get_brand_wcpay_request:ok") {
console.log('res', res);
} else {
console.log('err', res);
} // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
}
);
},
callpay: function() {
if (typeof WeixinJSBridge == "undefined") {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
}
} else {
this.jsApiCall();
}
},
2.1 wxpay.js
var express = require('express');
var request = require('request');
var Q = require("q");
var crypto = require('crypto');
var ejs = require('ejs');
var fs = require('fs');
// 需要的参数设置 自行定义
var config = require("./../config");
var router = express.Router();
var axios = require('axios') /* 微信支付 */
var key = config.baibu.key; //此处为申请微信支付的API密码
var APPID = config.AppID;
var WxPay = {
//解析XML
getXMLNodeValue: function(node_name, xml) {
var tmp = xml.split("<" + node_name + ">");
var _tmp = tmp[1].split("</" + node_name + ">");
return _tmp[0];
},
//签名时候的参数不需要转换为小写的
raw: function(args) {
var keys = Object.keys(args);
keys = keys.sort()
var newArgs = {};
keys.forEach(function(key) {
newArgs[key] = args[key];
});
var string = '';
for (var k in newArgs) {
string += '&' + k + '=' + newArgs[k];
}
string = string.substr(1);
return string;
},
//签名
paysignjs: function(appid, nonceStr, package, signType, timeStamp) {
var ret = {
appId: appid,
nonceStr: nonceStr,
package: package,
signType: signType,
timeStamp: timeStamp
};
var string = this.raw(ret);
string = string + '&key=' + key;
var sign = crypto.createHash('md5').update(string, 'utf8').digest('hex');
return sign.toUpperCase();
},
//签名加密算法
paysignjsapi: function(appid, attach, body, mch_id, nonce_str, notify_url, openid, out_trade_no, spbill_create_ip, total_fee, trade_type) {
var ret = {
appid: appid,
attach: attach,
body: body,
mch_id: mch_id,
nonce_str: nonce_str,
notify_url: notify_url,
openid: openid,
out_trade_no: out_trade_no,
spbill_create_ip: spbill_create_ip,
total_fee: total_fee,
trade_type: trade_type
};
var string = this.raw(ret);
string = string + '&key=' + key; //key为在微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
var crypto = require('crypto');
var sign = crypto.createHash('md5').update(string, 'utf8').digest('hex');
return sign.toUpperCase();
},
// 随机字符串产生函数
createNonceStr: function() {
return Math.random().toString(36).substr(2, 15);
},
// 时间戳产生函数
createTimeStamp: function() {
return parseInt(new Date().getTime() / 1000) + '';
},
// 此处的attach不能为空值 否则微信提示签名错误
order: function(_order) {
var deferred = Q.defer();
var appid = APPID;
var nonce_str = this.createNonceStr();
var timeStamp = this.createTimeStamp();
var url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
var formData = "<xml>";
formData += "<appid>" + appid + "</appid>"; //appid
formData += "<attach>" + _order.attach + "</attach>"; //附加数据
formData += "<body>" + _order.body + "</body>";
formData += "<mch_id>" + _order.mch_id + "</mch_id>"; //商户号
formData += "<nonce_str>" + nonce_str + "</nonce_str>"; //随机字符串,不长于32位。
formData += "<notify_url>" + _order.notify_url + "</notify_url>";
formData += "<openid>" + _order.openid + "</openid>";
formData += "<out_trade_no>" + _order.out_trade_no + "</out_trade_no>"; // 建议根据当前系统时间加随机序列来生成订单号
formData += "<spbill_create_ip>" + _order.ip + "</spbill_create_ip>";
formData += "<total_fee>" + _order.total_fee + "</total_fee>";
formData += "<trade_type>JSAPI</trade_type>"; //交易类型
formData += "<sign>" + this.paysignjsapi(appid, _order.attach, _order.body, _order.mch_id, nonce_str, _order.notify_url, _order.openid, _order.out_trade_no, _order.ip, _order.total_fee, 'JSAPI') + "</sign>";
formData += "</xml>";
var self = this;
request({
url: url,
method: 'POST',
body: formData
}, function(err, response, body) {
if (!err && response.statusCode == 200) {
console.log('11支付成功', body);
var prepay_id = self.getXMLNodeValue('prepay_id', body.toString("utf-8"));
var tmp = prepay_id.split('[');
var tmp1 = tmp[2].split(']');
console.log('prepay_id', tmp1[0]);
//签名
var _paySignjs = self.paysignjs(appid, nonce_str, 'prepay_id=' + tmp1[0], 'MD5', timeStamp);
var args = {
appId: appid,
timeStamp: timeStamp,
nonceStr: nonce_str,
signType: "MD5",
package: 'prepay_id=' + tmp1[0],
paySign: _paySignjs
};
console.log('11.1', args);
deferred.resolve(args);
} else {
console.log('12支付失败', body);
}
});
return deferred.promise;
},
// 支付成功 返回验证 签名
MakeSign: function(MaSign) {
var ret = {
appid: MaSign.appid,
attach: MaSign.attach,
bank_type: MaSign.bank_type,
cash_fee: MaSign.cash_fee,
fee_type: MaSign.fee_type,
is_subscribe: MaSign.is_subscribe,
mch_id: MaSign.mch_id,
nonce_str: MaSign.nonce_str,
openid: MaSign.openid,
out_trade_no: MaSign.out_trade_no,
result_code: MaSign.result_code,
return_code: MaSign.return_code,
time_end: MaSign.time_end,
total_fee: MaSign.total_fee,
trade_type: MaSign.trade_type,
transaction_id: MaSign.transaction_id,
};
var string = this.raw(ret);
string = string + '&key=' + key; //key为在微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
var crypto = require('crypto');
var sign = crypto.createHash('md5').update(string, 'utf8').digest('hex');
return sign.toUpperCase();
},
//支付回调通知
notify: function(obj) {
var output = "";
if (obj.return_code == "SUCCESS") {
var reply = {
return_code: "SUCCESS",
return_msg: "OK"
};
output = "<xml><return_code><![CDATA[" + reply.return_code + "]]></return_code><return_msg><![CDATA[" + reply.return_msg + "]]></return_msg></xml>";
} else {
var reply = {
return_code: "FAIL",
return_msg: "FAIL"
};
output = "<xml><return_code><![CDATA[" + reply.return_code + "]]></return_code><return_msg><![CDATA[" + reply.return_msg + "]]></return_msg></xml>";
}
return output;
},
};
module.exports = WxPay;
3. 支付成功通知
var request = require('request');
var config = require("./../config"); // 需要的参数设置 自行定义
var WxPay = require("./wxpay");
// 支付成功回调接口
const payComplete = async(ctx, next) => {
const { request: req, response: res } = ctx
let obj = await postData(ctx);
//签名验证,并校验返回的订单金额是否与商户侧的订单金额一致
var MaSign = WxPay.MakeSign(obj); //生成验签签名
var fee = await WxPay.GetOrderInfo(obj).then(function(res) {
if (res.status === 200) {
var info = res.data;
if (info.code === '1') {
return info.data[0];
}
}
}).catch(function(err) {
console.log('err', err);
});
var Results = '';
if (MaSign === obj.sign && fee.total_fee == obj.total_fee) { //签名验证
// console.log('MaSign,obj.sign', MaSign, obj.sign);
// console.log('fee', fee.total_fee, obj.total_fee);
Results = WxPay.notify(obj); //判断微信返回 -->通知结果 并 -->返回通知给微信
}
ctx.body = Results;
}; function postData(ctx) {
return new Promise((resolve, reject) => {
try {
var _da = '';
ctx.req.on('data', (data) => {
/*微信服务器传过来的是xml格式的,是buffer类型,因为js本身只有字符串数据类型,所以需要通过toString把xml转换为字符串*/
_da += data.toString("utf-8");
})
ctx.req.on('end', () => {
console.log("end: " + _da);
let postdata = parser(_da);
resolve(postdata)
})
} catch (err) {
reject(err)
}
})
} function parser(_da) {
var xml = _da;
xml = xml.slice('<xml>'.length, xml.indexOf('</xml>'));
var tag = xml.match(/<\w+>/g);
var len = tag.length;
var obj = new Object(); //将微信 支付成功 返回的 xml 数据处理为json格式
for (var i = 0; i < len; i++) {
var node_name = tag[i].replace(/\<|\>/g, '');
var tmp = xml.split("<" + node_name + ">");
var _tmp = tmp[1].split("</" + node_name + ">");
var result = _tmp[0];
if (result.match(/^(?!.*CDATA)/)) {
obj[node_name] = result;
} else {
obj[node_name] = result.split('[')[2].split(']')[0];
}
}
return obj;
}
module.exports = { payComplete };
在这里再次感谢在人生路上帮助我的人!谢谢你们。
如果以上代码对您有用,欢迎打赏!如有错误的地方也请您留言指出。
(支付宝) (微信)
node 微信支付的更多相关文章
- url带#号,微信支付那些坑
现在前端很多框架的前端路由都带#号,主要为了做到无刷新跳转页面. 在微信公众号做微信支付时,配置的支付路径比如是http://www.eee.com/#/order,在调微信支付的方法时错误信息是'U ...
- android微信支付总结+自己搭建服务器
1.前期注册操作 1-1:微信开发平台:https://open.weixin.qq.com/ 1-2:创建移动应用 签名获取: 1.将自己的apk签名打包,运行到手机上. 2.将微信支付的签名工具, ...
- 微信支付 APP 支付方式的服务器端处理程序
对于微信的APP的支付,客户服务说只能通过微信开放平台申请.后来在公众帐号确实发现了证据: 微信支付在申请的时候就比较严(麻烦),对服务类的一些支付,本来商品就是虚拟的,所以需要将商品描述的比较详细 ...
- java之微信支付通知
微信支付,是现在大多数平台都需要接入的一个支付方式,没办法,谁让现在的用户都习惯了这种消费方式呢 我今天只说说微信支付通知,我们后台怎么接收通知,并把我们的订单的状态改为已支付, 至于为什么不说支付的 ...
- ThinkPHP5.0 实现 app微信支付功能
相对于之前随笔写的<ThinkPHP5.0实现app支付宝支付功能>来说,php对接app微信支付功能就相对简单的多了,最近有加我的朋友问到app微信支付,所以我把app微信支付的demo ...
- DELPHI微信支付代码
DELPHI微信支付代码 不管是微信支付还是支付宝支付, 3个最棘手的问题是:1,如何生成签名2,支付请求如何提交3, 如何验证签名 下面就围绕这二个问题来讲. 我使用的是XE3. 先看微信支付: ...
- 移动APP 微信支付完整过程(wxPay 方案一)
apicloud.weixinpay官方提供了两种方案. 本模块封装了两套支付方案: 方案一:开发者通过 getOrderId.payOrder 自己处理签名过程(微信开放平台建议把 getOrder ...
- 微信小程序微信支付的一些坑
使用的是Node.js作为后端 统一下单: appid:这里的appid是调起微信支付的appid mch_id:商户号,需要注意的是商户号要与appid对应 nonce_str:Math.rando ...
- 微信支付-公众号支付H5调用支付详解
微信公众号支付 最近项目需要微信支付,然后看了下微信公众号支付,,虽然不难,但是细节还是需要注意的,用了大半天时间写了个demo,并且完整的测试了一下支付流程,下面分享一下微信公众号支付的经验. 一. ...
随机推荐
- navisworks卸载/完美解决安装失败/如何彻底卸载清除干净navisworks各种残留注册表和文件的方法
在卸载navisworks重装navisworks时发现安装失败,提示是已安装navisworks或安装失败.这是因为上一次卸载navisworks没有清理干净,系统会误认为已经安装naviswork ...
- DjangoModels
传智博客的python的笔记 数据库配置 ORM简介 MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换 ...
- 吴裕雄--天生自然HTML学习笔记:HTML 链接
HTML 链接 HTML 使用超级链接与网络上的另一个文档相连.几乎可以在所有的网页中找到链接.点击链接可以从一张页面跳转到另一张页面. HTML 超链接(链接) HTML使用标签 <a> ...
- 吴裕雄--天生自然 R语言开发学习:图形初阶(续一)
# ----------------------------------------------------# # R in Action (2nd ed): Chapter 3 # # Gettin ...
- Java POI导出Excel不弹框选择下载路径(下载文件不选择下载路径,默认) Chrome
在Chrome浏览器中,Java导出Excel文件时,浏览器弹出提示框,需要选择下载路径 在Chrome中的高级设置中,把“下载前询问每个文件的保存位置”去掉就解决了 DEEPLOVE(LC)
- 直播问答App乃虚火,调侃知识终不能长久盈利
随着王思聪在微博宣布"我.我乐意",一款叫"冲顶大会"的App冲到了大众面前,紧接着"芝士超人"携10亿元奖金从天而降,瞬间之内,在线答 ...
- Proto3:Arena分配指南
Arena分配是仅C++有的功能,在使用Protocol Buffer时,它可以帮助你优化你的内存使用,提高性能.在.proto文件中启用Arena分配会在生成的C++代码中添加处理Arena分配的额 ...
- Turn and Stun server · J
本文简介了Turnserver(Turn + Stun)服务器的搭建.Turnserver主要提供了stun服务,支撑NAT.防火墙穿透,turn服务器,支撑打洞失败时的数据中转.使用场景上类似于前端 ...
- Qt 隐藏标题栏 窗口移动 鼠标事件
摘要 隐藏标题栏 头文件声明鼠标移动虚函数 .cpp文件实现功能 1 setWindowFlags(Qt::FramelessWindowHint | windowFlags()); 无标题栏移动窗体 ...
- Android中Intent的各种常见作用。
Android开发之Intent.Action 1 Intent.ACTION_MAIN String: android.intent.action.MAIN 标识Activity为一个程序的开始. ...