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