您的位置:首页 > 移动开发 > 微信开发

单页面应用接入微信填坑之二(微信支付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)支付没有问题,但是当在安卓手机中却无法正常支付。后来弄了很久,发现在上述:微信支付商户平台配置->支付授权目录时并没有写完全(只写了一个目录)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: