WeChat JSAPI payment v3 process (uniapp and node version)

1. WeChat JSAPI payment

Please prepare the preparation documents before accessing to obtain relevant configuration data in advance, otherwise you may be confused about the data required below!
And you need to know the WeChat JSAPI payment documentation in advance

2. Obtain user openid

Get openid method example

3. h5 transfer payment

1. The first method calls WeChat payment service through WeixinJSBridge

For parameter acquisition, please refer to this article JSAPI payment signature

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. The second method calls the WeChat payment service through wx.chooseWXPay in js-sdk

First, you need to read the js-sdk document, js-sdk document
Get the ability to pay through js-sdk

npm install weixin-js-sdk

For getJssdk in the following, please refer to the method of JS-SDK permission signature in this article

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);
	});
}

Call wx.chooseWXPay to call the payment
parameters, please refer to this article JSAPI payment signature

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) {
    
    
    // 支付成功后的回调函数
  }
});

4. Relevant interfaces corresponding to node docking

1. JS-SDK permission signature

To use the getJssdk interface corresponding to the above directory 3-2,
you need to prepare
appid in advance: appid credential
appSecret: key

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}&timestamp=${
      
      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 payment signature

JSAPI payment signature order document-https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml
need to prepare
appid in advance: appid certificate
mchid: merchant number
serial_no: merchant serial number
pem: private certificate key

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. Callback for successful payment

Payment result notification document - https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_5.shtml
Above 2. JSAPI pays the signature and sends the parameters to the front end. After the payment is initiated, the payment is successful and the call will be notified notify_url address to receive parameters.
Need to prepare
key: APIv3 key (WeChat Merchant Platform—>Account Settings—>API Security—>Set APIv3 Key)

//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)

//-----------------------------------------------------
// 进行相关回调通知,数据库操作,消息提醒等等。。。
//-----------------------------------------------------

The encrypted parameters of decodeByAES decryption callback

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;
}

Guess you like

Origin blog.csdn.net/weixin_41891519/article/details/123605439