|
@@ -134,65 +134,10 @@ const tokenPackQuantity = ref<number>(0);
|
|
|
const paymentMethod = ref<'alipay' | 'wechat'>('alipay');
|
|
const paymentMethod = ref<'alipay' | 'wechat'>('alipay');
|
|
|
const inWechat = ref(false);
|
|
const inWechat = ref(false);
|
|
|
const openid = ref('');
|
|
const openid = ref('');
|
|
|
-const wxSdkReady = ref(false);
|
|
|
|
|
const paying = ref(false);
|
|
const paying = ref(false);
|
|
|
const lastPayClick = ref(0); // 防重:记录上次点击时间
|
|
const lastPayClick = ref(0); // 防重:记录上次点击时间
|
|
|
let checkTimer: ReturnType<typeof setInterval> | null = null;
|
|
let checkTimer: ReturnType<typeof setInterval> | null = null;
|
|
|
|
|
|
|
|
-// ==================== 微信 JS-SDK 初始化 ====================
|
|
|
|
|
-async function initWxJsSdk(): Promise<boolean> {
|
|
|
|
|
- // #ifdef H5
|
|
|
|
|
- const wx = (window as any).wx;
|
|
|
|
|
- if (!wx) {
|
|
|
|
|
- console.warn('[WeChat Pay] 微信 JS-SDK 未加载');
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- try {
|
|
|
|
|
- // 获取 JS-SDK 配置签名
|
|
|
|
|
- const configRes = await post<{
|
|
|
|
|
- debug: boolean;
|
|
|
|
|
- appId: string;
|
|
|
|
|
- timestamp: string;
|
|
|
|
|
- nonceStr: string;
|
|
|
|
|
- signature: string;
|
|
|
|
|
- jsApiList: string[];
|
|
|
|
|
- }>('/payment/wechat/jsapi-config', {
|
|
|
|
|
- // 使用当前页面的完整 URL(不含 OAuth code 参数)
|
|
|
|
|
- url: window.location.origin + window.location.pathname + window.location.hash
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- return new Promise((resolve) => {
|
|
|
|
|
- wx.config({
|
|
|
|
|
- debug: false,
|
|
|
|
|
- appId: configRes.appId,
|
|
|
|
|
- timestamp: configRes.timestamp,
|
|
|
|
|
- nonceStr: configRes.nonceStr,
|
|
|
|
|
- signature: configRes.signature,
|
|
|
|
|
- jsApiList: configRes.jsApiList || ['chooseWXPay'],
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- wx.ready(() => {
|
|
|
|
|
- console.log('[WeChat Pay] JS-SDK 初始化成功');
|
|
|
|
|
- wxSdkReady.value = true;
|
|
|
|
|
- resolve(true);
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- wx.error((err: any) => {
|
|
|
|
|
- console.error('[WeChat Pay] JS-SDK 初始化失败:', err);
|
|
|
|
|
- resolve(false);
|
|
|
|
|
- });
|
|
|
|
|
- });
|
|
|
|
|
- } catch (e) {
|
|
|
|
|
- console.error('[WeChat Pay] 获取JS-SDK配置失败:', e);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- // #endif
|
|
|
|
|
- // #ifndef H5
|
|
|
|
|
- return false;
|
|
|
|
|
- // #endif
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
// ==================== 微信 OAuth 处理 ====================
|
|
// ==================== 微信 OAuth 处理 ====================
|
|
|
async function handleWechatOAuth() {
|
|
async function handleWechatOAuth() {
|
|
|
// #ifdef H5
|
|
// #ifdef H5
|
|
@@ -280,10 +225,6 @@ onMounted(async () => {
|
|
|
paymentMethod.value = 'wechat';
|
|
paymentMethod.value = 'wechat';
|
|
|
// 处理 OAuth 回调
|
|
// 处理 OAuth 回调
|
|
|
await handleWechatOAuth();
|
|
await handleWechatOAuth();
|
|
|
- // 如果有 openid,提前初始化 JS-SDK
|
|
|
|
|
- if (openid.value) {
|
|
|
|
|
- initWxJsSdk(); // 异步初始化,不阻塞
|
|
|
|
|
- }
|
|
|
|
|
// 微信环境不预创建订单(JSAPI 支付时另外创建)
|
|
// 微信环境不预创建订单(JSAPI 支付时另外创建)
|
|
|
} else {
|
|
} else {
|
|
|
// 非微信环境:进入页面时直接创建订单
|
|
// 非微信环境:进入页面时直接创建订单
|
|
@@ -350,8 +291,9 @@ async function checkWechatOrderStatus(orderNo: string): Promise<string | null> {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ==================== 微信 JSAPI 支付(公众号内) ====================
|
|
// ==================== 微信 JSAPI 支付(公众号内) ====================
|
|
|
|
|
+// 使用 WeixinJSBridge.invoke 直接调起(官方 V3 推荐方式,无需 JSSDK 配置)
|
|
|
|
|
+// 参考: https://pay.weixin.qq.com/doc/v3/merchant/4012791870
|
|
|
async function handleWechatJsapiPay() {
|
|
async function handleWechatJsapiPay() {
|
|
|
- // #ifdef H5
|
|
|
|
|
// 如果没有 openid,需要先授权
|
|
// 如果没有 openid,需要先授权
|
|
|
if (!openid.value) {
|
|
if (!openid.value) {
|
|
|
await redirectToWechatOAuth();
|
|
await redirectToWechatOAuth();
|
|
@@ -381,87 +323,73 @@ async function handleWechatJsapiPay() {
|
|
|
orderInfo.value.orderNo = result.orderNo;
|
|
orderInfo.value.orderNo = result.orderNo;
|
|
|
uni.hideLoading();
|
|
uni.hideLoading();
|
|
|
|
|
|
|
|
- const wx = (window as any).wx;
|
|
|
|
|
-
|
|
|
|
|
- // 确保 JS-SDK 已初始化
|
|
|
|
|
- if (!wxSdkReady.value) {
|
|
|
|
|
- const ok = await initWxJsSdk();
|
|
|
|
|
- if (!ok) {
|
|
|
|
|
- uni.showToast({ title: '微信支付初始化失败,请使用扫码支付', icon: 'none' });
|
|
|
|
|
- paying.value = false;
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 调起微信支付
|
|
|
|
|
- wx.chooseWXPay({
|
|
|
|
|
- appId: result.payParams.appId,
|
|
|
|
|
- timestamp: result.payParams.timeStamp,
|
|
|
|
|
- nonceStr: result.payParams.nonceStr,
|
|
|
|
|
- package: result.payParams.package,
|
|
|
|
|
- signType: result.payParams.signType,
|
|
|
|
|
- paySign: result.payParams.paySign,
|
|
|
|
|
- success: async () => {
|
|
|
|
|
- console.log('[WeChat Pay] wx.chooseWXPay 返回成功,查询订单确认...');
|
|
|
|
|
- // 官方要求:必须调用查询订单API确认最终状态,避免依赖回调直接展示
|
|
|
|
|
- const orderStatus = await checkWechatOrderStatus(result.orderNo);
|
|
|
|
|
- paying.value = false;
|
|
|
|
|
-
|
|
|
|
|
- if (orderStatus === 'SUCCESS') {
|
|
|
|
|
- uni.showModal({
|
|
|
|
|
- title: '支付成功',
|
|
|
|
|
- content: productType.value === 'token-pack' ? '积分包购买成功!' : '恭喜您订阅成功!',
|
|
|
|
|
- showCancel: false,
|
|
|
|
|
- success: () => {
|
|
|
|
|
- uni.switchTab({ url: '/pages/mine/index' });
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
- } else {
|
|
|
|
|
- uni.showToast({ title: '订单确认中,请稍后查看', icon: 'none' });
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- cancel: () => {
|
|
|
|
|
- console.log('[WeChat Pay] 用户取消支付');
|
|
|
|
|
- paying.value = false;
|
|
|
|
|
- uni.showToast({ title: '已取消支付', icon: 'none' });
|
|
|
|
|
- },
|
|
|
|
|
- fail: async (err: any) => {
|
|
|
|
|
- console.error('[WeChat Pay] wx.chooseWXPay fail:', err);
|
|
|
|
|
- // fail 可能并不是真正的失败(用户取消也可能进这里),查询订单确认
|
|
|
|
|
- const orderStatus = await checkWechatOrderStatus(result.orderNo);
|
|
|
|
|
- paying.value = false;
|
|
|
|
|
-
|
|
|
|
|
- if (orderStatus === 'SUCCESS') {
|
|
|
|
|
- uni.showModal({
|
|
|
|
|
- title: '支付成功',
|
|
|
|
|
- content: productType.value === 'token-pack' ? '积分包购买成功!' : '恭喜您订阅成功!',
|
|
|
|
|
- showCancel: false,
|
|
|
|
|
- success: () => {
|
|
|
|
|
- uni.switchTab({ url: '/pages/mine/index' });
|
|
|
|
|
|
|
+ // 使用 WeixinJSBridge.invoke 调起支付
|
|
|
|
|
+ const onBridgeReady = () => {
|
|
|
|
|
+ (window as any).WeixinJSBridge.invoke(
|
|
|
|
|
+ 'getBrandWXPayRequest',
|
|
|
|
|
+ {
|
|
|
|
|
+ "appId": result.payParams.appId,
|
|
|
|
|
+ "timeStamp": result.payParams.timeStamp,
|
|
|
|
|
+ "nonceStr": result.payParams.nonceStr,
|
|
|
|
|
+ "package": result.payParams.package,
|
|
|
|
|
+ "signType": result.payParams.signType || "RSA",
|
|
|
|
|
+ "paySign": result.payParams.paySign,
|
|
|
|
|
+ },
|
|
|
|
|
+ async (res: any) => {
|
|
|
|
|
+ console.log('[WeChat Pay] WeixinJSBridge 回调:', JSON.stringify(res));
|
|
|
|
|
+ // 官方要求:必须调用查询订单API确认最终状态
|
|
|
|
|
+ const orderStatus = await checkWechatOrderStatus(result.orderNo);
|
|
|
|
|
+ paying.value = false;
|
|
|
|
|
+
|
|
|
|
|
+ if (res.err_msg === "get_brand_wcpay_request:ok") {
|
|
|
|
|
+ if (orderStatus === 'SUCCESS') {
|
|
|
|
|
+ uni.showModal({
|
|
|
|
|
+ title: '支付成功',
|
|
|
|
|
+ content: productType.value === 'token-pack' ? '积分包购买成功!' : '恭喜您订阅成功!',
|
|
|
|
|
+ showCancel: false,
|
|
|
|
|
+ success: () => uni.switchTab({ url: '/pages/mine/index' })
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uni.showToast({ title: '订单确认中,请稍后查看', icon: 'none' });
|
|
|
}
|
|
}
|
|
|
- });
|
|
|
|
|
- } else {
|
|
|
|
|
- // 确认支付失败,引导使用扫码支付
|
|
|
|
|
- uni.showModal({
|
|
|
|
|
- title: '支付失败',
|
|
|
|
|
- content: '微信支付调起失败,是否使用扫码支付?',
|
|
|
|
|
- confirmText: '扫码支付',
|
|
|
|
|
- cancelText: '取消',
|
|
|
|
|
- success: (res) => {
|
|
|
|
|
- if (res.confirm && result.orderNo) {
|
|
|
|
|
- inWechat.value = false;
|
|
|
|
|
- handlePay();
|
|
|
|
|
|
|
+ } else if (res.err_msg === "get_brand_wcpay_request:cancel") {
|
|
|
|
|
+ uni.showToast({ title: '已取消支付', icon: 'none' });
|
|
|
|
|
+ } else if (orderStatus === 'SUCCESS') {
|
|
|
|
|
+ // 桥回调失败但订单已支付(延迟回调)
|
|
|
|
|
+ uni.showModal({
|
|
|
|
|
+ title: '支付成功',
|
|
|
|
|
+ content: productType.value === 'token-pack' ? '积分包购买成功!' : '恭喜您订阅成功!',
|
|
|
|
|
+ showCancel: false,
|
|
|
|
|
+ success: () => uni.switchTab({ url: '/pages/mine/index' })
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uni.showModal({
|
|
|
|
|
+ title: '支付失败',
|
|
|
|
|
+ content: '微信支付调起失败,是否使用扫码支付?',
|
|
|
|
|
+ confirmText: '扫码支付',
|
|
|
|
|
+ cancelText: '取消',
|
|
|
|
|
+ success: (modalRes: any) => {
|
|
|
|
|
+ if (modalRes.confirm && result.orderNo) {
|
|
|
|
|
+ inWechat.value = false;
|
|
|
|
|
+ handlePay();
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ );
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // WeixinJSBridge 可能尚未就绪,监听就绪事件
|
|
|
|
|
+ if (typeof (window as any).WeixinJSBridge === 'undefined') {
|
|
|
|
|
+ document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ onBridgeReady();
|
|
|
|
|
+ }
|
|
|
} catch (error: any) {
|
|
} catch (error: any) {
|
|
|
paying.value = false;
|
|
paying.value = false;
|
|
|
uni.hideLoading();
|
|
uni.hideLoading();
|
|
|
console.error('[WeChat Pay] 创建JSAPI支付失败:', error);
|
|
console.error('[WeChat Pay] 创建JSAPI支付失败:', error);
|
|
|
- // 如果 JSAPI 支付失败(可能是 openid 过期),清除缓存让用户重新授权
|
|
|
|
|
if (error.message && error.message.includes('openid')) {
|
|
if (error.message && error.message.includes('openid')) {
|
|
|
localStorage.removeItem('wx_openid');
|
|
localStorage.removeItem('wx_openid');
|
|
|
openid.value = '';
|
|
openid.value = '';
|
|
@@ -470,7 +398,6 @@ async function handleWechatJsapiPay() {
|
|
|
}
|
|
}
|
|
|
uni.showToast({ title: error.message || '支付失败', icon: 'none' });
|
|
uni.showToast({ title: error.message || '支付失败', icon: 'none' });
|
|
|
}
|
|
}
|
|
|
- // #endif
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async function handlePay() {
|
|
async function handlePay() {
|