单页面应用接入微信填坑之二(微信支付Nodejs)
来源:互联网 发布:制作歌曲伴奏软件 编辑:程序博客网 时间:2024/06/06 00:25
先记录一下正常接入微信支付步骤
微信公众号配置:
1. 开通微信公众号
这里就没什么要讲的了
2.服务器配置
进入微信公众平台->开发->基本配置->服务器配置,之后填写服务器地址和令牌,并按照微信官方教程配置即可。下面是我自己的一段Node.js版本的简单服务器配置:
var http = require("http");var url = require("url");var crypto = require('crypto');var token = 'abc123'; //令牌function getQuery(name,str) { var reg = new RegExp(name+'=([^&]*)'); var matches = reg.exec(str); if (matches) { return matches[1]; } return '';}/***监听请求*/function onRequest(request,response){ var urlParams = url.parse(request.url); var result = verify(urlParams.query); response.writeHead(200,{"Content-type":"text/plain; charset=UTF-8"}); response.write(result); response.end();}function verify(query) { var signature = getQuery('signature',query); var timestamp = getQuery('timestamp',query); var nonce = getQuery('nonce',query); var echostr = getQuery('echostr',query); var arr = [token,timestamp,nonce]; arr.sort(); var tempStr = arr.join(''); var sha1 = crypto.createHash('sha1'); var resultCode = sha1.update(tempStr,'utf-8').digest('hex'); if (resultCode === signature) { return echostr; } return 'nomatch';}http.createServer(onRequest).listen(80);
3.js域名设置
进入微信公众平台->公众号设置->功能配置,之后填写业务域名、js接口安全域名和网页授权域名,这三个域名的作用设置时看官方解释即可。
4.获取开发者密码与AppId
进入微信公众平台->基本配置,设置或获取开发者密码与AppId,在获取用户user_info中将用到。
微信支付商户平台配置:
公众平台微信支付公众号支付授权目录、扫码支付回调URL配置入口已于8月1日迁移至商户平台(pay.weixin.qq.com)
1.支付授权目录配置
微信支付商户平台->产品中心->开发配置->支付配置->添加授权目录,在微信中调用支付时必须在添加的目录中,否则将无法支付。
2.设置API密钥
微信支付商户平台->账户中心->API安全->设置API密钥,将密钥记下(创建统一支付订单时需要)
开发配置:
1.通过微信授权获取用户openid
第一步:用户同意授权,获取code
访问下面的链接:
'https://open.weixin.qq.com/connect/oauth2/authorize?appid='+微信公众号AppId+'&redirect_uri='+跳转地址+'wxoauth&response_type=code&scope=snsapi_userinfo#wechat_redirect'
还记得我们在上面微信公众号配置->js域名设置,填写的网页授权域名吗?当我们使用上面的链接获取到code之后微信就会在URL query中携带着code跳转到网页授权域名。
第二步:通过code换取网页授权access_token
通过GET方式获取到ACCESS_TOKEN:
getWXToken: function(code) { let reqUrl = 'https://api.weixin.qq.com/sns/oauth2/access_token?'; let params = { appid: wxConfig.appid,//微信公众号AppId secret: wxConfig.appSecret,//微信公众号开发者密码 code: code,//上一步获取到的code grant_type: 'authorization_code' }; let options = { method: 'get', url: reqUrl + CommonUtil.json2RequestParam(params) }; return new Promise((resolve, reject) => { request(options, function(err, res, body) { if (res) { resolve(body); } else { reject(err); } }) });},
在上面的代码中用到了request库,access_token和用户openid就存在于body中.如果不需要获取用户信息,那么到这一步就可以了。
第三步:拉取用户信息(需scope为 snsapi_userinfo)
getWXUserInfo: function(AccessToken, openId) { let reqUrl = 'https://api.weixin.qq.com/sns/userinfo?'; let params = { access_token: AccessToken,//上一步获取到的AccessToken openid: openId,//上一步获取到的用户openId lang: 'zh_CN' }; let options = { method: 'get', url: reqUrl + CommonUtil.json2RequestParam(params) }; return new Promise((resolve, reject) => { request(options, function(err, res, body) { if (res) { resolve(body); } else { reject(err); } }); })},
同样的,获取到的用户信息(头像、昵称等)就存在于body中。
上述代码可查看https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842 微信官方文档
2.创建微信统一订单
//微信支付相关var fs = require('fs');var path = require('path');var wxConfig = require('../config/weixin');var util = require('./weixinUtil');var request = require('request');var md5 = require('MD5');function WXPay() { if (!(this instanceof WXPay)) { return new WXPay(arguments[0]); }; this.options = arguments[0]; this.wxpayID = { appid: wxConfig.appid, mch_id: wxConfig.mch_id };};WXPay.mix = function() { switch (arguments.length) { case 1: var obj = arguments[0]; for (var key in obj) { if (WXPay.prototype.hasOwnProperty(key)) { throw new Error('Prototype method exist. method: ' + key); } WXPay.prototype[key] = obj[key]; } break; case 2: var key = arguments[0].toString(), fn = arguments[1]; if (WXPay.prototype.hasOwnProperty(key)) { throw new Error('Prototype method exist. method: ' + key); } WXPay.prototype[key] = fn; break; }};WXPay.mix('option', function(option) { for (var k in option) { this.options[k] = option[k]; }});WXPay.mix('sign', function(param) { var str = ''; var arr = []; for (var name in param) { if (param[name] != null && param[name] != '') { arr.push(name + '=' + param[name]); } } arr.sort(); str = arr.join('&'); str = str + '&key=' + wxConfig.mch_key; return md5(str).toUpperCase();});WXPay.mix('createWCPayOrder', function(order) { order.spbill_create_ip = order.spbill_create_ip.match(/\d+.\d+.\d+.\d+/)[0];//请求Ip order.trade_type = "JSAPI"; order.nonce_str = order.nonce_str || util.generateNonceString();//随机字符串 util.mix(order, this.wxpayID);//加入公众号AppId和微信支付商户Id order.sign = this.sign(order); var self = this; return new Promise(function(resolve, reject) { self.requestUnifiedOrder(order, function(err, data) { if (err) { reject(err); } else { if (data.return_code == 'SUCCESS' && data.result_code == 'SUCCESS') { var resParam = { "appId": data.appid, //公众号名称,由商户传入 "timeStamp": Math.floor(Date.now() / 1000) + "", //时间戳,自1970年以来的秒数 "nonceStr": data.nonce_str, //随机串 "package": "prepay_id=" + data.prepay_id, "signType": "MD5" //微信签名方式: }; resParam.paySign = self.sign(resParam); resolve(resParam); } else { reject(data); } } }, function(err) { reject(err); }); });});WXPay.mix('requestUnifiedOrder', function(order, fn, errFn) { request({ url: "https://api.mch.weixin.qq.com/pay/unifiedorder", method: 'POST', body: util.buildXML(order) }, function(err, response, body) { if (err) { errFn(); } else { console.log('body:' + body); util.parseXML(body, fn); } });});exports = module.exports = WXPay;
以上代码是一个封装好的创建微信统一支付的类,使用方法如下:
var wxpay = new WeixinPay();var wxOrder = await wxpay.createWCPayOrder({ openid: ctx.session.openid,//用户openid body: '购买商品', detail: '购买商品', out_trade_no: orderDetail.code, //平台内部订单号 total_fee: parseInt(orderDetail.totalPrice*100,10),//总价格 spbill_create_ip: ctx.ip,//ip notify_url: 'http://baebae.cn/api/order/paynotify'//支付回调地址});
接下来将wxOrder返回给前端即可。
3.前端调用微信支付
wxPay: function(order, fn) { if (typeof window.WeixinJSBridge === 'undefined') { return; } window.WeixinJSBridge.invoke( 'getBrandWCPayRequest', order, function(res) { if (res.err_msg == "get_brand_wcpay_request:ok") { fn(true); } else { fn(false); } } );},
通过将上一步wxOrder传入上面的方法即可唤起微信支付,当用户支付后微信将回调上一步创建订单时传入的回调地址。
4.回调处理
payNotify: async function(ctx) { var body = ctx.request.body; if (body.return_code == 'SUCCESS' && body.result_code == 'SUCCESS') { // 支付成功处理 } var message = '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>'; ctx.body = message;},
这个方法便是回调处理方法,当请求中的return_code和result_code皆为SUCCESS表示支付成功,之后还应返回一段状态XML(即上述代码中的message)给微信表示已获取到微信提示,否则微信将以某种策略一直请求回调地址。微信官方文档: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
遇到的坑
我有两个支付页面,当我按照上面的步骤做了之后发现在我的手机中(IOS)支付没有问题,但是当在安卓手机中却无法正常支付。后来弄了很久,发现在上述:微信支付商户平台配置->支付授权目录时并没有写完全(只写了一个目录)。
- 单页面应用接入微信填坑之二(微信支付Nodejs)
- 单页面应用接入微信填坑之一(微信分享)
- unity微信支付接入总结(客户端下单)
- 微信支付趟坑之旅之单页面应用
- 在应用中接入微信支付(V3)
- 应用接入微信支付——移动支付
- nodejs 微信支付
- NodeJS微信支付
- 支付宝支付接入+微信支付
- Android支付之微信支付(二)
- iOS 之微信支付和支付宝合集(二)
- iOS接入微信支付
- 微信支付接入指南
- Android 微信支付接入
- Android 接入微信支付
- Android 接入微信支付
- java-微信支付接入
- Android微信支付接入
- C语言实验——求阶乘(循环结构)
- java中两个变量之间交换方法总结
- 法国艺术家在城堡上创作的多彩棱镜壁画
- C++类内静态成员的初始化
- c++面向对象编程——类的初步学习
- 单页面应用接入微信填坑之二(微信支付Nodejs)
- Drools7.5.0教程-目录
- 通过javascript获取本周或上(n)周下(n)周的日期
- bLue的平行四边形
- Shiro——多个Realm的配置
- [Codeforces 891C] Envy
- Help Jimmy POJ
- 上传头像_
- Java动态生成类以及动态添加属性