一、微信JSAPI支付
请提前准备好接入前的准备文档获取相关的配置数据,否则下面需要的数据你可能会比较懵!
并且需要提前了解微信JSAPI支付文档
二、获取用户openid
获取openid方法例子
三、h5调起支付
1.第一种通过WeixinJSBridge调起微信支付服务
参数获取请看本文JSAPI支付签名
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
{
appId:'xxxxxxxx',//公众号ID,由商户传入
timeStamp:'xxxxxxxx',//时间戳,自1970年以来的秒数
nonceStr:'xxxxxxxx',//随机串
package:'xxxxxxxx', // 统一支付接口返回的prepay_id参数值
signType:"RSA",//微信签名方式:
paySign:'xxxxxxxx',//微信签名
},
(res) =>{
if (res.err_msg == "get_brand_wcpay_request:ok") {
//支付成功
}
}
);
2.第二种通过js-sdk中的wx.chooseWXPay调起微信支付服务
首先需要阅读js-sdk文档,js-sdk文档 通过js-sdk获取支付的能力
npm install weixin-js-sdk
下文中的getJssdk获取请看本文JS-SDK权限签名的方法
import wx from 'weixin-js-sdk';
import {getJssdk} from "@/api/wx.js";
export const setJsSdk = async ()=>{
let params = {
url:location.href.split('#')[0]
}
//JS-SDK使用权限签名
let res = await getJssdk(params);
let data = res.data;
wx.config({
debug: false,
appId: appid, // 必填,公众号的唯一标识
jsApiList:[
'chooseWXPay',//发起微信支付
],
timestamp:data.timestamp,//生成签名的时间戳
nonceStr:data.nonceStr,//生成签名的随机串
signature:data.signature,//签名
});
wx.error(e => {
console.log('wx sdk errors:', e);
});
}
调用wx.chooseWXPay进行支付调起
参数获取请看本文JSAPI支付签名
wx.chooseWXPay({
timestamp: xxxxxxxx, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
nonceStr: 'xxxxxxxx', // 支付签名随机串,不长于 32 位
package: 'xxxxxxxx', // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
signType: 'xxxxxxxx', // 微信支付V3的传入RSA,微信支付V2的传入格式与V2统一下单的签名格式保持一致
paySign: 'xxxxxxxx', // 支付签名
success: function (res) {
// 支付成功后的回调函数
}
});
四、node对接对应的相关接口
1.JS-SDK权限签名
对应上面目录三-2的getJssdk接口使用
需提前准备
appid:appid凭证
appSecret:密钥
const crypto = require("crypto");
const axios = require("axios");
const appid = "xxxxxxxx";
const appSecret = 'xxxxxxxx';
//获取js-sdk参数
const getJssdk = (params)=>{
//-----------------------------------------------------------------------------
// 这两个接口获取的临时数据必须进行定时任务每个小时更新一次缓存起来
// 由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。
//获取access_token
let result1 = await axios(`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${appSecret}`);
let access_token = result1.data.access_token;
//获取jsapi_ticket 是公众号用于调用微信JS接口的临时票据
let result2 = await axios(`https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${access_token}&type=jsapi`);
let jsapi_ticket = result2.data.ticket;
//-----------------------------------------------------------------------------
let timestamp = new Date().getTime();//时间戳
let url = params.url;//当前网页的URL,不包含#及其后面部分
let nonceStr = 'xxxxxxxxxxxxxxxxxxxx';//随机字符串
let string1 = `jsapi_ticket=${jsapi_ticket}&noncestr=${nonceStr}×tamp=${timestamp}&url=${url}`;
let signature = jiamiSha1(string1);//sha1加密
//返回数据给客户端
return {
code:0,
message:'',
data:{
timestamp,
nonceStr,
signature
}
}
}
//sha1加密
function jiamiSha1(str){
// 需要加密的字符串
let sf = crypto.createHash('sha1');//使用加密算法
sf.update(str);//进行加密
let content = sf.digest("hex");//以二进制数据为字符串形式展示
return content;
};
module.exports = getJssdk;
2.JSAPI支付签名
JSAPI支付签名下单文档-https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml 需提前准备
appid:appid凭证
mchid:商户号
serial_no:商户序列号
pem:证书私钥
const axios = require("axios");
const crypto = require("crypto");
const pem = require("./apiclient_key.js");
const appid = "xxxxxxxxxxxxx";//appid
const mchid = 'xxxxxxxxxxxxx';//商户号
const serial_no = 'xxxxxxxxxxxxx';//商户序列号
// jsapi下单
/*
data:{
out_trade_no:'商户订单号',
description:'说明'
attach:'携带回调参数'
notify_url:'通知地址',
total:'分',
openid:'用户openid',
}
*/
function paysgin(data){
return new Promise(async(resolve,reject)=>{
let url = '/v3/pay/transactions/jsapi';
let params = {
"mchid": mchid,//直连商户号
"out_trade_no": data.out_trade_no,//商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
"appid": appid,//应用ID
"description": data.description,//商品描述
"attach":JSON.stringify(data.attach),//附加数据
"notify_url": data.notify_url,//通知地址
"amount": {
"total": data.total,//总金额,单位为分
"currency": "CNY"
},
"payer": {
"openid": data.openid//用户标识
}
}
//获取prepay_id
let result = await axios({
url:"https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi",
method:"post",
headers:{
"Authorization":sgin('POST',url,params)
},
data:params
});
// 配置调起支付参数
let prepay_id = result.data.prepay_id;
let timestamp = parseInt(new Date().getTime()/1000).toString();
let nonce_str = new Date().getTime().toString();
let jiamiPaySign = appid + "\n" + timestamp + "\n" + nonce_str + "\n" + `prepay_id=${prepay_id}` + "\n";
let signaturePaySign = sha256(jiamiPaySign);
//-----------------------------------------------------
// 保存支付参数到数据库
//-----------------------------------------------------
resolve({
code:0,
msg:'',
data:{
appId:appid,//公众号ID,由商户传入
timeStamp:timestamp,//时间戳,自1970年以来的秒数
nonceStr:nonce_str,//随机串
package:`prepay_id=${prepay_id}`,
signType:"RSA",//微信签名方式:
paySign:signaturePaySign,//微信签名
}
});
})
}
//RSA-SHA256加密
function sha256(str){
let privateKey = pem;
let sign = crypto.createSign('RSA-SHA256');
sign.update(Buffer.from(str, 'utf-8'));
let signature = sign.sign(privateKey, 'base64');
return signature;
}
//签名
function sgin(method,url,params=""){
let timestamp = parseInt(new Date().getTime()/1000);
let nonce_str = new Date().getTime();
params = JSON.parse(JSON.stringify(params));
let message = method + "\n"
+ url + "\n"
+ timestamp + "\n"
+ nonce_str + "\n"
+ JSON.stringify(params) + "\n";
let signature = sha256(message);
let auth = `WECHATPAY2-SHA256-RSA2048 mchid="${mchid}",serial_no="${serial_no}",nonce_str="${nonce_str}",timestamp="${timestamp}",signature="${signature}"`;
return auth;
}
module.exports = paysgin
3.支付成功的回调
支付结果通知文档-https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_5.shtml 在上面2.JSAPI支付签名后把参数给前端,发起支付后,支付成功会通知调用notify_url地址进行接收参数。
需要准备
key:APIv3密钥(微信商户平台—>账户设置—>API安全—>设置APIv3密钥)
//event.body 是 回调回来的数据
let body = JSON.parse(event.body);
console.log('body : ', body.resource)
const key = "xxxxxxxxxxxxxxx";
const ciphertext = body.resource.ciphertext;
const nonce = body.resource.nonce;
const associated_data = body.resource.associated_data;
//解密
let data = JSON.parse(decodeByAES(ciphertext,key,nonce,associated_data));
data.attach = JSON.parse(decodeURIComponent(data.attach));
data.success_time = decodeURIComponent(data.success_time.replace(/\+/g, '%20').replace(/T/g,' '));
console.log("解密",data)
//-----------------------------------------------------
// 进行相关回调通知,数据库操作,消息提醒等等。。。
//-----------------------------------------------------
decodeByAES解密回调的加密参数
const crypto = require("crypto");
function decodeByAES(cipherText,key,iv,add){
let rst = '';
cipherText = Buffer.from(cipherText, 'base64');
let authTag = cipherText.slice(cipherText.length - 16);
let data = cipherText.slice(0, cipherText.length - 16);
let decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(authTag);
decipher.setAAD(Buffer.from(add));
rst = decipher.update(data, 'binary', 'utf8');
try {
rst += decipher.final('utf-8');
} catch (e) {
think.logger.error(e.toString());
}
return rst;
}