fix: 修复微信支付回跳刷新与查单确认

This commit is contained in:
2026-05-14 23:52:01 +08:00
parent cf3dcc6195
commit 2801b55d2f
21 changed files with 880 additions and 119 deletions

View File

@@ -70,6 +70,7 @@ import {
import { copyTextToClipboard } from '../../services/clipboard';
import {
claimRpgProfileTaskReward,
confirmWechatRpgProfileRechargeOrder,
createRpgProfileRechargeOrder,
getRpgProfileReferralInviteCenter,
getRpgProfileRechargeCenter,
@@ -216,6 +217,11 @@ const WECHAT_JS_SDK_URL = 'https://res.wx.qq.com/open/js/jweixin-1.6.0.js';
type ProfilePopupPanel = 'invite' | 'redeem' | 'community';
type RechargeTab = 'points' | 'membership';
type WechatMiniProgramPaymentStatus = 'success' | 'fail' | 'cancel';
type WechatPayResult = {
requestId: string;
orderId: string | null;
status: WechatMiniProgramPaymentStatus;
};
type DiscoverChannel =
| 'recommend'
| 'today'
@@ -2342,6 +2348,37 @@ function clearWechatPayResultHash() {
window.history.replaceState(null, '', nextUrl);
}
function readWechatPayResultFromHash(): WechatPayResult | null {
if (typeof window === 'undefined') {
return null;
}
const result = new URLSearchParams(
window.location.hash.replace(/^#/, ''),
).get('wx_pay_result');
if (!result) {
return null;
}
const [requestId = '', rawStatus = ''] = result.split(':');
const orderId = requestId
.replace(/^wechat_pay_/, '')
.replace(/_\d+$/, '')
.trim();
const status =
rawStatus === 'success'
? 'success'
: rawStatus === 'cancel'
? 'cancel'
: 'fail';
return {
requestId,
orderId: orderId || null,
status,
};
}
function loadWechatJsSdk() {
if (typeof window === 'undefined') {
return Promise.reject(new Error('请在微信小程序内完成支付'));
@@ -2385,7 +2422,7 @@ function loadWechatJsSdk() {
async function requestWechatMiniProgramPayment(
payload: WechatMiniProgramPayParams | null | undefined,
orderId: string,
) {
): Promise<void> {
if (!payload) {
return Promise.reject(new Error('请在微信小程序内完成支付'));
}
@@ -2396,35 +2433,20 @@ async function requestWechatMiniProgramPayment(
}
const navigateTo = miniProgram.navigateTo;
return new Promise<WechatMiniProgramPaymentStatus>((resolve) => {
const requestId = `wechat_pay_${orderId}_${Date.now()}`;
const handleHashChange = () => {
const params = new URLSearchParams(
window.location.hash.replace(/^#/, ''),
);
const result = params.get('wx_pay_result') ?? '';
const [resultRequestId, status] = result.split(':');
if (resultRequestId !== requestId) {
return;
}
window.removeEventListener('hashchange', handleHashChange);
resolve(
status === 'success'
? 'success'
: status === 'cancel'
? 'cancel'
: 'fail',
);
};
window.addEventListener('hashchange', handleHashChange);
const requestId = `wechat_pay_${orderId}_${Date.now()}`;
return new Promise<void>((resolve, reject) => {
navigateTo({
url: `/pages/wechat-pay/index?requestId=${encodeURIComponent(requestId)}&orderId=${encodeURIComponent(orderId)}&payParams=${encodeURIComponent(JSON.stringify(payload))}`,
success() {
resolve();
},
fail(error) {
window.removeEventListener('hashchange', handleHashChange);
console.error('[wechat-pay] navigateTo failed', error);
resolve('fail');
reject(
error instanceof Error
? error
: new Error('请在微信小程序内完成支付'),
);
},
});
});
@@ -3368,6 +3390,7 @@ export function RpgEntryHomeView({
useState<LegalDocumentId | null>(null);
const profileCopyResetTimerRef = useRef<number | null>(null);
const avatarFileInputRef = useRef<HTMLInputElement | null>(null);
const pendingWechatRechargeOrderIdRef = useRef<string | null>(null);
const [isNicknameModalOpen, setIsNicknameModalOpen] = useState(false);
const [nicknameInput, setNicknameInput] = useState('');
const [nicknameError, setNicknameError] = useState<string | null>(null);
@@ -3823,6 +3846,55 @@ export function RpgEntryHomeView({
})
.finally(() => setIsLoadingRechargeCenter(false));
};
const refreshRechargeState = useCallback(
() => {
loadRechargeCenter();
setSubmittingRechargeProductId(null);
pendingWechatRechargeOrderIdRef.current = null;
},
[loadRechargeCenter],
);
const handleWechatPayResult = useCallback(() => {
const payResult = readWechatPayResultFromHash();
if (!payResult) {
return;
}
if (
pendingWechatRechargeOrderIdRef.current &&
payResult.orderId &&
payResult.orderId !== pendingWechatRechargeOrderIdRef.current
) {
return;
}
if (payResult.status === 'success') {
setRechargeSuccess('支付已提交');
if (payResult.orderId) {
void confirmWechatRpgProfileRechargeOrder(payResult.orderId)
.then((response) => {
setRechargeCenter(response.center);
setRechargeSuccess(
response.order.status === 'paid' ? '已到账' : '支付已提交',
);
setSubmittingRechargeProductId(null);
pendingWechatRechargeOrderIdRef.current = null;
})
.catch(() => refreshRechargeState());
} else {
refreshRechargeState();
}
void onRechargeSuccess?.();
} else if (payResult.status === 'cancel') {
setRechargeSuccess('支付已取消');
refreshRechargeState();
} else {
setRechargeError('微信支付未完成');
refreshRechargeState();
}
clearWechatPayResultHash();
}, [onRechargeSuccess, refreshRechargeState]);
const openRechargeModal = () => {
if (!authUi?.user) {
authUi?.openLoginModal();
@@ -3847,63 +3919,44 @@ export function RpgEntryHomeView({
void createRpgProfileRechargeOrder(product.productId, paymentChannel)
.then(async (response) => {
if (paymentChannel === WECHAT_MINI_PROGRAM_PAYMENT_CHANNEL) {
const status = await requestWechatMiniProgramPayment(
pendingWechatRechargeOrderIdRef.current = response.order.orderId;
await requestWechatMiniProgramPayment(
response.wechatMiniProgramPayParams,
response.order.orderId,
);
if (status === 'cancel') {
setRechargeCenter(response.center);
setRechargeSuccess('支付已取消');
return;
}
if (status !== 'success') {
throw new Error('微信支付未完成');
}
setRechargeSuccess('支付已提交');
loadRechargeCenter();
setRechargeCenter(response.center);
return;
} else {
setRechargeCenter(response.center);
setRechargeSuccess('已到账');
pendingWechatRechargeOrderIdRef.current = null;
setSubmittingRechargeProductId(null);
}
void onRechargeSuccess?.();
})
.catch((error: unknown) => {
pendingWechatRechargeOrderIdRef.current = null;
setRechargeError(error instanceof Error ? error.message : '充值失败');
})
.finally(() => setSubmittingRechargeProductId(null));
setSubmittingRechargeProductId(null);
});
};
useEffect(() => {
if (!isRechargeOpen) {
return undefined;
}
const handleWechatPayResult = () => {
const result = new URLSearchParams(
window.location.hash.replace(/^#/, ''),
).get('wx_pay_result');
if (!result) {
return;
}
const [, status] = result.split(':');
if (status === 'success') {
setRechargeSuccess('支付已提交');
loadRechargeCenter();
void onRechargeSuccess?.();
clearWechatPayResultHash();
} else if (status === 'cancel') {
setRechargeSuccess('支付已取消');
clearWechatPayResultHash();
} else {
setRechargeError('微信支付未完成');
clearWechatPayResultHash();
}
const handleResume = () => {
handleWechatPayResult();
};
window.addEventListener('hashchange', handleWechatPayResult);
handleWechatPayResult();
return () =>
window.removeEventListener('hashchange', handleWechatPayResult);
}, [isRechargeOpen, onRechargeSuccess]);
window.addEventListener('hashchange', handleResume);
window.addEventListener('focus', handleResume);
window.addEventListener('pageshow', handleResume);
document.addEventListener('visibilitychange', handleResume);
handleResume();
return () => {
window.removeEventListener('hashchange', handleResume);
window.removeEventListener('focus', handleResume);
window.removeEventListener('pageshow', handleResume);
document.removeEventListener('visibilitychange', handleResume);
};
}, [handleWechatPayResult]);
const loadTaskCenter = () => {
setTaskCenterError(null);
setIsLoadingTaskCenter(true);