单页面应用接入微信填坑之二(微信支付Nodejs)
2017-11-18 16:13
253 查看
先记录一下正常接入微信支付步骤
微信公众号配置:
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)
- 微信小程序微信支付接入开发
- C#开发微信门户及应用(32)--微信支付接入和API封装使用
- 微信小程序微信支付接入开发教程
- 微信小程序微信支付接入开发
- 微信小程序微信支付接入开发实例详解
- C#开发微信门户及应用(32)--微信支付接入和API封装使用
- 微信小程序微信支付接入开发教程
- 微信接入验证Nodejs版例子程序
- 微信接入验证Nodejs版例子程序
- 微信小程序如何接入微信支付
- 在微信中如何接入支付宝的提示打开浏览器样式:方法二简易方法
- WeixinJSBridge调用公众号支付,微信WeixinJSBridge.invoke发起微信支付
- cocos2dJS之接入微信公众JSSDK
- 微信支付接入流程——公众号支付
- 【友盟 社会化组件之二】集成友盟的第三方登录接口(微信、qq、新浪微博)
- <android> 第三方支付sdk接入 支付宝、微信支付
- 接入第三方现在支付之微信支付所踩坑记
- C# 给自己的网址接入微信扫描登录入口
- 在Web应用中接入微信支付的流程之极简清晰版