Persist api-server logs and refresh recharge balance

This commit is contained in:
2026-05-15 01:07:39 +08:00
parent 2801b55d2f
commit 8ade75390c
11 changed files with 406 additions and 54 deletions

View File

@@ -1031,7 +1031,10 @@ test('profile recharge modal buys points through mock channel outside mini progr
'mock',
);
});
expect(await screen.findByText('已到账')).toBeTruthy();
expect(
await screen.findByRole('dialog', { name: '支付成功' }),
).toBeTruthy();
expect(screen.getByText('已到账,账户状态已刷新。')).toBeTruthy();
expect(onRechargeSuccess).toHaveBeenCalledTimes(1);
});
@@ -1114,7 +1117,10 @@ test('profile recharge modal posts requestPayment params in mini program web-vie
});
expect(navigateUrl).toContain('order-wechat-1');
expect(decodeURIComponent(navigateUrl)).toContain('prepay_id=wx-prepay');
expect(await screen.findByText('已到账')).toBeTruthy();
expect(
await screen.findByRole('dialog', { name: '支付成功' }),
).toBeTruthy();
expect(screen.getByText('已到账,账户状态已刷新。')).toBeTruthy();
expect(mockConfirmWechatRpgProfileRechargeOrder).toHaveBeenCalledWith(
'order-wechat-1',
);
@@ -1202,7 +1208,9 @@ test('profile recharge modal loads wechat js sdk before mini program payment bri
window.location.hash = `wx_pay_result=${requestId}:success`;
window.dispatchEvent(new HashChangeEvent('hashchange'));
});
expect(await screen.findByText('已到账')).toBeTruthy();
expect(
await screen.findByRole('dialog', { name: '支付成功' }),
).toBeTruthy();
});
test('profile recharge modal releases submitting state after cancelled wechat pay result', async () => {
@@ -1283,7 +1291,10 @@ test('profile recharge modal releases submitting state after cancelled wechat pa
window.dispatchEvent(new HashChangeEvent('hashchange'));
});
expect(await screen.findByText('支付已取消')).toBeTruthy();
expect(
await screen.findByRole('dialog', { name: '支付已取消' }),
).toBeTruthy();
expect(screen.getByText('本次没有扣款,账户状态未发生变化。')).toBeTruthy();
await waitFor(() => {
expect(
within(screen.getByRole('button', { name: /60/u })).getByText(

View File

@@ -1,7 +1,9 @@
import {
ArrowRight,
AlertCircle,
BookOpen,
Camera,
CheckCircle2,
ChevronDown,
ChevronRight,
Clock3,
@@ -26,6 +28,7 @@ import {
Ticket,
UserPlus,
UserRound,
XCircle,
} from 'lucide-react';
import {
type ComponentType,
@@ -222,6 +225,12 @@ type WechatPayResult = {
orderId: string | null;
status: WechatMiniProgramPaymentStatus;
};
type RechargePaymentResultKind = 'success' | 'pending' | 'cancel' | 'failed';
type RechargePaymentResult = {
kind: RechargePaymentResultKind;
title: string;
message: string;
};
type DiscoverChannel =
| 'recommend'
| 'today'
@@ -2501,7 +2510,6 @@ function ProfileRechargeModal({
center,
isLoading,
error,
success,
submittingProductId,
activeTab,
onTabChange,
@@ -2512,7 +2520,6 @@ function ProfileRechargeModal({
center: ProfileRechargeCenterResponse | null;
isLoading: boolean;
error: string | null;
success: string | null;
submittingProductId: string | null;
activeTab: RechargeTab;
onTabChange: (tab: RechargeTab) => void;
@@ -2582,11 +2589,6 @@ function ProfileRechargeModal({
</button>
</div>
) : null}
{success ? (
<div className="platform-profile-success mt-4 rounded-2xl px-3 py-2 text-xs font-semibold">
{success}
</div>
) : null}
{isLoading ? (
<div className="mt-4 grid gap-3 sm:grid-cols-2">
@@ -2619,6 +2621,62 @@ function ProfileRechargeModal({
);
}
function RechargePaymentResultModal({
result,
onClose,
}: {
result: RechargePaymentResult;
onClose: () => void;
}) {
const Icon =
result.kind === 'success'
? CheckCircle2
: result.kind === 'cancel'
? XCircle
: AlertCircle;
const iconClass =
result.kind === 'success'
? 'text-[var(--platform-success-text)]'
: result.kind === 'cancel'
? 'text-[var(--platform-text-soft)]'
: 'text-[var(--platform-button-danger-text)]';
return (
<div className="platform-modal-backdrop fixed inset-0 z-[90] flex items-center justify-center px-4 py-6">
<div
role="dialog"
aria-modal="true"
aria-labelledby="recharge-payment-result-title"
className="platform-modal-shell platform-remap-surface w-full max-w-sm overflow-hidden rounded-[1.4rem]"
>
<div className="px-5 pb-5 pt-6 text-center">
<div
className={`mx-auto flex h-14 w-14 items-center justify-center rounded-full bg-white/10 ${iconClass}`}
>
<Icon className="h-8 w-8" aria-hidden="true" />
</div>
<div
id="recharge-payment-result-title"
className="mt-4 text-xl font-black text-[var(--platform-text-strong)]"
>
{result.title}
</div>
<div className="mt-3 text-sm font-semibold leading-6 text-[var(--platform-text-soft)]">
{result.message}
</div>
<button
type="button"
onClick={onClose}
className="platform-primary-button mt-5 w-full rounded-2xl px-4 py-3 text-sm font-black"
>
</button>
</div>
</div>
</div>
);
}
function WalletLedgerModal({
ledger,
fallbackBalance,
@@ -3324,7 +3382,8 @@ export function RpgEntryHomeView({
useState<ProfileRechargeCenterResponse | null>(null);
const [isLoadingRechargeCenter, setIsLoadingRechargeCenter] = useState(false);
const [rechargeError, setRechargeError] = useState<string | null>(null);
const [rechargeSuccess, setRechargeSuccess] = useState<string | null>(null);
const [rechargePaymentResult, setRechargePaymentResult] =
useState<RechargePaymentResult | null>(null);
const [activeRechargeTab, setActiveRechargeTab] =
useState<RechargeTab>('points');
const [submittingRechargeProductId, setSubmittingRechargeProductId] =
@@ -3869,27 +3928,56 @@ export function RpgEntryHomeView({
}
if (payResult.status === 'success') {
setRechargeSuccess('支付已提交');
setRechargePaymentResult({
kind: 'pending',
title: '支付已提交',
message: '正在确认到账状态,请稍后查看余额或会员状态。',
});
if (payResult.orderId) {
void confirmWechatRpgProfileRechargeOrder(payResult.orderId)
.then((response) => {
setRechargeCenter(response.center);
setRechargeSuccess(
response.order.status === 'paid' ? '已到账' : '支付已提交',
setRechargePaymentResult(
response.order.status === 'paid'
? {
kind: 'success',
title: '支付成功',
message: '已到账,账户状态已刷新。',
}
: {
kind: 'pending',
title: '支付已提交',
message: '正在等待微信支付确认,请稍后查看账户状态。',
},
);
setSubmittingRechargeProductId(null);
pendingWechatRechargeOrderIdRef.current = null;
})
.catch(() => refreshRechargeState());
.catch(() => {
setRechargePaymentResult({
kind: 'pending',
title: '支付已提交',
message: '暂时没能确认到账状态,请稍后查看余额或会员状态。',
});
refreshRechargeState();
});
} else {
refreshRechargeState();
}
void onRechargeSuccess?.();
} else if (payResult.status === 'cancel') {
setRechargeSuccess('支付已取消');
setRechargePaymentResult({
kind: 'cancel',
title: '支付已取消',
message: '本次没有扣款,账户状态未发生变化。',
});
refreshRechargeState();
} else {
setRechargeError('微信支付未完成');
setRechargePaymentResult({
kind: 'failed',
title: '支付未完成',
message: '微信支付没有完成,本次不会入账。',
});
refreshRechargeState();
}
@@ -3902,7 +3990,6 @@ export function RpgEntryHomeView({
}
setIsRechargeOpen(true);
setRechargeSuccess(null);
loadRechargeCenter();
};
const buyRechargeProduct = (product: ProfileRechargeProduct) => {
@@ -3915,7 +4002,6 @@ export function RpgEntryHomeView({
: 'mock';
setSubmittingRechargeProductId(product.productId);
setRechargeError(null);
setRechargeSuccess(null);
void createRpgProfileRechargeOrder(product.productId, paymentChannel)
.then(async (response) => {
if (paymentChannel === WECHAT_MINI_PROGRAM_PAYMENT_CHANNEL) {
@@ -3928,7 +4014,11 @@ export function RpgEntryHomeView({
return;
} else {
setRechargeCenter(response.center);
setRechargeSuccess('已到账');
setRechargePaymentResult({
kind: 'success',
title: '支付成功',
message: '已到账,账户状态已刷新。',
});
pendingWechatRechargeOrderIdRef.current = null;
setSubmittingRechargeProductId(null);
}
@@ -5712,7 +5802,6 @@ export function RpgEntryHomeView({
center={rechargeCenter}
isLoading={isLoadingRechargeCenter}
error={rechargeError}
success={rechargeSuccess}
submittingProductId={submittingRechargeProductId}
activeTab={activeRechargeTab}
onTabChange={setActiveRechargeTab}
@@ -5721,6 +5810,12 @@ export function RpgEntryHomeView({
onBuy={buyRechargeProduct}
/>
) : null;
const rechargePaymentResultModal: ReactNode = rechargePaymentResult ? (
<RechargePaymentResultModal
result={rechargePaymentResult}
onClose={() => setRechargePaymentResult(null)}
/>
) : null;
if (!isDesktopLayout) {
const isMobileRecommendTab = activeTab === 'home';
@@ -5804,6 +5899,7 @@ export function RpgEntryHomeView({
) : null}
{rewardCodeModal}
{rechargeModal}
{rechargePaymentResultModal}
{isTaskCenterOpen ? (
<ProfileTaskCenterModal
center={taskCenter}
@@ -5935,6 +6031,7 @@ export function RpgEntryHomeView({
</div>
{rewardCodeModal}
{rechargeModal}
{rechargePaymentResultModal}
{isTaskCenterOpen ? (
<ProfileTaskCenterModal
center={taskCenter}