Files
Genarrative/src/components/platform-entry/usePlatformProfileCenterController.ts
T
k88936 fc0dcb0782
Project CI / Repository checks (push) Successful in 50s
Project CI / Frontend tests (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
修复泥点消耗刷新不及时 (#138)
refactor:
- 主站复用game agent的zustand store

fix:
- 修复旧请求覆盖问题
- 修复账号切换问题

- 主站原本已经每 4 秒轮询 external generation 任务状态, 在这里补充触发余额刷新的时机

---------

Co-authored-by: 段舒康 <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/138
Co-authored-by: 王德宇 <kvtodev@outlook.com>
Co-committed-by: 王德宇 <kvtodev@outlook.com>
2026-08-08 10:25:02 +08:00

1898 lines
60 KiB
TypeScript

import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import type { PlatformProfileRechargeNativePaymentState } from '../../../packages/shared/src/components/PlatformProfileRechargeModal';
import {
type ConfirmWechatProfileRechargeOrderResponse,
type ProfileRechargeCenterResponse,
type ProfileRechargeOrder,
type ProfileRechargeProduct,
type ProfileReferralInviteCenterResponse,
type ProfileWalletLedgerResponse,
type RedeemProfileRewardCodeResponse,
} from '../../../packages/shared/src/contracts/runtime';
import { clearStoredAccessToken } from '../../services/apiClient';
import { type AuthUser, startWechatBind } from '../../services/authService';
import {
getHostRuntime,
requestHostLogin,
requestHostPayment,
} from '../../services/host-bridge/hostBridge';
import {
resolveProfileRechargeProductPaymentChannel,
WECHAT_H5_PAYMENT_CHANNEL,
WECHAT_JSAPI_PAYMENT_CHANNEL,
WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_CHANNEL,
WECHAT_NATIVE_PAYMENT_CHANNEL,
} from '../../services/payment/paymentPlatform';
import { redirectToPaymentUrl } from '../../services/payment/paymentRedirect';
import { requestWechatJsapiPayment } from '../../services/payment/wechatJsapiPayment';
import {
confirmWechatPlatformProfileRechargeOrder,
createPlatformProfileRechargeOrder,
getPlatformProfileRechargeCenter,
getPlatformProfileReferralInviteCenter,
getPlatformProfileWalletLedger,
redeemPlatformProfileReferralInviteCode,
redeemPlatformProfileRewardCode,
watchWechatPlatformProfileRechargeOrder,
} from '../../services/platform-entry/platformProfileClient';
import { usePlatformWalletStore } from '../../stores/usePlatformWalletStore';
import {
type CopyFeedbackState,
useCopyFeedback,
} from '../common/useCopyFeedback';
const PROFILE_INVITE_QUERY_KEYS = ['inviteCode', 'invite_code'] as const;
const WECHAT_NATIVE_CONFIRM_RETRY_DELAYS_MS = [800, 1600] as const;
const WECHAT_NATIVE_WATCH_RETRY_DELAY_MS = 1000;
const WECHAT_PAY_CONFIRM_RETRY_DELAYS_MS = [800, 1600, 3000] as const;
const WECHAT_PAY_RESULT_RECHECK_INTERVAL_MS = 250;
const WECHAT_PAY_RESULT_RECHECK_TIMEOUT_MS = 10000;
const WECHAT_RECHARGE_UNSUPPORTED_DEVICE_TEXT = '当前登录设备不支持充值';
const WECHAT_JSAPI_MISSING_IDENTITY_TEXT = '缺少微信';
export type ProfileReferralPanel = 'invite' | 'redeem' | 'community';
export type ProfilePopupPanel = ProfileReferralPanel;
type WechatPayResult = {
requestId: string;
orderId: string | null;
status: 'success' | 'cancel' | 'fail';
errorMessage: string | null;
};
type RechargePaymentResultKind =
| 'success'
| 'pending'
| 'cancel'
| 'failed'
| 'expired';
export type RechargePaymentResult = {
kind: RechargePaymentResultKind;
title: string;
message: string;
};
export type WechatRechargeOrderConfirmationState = {
orderId: string;
};
export type NativeWechatPaymentState =
PlatformProfileRechargeNativePaymentState;
type AccountLifecycle = Readonly<{
ownerUserId: string;
revision: number;
}>;
type WechatRechargeOrderLifecycle = Readonly<{
orderId: string;
account: AccountLifecycle;
abortController: AbortController;
}>;
type WechatRechargeConfirmationOptions = Readonly<{
signal: AbortSignal;
isCurrent: () => boolean;
}>;
function isWechatJsapiMissingIdentityError(error: unknown) {
return (
error instanceof Error &&
error.message.includes(WECHAT_JSAPI_MISSING_IDENTITY_TEXT)
);
}
function isWechatRechargeUnsupportedDeviceError(error: unknown) {
return (
error instanceof Error &&
error.message.includes(WECHAT_RECHARGE_UNSUPPORTED_DEVICE_TEXT)
);
}
type UsePlatformProfileCenterControllerArgs = {
activeTab: string;
isAuthenticated: boolean;
showRechargeEntry: boolean;
onRechargeSuccess?: () => void | Promise<void>;
requestLogin: () => void;
currentUser: AuthUser | null | undefined;
};
function readProfileInviteCodeFromLocationSearch(search: string) {
const params = new URLSearchParams(search);
for (const key of PROFILE_INVITE_QUERY_KEYS) {
const value = (params.get(key) ?? '')
.trim()
.replace(/[^0-9a-z]/giu, '')
.toUpperCase();
if (value) {
return value;
}
}
return '';
}
function clearWechatPayResultHash() {
if (typeof window === 'undefined') {
return;
}
const rawHash = window.location.hash.replace(/^#/, '');
if (!rawHash.includes('wx_pay_result=')) {
return;
}
const params = new URLSearchParams(rawHash);
params.delete('wx_pay_result');
const nextHash = params.toString();
const nextUrl = `${window.location.pathname}${window.location.search}${nextHash ? `#${nextHash}` : ''}`;
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 = '', explicitOrderId = '', ...rawErrors] =
result.split(':');
const inferredOrderId = requestId
.replace(/^wechat_pay_/, '')
.replace(/_\d+$/, '')
.trim();
const orderId = explicitOrderId.trim() || inferredOrderId;
const status =
rawStatus === 'success'
? 'success'
: rawStatus === 'cancel'
? 'cancel'
: 'fail';
let errorMessage: string | null = null;
const rawError = rawErrors.join(':');
if (rawError) {
try {
errorMessage = decodeURIComponent(rawError);
} catch (_error) {
errorMessage = rawError;
}
}
return {
requestId,
orderId: orderId || null,
status,
errorMessage,
};
}
function createWechatRechargeAbortError(signal: AbortSignal) {
return signal.reason instanceof Error
? signal.reason
: new DOMException('Wechat recharge confirmation aborted', 'AbortError');
}
function assertWechatRechargeConfirmationActive(
options: WechatRechargeConfirmationOptions,
) {
if (options.signal.aborted || !options.isCurrent()) {
throw createWechatRechargeAbortError(options.signal);
}
}
function waitWechatPayConfirmDelay(delayMs: number, signal: AbortSignal) {
return new Promise<void>((resolve, reject) => {
if (signal.aborted) {
reject(createWechatRechargeAbortError(signal));
return;
}
const timerId = window.setTimeout(() => {
signal.removeEventListener('abort', handleAbort);
resolve();
}, delayMs);
const handleAbort = () => {
window.clearTimeout(timerId);
signal.removeEventListener('abort', handleAbort);
reject(createWechatRechargeAbortError(signal));
};
signal.addEventListener('abort', handleAbort, { once: true });
});
}
function isWechatRechargeOrderTerminalForConfirmation(
order: Pick<ProfileRechargeOrder, 'status' | 'expirationCheckedAt'>,
) {
if (order.status === 'pending') {
return false;
}
if (order.status === 'expired' && !order.expirationCheckedAt) {
return false;
}
return true;
}
async function confirmWechatRechargeOrderUntilSettled(
orderId: string,
options: WechatRechargeConfirmationOptions,
): Promise<ConfirmWechatProfileRechargeOrderResponse> {
assertWechatRechargeConfirmationActive(options);
let latestResponse = await confirmWechatPlatformProfileRechargeOrder(
orderId,
{ signal: options.signal },
);
assertWechatRechargeConfirmationActive(options);
if (isWechatRechargeOrderTerminalForConfirmation(latestResponse.order)) {
return latestResponse;
}
for (const delayMs of WECHAT_PAY_CONFIRM_RETRY_DELAYS_MS) {
assertWechatRechargeConfirmationActive(options);
await waitWechatPayConfirmDelay(delayMs, options.signal);
assertWechatRechargeConfirmationActive(options);
latestResponse = await confirmWechatPlatformProfileRechargeOrder(orderId, {
signal: options.signal,
});
assertWechatRechargeConfirmationActive(options);
if (isWechatRechargeOrderTerminalForConfirmation(latestResponse.order)) {
return latestResponse;
}
}
try {
assertWechatRechargeConfirmationActive(options);
const streamedResponse = await watchWechatPlatformProfileRechargeOrder(
orderId,
{
signal: options.signal,
},
);
assertWechatRechargeConfirmationActive(options);
return streamedResponse;
} catch {
assertWechatRechargeConfirmationActive(options);
return latestResponse;
}
}
async function confirmWechatRechargeOrderQuickly(
orderId: string,
options: WechatRechargeConfirmationOptions,
): Promise<ConfirmWechatProfileRechargeOrderResponse> {
assertWechatRechargeConfirmationActive(options);
let latestResponse = await confirmWechatPlatformProfileRechargeOrder(
orderId,
{ signal: options.signal },
);
assertWechatRechargeConfirmationActive(options);
if (isWechatRechargeOrderTerminalForConfirmation(latestResponse.order)) {
return latestResponse;
}
for (const delayMs of WECHAT_NATIVE_CONFIRM_RETRY_DELAYS_MS) {
assertWechatRechargeConfirmationActive(options);
await waitWechatPayConfirmDelay(delayMs, options.signal);
assertWechatRechargeConfirmationActive(options);
latestResponse = await confirmWechatPlatformProfileRechargeOrder(orderId, {
signal: options.signal,
});
assertWechatRechargeConfirmationActive(options);
if (isWechatRechargeOrderTerminalForConfirmation(latestResponse.order)) {
return latestResponse;
}
}
return latestResponse;
}
function buildRechargePaymentResultForOrder(
order: Pick<ProfileRechargeOrder, 'status' | 'expirationCheckedAt'>,
): RechargePaymentResult {
switch (order.status) {
case 'paid':
return {
kind: 'success',
title: '支付成功',
message: '已到账,泥点余额已刷新。',
};
case 'expired':
if (!order.expirationCheckedAt) {
return {
kind: 'pending',
title: '支付处理中',
message: '正在等待到账状态确认,请稍后查看泥点余额。',
};
}
return {
kind: 'expired',
title: '支付已过期',
message: '订单已超过支付时限,本次没有入账。',
};
case 'closed':
return {
kind: 'cancel',
title: '支付未完成',
message: '本次没有扣款,泥点余额未发生变化。',
};
case 'failed':
case 'refunded':
return {
kind: 'failed',
title: '支付未完成',
message: '微信支付没有完成,本次不会入账。',
};
case 'pending':
default:
return {
kind: 'pending',
title: '支付处理中',
message: '正在等待到账状态确认,请稍后查看泥点余额。',
};
}
}
export function usePlatformProfileCenterController({
activeTab,
isAuthenticated,
showRechargeEntry,
onRechargeSuccess,
requestLogin,
currentUser,
}: UsePlatformProfileCenterControllerArgs) {
// 中文注释:个人中心里的充值、任务、邀请码、账单等账户商业能力统一由这个 controller 托管,
// 页面层只消费状态与回调,不再直接堆叠一大组本地 state / effect / callback。
const [isRewardCodeOpen, setIsRewardCodeOpen] = useState(false);
const [rewardCodeInput, setRewardCodeInput] = useState('');
const [isSubmittingRewardCode, setIsSubmittingRewardCode] = useState(false);
const [rewardCodeError, setRewardCodeError] = useState<string | null>(null);
const [rewardCodeSuccess, setRewardCodeSuccess] = useState<string | null>(
null,
);
const [isRechargeOpen, setIsRechargeOpen] = useState(false);
const [rechargeCenter, setRechargeCenter] =
useState<ProfileRechargeCenterResponse | null>(null);
const [isLoadingRechargeCenter, setIsLoadingRechargeCenter] = useState(false);
const [rechargeError, setRechargeError] = useState<string | null>(null);
const [rechargePaymentResult, setRechargePaymentResult] =
useState<RechargePaymentResult | null>(null);
const [
wechatRechargeOrderConfirmationState,
setWechatRechargeOrderConfirmationState,
] = useState<WechatRechargeOrderConfirmationState | null>(null);
const [nativeWechatPayment, setNativeWechatPayment] =
useState<NativeWechatPaymentState | null>(null);
const [submittingRechargeProductId, setSubmittingRechargeProductId] =
useState<string | null>(null);
const [isWalletLedgerOpen, setIsWalletLedgerOpen] = useState(false);
const [walletLedger, setWalletLedger] =
useState<ProfileWalletLedgerResponse | null>(null);
const [walletLedgerError, setWalletLedgerError] = useState<string | null>(
null,
);
const [isLoadingWalletLedger, setIsLoadingWalletLedger] = useState(false);
const [profilePopupPanel, setProfilePopupPanel] =
useState<ProfilePopupPanel | null>(null);
const [referralCenter, setReferralCenter] =
useState<ProfileReferralInviteCenterResponse | null>(null);
const [isLoadingReferral, setIsLoadingReferral] = useState(false);
const [isReferralCenterInitialized, setIsReferralCenterInitialized] =
useState(false);
const pendingProfileInviteCode = useMemo(
() =>
typeof window === 'undefined'
? ''
: readProfileInviteCodeFromLocationSearch(window.location.search),
[],
);
const promptedLoginForInviteQueryRef = useRef(false);
const autoOpenedInviteQueryRef = useRef(false);
const [referralRedeemCode, setReferralRedeemCode] = useState(
pendingProfileInviteCode,
);
const [isSubmittingReferralRedeem, setIsSubmittingReferralRedeem] =
useState(false);
const [referralError, setReferralError] = useState<string | null>(null);
const [referralSuccess, setReferralSuccess] = useState<string | null>(null);
const { copyState: inviteCopyState, copyText: copyInviteText } =
useCopyFeedback();
const pendingWechatRechargeOrderRef =
useRef<WechatRechargeOrderLifecycle | null>(null);
const confirmingWechatRechargeOrderRef =
useRef<WechatRechargeOrderLifecycle | null>(null);
const rechargeSubmissionRef = useRef<symbol | null>(null);
const accountWriteAbortControllerRef = useRef(new AbortController());
const rechargeCenterReadRevisionRef = useRef(0);
const accountLifecycleRevisionRef = useRef(0);
const walletLedgerReadRevisionRef = useRef(0);
const referralCenterReadRevisionRef = useRef(0);
const currentUserId = currentUser?.id ?? '';
const currentUserIdRef = useRef(currentUserId);
currentUserIdRef.current = currentUserId;
const captureAccountLifecycle = useCallback(
(): AccountLifecycle => ({
ownerUserId: currentUserId,
revision: accountLifecycleRevisionRef.current,
}),
[currentUserId],
);
const isAccountLifecycleCurrent = useCallback(
(account: AccountLifecycle) =>
account.revision === accountLifecycleRevisionRef.current &&
account.ownerUserId === currentUserIdRef.current,
[],
);
const isRechargeOrderCurrent = useCallback(
(
order: WechatRechargeOrderLifecycle | null | undefined,
): order is WechatRechargeOrderLifecycle =>
Boolean(order) && isAccountLifecycleCurrent(order!.account),
[isAccountLifecycleCurrent],
);
const isSameRechargeOrder = useCallback(
(
left: WechatRechargeOrderLifecycle | null | undefined,
right: WechatRechargeOrderLifecycle | null | undefined,
) => {
if (!left || !right) {
return false;
}
return (
left.orderId === right.orderId &&
left.account.ownerUserId === right.account.ownerUserId &&
left.account.revision === right.account.revision
);
},
[],
);
const abortRechargeOrders = useCallback(
(...orders: Array<WechatRechargeOrderLifecycle | null | undefined>) => {
const controllers = new Set(
orders.flatMap((order) => (order ? [order.abortController] : [])),
);
controllers.forEach((controller) => controller.abort());
},
[],
);
const createRechargeOrderLifecycle = useCallback(
(
orderId: string,
account: AccountLifecycle,
): WechatRechargeOrderLifecycle => ({
orderId,
account,
abortController: new AbortController(),
}),
[],
);
const createRechargeConfirmationOptions = useCallback(
(
order: WechatRechargeOrderLifecycle,
): WechatRechargeConfirmationOptions => ({
signal: order.abortController.signal,
isCurrent: () =>
isRechargeOrderCurrent(order) &&
(isSameRechargeOrder(pendingWechatRechargeOrderRef.current, order) ||
isSameRechargeOrder(confirmingWechatRechargeOrderRef.current, order)),
}),
[isRechargeOrderCurrent, isSameRechargeOrder],
);
const walletOwnerUserId = usePlatformWalletStore(
(state) => state.ownerUserId,
);
const storedMudPointBalance = usePlatformWalletStore(
(state) => state.mudPointBalance,
);
const applyWalletBalanceSnapshot = usePlatformWalletStore(
(state) => state.applyWalletBalanceSnapshot,
);
const captureWalletBalanceSnapshot = usePlatformWalletStore(
(state) => state.captureWalletBalanceSnapshot,
);
const onWalletBalanceMayHaveChanged = usePlatformWalletStore(
(state) => state.onWalletBalanceMayHaveChanged,
);
const walletOwnerMatchesCurrentUser =
Boolean(currentUserId) && walletOwnerUserId === currentUserId;
const mudPointBalance = walletOwnerMatchesCurrentUser
? storedMudPointBalance
: null;
useEffect(() => {
accountWriteAbortControllerRef.current.abort();
const accountWriteAbortController = new AbortController();
accountWriteAbortControllerRef.current = accountWriteAbortController;
rechargeCenterReadRevisionRef.current += 1;
accountLifecycleRevisionRef.current += 1;
walletLedgerReadRevisionRef.current += 1;
referralCenterReadRevisionRef.current += 1;
const pendingOrder = pendingWechatRechargeOrderRef.current;
const confirmingOrder = confirmingWechatRechargeOrderRef.current;
abortRechargeOrders(pendingOrder, confirmingOrder);
if (
(pendingOrder && pendingOrder.account.ownerUserId !== currentUserId) ||
(confirmingOrder && confirmingOrder.account.ownerUserId !== currentUserId)
) {
clearWechatPayResultHash();
}
pendingWechatRechargeOrderRef.current = null;
confirmingWechatRechargeOrderRef.current = null;
rechargeSubmissionRef.current = null;
setRechargeCenter(null);
setIsLoadingRechargeCenter(false);
setRechargeError(null);
setRechargePaymentResult(null);
setWechatRechargeOrderConfirmationState(null);
setNativeWechatPayment(null);
setSubmittingRechargeProductId(null);
setIsWalletLedgerOpen(false);
setWalletLedger(null);
setWalletLedgerError(null);
setIsLoadingWalletLedger(false);
setIsRewardCodeOpen(false);
setRewardCodeInput('');
setIsSubmittingRewardCode(false);
setRewardCodeError(null);
setRewardCodeSuccess(null);
setProfilePopupPanel(null);
setReferralCenter(null);
setIsLoadingReferral(false);
setIsReferralCenterInitialized(false);
setReferralRedeemCode('');
setIsSubmittingReferralRedeem(false);
setReferralError(null);
setReferralSuccess(null);
return () => {
accountWriteAbortController.abort();
};
}, [abortRechargeOrders, currentUserId]);
const rechargeCenterReadAbortControllerRef = useRef<AbortController | null>(
null,
);
useEffect(() => {
return () => {
rechargeCenterReadRevisionRef.current += 1;
accountLifecycleRevisionRef.current += 1;
referralCenterReadRevisionRef.current += 1;
abortRechargeOrders(
pendingWechatRechargeOrderRef.current,
confirmingWechatRechargeOrderRef.current,
);
pendingWechatRechargeOrderRef.current = null;
confirmingWechatRechargeOrderRef.current = null;
rechargeCenterReadAbortControllerRef.current?.abort();
rechargeCenterReadAbortControllerRef.current = null;
};
}, [abortRechargeOrders]);
// 中文注释:支持带邀请码 query 的直达场景,登录成功后自动打开兑换面板并复用同一套输入状态。
useEffect(() => {
if (!pendingProfileInviteCode || autoOpenedInviteQueryRef.current) {
return;
}
if (!currentUser) {
if (!promptedLoginForInviteQueryRef.current) {
promptedLoginForInviteQueryRef.current = true;
requestLogin();
}
return;
}
autoOpenedInviteQueryRef.current = true;
setReferralRedeemCode(pendingProfileInviteCode);
setReferralError(null);
setReferralSuccess(null);
setProfilePopupPanel('redeem');
}, [currentUser, pendingProfileInviteCode, requestLogin]);
const loadWalletLedger = useCallback(() => {
const snapshotOwnerUserId = currentUserId;
const accountRevision = accountLifecycleRevisionRef.current;
const revision = ++walletLedgerReadRevisionRef.current;
setWalletLedgerError(null);
setIsLoadingWalletLedger(true);
void getPlatformProfileWalletLedger()
.then((ledger) => {
if (
revision === walletLedgerReadRevisionRef.current &&
accountRevision === accountLifecycleRevisionRef.current &&
currentUserIdRef.current === snapshotOwnerUserId
) {
setWalletLedger(ledger);
}
})
.catch((error: unknown) => {
if (
revision !== walletLedgerReadRevisionRef.current ||
accountRevision !== accountLifecycleRevisionRef.current ||
currentUserIdRef.current !== snapshotOwnerUserId
) {
return;
}
setWalletLedger(null);
setWalletLedgerError(
error instanceof Error ? error.message : '读取泥点账单失败',
);
})
.finally(() => {
if (
revision === walletLedgerReadRevisionRef.current &&
accountRevision === accountLifecycleRevisionRef.current &&
currentUserIdRef.current === snapshotOwnerUserId
) {
setIsLoadingWalletLedger(false);
}
});
}, [currentUserId]);
const openWalletLedgerPanel = useCallback(() => {
setIsWalletLedgerOpen(true);
loadWalletLedger();
}, [loadWalletLedger]);
const applyRechargeCenter = useCallback(
(
center: ProfileRechargeCenterResponse,
account: AccountLifecycle,
refreshWallet: boolean,
walletSnapshot: ReturnType<typeof captureWalletBalanceSnapshot>,
) => {
if (
!isAccountLifecycleCurrent(account) ||
usePlatformWalletStore.getState().ownerUserId !== account.ownerUserId
) {
return false;
}
rechargeCenterReadRevisionRef.current += 1;
rechargeCenterReadAbortControllerRef.current?.abort();
rechargeCenterReadAbortControllerRef.current = null;
setIsLoadingRechargeCenter(false);
setRechargeError(null);
setRechargeCenter(center);
if (center.mudPointBalance && walletSnapshot) {
applyWalletBalanceSnapshot(walletSnapshot, center.mudPointBalance);
}
if (refreshWallet || !center.mudPointBalance) {
void onWalletBalanceMayHaveChanged();
}
return true;
},
[
applyWalletBalanceSnapshot,
isAccountLifecycleCurrent,
onWalletBalanceMayHaveChanged,
],
);
const loadRechargeCenter = useCallback(() => {
const account = captureAccountLifecycle();
const walletSnapshot = captureWalletBalanceSnapshot(account.ownerUserId);
const revision = ++rechargeCenterReadRevisionRef.current;
rechargeCenterReadAbortControllerRef.current?.abort();
const abortController = new AbortController();
rechargeCenterReadAbortControllerRef.current = abortController;
setRechargeError(null);
setIsLoadingRechargeCenter(true);
void getPlatformProfileRechargeCenter({ signal: abortController.signal })
.then((center) => {
if (
!abortController.signal.aborted &&
revision === rechargeCenterReadRevisionRef.current &&
isAccountLifecycleCurrent(account)
) {
applyRechargeCenter(center, account, false, walletSnapshot);
}
})
.catch((error: unknown) => {
if (
abortController.signal.aborted ||
revision !== rechargeCenterReadRevisionRef.current ||
!isAccountLifecycleCurrent(account)
) {
return;
}
setRechargeCenter(null);
setRechargeError(
error instanceof Error ? error.message : '读取泥点购买信息失败',
);
})
.finally(() => {
if (rechargeCenterReadAbortControllerRef.current === abortController) {
rechargeCenterReadAbortControllerRef.current = null;
}
if (
revision === rechargeCenterReadRevisionRef.current &&
isAccountLifecycleCurrent(account)
) {
setIsLoadingRechargeCenter(false);
}
});
}, [
applyRechargeCenter,
captureAccountLifecycle,
captureWalletBalanceSnapshot,
isAccountLifecycleCurrent,
]);
const refreshRechargeState = useCallback(
(account: AccountLifecycle) => {
if (!isAccountLifecycleCurrent(account)) {
return;
}
loadRechargeCenter();
setSubmittingRechargeProductId(null);
abortRechargeOrders(
pendingWechatRechargeOrderRef.current,
confirmingWechatRechargeOrderRef.current,
);
pendingWechatRechargeOrderRef.current = null;
confirmingWechatRechargeOrderRef.current = null;
setWechatRechargeOrderConfirmationState(null);
setNativeWechatPayment(null);
},
[abortRechargeOrders, isAccountLifecycleCurrent, loadRechargeCenter],
);
const handleWechatPayResult = useCallback(() => {
const payResult = readWechatPayResultFromHash();
if (!payResult) {
return false;
}
const pendingOrder = pendingWechatRechargeOrderRef.current;
if (pendingOrder && !isRechargeOrderCurrent(pendingOrder)) {
return false;
}
if (
pendingOrder &&
payResult.orderId &&
payResult.orderId !== pendingOrder.orderId
) {
return false;
}
// 中文注释:无内存 pending order 的小程序回跳暂按当前账号上下文处理,后端订单
// owner 校验仍是最终安全边界;跨 WebView 恢复若引入持久关联,必须同时校验
// requestId、orderId 与 ownerUserId。
const account = pendingOrder?.account ?? captureAccountLifecycle();
if (!account.ownerUserId || !isAccountLifecycleCurrent(account)) {
return false;
}
if (payResult.status === 'success') {
const orderId = payResult.orderId || pendingOrder?.orderId;
if (!orderId) {
clearWechatPayResultHash();
return true;
}
const order =
pendingOrder ?? createRechargeOrderLifecycle(orderId, account);
if (
isSameRechargeOrder(confirmingWechatRechargeOrderRef.current, order)
) {
clearWechatPayResultHash();
return true;
}
confirmingWechatRechargeOrderRef.current = order;
setWechatRechargeOrderConfirmationState({ orderId });
setSubmittingRechargeProductId(null);
setRechargePaymentResult(null);
const walletSnapshot = captureWalletBalanceSnapshot(account.ownerUserId);
void confirmWechatRechargeOrderUntilSettled(
orderId,
createRechargeConfirmationOptions(order),
)
.then((response) => {
if (
!isRechargeOrderCurrent(order) ||
!isSameRechargeOrder(
confirmingWechatRechargeOrderRef.current,
order,
)
) {
return;
}
const result = buildRechargePaymentResultForOrder(response.order);
const isPaid = result.kind === 'success';
if (
!applyRechargeCenter(response.center, account, true, walletSnapshot)
) {
return;
}
if (
isSameRechargeOrder(pendingWechatRechargeOrderRef.current, order)
) {
pendingWechatRechargeOrderRef.current = null;
}
confirmingWechatRechargeOrderRef.current = null;
order.abortController.abort();
setWechatRechargeOrderConfirmationState(null);
setRechargePaymentResult(result);
if (isPaid) {
void onRechargeSuccess?.();
}
clearWechatPayResultHash();
})
.catch(() => {
if (
!isRechargeOrderCurrent(order) ||
!isSameRechargeOrder(
confirmingWechatRechargeOrderRef.current,
order,
)
) {
return;
}
confirmingWechatRechargeOrderRef.current = null;
setWechatRechargeOrderConfirmationState(null);
setRechargePaymentResult({
kind: 'pending',
title: '支付处理中',
message: '暂时没能确认到账状态,请稍后查看泥点余额。',
});
clearWechatPayResultHash();
});
} else if (payResult.status === 'cancel') {
setRechargePaymentResult({
kind: 'cancel',
title: '支付已取消',
message: '本次没有扣款,泥点余额未发生变化。',
});
setWechatRechargeOrderConfirmationState(null);
refreshRechargeState(account);
} else {
const detail = payResult.errorMessage
? `微信返回:${payResult.errorMessage}`
: '微信支付没有完成,本次不会入账。';
setRechargePaymentResult({
kind: 'failed',
title: '支付未完成',
message: detail,
});
setWechatRechargeOrderConfirmationState(null);
refreshRechargeState(account);
}
clearWechatPayResultHash();
return true;
}, [
applyRechargeCenter,
captureAccountLifecycle,
captureWalletBalanceSnapshot,
createRechargeConfirmationOptions,
createRechargeOrderLifecycle,
isAccountLifecycleCurrent,
isRechargeOrderCurrent,
isSameRechargeOrder,
onRechargeSuccess,
refreshRechargeState,
]);
const pollWechatPayResultFromHash = useCallback(
() => handleWechatPayResult(),
[handleWechatPayResult],
);
const confirmPendingWechatRechargeOrder = useCallback(() => {
if (nativeWechatPayment) {
return false;
}
const order = pendingWechatRechargeOrderRef.current;
if (
!isRechargeOrderCurrent(order) ||
isSameRechargeOrder(confirmingWechatRechargeOrderRef.current, order)
) {
return false;
}
confirmingWechatRechargeOrderRef.current = order;
setWechatRechargeOrderConfirmationState({ orderId: order.orderId });
setRechargePaymentResult(null);
const walletSnapshot = captureWalletBalanceSnapshot(
order.account.ownerUserId,
);
void confirmWechatRechargeOrderUntilSettled(
order.orderId,
createRechargeConfirmationOptions(order),
)
.then((response) => {
if (
!isRechargeOrderCurrent(order) ||
!isSameRechargeOrder(confirmingWechatRechargeOrderRef.current, order)
) {
return;
}
const result = buildRechargePaymentResultForOrder(response.order);
const isPaid = result.kind === 'success';
if (
!applyRechargeCenter(
response.center,
order.account,
true,
walletSnapshot,
)
) {
return;
}
pendingWechatRechargeOrderRef.current = null;
confirmingWechatRechargeOrderRef.current = null;
order.abortController.abort();
setWechatRechargeOrderConfirmationState(null);
setSubmittingRechargeProductId(null);
setRechargePaymentResult(result);
if (isPaid) {
void onRechargeSuccess?.();
}
})
.catch(() => {
if (
!isRechargeOrderCurrent(order) ||
!isSameRechargeOrder(confirmingWechatRechargeOrderRef.current, order)
) {
return;
}
confirmingWechatRechargeOrderRef.current = null;
setWechatRechargeOrderConfirmationState(null);
setRechargePaymentResult({
kind: 'pending',
title: '支付处理中',
message: '暂时没能确认到账状态,请稍后查看泥点余额。',
});
});
return true;
}, [
applyRechargeCenter,
captureWalletBalanceSnapshot,
createRechargeConfirmationOptions,
isRechargeOrderCurrent,
isSameRechargeOrder,
nativeWechatPayment,
onRechargeSuccess,
]);
const openRechargeModal = useCallback(() => {
if (!currentUser) {
requestLogin();
return;
}
setIsRechargeOpen(true);
loadRechargeCenter();
}, [currentUser, loadRechargeCenter, requestLogin]);
const openRewardCodeModal = useCallback(() => {
setIsRewardCodeOpen(true);
setRewardCodeError(null);
setRewardCodeSuccess(null);
}, []);
const openRechargeOrRewardCodeModal = useCallback(() => {
if (showRechargeEntry) {
openRechargeModal();
return;
}
openRewardCodeModal();
}, [openRechargeModal, openRewardCodeModal, showRechargeEntry]);
const closeNativeWechatPayment = useCallback(() => {
if (!nativeWechatPayment || nativeWechatPayment.isConfirming) {
return;
}
const pendingOrder = pendingWechatRechargeOrderRef.current;
if (
pendingOrder?.orderId === nativeWechatPayment.orderId &&
isRechargeOrderCurrent(pendingOrder)
) {
pendingOrder.abortController.abort();
pendingWechatRechargeOrderRef.current = null;
}
setNativeWechatPayment((current) =>
current?.orderId === nativeWechatPayment.orderId && !current.isConfirming
? null
: current,
);
}, [isRechargeOrderCurrent, nativeWechatPayment]);
const buyRechargeProduct = useCallback(
(product: ProfileRechargeProduct) => {
const account = captureAccountLifecycle();
if (
rechargeSubmissionRef.current ||
submittingRechargeProductId ||
!account.ownerUserId ||
!isAccountLifecycleCurrent(account)
) {
return;
}
const submissionToken = Symbol('profile-recharge-submission');
rechargeSubmissionRef.current = submissionToken;
const requestSignal = accountWriteAbortControllerRef.current.signal;
if (requestSignal.aborted) {
rechargeSubmissionRef.current = null;
return;
}
const walletSnapshot = captureWalletBalanceSnapshot(account.ownerUserId);
let createdOrder: WechatRechargeOrderLifecycle | null = null;
const paymentChannel = resolveProfileRechargeProductPaymentChannel(
{ kind: product.kind },
{},
);
setSubmittingRechargeProductId(product.productId);
setRechargeError(null);
setRechargePaymentResult(null);
setWechatRechargeOrderConfirmationState(null);
setNativeWechatPayment(null);
void createPlatformProfileRechargeOrder(
product.productId,
paymentChannel,
{
signal: requestSignal,
},
)
.then(async (response) => {
if (!isAccountLifecycleCurrent(account)) {
return;
}
const order = createRechargeOrderLifecycle(
response.order.orderId,
account,
);
abortRechargeOrders(
pendingWechatRechargeOrderRef.current,
confirmingWechatRechargeOrderRef.current,
);
createdOrder = order;
pendingWechatRechargeOrderRef.current = order;
confirmingWechatRechargeOrderRef.current = null;
if (paymentChannel === WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_CHANNEL) {
if (
!applyRechargeCenter(
response.center,
account,
true,
walletSnapshot,
) ||
!isRechargeOrderCurrent(order)
) {
return;
}
const paymentHandled = await requestHostPayment({
payload: response.wechatMiniProgramPayParams,
orderId: response.order.orderId,
});
if (!isRechargeOrderCurrent(order)) {
return;
}
if (!paymentHandled) {
throw new Error('请在微信小程序内完成支付');
}
return;
}
if (paymentChannel === WECHAT_JSAPI_PAYMENT_CHANNEL) {
if (
!applyRechargeCenter(
response.center,
account,
true,
walletSnapshot,
) ||
!isRechargeOrderCurrent(order)
) {
return;
}
setRechargePaymentResult({
kind: 'pending',
title: '正在打开微信支付',
message: '请在微信支付面板中完成支付。',
});
await requestWechatJsapiPayment(
response.wechatMiniProgramPayParams,
);
if (!isRechargeOrderCurrent(order)) {
return;
}
setRechargePaymentResult({
kind: 'pending',
title: '支付处理中',
message: '正在查询微信支付到账状态。',
});
const confirmationWalletSnapshot = captureWalletBalanceSnapshot(
account.ownerUserId,
);
void confirmWechatRechargeOrderUntilSettled(
response.order.orderId,
createRechargeConfirmationOptions(order),
)
.then((confirmResponse) => {
if (!isRechargeOrderCurrent(order)) {
return;
}
const result = buildRechargePaymentResultForOrder(
confirmResponse.order,
);
const isPaid = result.kind === 'success';
if (
!applyRechargeCenter(
confirmResponse.center,
account,
true,
confirmationWalletSnapshot,
)
) {
return;
}
setRechargePaymentResult(result);
if (result.kind !== 'pending') {
if (
isSameRechargeOrder(
pendingWechatRechargeOrderRef.current,
order,
)
) {
pendingWechatRechargeOrderRef.current = null;
}
order.abortController.abort();
}
if (isPaid) {
void onRechargeSuccess?.();
}
})
.catch(() => {
if (!isRechargeOrderCurrent(order)) {
return;
}
setRechargePaymentResult({
kind: 'pending',
title: '等待微信确认',
message: '暂时没能确认到账状态,请稍后再试。',
});
});
return;
}
if (paymentChannel === WECHAT_H5_PAYMENT_CHANNEL) {
const h5Url = response.wechatH5Payment?.h5Url?.trim();
if (!h5Url) {
throw new Error('微信 H5 支付链接生成失败');
}
if (
!applyRechargeCenter(
response.center,
account,
true,
walletSnapshot,
) ||
!isRechargeOrderCurrent(order)
) {
return;
}
setRechargePaymentResult({
kind: 'pending',
title: '正在打开微信支付',
message: '完成支付后返回页面确认到账状态。',
});
if (!isRechargeOrderCurrent(order)) {
return;
}
await redirectToPaymentUrl(h5Url);
return;
}
if (paymentChannel === WECHAT_NATIVE_PAYMENT_CHANNEL) {
const wechatNativePayment = response.wechatNativePayment;
const codeUrl = wechatNativePayment?.codeUrl?.trim();
const expiresAt = wechatNativePayment?.expiresAt?.trim();
if (!wechatNativePayment || !codeUrl || !expiresAt) {
throw new Error('微信 Native 支付链接生成失败');
}
if (
!applyRechargeCenter(
response.center,
account,
true,
walletSnapshot,
) ||
!isRechargeOrderCurrent(order)
) {
return;
}
setNativeWechatPayment({
...wechatNativePayment,
codeUrl,
expiresAt,
orderId: response.order.orderId,
productTitle: response.order.productTitle,
amountCents: response.order.amountCents,
isConfirming: false,
});
setSubmittingRechargeProductId(null);
return;
}
throw new Error('充值支付渠道无效');
})
.catch((error: unknown) => {
if (!isAccountLifecycleCurrent(account)) {
return;
}
if (
createdOrder &&
isSameRechargeOrder(
pendingWechatRechargeOrderRef.current,
createdOrder,
)
) {
createdOrder.abortController.abort();
pendingWechatRechargeOrderRef.current = null;
}
setNativeWechatPayment(null);
if (
paymentChannel === WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_CHANNEL &&
getHostRuntime().kind === 'wechat_mini_program' &&
isWechatRechargeUnsupportedDeviceError(error)
) {
clearStoredAccessToken();
setSubmittingRechargeProductId(null);
setRechargeError(null);
setRechargePaymentResult({
kind: 'pending',
title: '需要重新登录',
message: '正在打开微信小程序登录,请重新登录后再支付。',
});
void requestHostLogin().catch((miniProgramLoginError: unknown) => {
if (!isAccountLifecycleCurrent(account)) {
return;
}
setRechargePaymentResult(null);
setRechargeError(
miniProgramLoginError instanceof Error
? miniProgramLoginError.message
: '请在微信小程序内重新登录后再支付',
);
});
return;
}
if (
paymentChannel === WECHAT_JSAPI_PAYMENT_CHANNEL &&
isWechatJsapiMissingIdentityError(error)
) {
setSubmittingRechargeProductId(null);
setRechargePaymentResult({
kind: 'pending',
title: '需要微信授权',
message: '正在跳转微信授权,授权后请重新发起支付。',
});
void startWechatBind().catch((wechatLoginError: unknown) => {
if (!isAccountLifecycleCurrent(account)) {
return;
}
setRechargePaymentResult(null);
setRechargeError(
wechatLoginError instanceof Error
? wechatLoginError.message
: '微信授权暂不可用,请稍后再试',
);
});
return;
}
setRechargeError(error instanceof Error ? error.message : '充值失败');
setSubmittingRechargeProductId(null);
})
.finally(() => {
if (rechargeSubmissionRef.current === submissionToken) {
rechargeSubmissionRef.current = null;
}
});
},
[
applyRechargeCenter,
abortRechargeOrders,
captureAccountLifecycle,
captureWalletBalanceSnapshot,
createRechargeConfirmationOptions,
createRechargeOrderLifecycle,
isAccountLifecycleCurrent,
isRechargeOrderCurrent,
isSameRechargeOrder,
onRechargeSuccess,
submittingRechargeProductId,
],
);
const confirmNativeWechatPayment = useCallback(() => {
if (!nativeWechatPayment || nativeWechatPayment.isConfirming) {
return;
}
const order = pendingWechatRechargeOrderRef.current;
if (
!isRechargeOrderCurrent(order) ||
order.orderId !== nativeWechatPayment.orderId
) {
return;
}
setNativeWechatPayment((current) =>
current && current.orderId === nativeWechatPayment.orderId
? { ...current, isConfirming: true, confirmMessage: undefined }
: current,
);
const walletSnapshot = captureWalletBalanceSnapshot(
order.account.ownerUserId,
);
void confirmWechatRechargeOrderQuickly(
nativeWechatPayment.orderId,
createRechargeConfirmationOptions(order),
)
.then((response) => {
if (!isRechargeOrderCurrent(order)) {
return;
}
const result = buildRechargePaymentResultForOrder(response.order);
const isPaid = result.kind === 'success';
if (
!applyRechargeCenter(
response.center,
order.account,
true,
walletSnapshot,
)
) {
return;
}
if (result.kind !== 'pending') {
setNativeWechatPayment(null);
if (
isSameRechargeOrder(pendingWechatRechargeOrderRef.current, order)
) {
pendingWechatRechargeOrderRef.current = null;
}
order.abortController.abort();
setRechargePaymentResult(result);
if (isPaid) {
void onRechargeSuccess?.();
}
} else {
setNativeWechatPayment((current) =>
current && current.orderId === nativeWechatPayment.orderId
? {
...current,
isConfirming: false,
confirmMessage: '暂未确认到账,请确认付款完成后再点一次。',
}
: current,
);
}
})
.catch(() => {
if (!isRechargeOrderCurrent(order)) {
return;
}
setNativeWechatPayment((current) =>
current && current.orderId === nativeWechatPayment.orderId
? {
...current,
isConfirming: false,
confirmMessage: '暂时没能确认到账状态,请稍后再试。',
}
: current,
);
})
.finally(() => {
if (isRechargeOrderCurrent(order)) {
setSubmittingRechargeProductId(null);
}
});
}, [
applyRechargeCenter,
captureWalletBalanceSnapshot,
createRechargeConfirmationOptions,
isRechargeOrderCurrent,
isSameRechargeOrder,
nativeWechatPayment,
onRechargeSuccess,
]);
useEffect(() => {
const orderId = nativeWechatPayment?.orderId;
const expiresAtMs = Date.parse(nativeWechatPayment?.expiresAt ?? '');
const order = pendingWechatRechargeOrderRef.current;
if (
!orderId ||
!Number.isFinite(expiresAtMs) ||
!isRechargeOrderCurrent(order) ||
order.orderId !== orderId
) {
return undefined;
}
let cancelled = false;
const orderConfirmationOptions = createRechargeConfirmationOptions(order);
const effectAbortController = new AbortController();
const abortEffectRequest = () => {
effectAbortController.abort(orderConfirmationOptions.signal.reason);
};
if (orderConfirmationOptions.signal.aborted) {
abortEffectRequest();
} else {
orderConfirmationOptions.signal.addEventListener(
'abort',
abortEffectRequest,
{ once: true },
);
}
const confirmationOptions = {
...orderConfirmationOptions,
signal: effectAbortController.signal,
};
const watchUntilSettled = async () => {
while (
!cancelled &&
Date.now() < expiresAtMs &&
confirmationOptions.isCurrent()
) {
try {
assertWechatRechargeConfirmationActive(confirmationOptions);
const walletSnapshot = captureWalletBalanceSnapshot(
order.account.ownerUserId,
);
const response = await watchWechatPlatformProfileRechargeOrder(
orderId,
{
signal: confirmationOptions.signal,
},
);
assertWechatRechargeConfirmationActive(confirmationOptions);
if (
cancelled ||
!response ||
!isSameRechargeOrder(pendingWechatRechargeOrderRef.current, order)
) {
return;
}
const result = buildRechargePaymentResultForOrder(response.order);
if (
!applyRechargeCenter(
response.center,
order.account,
true,
walletSnapshot,
)
) {
return;
}
if (result.kind === 'pending') {
await waitWechatPayConfirmDelay(
WECHAT_NATIVE_WATCH_RETRY_DELAY_MS,
confirmationOptions.signal,
);
assertWechatRechargeConfirmationActive(confirmationOptions);
continue;
}
pendingWechatRechargeOrderRef.current = null;
if (
isSameRechargeOrder(confirmingWechatRechargeOrderRef.current, order)
) {
confirmingWechatRechargeOrderRef.current = null;
}
order.abortController.abort();
setNativeWechatPayment((current) =>
current?.orderId === orderId ? null : current,
);
setSubmittingRechargeProductId(null);
setRechargePaymentResult(result);
if (result.kind === 'success') {
void onRechargeSuccess?.();
}
return;
} catch {
if (
cancelled ||
confirmationOptions.signal.aborted ||
!confirmationOptions.isCurrent()
) {
return;
}
}
try {
await waitWechatPayConfirmDelay(
WECHAT_NATIVE_WATCH_RETRY_DELAY_MS,
confirmationOptions.signal,
);
assertWechatRechargeConfirmationActive(confirmationOptions);
} catch {
return;
}
}
};
void watchUntilSettled();
return () => {
cancelled = true;
orderConfirmationOptions.signal.removeEventListener(
'abort',
abortEffectRequest,
);
effectAbortController.abort();
};
}, [
nativeWechatPayment?.expiresAt,
nativeWechatPayment?.orderId,
applyRechargeCenter,
captureWalletBalanceSnapshot,
createRechargeConfirmationOptions,
isRechargeOrderCurrent,
isSameRechargeOrder,
onRechargeSuccess,
]);
// 中文注释:H5 / 小程序支付返回页、页面恢复和 hash 轮询都统一走同一套到账确认逻辑,
// 避免页面组件自己感知微信支付细节。
useEffect(() => {
const handleHashChange = () => {
handleWechatPayResult();
};
const handleResume = () => {
if (
typeof document !== 'undefined' &&
document.visibilityState === 'hidden'
) {
return;
}
if (!handleWechatPayResult()) {
confirmPendingWechatRechargeOrder();
}
};
window.addEventListener('hashchange', handleHashChange);
window.addEventListener('focus', handleResume);
window.addEventListener('pageshow', handleResume);
document.addEventListener('visibilitychange', handleResume);
handleWechatPayResult();
return () => {
window.removeEventListener('hashchange', handleHashChange);
window.removeEventListener('focus', handleResume);
window.removeEventListener('pageshow', handleResume);
document.removeEventListener('visibilitychange', handleResume);
};
}, [confirmPendingWechatRechargeOrder, handleWechatPayResult]);
useEffect(() => {
if (!submittingRechargeProductId || wechatRechargeOrderConfirmationState) {
return undefined;
}
const startedAt = Date.now();
let timer: number | null = null;
const pollPayResult = () => {
if (pollWechatPayResultFromHash()) {
return;
}
if (Date.now() - startedAt >= WECHAT_PAY_RESULT_RECHECK_TIMEOUT_MS) {
return;
}
timer = window.setTimeout(
pollPayResult,
WECHAT_PAY_RESULT_RECHECK_INTERVAL_MS,
);
};
timer = window.setTimeout(
pollPayResult,
WECHAT_PAY_RESULT_RECHECK_INTERVAL_MS,
);
return () => {
if (timer !== null) {
window.clearTimeout(timer);
}
};
}, [
pollWechatPayResultFromHash,
submittingRechargeProductId,
wechatRechargeOrderConfirmationState,
]);
const loadReferralCenter = useCallback(() => {
const account = captureAccountLifecycle();
const revision = ++referralCenterReadRevisionRef.current;
setIsLoadingReferral(true);
setIsReferralCenterInitialized(false);
void getPlatformProfileReferralInviteCenter()
.then((center) => {
if (
revision === referralCenterReadRevisionRef.current &&
isAccountLifecycleCurrent(account)
) {
setReferralCenter(center);
}
})
.catch((error: unknown) => {
if (
revision !== referralCenterReadRevisionRef.current ||
!isAccountLifecycleCurrent(account)
) {
return;
}
setReferralCenter(null);
setReferralError(
error instanceof Error ? error.message : '读取邀请码失败',
);
})
.finally(() => {
if (
revision === referralCenterReadRevisionRef.current &&
isAccountLifecycleCurrent(account)
) {
setIsReferralCenterInitialized(true);
setIsLoadingReferral(false);
}
});
}, [captureAccountLifecycle, isAccountLifecycleCurrent]);
useEffect(() => {
if (activeTab !== 'profile' || !isAuthenticated) {
setIsReferralCenterInitialized(false);
setReferralCenter(null);
return;
}
loadReferralCenter();
}, [activeTab, isAuthenticated, loadReferralCenter]);
const openProfilePopupPanel = useCallback(
(panel: ProfileReferralPanel) => {
setProfilePopupPanel(panel);
setReferralError(null);
setReferralSuccess(null);
if (panel === 'redeem') {
setReferralRedeemCode(pendingProfileInviteCode);
}
if (panel === 'community') {
return;
}
if (!isReferralCenterInitialized && !isLoadingReferral) {
loadReferralCenter();
}
},
[
isLoadingReferral,
isReferralCenterInitialized,
loadReferralCenter,
pendingProfileInviteCode,
],
);
const closeProfilePopupPanel = useCallback(() => {
setProfilePopupPanel(null);
}, []);
const copyInviteInfo = useCallback(() => {
if (!referralCenter?.inviteCode) {
return;
}
const inviteUrl =
typeof window === 'undefined'
? referralCenter.inviteLinkPath
: new URL(referralCenter.inviteLinkPath, window.location.origin).href;
void copyInviteText(`${referralCenter.inviteCode} ${inviteUrl}`).then(
(copied) => {
setReferralSuccess(copied ? '已复制' : '复制失败');
},
);
}, [copyInviteText, referralCenter]);
const submitReferralRedeemCode = useCallback(() => {
const inviteCode = referralRedeemCode
.trim()
.replace(/[^0-9a-z]/gi, '')
.toUpperCase();
if (isSubmittingReferralRedeem || !inviteCode) {
return;
}
setIsSubmittingReferralRedeem(true);
setReferralError(null);
setReferralSuccess(null);
const snapshotOwnerUserId = currentUserId;
const accountRevision = accountLifecycleRevisionRef.current;
const requestSignal = accountWriteAbortControllerRef.current.signal;
if (requestSignal.aborted) {
setIsSubmittingReferralRedeem(false);
return;
}
void redeemPlatformProfileReferralInviteCode(inviteCode, {
signal: requestSignal,
})
.then((response) => {
if (
accountRevision !== accountLifecycleRevisionRef.current ||
currentUserIdRef.current !== snapshotOwnerUserId
) {
return;
}
setReferralCenter(response.center);
setReferralRedeemCode('');
setReferralSuccess('已填写');
void onWalletBalanceMayHaveChanged();
void onRechargeSuccess?.();
})
.catch((error: unknown) => {
if (
accountRevision !== accountLifecycleRevisionRef.current ||
currentUserIdRef.current !== snapshotOwnerUserId
) {
return;
}
setReferralError(
error instanceof Error ? error.message : '填写邀请码失败',
);
})
.finally(() => {
if (
accountRevision === accountLifecycleRevisionRef.current &&
currentUserIdRef.current === snapshotOwnerUserId
) {
setIsSubmittingReferralRedeem(false);
}
});
}, [
currentUserId,
isSubmittingReferralRedeem,
onRechargeSuccess,
onWalletBalanceMayHaveChanged,
referralRedeemCode,
]);
const submitRewardCode = useCallback(() => {
if (isSubmittingRewardCode || !rewardCodeInput.trim()) {
return;
}
setIsSubmittingRewardCode(true);
setRewardCodeError(null);
setRewardCodeSuccess(null);
const snapshotOwnerUserId = currentUserId;
const accountRevision = accountLifecycleRevisionRef.current;
const requestSignal = accountWriteAbortControllerRef.current.signal;
if (requestSignal.aborted) {
setIsSubmittingRewardCode(false);
return;
}
void redeemPlatformProfileRewardCode(rewardCodeInput, {
signal: requestSignal,
})
.then((response: RedeemProfileRewardCodeResponse) => {
if (
accountRevision !== accountLifecycleRevisionRef.current ||
currentUserIdRef.current !== snapshotOwnerUserId
) {
return;
}
setRewardCodeInput('');
setRewardCodeSuccess(`已到账 ${response.amountGranted} 泥点`);
void onWalletBalanceMayHaveChanged();
void onRechargeSuccess?.();
})
.catch((error: unknown) => {
if (
accountRevision !== accountLifecycleRevisionRef.current ||
currentUserIdRef.current !== snapshotOwnerUserId
) {
return;
}
setRewardCodeError(error instanceof Error ? error.message : '兑换失败');
})
.finally(() => {
if (
accountRevision === accountLifecycleRevisionRef.current &&
currentUserIdRef.current === snapshotOwnerUserId
) {
setIsSubmittingRewardCode(false);
}
});
}, [
currentUserId,
isSubmittingRewardCode,
onRechargeSuccess,
onWalletBalanceMayHaveChanged,
rewardCodeInput,
]);
const rechargeModalCenter = useMemo(() => {
if (!walletOwnerMatchesCurrentUser || !rechargeCenter) {
return null;
}
return {
...rechargeCenter,
walletBalance:
mudPointBalance?.totalPoints ?? rechargeCenter.walletBalance,
mudPointBalance: mudPointBalance ?? rechargeCenter.mudPointBalance,
};
}, [mudPointBalance, rechargeCenter, walletOwnerMatchesCurrentUser]);
return {
closeNativeWechatPayment,
closeProfilePopupPanel,
confirmNativeWechatPayment,
inviteCopyState: inviteCopyState as CopyFeedbackState,
isLoadingRechargeCenter,
isLoadingReferral,
isLoadingWalletLedger,
isRechargeOpen,
isRewardCodeOpen,
isSubmittingReferralRedeem,
isSubmittingRewardCode,
isWalletLedgerOpen,
loadRechargeCenter,
loadReferralCenter,
loadWalletLedger,
nativeWechatPayment,
openProfilePopupPanel,
openRechargeOrRewardCodeModal,
openRewardCodeModal,
openWalletLedgerPanel,
profilePopupPanel,
rechargeCenter: rechargeModalCenter,
rechargeError,
rechargePaymentResult,
referralCenter,
referralError,
referralRedeemCode,
referralSuccess,
rewardCodeError,
rewardCodeInput,
rewardCodeSuccess,
setIsRechargeOpen,
setIsRewardCodeOpen,
setIsWalletLedgerOpen,
setRechargePaymentResult,
setReferralRedeemCode,
setRewardCodeInput,
showRechargeEntry,
submittingRechargeProductId,
submitReferralRedeemCode,
submitRewardCode,
walletLedger,
walletLedgerError,
wechatRechargeOrderConfirmationState,
buyRechargeProduct,
copyInviteInfo,
};
}