add lifecycle token for payment

This commit is contained in:
2026-07-17 11:09:59 +08:00
parent 20e35e83c4
commit 51ccd54f48
2 changed files with 69 additions and 16 deletions
+43 -16
View File
@@ -5378,6 +5378,7 @@ export function WorkspaceLauncher({
useState<string | null>(null);
const [nativeRechargePayment, setNativeRechargePayment] =
useState<PlatformProfileRechargeNativePaymentState | null>(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)}
@@ -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<void>((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', () => {