1、H5支付和jssdk微信支付的区别
H5支付:只能在微信外部的浏览器进行支付
jssdk支付:在微信里面完成的支付
2、什么是JSSDK?
可以让微信里面的web页面有能力调用原生的api功能,这就叫jssdk(能力有限,相比微信小程序调用原生的api的能力差一些)
通过使用微信JS-SDK,网页开发者可以借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫、卡券、支付等微信特有的能力,为微信用户提供更优质的网页体验。
3、获取 appid appsecret 和商户号 商户key
微信公众号里面获取appid appsecret (找到:基本配置)
商户ID和商户Key如何获取:
商户key:微信商户平台:https://pay.weixin.qq.com
扫码登录时(选择对应商户号)
秘钥是随便填写的
官网:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115
1、配置微信JSSDK支付之前的准备工作**重要:
(一)公众号里面的配置
1、配置JS安全域名: 公众号设置->功能设置->JS接口安全域名 xxxx.com
2、支付需要配置支付域名:公众号设置->功能设置->网页授权域名 ( 支付的时候配置的)
3、配置IP白名单:基本配置->IP白名单
(二)商户平台里面配置支付目录:
① 配置JS安全域名:
注意:JS接口安全域名是前端部署服务器后的链接,而不是你node部署的服务器
点击下载文件:把文件复制到服务器项目的目录里面
做支付必须得配置它,只配置jssdk的话可以不用配置它
②配置IP白名单
想在哪里调用微信jssdk的参数,就在哪设置,比如要在118.123.14.36这台服务器里面配置,就输入服务器的ip地址
③商户平台里面配置支付目录
比如:
http://a.xxx.com/index.html http://a.xxx.com/
http://a.xxx.com/pay/index.html http://a.xxx.com/pay/
①引入js
②wx.ready里面放置对应的代码,wx.getNetworkType是获取网络
③配置config
获取签名:
第一步:获取access_token
文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183
通过下面地址获取
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx7bf3787c783116e4&secret=cca407db4d8459c907d2c59274ca6d17
// 在浏览器数据获取到的结果
{"access_token":"10__sxf1kkKOcmLVcU3kNdQg3fsBeZ1522zvacAZ1dcqRc6Cot-Y-V1Pdn3keTXmV-SwDOZ5nSa3Tcpvk0nXbVZUT8l15T0aJBX_C2WesrsZapaBK9tQ0_cnrBmExIVZZiADAEII","expires_in":7200}
哪里加入白名单,就在哪里获取
第二步:根据access_token获取ticket
https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=10__sxf1kkKOcmLVcU3kNdQg3fsBeZ1522zvacAZ1dcqRc6Cot-Y-V1Pdn3keTXmV-SwDOZ5nSa3Tcpvk0nXbVZUT8l15T0aJBX_C2WesrsZapaBK9tQ0_cnrBmExIVZZiADAEII&type=jsapi
// 在浏览器数据获取到的结果
{"errcode":0,"errmsg":"ok","ticket":"HoagFKDcsGMVCIY2vOjf9g2_I8_P118MfAFHMc-AncAVkaHekeNmVlA2LK4JEznnejfTxR2EnyrGCeFwFEmHpQ","expires_in":7200}
第三步:根据ticket获取签名
http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign
用工具获取,url是要调用jssdk的地址
点击生成签名,获取到了签名
signature : 62d5ba349c3559869981be73f28ce575c3947af8
使用官方JS-SDK的流程::
1.必须配置js接口的安全域名 找到公众号里面的设置->公众号设置->JS接口安全域名->功能设置->JS接口安全域名
2、点开下面地址配置安全域名:
https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
3、获取测试的appid appsecret
开发者工具->公众平台测试账户
获取正式的appid appsecret
基本配置->公众号开发信息
4、下载官方的JS-SDK 实例包
下载地址: 最下面https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115
配置appid appsecret
5、jsApiList: [] 加入权限
6、wx.ready(function () {})里面 使用对应的实例
nodejs
用到了wechat-api这个包生成签名
1.安装wechat-api
cnpm install wechat-api --save
2、引入var API = require('wechat-api');
3、实例化配置
var api = new API(config.wxappid,config.wxappsecret) ;
4、生成config的参数
var param = {
debug: false,
jsApiList: ['checkJsApi',
'chooseImage',
'getNetworkType',
'openLocation',
'getLocation',
'scanQRCode'
],
url: req.body.url
};
api.getJsConfig(param, function(err,result){
res.send(result);
});
手机端的调试工具:
准备工作:配置微信JSSDK支付之前的准备工作***重要(文章最上面以提)
微信支付的三大步骤:
1、获取用户openID:
①在关注者与公众号产生消息交互后,公众号可获得关注者的OpenID,对于不同公众号,同一用户的openid不同id
②用户同意授权,获取code https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx4518345b2ab754e1&redirect_uri=http://www.jxy-edu.com/result.jsp&response_type=code&scope=snsapi_userinfo#wechat_redirect
2、通过获取的code,返回当前关注者在当前服务号唯一的openId https://api.weixin.qq.com/sns/oauth2/access_token?appid=wx4518345b2ab754e1&secret=4aaf7d8e73e06714212c48cc35dd1fd8&code=0215wGRM0gyO872TmNRM0F8IRM05wGRk&grant_type=authorization_code
3、获取prepay_id 和 paySign
微信支付文档:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1
获取code和openid文档:
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
理解:
①拿到 code,②通过nodejs后台a.sayid760.com/getOpenid 方法去获取openid ③通过ajax请求wechat(生成jssdk配置)④点击支付,调用下单接口(把参数传给后台)(请求支付的时候向 微信官方 调用下单接口,微信给返回 预付信息 )(后端返回jssdk签名字段)
示例链接: (复制进草料生成二维码)
// 获取openid ( 即 accessToke ) 可以保存到localStorage
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx616683031bf5c67d&redirect_uri=http://a.sayid760.com&response_type=code&scope=snsapi_userinfo#wechat_redirect
// 或者
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx7bf3787c783116e4&redirect_uri=http://a.sayid760.com?cid=123&name=zhangsan&response_type=code&scope=snsapi_base#wechat_redirect
扫描二维码
// index.html
|- module
| |- config.js
| |- wechatPay.js
|- views
| |- index.ejs
|- app.js
// views/index.ejs
微信测试页面
// app.js
/*
用到了wechat-api这个包生成签名
1.安装wechat-api
cnpm install wechat-api --save
2、引入var API = require('wechat-api');
3、实例化配置
var api = new API(config.wxappid,config.wxappsecret) ;
4、生成config的参数
var param = {
debug: false, 调试模式
jsApiList: ['checkJsApi',
'chooseImage',
'getNetworkType',
'openLocation',
'getLocation',
'scanQRCode'
],
url: req.body.url
};
api.getJsConfig(param, function(err,result){
res.send(result);
});
*/
var config = require('./module/config');
var API = require('wechat-api');
//引入统一下单的api
var wechatPay = require('./module/wechatPay');
var express = require('express');
var bodyParser = require('body-parser');
var xmlparser = require('express-xml-bodyparser');
var request = require('request');
var api = new API(config.wxappid,config.wxappsecret)
var app = new express();
//xmlparser
app.use(xmlparser());
app.use(express.static('./public'));
//使用中间件body-parser获取post参数
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
app.set('view engine','ejs');
app.post('/wechat',function (req, res, next) {
//使用wechat-api获取JSconfig
var param = {
debug: false,
jsApiList: ['checkJsApi',
'onMenuShareTimeline',
'onMenuShareAppMessage',
'onMenuShareQQ',
'onMenuShareWeibo',
'onMenuShareQZone',
'hideMenuItems',
'showMenuItems',
'hideAllNonBaseMenuItem',
'showAllNonBaseMenuItem',
'translateVoice',
'startRecord',
'stopRecord',
'onVoiceRecordEnd',
'playVoice',
'onVoicePlayEnd',
'pauseVoice',
'stopVoice',
'uploadVoice',
'downloadVoice',
'chooseImage',
'previewImage',
'uploadImage',
'downloadImage',
'getNetworkType',
'openLocation',
'getLocation',
'hideOptionMenu',
'showOptionMenu',
'closeWindow',
'scanQRCode',
'chooseWXPay',
'openProductSpecificView',
'addCard',
'chooseCard',
'openCard'],
url: req.body.url
};
/*api.getTicket(function(err,result){
console.log(err);
console.log(result);
});*/
api.getJsConfig(param, function(err,result){
// console.log(err)
// console.log(result)
res.send(result);
});
// console.log('执行');
})
//获取openid返回客户端
app.get('/getOpenId',function(req, res){
var code=req.query.code;
var pay=new wechatPay();
//openid
pay.getAccessToken(code,function(err,data){
console.log(data);
res.json(data);
})
})
app.get('/',function (req, res) {
//使用wechat-api获取JSconfig
//使用wechat-api获取JSconfig
res.render('index');
})
app.get('/order',function (req, res) {
//使用wechat-api获取JSconfig
//使用wechat-api获取JSconfig
var openid=req.query.openid;
var pay=new wechatPay();
pay.createOrder({
openid:openid,
notify_url : 'http://a.sayid760.com/notifyUrl', //微信支付完成后的回调
out_trade_no : new Date().getTime(), //订单号
attach : '名称',
body : '购买信息',
total_fee : '1', // 此处的额度为分
spbill_create_ip : req.connection.remoteAddress.replace(/::ffff:/, ''),
}, function (error, responseData) {
if(error){
console.log(error);
}
res.json(responseData); /*签名字段*/
});
})
app.post('/notifyUrl',function (req, res) {
//使用wechat-api获取JSconfig
//接受xml
var pay=new wechatPay();
var notifyObj=req.body.xml;
var signObj={};
for(var attr in notifyObj){
if(attr !='sign'){
signObj[attr]=notifyObj[attr][0]
}
}
console.log(pay.getSign(signObj));
console.log('--------------------------');
console.log(req.body.xml.sign[0]);
})
app.listen(8002,function(){
console.log('port 8003 is running!');
});
// module/config.js
var config={
mch_id: '1535063641',
wxappid: "wx616683031bf5c67d",
wxappsecret:'f506525621307c15c0df13c94363a848',
wxpaykey: 'c23fdgas768fdhASdsad121hgkf684gd'
}
module.exports=config;
// module/wechatPay.js
var url = require('url');
var queryString = require('querystring');
var crypto = require('crypto');
var request = require('request');
var xml2jsparseString = require('xml2js').parseString;
// 引入项目的配置信息
var config = require('./config.js');
// wechat 支付类 (使用 es6 的语法)
class WechatPay { /**
* 构造函数
* @param params 传递进来的方法
*/
constructor(){
// this.userInfo = userInfo;
// console.log(this.userInfo);
}
/**
* 获取微信统一下单参数
*/
getUnifiedorderXmlParams(obj){
var body = ' ' +
''+config.wxappid+' ' +
''+obj.attach+' ' +
''+obj.body+' ' +
''+config.mch_id+' ' +
''+obj.nonce_str+' ' +
''+obj.notify_url+' ' +
''+obj.openid+' ' +
''+obj.out_trade_no+' '+
''+obj.spbill_create_ip+' ' +
''+obj.total_fee+' ' +
''+obj.trade_type+' ' +
''+obj.sign+' ' +
' ';
return body;
}
/**
* 获取微信统一下单的接口数据
*/
getPrepayId(obj){
var that = this;
// 生成统一下单接口参数
var UnifiedorderParams = {
appid : config.wxappid,
attach : obj.attach,
body : obj.body,
mch_id : config.mch_id,
nonce_str: this.createNonceStr(),
notify_url : obj.notify_url,// 微信付款后的回调地址
openid : obj.openid, //改
out_trade_no : obj.out_trade_no,//new Date().getTime(), //订单号
spbill_create_ip : obj.spbill_create_ip,
total_fee : obj.total_fee,
trade_type : 'JSAPI',
// sign : getSign(),
};
// 返回 promise 对象
return new Promise(function (resolve, reject) {
// 获取 sign 参数
UnifiedorderParams.sign = that.getSign(UnifiedorderParams);
var url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
request.post({url : url, body:JSON.stringify(that.getUnifiedorderXmlParams(UnifiedorderParams))}, function (error, response, body) {
var prepay_id = '';
if (!error && response.statusCode == 200) {
// 微信返回的数据为 xml 格式, 需要装换为 json 数据, 便于使用
xml2jsparseString(body, {async:true}, function (error, result) {
prepay_id = result.xml.prepay_id[0];
// 放回数组的第一个元素
resolve(prepay_id);
});
} else {
reject(body);
}
});
})
}
/**
* 获取微信支付的签名
* @param payParams
*/
getSign(signParams){
// 按 key 值的ascll 排序
var keys = Object.keys(signParams);
keys = keys.sort();
var newArgs = {};
keys.forEach(function (val, key) {
if (signParams[val]){
newArgs[val] = signParams[val];
}
})
var string = queryString.stringify(newArgs)+'&key='+config.wxpaykey;
// 生成签名
return crypto.createHash('md5').update(queryString.unescape(string), 'utf8').digest("hex").toUpperCase();
}
/**
* 微信支付的所有参数
* @param req 请求的资源, 获取必要的数据
* @returns {{appId: string, timeStamp: Number, nonceStr: *, package: string, signType: string, paySign: *}}
*/
getBrandWCPayParams( obj, callback ){
var that = this;
var prepay_id_promise = that.getPrepayId(obj);
prepay_id_promise.then(function (prepay_id) {
var prepay_id = prepay_id;
var wcPayParams = {
"appId" : config.wxappid, //公众号名称,由商户传入
"timeStamp" : parseInt(new Date().getTime() / 1000).toString(), //时间戳,自1970年以来的秒数
"nonceStr" : that.createNonceStr(), //随机串
// 通过统一下单接口获取
"package" : "prepay_id="+prepay_id,
"signType" : "MD5", //微信签名方式:
};
wcPayParams.paySign = that.getSign(wcPayParams); //微信支付签名
callback(null, wcPayParams);
},function (error) {
callback(error);
});
}
/**
* 获取随机的NonceStr
*/
createNonceStr() {
return Math.random().toString(36).substr(2, 15);
};
//获取微信的 AccessToken openid
getAccessToken(code, cb){
var that = this;
var getAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+config.wxappid+"&secret="+config.wxappsecret+"&code="+code+"&grant_type=authorization_code";
request.post({url : getAccessTokenUrl}, function (error, response, body) {
if (!error && response.statusCode == 200){
if (40029 == body.errcode) {
cb(error, body);
} else {
body = JSON.parse(body);
cb(null, body);
}
} else {
cb(error);
}
});
}
/**
* 创建订单
*/
createOrder(obj, cb){
this.getBrandWCPayParams(obj, function (error, responseData) {
if (error) {
cb(error);
} else {
cb(null, responseData);
}
});
}
}
module.exports = WechatPay;
1、测试的时候经常404,打印错误信息提示没把ip输入白名单,明明刚才已经填好白名单,原因是笔记本电脑的ip地址(百度ip)自动改变了。
2、每次url变化之后都需要重新微信jssdk授权,虽然每次授权url除去#后都是一样的,但是必须这么做,微信的机制。
3、使用微信自定义分享功能的时候,当分享链接中存在中文的时候,一定要进行encodeURIComponent(),否则在安卓手机上能成功自定义分享,ios手机上则不能成功分享。查阅资料后得知是安卓手机会自动encodeURIComponent,而ios不会。