|
|
@@ -136,6 +136,7 @@ const inWechat = ref(false);
|
|
|
const openid = ref('');
|
|
|
const wxSdkReady = ref(false);
|
|
|
const paying = ref(false);
|
|
|
+const lastPayClick = ref(0); // 防重:记录上次点击时间
|
|
|
let checkTimer: ReturnType<typeof setInterval> | null = null;
|
|
|
|
|
|
// ==================== 微信 JS-SDK 初始化 ====================
|
|
|
@@ -335,6 +336,19 @@ async function createOrder() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// 查询微信订单状态(用于支付后确认)
|
|
|
+async function checkWechatOrderStatus(orderNo: string): Promise<string | null> {
|
|
|
+ try {
|
|
|
+ const result = await get<any>(`/payment/wechat/query/${orderNo}`);
|
|
|
+ const state = result?.data?.trade_state || result?.trade_state || null;
|
|
|
+ console.log('[WeChat Pay] 订单查询结果:', orderNo, state);
|
|
|
+ return state;
|
|
|
+ } catch (e) {
|
|
|
+ console.error('[WeChat Pay] 订单查询失败:', e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// ==================== 微信 JSAPI 支付(公众号内) ====================
|
|
|
async function handleWechatJsapiPay() {
|
|
|
// #ifdef H5
|
|
|
@@ -387,39 +401,60 @@ async function handleWechatJsapiPay() {
|
|
|
package: result.payParams.package,
|
|
|
signType: result.payParams.signType,
|
|
|
paySign: result.payParams.paySign,
|
|
|
- success: () => {
|
|
|
- console.log('[WeChat Pay] 支付成功');
|
|
|
+ success: async () => {
|
|
|
+ console.log('[WeChat Pay] wx.chooseWXPay 返回成功,查询订单确认...');
|
|
|
+ // 官方要求:必须调用查询订单API确认最终状态,避免依赖回调直接展示
|
|
|
+ const orderStatus = await checkWechatOrderStatus(result.orderNo);
|
|
|
paying.value = false;
|
|
|
- uni.showModal({
|
|
|
- title: '支付成功',
|
|
|
- content: productType.value === 'token-pack' ? '积分包购买成功!' : '恭喜您订阅成功!',
|
|
|
- showCancel: false,
|
|
|
- success: () => {
|
|
|
- uni.switchTab({ url: '/pages/mine/index' });
|
|
|
- }
|
|
|
- });
|
|
|
+
|
|
|
+ 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: (err: any) => {
|
|
|
- console.error('[WeChat Pay] 支付失败:', err);
|
|
|
+ fail: async (err: any) => {
|
|
|
+ console.error('[WeChat Pay] wx.chooseWXPay fail:', err);
|
|
|
+ // fail 可能并不是真正的失败(用户取消也可能进这里),查询订单确认
|
|
|
+ const orderStatus = await checkWechatOrderStatus(result.orderNo);
|
|
|
paying.value = false;
|
|
|
- // 可能是签名错误等,引导用户使用扫码支付
|
|
|
- uni.showModal({
|
|
|
- title: '支付失败',
|
|
|
- content: '微信支付调起失败,是否使用扫码支付?',
|
|
|
- confirmText: '扫码支付',
|
|
|
- cancelText: '取消',
|
|
|
- success: (res) => {
|
|
|
- if (res.confirm && result.orderNo) {
|
|
|
- inWechat.value = false;
|
|
|
- handlePay();
|
|
|
+
|
|
|
+ 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: (res) => {
|
|
|
+ if (res.confirm && result.orderNo) {
|
|
|
+ inWechat.value = false;
|
|
|
+ handlePay();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
}
|
|
|
});
|
|
|
} catch (error: any) {
|
|
|
@@ -439,6 +474,14 @@ async function handleWechatJsapiPay() {
|
|
|
}
|
|
|
|
|
|
async function handlePay() {
|
|
|
+ // 防重处理:2秒内禁止重复点击(官方要求按钮防抖)
|
|
|
+ const now = Date.now();
|
|
|
+ if (now - lastPayClick.value < 2000) {
|
|
|
+ console.log('[Payment] 防重:忽略重复点击');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ lastPayClick.value = now;
|
|
|
+
|
|
|
if (!planId.value && productType.value !== 'token-pack') {
|
|
|
uni.showToast({ title: '参数错误', icon: 'none' });
|
|
|
return;
|