From 51ccd54f48a3396b838a209b1546ccb73a81774e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Fri, 17 Jul 2026 11:09:59 +0800 Subject: [PATCH] add lifecycle token for payment --- apps/ai-game-creator-shell/src/App.tsx | 59 ++++++++++++++----- .../tests/appSurface.test.ts | 26 ++++++++ 2 files changed, 69 insertions(+), 16 deletions(-) diff --git a/apps/ai-game-creator-shell/src/App.tsx b/apps/ai-game-creator-shell/src/App.tsx index 2b9d9713a..84f61bea8 100644 --- a/apps/ai-game-creator-shell/src/App.tsx +++ b/apps/ai-game-creator-shell/src/App.tsx @@ -5378,6 +5378,7 @@ export function WorkspaceLauncher({ useState(null); const [nativeRechargePayment, setNativeRechargePayment] = useState(null); + const rechargeLifecycleRef = useRef(0); const [launcherNotice, setLauncherNotice] = useState<{ title: string; message: string; @@ -6213,20 +6214,30 @@ export function WorkspaceLauncher({ } async function loadRechargeCenter() { + const rechargeLifecycle = rechargeLifecycleRef.current; setRechargeLoading(true); setRechargeError(null); try { - applyRechargeCenter(await getClientProfileRechargeCenter()); + const center = await getClientProfileRechargeCenter(); + if (rechargeLifecycleRef.current !== rechargeLifecycle) { + return; + } + applyRechargeCenter(center); } catch (error) { - setRechargeError( - error instanceof Error ? error.message : '读取泥点购买信息失败', - ); + if (rechargeLifecycleRef.current === rechargeLifecycle) { + setRechargeError( + error instanceof Error ? error.message : '读取泥点购买信息失败', + ); + } } finally { - setRechargeLoading(false); + if (rechargeLifecycleRef.current === rechargeLifecycle) { + setRechargeLoading(false); + } } } function openRecharge() { + rechargeLifecycleRef.current += 1; setRechargeOpen(true); setRechargeError(null); void loadRechargeCenter(); @@ -6236,12 +6247,16 @@ export function WorkspaceLauncher({ if (submittingRechargeProductId) { return; } + const rechargeLifecycle = rechargeLifecycleRef.current; setSubmittingRechargeProductId(product.productId); setRechargeError(null); try { const response = await createClientProfileRechargeOrder( product.productId, ); + if (rechargeLifecycleRef.current !== rechargeLifecycle) { + return; + } applyRechargeCenter(response.center); const nativePayment = response.wechatNativePayment; const codeUrl = nativePayment?.codeUrl?.trim(); @@ -6258,9 +6273,13 @@ export function WorkspaceLauncher({ isConfirming: false, }); } catch (error) { - setRechargeError(error instanceof Error ? error.message : '充值失败'); + if (rechargeLifecycleRef.current === rechargeLifecycle) { + setRechargeError(error instanceof Error ? error.message : '充值失败'); + } } finally { - setSubmittingRechargeProductId(null); + if (rechargeLifecycleRef.current === rechargeLifecycle) { + setSubmittingRechargeProductId(null); + } } } @@ -6268,6 +6287,7 @@ export function WorkspaceLauncher({ if (!nativeRechargePayment || nativeRechargePayment.isConfirming) { return; } + const rechargeLifecycle = rechargeLifecycleRef.current; const orderId = nativeRechargePayment.orderId; setNativeRechargePayment((current) => current?.orderId === orderId @@ -6276,6 +6296,9 @@ export function WorkspaceLauncher({ ); try { const response = await confirmClientWechatProfileRechargeOrder(orderId); + if (rechargeLifecycleRef.current !== rechargeLifecycle) { + return; + } applyRechargeCenter(response.center); if (response.order.status === 'paid') { setNativeRechargePayment(null); @@ -6293,15 +6316,17 @@ export function WorkspaceLauncher({ : current, ); } catch { - setNativeRechargePayment((current) => - current?.orderId === orderId - ? { - ...current, - isConfirming: false, - confirmMessage: '暂时没能确认到账状态,请稍后再试。', - } - : current, - ); + if (rechargeLifecycleRef.current === rechargeLifecycle) { + setNativeRechargePayment((current) => + current?.orderId === orderId + ? { + ...current, + isConfirming: false, + confirmMessage: '暂时没能确认到账状态,请稍后再试。', + } + : current, + ); + } } } @@ -9400,8 +9425,10 @@ export function WorkspaceLauncher({ submittingProductId={submittingRechargeProductId} nativePayment={nativeRechargePayment} onClose={() => { + rechargeLifecycleRef.current += 1; setRechargeOpen(false); setNativeRechargePayment(null); + setSubmittingRechargeProductId(null); }} onRetry={() => void loadRechargeCenter()} onBuy={(product) => void buyRechargeProduct(product)} diff --git a/apps/ai-game-creator-shell/tests/appSurface.test.ts b/apps/ai-game-creator-shell/tests/appSurface.test.ts index 7f49e4658..fe3cc85a6 100644 --- a/apps/ai-game-creator-shell/tests/appSurface.test.ts +++ b/apps/ai-game-creator-shell/tests/appSurface.test.ts @@ -1565,6 +1565,8 @@ describe('AI 游戏创作 App 界面边界', () => { latestOrder: null, hasPointsRecharged: true, }; + let rechargeOrderRequestCount = 0; + let releaseLateRechargeOrder: (() => void) | null = null; const fetchSpy = vi .spyOn(globalThis, 'fetch') .mockImplementation(async (input: RequestInfo | URL) => { @@ -1578,6 +1580,12 @@ describe('AI 游戏创作 App 界面边界', () => { return new Response(JSON.stringify(rechargeCenter), { status: 200 }); } if (url === '/api/profile/recharge/orders') { + rechargeOrderRequestCount += 1; + if (rechargeOrderRequestCount === 2) { + await new Promise((resolve) => { + releaseLateRechargeOrder = resolve; + }); + } return new Response( JSON.stringify({ order: { @@ -1676,6 +1684,24 @@ describe('AI 游戏创作 App 界面边界', () => { expect(screen.queryByRole('dialog', { name: '微信扫码支付' })).toBeNull(); }); expect(screen.getByText('当前余额 180 泥点')).not.toBeNull(); + + fireEvent.click(screen.getByRole('button', { name: /60泥点.*购买/ })); + await waitFor(() => { + expect(rechargeOrderRequestCount).toBe(2); + }); + fireEvent.click( + screen.getByRole('button', { name: '关闭购买更多泥点' }), + ); + await act(async () => { + releaseLateRechargeOrder?.(); + }); + fireEvent.click(screen.getByRole('button', { name: '充值' })); + expect( + await screen.findByRole('dialog', { name: '购买更多泥点' }), + ).not.toBeNull(); + expect( + screen.queryByRole('dialog', { name: '微信扫码支付' }), + ).toBeNull(); }); it('opens the help notice and account menu from the sidebar', () => {