uniapp h5支付JSAPI 云函数版

发布于:2025-09-04 ⋅ 阅读:(12) ⋅ 点赞:(0)

uniapp h5支付 云函数版

1.支付JSAPI,要先有商户号,以及服务号,绑定认证后的服务号的appid,
在这里插入图片描述
2.再配置支付授权目录

代码开始
1.首先获取用户授权,获取openid,获取openid后获取code,去云函数进行解析
登录页面代码

created() {
			 this.getwxCode()
		},
		methods: {
			getwxCode() { // 非静默授权,第一次有弹框
				var local = 'https://www.baidu.cn/pay' //你的当前页面的地址
				// const local = window.location.href;
				var appid = 'wx8888888888' //公众号里有自己查,不会找的查一下百度
				this.code = this.getUrlCode().code // 截取code	
				// 判断地址栏参数有无code,如果没有code,页面地址就跳转到微信提供的获取code的链接
				if (this.code == null || this.code == '') {
					location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appid +
						"&redirect_uri=" +
						encodeURIComponent(local) + "&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect"

				} else {
					this.getUserByUserOpenId()
				}
			},
			getUserByUserOpenId(){
				uniCloud.callFunction({
					name: 'payfun', // 云函数名称
					data:{
						name:'gettoken',
						code: this.code
					},
					success: (res) => {
						console.log(res.result)
						if (res.result.code == 200) {
							uni.setStorageSync('openid', res.result.data.openid)
							if(res.result.data.openid){
								uni.navigateTo({
									url:'/pages/index/index'
								})
							}
						}
					}
				});
			},
			getUrlCode() {
				var url = location.search
				var theRequest = new Object()
				if (url.indexOf("?") != -1) {
					var str = url.substr(1)
					var strs = str.split("&")
					for (var i = 0; i < strs.length; i++) {
						theRequest[strs[i].split("=")[0]] = (strs[i].split("=")[1])
					}
				}
				return theRequest
			},
		}

payfun云函数,先安装npm i xml2js axios

'use strict';
const crypto = require('crypto');
const axios = require('axios');
const xml2js = require('xml2js');
let appid = "服务号的AppID"
let secret = "服务号的secret "
let mch_id = "商户号"
let apiKey = "企业微信的商户号的密钥"
const config = {
	appId: appid, // 小程序或公众号的AppID
	mchid: '你的商户号',
};
// 生成随机字符串
function generateNonceStr(length = 32) {
	return crypto.randomBytes(length).toString('hex').slice(0, length);
}

// 生成签名
function generateSign(params) {
	const keys = Object.keys(params).sort();
	const stringA = keys.map(key => `${key}=${params[key]}`).join('&');
	const stringSignTemp = `${stringA}&key=${apiKey}`;
	return crypto.createHash('md5').update(stringSignTemp).digest('hex').toUpperCase();
}

// 调用统一下单接口
async function unifiedOrder(orderData) {
	const { body, out_trade_no, total_fee, spbill_create_ip, notify_url, openid } = orderData;
	const nonce_str = generateNonceStr();
	const timestamp = Math.floor(Date.now() / 1000);
	const params = {
		appid,
		mch_id,
		nonce_str,
		body,
		out_trade_no,
		total_fee,
		spbill_create_ip,
		notify_url,
		trade_type: 'JSAPI',
		openid,
	};
	params.sign = generateSign(params);

	const xmlData = `<xml>
    ${Object.entries(params).map(([key, value]) => `<${key}>${value}</${key}>`).join('\n')}
  </xml>`;
	var config = {
		method: 'post',
		url: 'https://api.mch.weixin.qq.com/pay/unifiedorder',
		headers: {
			'Content-Type': 'application/xml'
		},
		data: xmlData
	};
	const response = await axios(config)
	const result = response.data;
	console.log(result)
	let prepayId = ''
	let result_code = ''
	let err_code_des = ''
	xml2js.parseString(result, (err, result) => {
		if (err) {
			console.log("Error parsing XML:", err);
			return;
		}
		
		result_code = result.xml.result_code[0]
		err_code_des = result.xml.err_code_des[0]
		if(result_code=='FAIL'){
			return
		}
		prepayId = result.xml.prepay_id[0];
	});
	return {
		prepayId: prepayId,
		result_code: result_code,
		err_code_des: err_code_des,
	};
}

// 构造 JSAPI 支付参数
function getJsApiParams(prepay_id) {
	const nonce_str = generateNonceStr();
	const timestamp = Math.floor(Date.now() / 1000);
	const params = {
		appId: appid,
		timeStamp: timestamp.toString(),
		nonceStr: nonce_str,
		package: `prepay_id=${prepay_id}`,
		signType: 'MD5',
	};

	params.paySign = generateSign(params);
	return params;
}
exports.main = async (event, context) => {
	if (event.name == 'gettoken') {
		console.log(event.code)
		var BASE_API = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' + appid +
			'&secret=' + secret + '&code=' + event.code + '&grant_type=authorization_code'
		const tokenres = await uniCloud.request({
			url: BASE_API,
			method: 'GET',
			timeout: 3600000
		})
		return {
			code: 200,
			data: tokenres.data
		}
		console.log(tokenres)
	}
	if (event.name == 'pay') {
		const clientIP = context.CLIENTIP;
		const orderData = {
			body: event.title, //商品名称
			out_trade_no: event.orderid, //订单号
			total_fee: 1, // 单位:分
			spbill_create_ip: clientIP,
			notify_url: '你的支付回调的云函数地址',
			openid: '', //用户openid
		};
		const unifiedOrderResult = await unifiedOrder(orderData);
		if (unifiedOrderResult.result_code == 'FAIL') {
			return {
				code: 0,
				msg: unifiedOrderResult.err_code_des
			}
		}

		const jsApiParams = getJsApiParams(unifiedOrderResult.prepayId);
		return {
			code: 200,
			data: jsApiParams
		}
	}

};

前端支付代码,先安装npm i jweixin-module

methods: {
			zhifu() {
				// 支付
				var that = this
				this.loading = true
				uniCloud.callFunction({
					name: 'payfun', // 云函数名称
					data: {
						name: 'pay',
						openid: this.openid,
						title:'商品名称',
						orderid:'订单编号'
					},
					success: (res) => {
						if (res.result.code == 200) {
							var jweixin = require('jweixin-module');
							jweixin.config({
								debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,线上环境需要改为false
								appId: res.result.data.appId, // 必填,公众号的唯一标识
								timestamp: res.result.data.timeStamp, // 必填,生成签名的时间戳
								nonceStr: res.result.data.nonceStr, // 必填,生成签名的随机串
								signature: res.result.data.paySign, // 必填,签名
								jsApiList: ['chooseWXPay'], // 必填,需要使用的JS接口列表	
							});
							console.log(jweixin)
							jweixin.ready(() => {
								jweixin.chooseWXPay({
									appId: res.result.data.appId, // 必填,公众号的唯一标识
									timestamp: res.result.data.timeStamp,
									nonceStr: res.result.data.nonceStr, // 支付签名随机串,不长于 32 位
									package: res.result.data.package, // 统一支付接口返回的prepay_id参数值
									signType: 'MD5', // 
									paySign: res.result.data.paySign, // 支付签名
									success: function(value) { //支付成功回调
										console.log(value)
										uni.showToast({
											title: value,
											icon: 'none',
											duration: 2000,
										})
										uni.showToast({
											title: '支付成功',
											icon: 'none',
											duration: 2000,
										})
										
									},
									cancel: function(res) {
										
										uni.showToast({
											title: '取消支付',
											icon: 'none',
											duration: 2000,
										})
										alert('取消支付')
									},
									fail: function(res) {
										
										uni.showToast({
											title: '支付失败',
											icon: 'none',
											duration: 2000,
										})
										alert('支付失败')
									}
								});
							});
						} else {
							uni.showToast({
								title: res.result.msg,
								icon: 'none',
								duration: 2000,
							})
						}
					}
				});
			},
		}

支付成功云函数回调代码

'use strict';
const xml2js = require('xml2js');
exports.main = async (event, context) => {
	const base64Data = event.body;
	// 解码 Base64
	const decodedData = Buffer.from(base64Data, 'base64').toString('utf-8');
	let code = ''
	let total_fee = ''
	xml2js.parseString(decodedData, (err, result) => {
		if (err) {
			console.log("Error parsing XML:", err);
			return;
		}
		code = result.xml.return_code[0];
		total_fee = result.xml.total_fee[0];
	});
	if(code=='SUCCESS'){
		console.log('支付成功')
	}
	//返回数据给客户端
	return event
};

参考文档
配置JSAPI支付授权目录 https://pay.weixin.qq.com/doc/v3/merchant/4013287088
统一下单 https://weixinzhifu.apifox.cn/api-25920360
支付成功回调 https://weixinzhifu.apifox.cn/api-26100246