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