267 lines
7.1 KiB
JavaScript
267 lines
7.1 KiB
JavaScript
|
|
// utils/wxpay.js
|
|||
|
|
const API_BASE_URL = 'https://yourdomain.com/api';
|
|||
|
|
|
|||
|
|
class WxPay {
|
|||
|
|
constructor() {
|
|||
|
|
this.isWeixin = this.checkWeixin();
|
|||
|
|
this.openid = uni.getStorageSync('wx_openid') || '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查是否在微信环境
|
|||
|
|
checkWeixin() {
|
|||
|
|
const ua = navigator.userAgent.toLowerCase();
|
|||
|
|
return ua.indexOf('micromessenger') !== -1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取 URL 参数
|
|||
|
|
getQueryParam(name) {
|
|||
|
|
const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
|
|||
|
|
const r = window.location.search.substr(1).match(reg);
|
|||
|
|
if (r != null) return decodeURIComponent(r[2]);
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 微信授权获取 code
|
|||
|
|
async wxAuth() {
|
|||
|
|
if (!this.isWeixin) {
|
|||
|
|
uni.showToast({ title: '请在微信中打开', icon: 'none' });
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const code = this.getQueryParam('code');
|
|||
|
|
|
|||
|
|
if (!code) {
|
|||
|
|
// 跳转微信授权
|
|||
|
|
const currentUrl = encodeURIComponent(window.location.href.split('#')[0]);
|
|||
|
|
const authUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${this.appId}&redirect_uri=${currentUrl}&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect`;
|
|||
|
|
window.location.href = authUrl;
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return code;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 通过 code 获取 openid
|
|||
|
|
async getOpenId(code) {
|
|||
|
|
try {
|
|||
|
|
const response = await uni.request({
|
|||
|
|
url: `${API_BASE_URL}/wx/auth`,
|
|||
|
|
method: 'GET',
|
|||
|
|
data: { code }
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (response[1].data.code === 0) {
|
|||
|
|
const { openid } = response[1].data.data;
|
|||
|
|
this.openid = openid;
|
|||
|
|
uni.setStorageSync('wx_openid', openid);
|
|||
|
|
return openid;
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('获取openid失败:', error);
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 初始化微信 JS-SDK
|
|||
|
|
async initWxJsSDK() {
|
|||
|
|
if (!this.isWeixin) return false;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 获取当前页面 URL(去掉 # 后面的部分)
|
|||
|
|
const currentUrl = window.location.href.split('#')[0];
|
|||
|
|
|
|||
|
|
const response = await uni.request({
|
|||
|
|
url: `${API_BASE_URL}/jssdk/config`,
|
|||
|
|
method: 'GET',
|
|||
|
|
data: { url: encodeURIComponent(currentUrl) }
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (response[1].data.code === 0) {
|
|||
|
|
const config = response[1].data.data;
|
|||
|
|
|
|||
|
|
// 引入微信 JS-SDK
|
|||
|
|
await this.loadWxJsSDK();
|
|||
|
|
|
|||
|
|
// 配置微信 JS-SDK
|
|||
|
|
wx.config({
|
|||
|
|
debug: false, // 开启调试模式
|
|||
|
|
appId: config.appId,
|
|||
|
|
timestamp: config.timestamp,
|
|||
|
|
nonceStr: config.nonceStr,
|
|||
|
|
signature: config.signature,
|
|||
|
|
jsApiList: [
|
|||
|
|
'chooseWXPay',
|
|||
|
|
'onMenuShareTimeline',
|
|||
|
|
'onMenuShareAppMessage',
|
|||
|
|
'scanQRCode'
|
|||
|
|
]
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return new Promise((resolve) => {
|
|||
|
|
wx.ready(() => {
|
|||
|
|
console.log('微信 JS-SDK 初始化成功');
|
|||
|
|
resolve(true);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
wx.error((err) => {
|
|||
|
|
console.error('微信 JS-SDK 初始化失败:', err);
|
|||
|
|
uni.showToast({ title: '微信初始化失败', icon: 'none' });
|
|||
|
|
resolve(false);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('初始化JS-SDK失败:', error);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 动态加载微信 JS-SDK
|
|||
|
|
loadWxJsSDK() {
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
if (typeof wx !== 'undefined') {
|
|||
|
|
resolve();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const script = document.createElement('script');
|
|||
|
|
script.src = 'https://res.wx.qq.com/open/js/jweixin-1.6.0.js';
|
|||
|
|
script.onload = resolve;
|
|||
|
|
script.onerror = reject;
|
|||
|
|
document.head.appendChild(script);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建支付订单
|
|||
|
|
async createPayment(orderData) {
|
|||
|
|
try {
|
|||
|
|
// 确保有 openid
|
|||
|
|
if (!this.openid) {
|
|||
|
|
const code = await this.wxAuth();
|
|||
|
|
if (code) {
|
|||
|
|
await this.getOpenId(code);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!this.openid) {
|
|||
|
|
throw new Error('获取用户信息失败');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const response = await uni.request({
|
|||
|
|
url: `${API_BASE_URL}/wxpay/unifiedorder`,
|
|||
|
|
method: 'POST',
|
|||
|
|
header: { 'Content-Type': 'application/json' },
|
|||
|
|
data: {
|
|||
|
|
openid: this.openid,
|
|||
|
|
body: orderData.body || '账户充值',
|
|||
|
|
total_fee: orderData.amount,
|
|||
|
|
out_trade_no: orderData.orderNo,
|
|||
|
|
attach: orderData.attach || ''
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (response[1].data.code === 0) {
|
|||
|
|
return response[1].data.data;
|
|||
|
|
} else {
|
|||
|
|
throw new Error(response[1].data.msg || '创建订单失败');
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('创建支付订单失败:', error);
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 发起微信支付
|
|||
|
|
async launchPayment(payData) {
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
wx.chooseWXPay({
|
|||
|
|
timestamp: payData.timeStamp,
|
|||
|
|
nonceStr: payData.nonceStr,
|
|||
|
|
package: payData.package,
|
|||
|
|
signType: payData.signType,
|
|||
|
|
paySign: payData.paySign,
|
|||
|
|
success: (res) => {
|
|||
|
|
if (res.errMsg === 'chooseWXPay:ok') {
|
|||
|
|
resolve({ success: true, message: '支付成功' });
|
|||
|
|
} else {
|
|||
|
|
reject(new Error('支付失败: ' + res.errMsg));
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
fail: (err) => {
|
|||
|
|
reject(new Error('支付失败: ' + JSON.stringify(err)));
|
|||
|
|
},
|
|||
|
|
cancel: () => {
|
|||
|
|
reject(new Error('用户取消支付'));
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 完整的支付流程
|
|||
|
|
async pay(amount, orderNo, attach = '') {
|
|||
|
|
try {
|
|||
|
|
// 1. 检查环境
|
|||
|
|
if (!this.isWeixin) {
|
|||
|
|
uni.showToast({ title: '请在微信中打开', icon: 'none' });
|
|||
|
|
return { success: false, message: '非微信环境' };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. 初始化 JS-SDK
|
|||
|
|
const initSuccess = await this.initWxJsSDK();
|
|||
|
|
if (!initSuccess) {
|
|||
|
|
return { success: false, message: '微信初始化失败' };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. 创建支付订单
|
|||
|
|
const payData = await this.createPayment({
|
|||
|
|
amount: amount,
|
|||
|
|
orderNo: orderNo,
|
|||
|
|
attach: attach
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 4. 发起支付
|
|||
|
|
const result = await this.launchPayment(payData);
|
|||
|
|
|
|||
|
|
// 5. 验证支付结果
|
|||
|
|
if (result.success) {
|
|||
|
|
// 支付成功,可以查询订单确认
|
|||
|
|
await this.verifyPayment(orderNo);
|
|||
|
|
return { success: true, message: '支付成功' };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('支付流程错误:', error);
|
|||
|
|
return {
|
|||
|
|
success: false,
|
|||
|
|
message: error.message || '支付失败'
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证支付结果
|
|||
|
|
async verifyPayment(orderNo) {
|
|||
|
|
try {
|
|||
|
|
const response = await uni.request({
|
|||
|
|
url: `${API_BASE_URL}/wxpay/query`,
|
|||
|
|
method: 'GET',
|
|||
|
|
data: { out_trade_no: orderNo }
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (response[1].data.code === 0) {
|
|||
|
|
const orderData = response[1].data.data;
|
|||
|
|
if (orderData.trade_state === 'SUCCESS') {
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('验证支付结果失败:', error);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default new WxPay();
|