fix: 暗色主题二轮修复 - 支付页面、订单状态、Stripe弹窗、Fallback组件

- PaymentForm: 快速金额选中暗色态、禁用按钮暗色态
- PaymentQRCode: Stripe错误框、成功图标、取消按钮暗色适配
- MobileOrderList: 底部文字对比度修复(slate-600→slate-400)
- OrderStatus: getStatusConfig增加isDark参数,所有状态色适配暗色
- result/page: getStatusConfig传isDark、链接按钮暗色、Fallback暗色背景
- stripe-popup/page: 金额/成功图标/链接/禁用按钮/Fallback暗色适配
- pay/page: 帮助图片容器、错误提示、Fallback暗色背景
- orders/page: 错误提示、Fallback暗色背景
- README: 补充lang参数说明
This commit is contained in:
erio
2026-03-14 03:27:24 +08:00
parent 3b5a3ba5df
commit 48e94c205a
9 changed files with 85 additions and 70 deletions

View File

@@ -308,6 +308,7 @@ Sub2API **v0.1.88** 及以上版本会自动拼接以下参数,无需手动添
| `user_id` | Sub2API 用户 ID |
| `token` | 用户登录 Token有 token 才能查看订单历史) |
| `theme` | `light`(默认)或 `dark` |
| `lang` | 界面语言,`zh`(默认)或 `en` |
| `ui_mode` | `standalone`(默认)或 `embedded`iframe 嵌入) |
---

View File

@@ -177,7 +177,7 @@ function OrdersContent() {
<div className={`flex min-h-screen items-center justify-center p-4 ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
<div className="text-center text-red-500">
<p className="text-lg font-medium">{text.missingAuth}</p>
<p className="mt-2 text-sm text-gray-500">{text.visitOrders}</p>
<p className={`mt-2 text-sm ${isDark ? 'text-slate-400' : 'text-gray-500'}`}>{text.visitOrders}</p>
</div>
</div>
);
@@ -238,10 +238,11 @@ function OrdersContent() {
function OrdersPageFallback() {
const searchParams = useSearchParams();
const locale = resolveLocale(searchParams.get('lang'));
const isDark = searchParams.get('theme') === 'dark';
return (
<div className="flex min-h-screen items-center justify-center">
<div className="text-gray-500">{pickLocaleText(locale, '加载中...', 'Loading...')}</div>
<div className={`flex min-h-screen items-center justify-center ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
<div className={isDark ? 'text-slate-400' : 'text-gray-500'}>{pickLocaleText(locale, '加载中...', 'Loading...')}</div>
</div>
);
}

View File

@@ -110,7 +110,7 @@ function PayContent() {
{pickLocaleText(locale, '帮助', 'Support')}
</div>
{helpImageUrl && (
<img src={helpImageUrl} alt="help" onClick={() => setHelpImageOpen(true)} className="mt-3 max-h-40 w-full cursor-zoom-in rounded-lg object-contain bg-white/70 p-2" />
<img src={helpImageUrl} alt="help" onClick={() => setHelpImageOpen(true)} className={`mt-3 max-h-40 w-full cursor-zoom-in rounded-lg object-contain p-2 ${isDark ? 'bg-slate-700/50' : 'bg-white/70'}`} />
)}
{helpText && (
<div className={['mt-3 space-y-1 text-sm leading-6', isDark ? 'text-slate-300' : 'text-slate-600'].join(' ')}>
@@ -301,7 +301,7 @@ function PayContent() {
<div className={`flex min-h-screen items-center justify-center p-4 ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
<div className="text-center text-red-500">
<p className="text-lg font-medium">{pickLocaleText(locale, '缺少认证信息', 'Missing authentication info')}</p>
<p className="mt-2 text-sm text-gray-500">
<p className={`mt-2 text-sm ${isDark ? 'text-slate-400' : 'text-gray-500'}`}>
{pickLocaleText(locale, '请从 Sub2API 平台正确访问充值页面', 'Please open the recharge page from the Sub2API platform')}
</p>
</div>
@@ -314,7 +314,7 @@ function PayContent() {
<div className={`flex min-h-screen items-center justify-center p-4 ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
<div className="text-center text-red-500">
<p className="text-lg font-medium">{pickLocaleText(locale, '用户不存在', 'User not found')}</p>
<p className="mt-2 text-sm text-gray-500">
<p className={`mt-2 text-sm ${isDark ? 'text-slate-400' : 'text-gray-500'}`}>
{pickLocaleText(locale, '请检查链接是否正确,或联系管理员', 'Please check whether the link is correct or contact the administrator')}
</p>
</div>
@@ -852,7 +852,7 @@ function PayContent() {
{pickLocaleText(locale, '帮助', 'Support')}
</div>
{helpImageUrl && (
<img src={helpImageUrl} alt="help" onClick={() => setHelpImageOpen(true)} className="mt-3 max-h-40 w-full cursor-zoom-in rounded-lg object-contain bg-white/70 p-2" />
<img src={helpImageUrl} alt="help" onClick={() => setHelpImageOpen(true)} className={`mt-3 max-h-40 w-full cursor-zoom-in rounded-lg object-contain p-2 ${isDark ? 'bg-slate-700/50' : 'bg-white/70'}`} />
)}
{helpText && (
<div className={['mt-3 space-y-1 text-sm leading-6', isDark ? 'text-slate-300' : 'text-slate-600'].join(' ')}>
@@ -935,9 +935,10 @@ function PayContent() {
function PayPageFallback() {
const searchParams = useSearchParams();
const locale = resolveLocale(searchParams.get('lang'));
const isDark = searchParams.get('theme') === 'dark';
return (
<div className="flex min-h-screen items-center justify-center">
<div className="text-gray-500">{pickLocaleText(locale, '加载中...', 'Loading...')}</div>
<div className={`flex min-h-screen items-center justify-center ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
<div className={isDark ? 'text-slate-400' : 'text-gray-500'}>{pickLocaleText(locale, '加载中...', 'Loading...')}</div>
</div>
);
}

View File

@@ -54,12 +54,12 @@ function closeCurrentWindow() {
}, 250);
}
function getStatusConfig(order: PublicOrderStatusSnapshot | null, locale: Locale, hasAccessToken: boolean) {
function getStatusConfig(order: PublicOrderStatusSnapshot | null, locale: Locale, hasAccessToken: boolean, isDark = false) {
if (!order) {
return locale === 'en'
? {
label: 'Payment Error',
color: 'text-red-600',
color: isDark ? 'text-red-400' : 'text-red-600',
icon: '✗',
message: hasAccessToken
? 'Unable to load the order status. Please try again later.'
@@ -67,7 +67,7 @@ function getStatusConfig(order: PublicOrderStatusSnapshot | null, locale: Locale
}
: {
label: '支付异常',
color: 'text-red-600',
color: isDark ? 'text-red-400' : 'text-red-600',
icon: '✗',
message: hasAccessToken ? '未查询到订单状态,请稍后重试。' : '订单访问凭证缺失,请返回原充值页查看订单结果。',
};
@@ -77,11 +77,11 @@ function getStatusConfig(order: PublicOrderStatusSnapshot | null, locale: Locale
return locale === 'en'
? {
label: 'Recharge Successful',
color: 'text-green-600',
color: isDark ? 'text-green-400' : 'text-green-600',
icon: '✓',
message: 'Your balance has been credited successfully.',
}
: { label: '充值成功', color: 'text-green-600', icon: '✓', message: '余额已成功到账!' };
: { label: '充值成功', color: isDark ? 'text-green-400' : 'text-green-600', icon: '✓', message: '余额已成功到账!' };
}
if (order.paymentSuccess) {
@@ -89,25 +89,25 @@ function getStatusConfig(order: PublicOrderStatusSnapshot | null, locale: Locale
return locale === 'en'
? {
label: 'Top-up Processing',
color: 'text-blue-600',
color: isDark ? 'text-blue-400' : 'text-blue-600',
icon: '⟳',
message: 'Payment succeeded, and the balance top-up is being processed.',
}
: { label: '充值处理中', color: 'text-blue-600', icon: '⟳', message: '支付成功,余额正在充值中...' };
: { label: '充值处理中', color: isDark ? 'text-blue-400' : 'text-blue-600', icon: '⟳', message: '支付成功,余额正在充值中...' };
}
if (order.rechargeStatus === 'failed') {
return locale === 'en'
? {
label: 'Payment Successful',
color: 'text-amber-600',
color: isDark ? 'text-amber-400' : 'text-amber-600',
icon: '!',
message:
'Payment succeeded, but the balance top-up has not completed yet. Please check again later or contact the administrator.',
}
: {
label: '支付成功',
color: 'text-amber-600',
color: isDark ? 'text-amber-400' : 'text-amber-600',
icon: '!',
message: '支付成功,但余额充值暂未完成,请稍后查看订单结果或联系管理员。',
};
@@ -116,30 +116,30 @@ function getStatusConfig(order: PublicOrderStatusSnapshot | null, locale: Locale
if (order.status === 'PENDING') {
return locale === 'en'
? { label: 'Awaiting Payment', color: 'text-yellow-600', icon: '⏳', message: 'The order has not been paid yet.' }
: { label: '等待支付', color: 'text-yellow-600', icon: '⏳', message: '订单尚未完成支付。' };
? { label: 'Awaiting Payment', color: isDark ? 'text-yellow-400' : 'text-yellow-600', icon: '⏳', message: 'The order has not been paid yet.' }
: { label: '等待支付', color: isDark ? 'text-yellow-400' : 'text-yellow-600', icon: '⏳', message: '订单尚未完成支付。' };
}
if (order.status === 'EXPIRED') {
return locale === 'en'
? {
label: 'Order Expired',
color: 'text-gray-500',
color: isDark ? 'text-slate-400' : 'text-gray-500',
icon: '⏰',
message: 'This order has expired. Please create a new order.',
}
: { label: '订单已超时', color: 'text-gray-500', icon: '⏰', message: '订单已超时,请重新充值。' };
: { label: '订单已超时', color: isDark ? 'text-slate-400' : 'text-gray-500', icon: '⏰', message: '订单已超时,请重新充值。' };
}
if (order.status === 'CANCELLED') {
return locale === 'en'
? { label: 'Order Cancelled', color: 'text-gray-500', icon: '✗', message: 'This order has been cancelled.' }
: { label: '订单已取消', color: 'text-gray-500', icon: '✗', message: '订单已被取消。' };
? { label: 'Order Cancelled', color: isDark ? 'text-slate-400' : 'text-gray-500', icon: '✗', message: 'This order has been cancelled.' }
: { label: '订单已取消', color: isDark ? 'text-slate-400' : 'text-gray-500', icon: '✗', message: '订单已被取消。' };
}
return locale === 'en'
? { label: 'Payment Error', color: 'text-red-600', icon: '✗', message: 'Please contact the administrator.' }
: { label: '支付异常', color: 'text-red-600', icon: '✗', message: '请联系管理员处理。' };
? { label: 'Payment Error', color: isDark ? 'text-red-400' : 'text-red-600', icon: '✗', message: 'Please contact the administrator.' }
: { label: '支付异常', color: isDark ? 'text-red-400' : 'text-red-600', icon: '✗', message: '请联系管理员处理。' };
}
function ResultContent() {
@@ -233,7 +233,7 @@ function ResultContent() {
);
}
const display = getStatusConfig(orderState, locale, Boolean(accessToken));
const display = getStatusConfig(orderState, locale, Boolean(accessToken), isDark);
return (
<div className={`flex min-h-screen items-center justify-center p-4 ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
@@ -254,14 +254,14 @@ function ResultContent() {
<button
type="button"
onClick={closeCurrentWindow}
className="text-sm text-blue-600 underline hover:text-blue-700"
className={`text-sm underline ${isDark ? 'text-blue-400 hover:text-blue-300' : 'text-blue-600 hover:text-blue-700'}`}
>
{text.closeNow}
</button>
</div>
)
) : (
<button type="button" onClick={goBack} className="mt-4 text-sm text-blue-600 underline hover:text-blue-700">
<button type="button" onClick={goBack} className={`mt-4 text-sm underline ${isDark ? 'text-blue-400 hover:text-blue-300' : 'text-blue-600 hover:text-blue-700'}`}>
{text.back}
</button>
)}
@@ -277,10 +277,11 @@ function ResultContent() {
function ResultPageFallback() {
const searchParams = useSearchParams();
const locale = resolveLocale(searchParams.get('lang'));
const isDark = searchParams.get('theme') === 'dark';
return (
<div className="flex min-h-screen items-center justify-center bg-slate-50">
<div className="text-gray-500">{pickLocaleText(locale, '加载中...', 'Loading...')}</div>
<div className={`flex min-h-screen items-center justify-center ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
<div className={isDark ? 'text-slate-400' : 'text-gray-500'}>{pickLocaleText(locale, '加载中...', 'Loading...')}</div>
</div>
);
}

View File

@@ -195,7 +195,7 @@ function StripePopupContent() {
className={`w-full max-w-md space-y-4 rounded-2xl border p-6 ${isDark ? 'border-slate-700 bg-slate-900' : 'border-slate-200 bg-white'} shadow-lg`}
>
<div className="text-center">
<div className="text-3xl font-bold text-blue-600">
<div className={`text-3xl font-bold ${isDark ? 'text-blue-400' : 'text-blue-600'}`}>
{'¥'}
{amount.toFixed(2)}
</div>
@@ -213,7 +213,7 @@ function StripePopupContent() {
<button
type="button"
onClick={() => window.close()}
className="w-full text-sm text-blue-600 underline hover:text-blue-700"
className={`w-full text-sm underline ${isDark ? 'text-blue-400 hover:text-blue-300' : 'text-blue-600 hover:text-blue-700'}`}
>
{text.closeWindow}
</button>
@@ -235,7 +235,7 @@ function StripePopupContent() {
className={`w-full max-w-md space-y-4 rounded-2xl border p-6 ${isDark ? 'border-slate-700 bg-slate-900' : 'border-slate-200 bg-white'} shadow-lg`}
>
<div className="text-center">
<div className="text-3xl font-bold text-blue-600">
<div className={`text-3xl font-bold ${isDark ? 'text-blue-400' : 'text-blue-600'}`}>
{'¥'}
{amount.toFixed(2)}
</div>
@@ -251,12 +251,12 @@ function StripePopupContent() {
</div>
) : stripeSuccess ? (
<div className="py-6 text-center">
<div className="text-5xl text-green-600">{'✓'}</div>
<div className={`text-5xl ${isDark ? 'text-green-400' : 'text-green-600'}`}>{'✓'}</div>
<p className={`mt-3 text-sm ${isDark ? 'text-slate-400' : 'text-gray-500'}`}>{text.successClosing}</p>
<button
type="button"
onClick={() => window.close()}
className="mt-4 text-sm text-blue-600 underline hover:text-blue-700"
className={`mt-4 text-sm underline ${isDark ? 'text-blue-400 hover:text-blue-300' : 'text-blue-600 hover:text-blue-700'}`}
>
{text.closeWindowManually}
</button>
@@ -280,7 +280,9 @@ function StripePopupContent() {
onClick={handleSubmit}
className={[
'w-full rounded-lg py-3 font-medium text-white shadow-md transition-colors',
stripeSubmitting ? 'bg-gray-400 cursor-not-allowed' : getPaymentMeta('stripe').buttonClass,
stripeSubmitting
? isDark ? 'bg-slate-700 text-slate-400 cursor-not-allowed' : 'bg-gray-400 cursor-not-allowed'
: getPaymentMeta('stripe').buttonClass,
].join(' ')}
>
{stripeSubmitting ? (
@@ -302,10 +304,11 @@ function StripePopupContent() {
function StripePopupFallback() {
const searchParams = useSearchParams();
const locale = resolveLocale(searchParams.get('lang'));
const isDark = searchParams.get('theme') === 'dark';
return (
<div className="flex min-h-screen items-center justify-center">
<div className="text-gray-500">{pickLocaleText(locale, '加载中...', 'Loading...')}</div>
<div className={`flex min-h-screen items-center justify-center ${isDark ? 'bg-slate-950' : 'bg-slate-50'}`}>
<div className={isDark ? 'text-slate-400' : 'text-gray-500'}>{pickLocaleText(locale, '加载中...', 'Loading...')}</div>
</div>
);
}

View File

@@ -134,7 +134,7 @@ export default function MobileOrderList({
{locale === 'en' ? 'Loading...' : '加载中...'}
</span>
) : (
<span className={['text-xs', isDark ? 'text-slate-600' : 'text-slate-300'].join(' ')}>
<span className={['text-xs', isDark ? 'text-slate-400' : 'text-slate-400'].join(' ')}>
{locale === 'en' ? 'Scroll up to load more' : '上滑加载更多'}
</span>
)}
@@ -142,7 +142,7 @@ export default function MobileOrderList({
)}
{!hasMore && orders.length > 0 && (
<div className={['py-2 text-center text-xs', isDark ? 'text-slate-600' : 'text-slate-400'].join(' ')}>
<div className={['py-2 text-center text-xs', isDark ? 'text-slate-400' : 'text-slate-400'].join(' ')}>
{locale === 'en' ? 'All orders loaded' : '已显示全部订单'}
</div>
)}

View File

@@ -15,16 +15,16 @@ interface OrderStatusProps {
locale?: Locale;
}
function getStatusConfig(order: PublicOrderStatusSnapshot, locale: Locale) {
function getStatusConfig(order: PublicOrderStatusSnapshot, locale: Locale, isDark = false) {
if (order.rechargeSuccess) {
return locale === 'en'
? {
label: 'Recharge Successful',
color: 'text-green-600',
color: isDark ? 'text-green-400' : 'text-green-600',
icon: '✓',
message: 'Your balance has been credited. Thank you for your payment.',
}
: { label: '充值成功', color: 'text-green-600', icon: '✓', message: '余额已到账,感谢您的充值!' };
: { label: '充值成功', color: isDark ? 'text-green-400' : 'text-green-600', icon: '✓', message: '余额已到账,感谢您的充值!' };
}
if (order.paymentSuccess) {
@@ -32,25 +32,25 @@ function getStatusConfig(order: PublicOrderStatusSnapshot, locale: Locale) {
return locale === 'en'
? {
label: 'Recharging',
color: 'text-blue-600',
color: isDark ? 'text-blue-400' : 'text-blue-600',
icon: '⟳',
message: 'Payment received. Recharging your balance...',
}
: { label: '充值中', color: 'text-blue-600', icon: '⟳', message: '支付成功,正在充值余额中,请稍候...' };
: { label: '充值中', color: isDark ? 'text-blue-400' : 'text-blue-600', icon: '⟳', message: '支付成功,正在充值余额中,请稍候...' };
}
if (order.rechargeStatus === 'failed') {
return locale === 'en'
? {
label: 'Payment Successful',
color: 'text-amber-600',
color: isDark ? 'text-amber-400' : 'text-amber-600',
icon: '!',
message:
'Payment completed, but the balance top-up has not finished yet. The system may retry automatically. Please check the order list later or contact the administrator if it remains unresolved.',
}
: {
label: '支付成功',
color: 'text-amber-600',
color: isDark ? 'text-amber-400' : 'text-amber-600',
icon: '!',
message:
'支付已完成,但余额充值暂未完成。系统可能会自动重试,请稍后在订单列表查看;如长时间未到账请联系管理员。',
@@ -62,14 +62,14 @@ function getStatusConfig(order: PublicOrderStatusSnapshot, locale: Locale) {
return locale === 'en'
? {
label: 'Payment Failed',
color: 'text-red-600',
color: isDark ? 'text-red-400' : 'text-red-600',
icon: '✗',
message:
'Payment was not completed. Please try again. If funds were deducted but not credited, contact the administrator.',
}
: {
label: '支付失败',
color: 'text-red-600',
color: isDark ? 'text-red-400' : 'text-red-600',
icon: '✗',
message: '支付未完成,请重新发起支付;如已扣款未到账,请联系管理员处理。',
};
@@ -77,35 +77,35 @@ function getStatusConfig(order: PublicOrderStatusSnapshot, locale: Locale) {
if (order.status === 'PENDING') {
return locale === 'en'
? { label: 'Awaiting Payment', color: 'text-yellow-600', icon: '⏳', message: 'The order has not been paid yet.' }
: { label: '等待支付', color: 'text-yellow-600', icon: '⏳', message: '订单尚未完成支付。' };
? { label: 'Awaiting Payment', color: isDark ? 'text-yellow-400' : 'text-yellow-600', icon: '⏳', message: 'The order has not been paid yet.' }
: { label: '等待支付', color: isDark ? 'text-yellow-400' : 'text-yellow-600', icon: '⏳', message: '订单尚未完成支付。' };
}
if (order.status === 'EXPIRED') {
return locale === 'en'
? {
label: 'Order Expired',
color: 'text-gray-500',
color: isDark ? 'text-slate-400' : 'text-gray-500',
icon: '⏰',
message: 'This order has expired. Please create a new one.',
}
: { label: '订单超时', color: 'text-gray-500', icon: '⏰', message: '订单已超时,请重新创建订单。' };
: { label: '订单超时', color: isDark ? 'text-slate-400' : 'text-gray-500', icon: '⏰', message: '订单已超时,请重新创建订单。' };
}
if (order.status === 'CANCELLED') {
return locale === 'en'
? { label: 'Cancelled', color: 'text-gray-500', icon: '✗', message: 'The order has been cancelled.' }
: { label: '已取消', color: 'text-gray-500', icon: '✗', message: '订单已取消。' };
? { label: 'Cancelled', color: isDark ? 'text-slate-400' : 'text-gray-500', icon: '✗', message: 'The order has been cancelled.' }
: { label: '已取消', color: isDark ? 'text-slate-400' : 'text-gray-500', icon: '✗', message: '订单已取消。' };
}
return locale === 'en'
? {
label: 'Payment Error',
color: 'text-red-600',
color: isDark ? 'text-red-400' : 'text-red-600',
icon: '✗',
message: 'Payment status is abnormal. Please contact the administrator.',
}
: { label: '支付异常', color: 'text-red-600', icon: '✗', message: '支付状态异常,请联系管理员处理。' };
: { label: '支付异常', color: isDark ? 'text-red-400' : 'text-red-600', icon: '✗', message: '支付状态异常,请联系管理员处理。' };
}
export default function OrderStatus({
@@ -156,7 +156,7 @@ export default function OrderStatus({
};
}, [orderId, currentOrder.paymentSuccess, currentOrder.rechargeSuccess, statusAccessToken]);
const config = getStatusConfig(currentOrder, locale);
const config = getStatusConfig(currentOrder, locale, dark);
const doneLabel = locale === 'en' ? 'Done' : '完成';
const backLabel = locale === 'en' ? 'Back to Recharge' : '返回充值';

View File

@@ -195,7 +195,9 @@ export default function PaymentForm({
onClick={() => handleQuickAmount(val)}
className={`rounded-lg border-2 px-4 py-3 text-center font-medium transition-colors ${
amount === val
? 'border-blue-500 bg-blue-50 text-blue-700'
? dark
? 'border-blue-500 bg-blue-900/40 text-blue-300'
: 'border-blue-500 bg-blue-50 text-blue-700'
: dark
? 'border-slate-700 bg-slate-900 text-slate-200 hover:border-slate-500'
: 'border-gray-200 bg-white text-gray-700 hover:border-gray-300'
@@ -373,12 +375,12 @@ export default function PaymentForm({
<button
type="submit"
disabled={!isValid || loading || pendingBlocked}
className={`w-full rounded-lg py-3 text-center font-medium text-white transition-colors ${
className={`w-full rounded-lg py-3 text-center font-medium transition-colors ${
isValid && !loading && !pendingBlocked
? getPaymentMeta(effectivePaymentType).buttonClass
? `text-white ${getPaymentMeta(effectivePaymentType).buttonClass}`
: dark
? 'cursor-not-allowed bg-slate-700 text-slate-300'
: 'cursor-not-allowed bg-gray-300'
? 'cursor-not-allowed bg-slate-700 text-slate-400'
: 'cursor-not-allowed bg-gray-300 text-gray-500'
}`}
>
{loading

View File

@@ -391,14 +391,17 @@ export default function PaymentQRCode({
if (cancelBlocked) {
return (
<div className="flex flex-col items-center space-y-4 py-8">
<div className="text-6xl text-green-600">{'✓'}</div>
<h2 className="text-xl font-bold text-green-600">{t.paid}</h2>
<div className={dark ? 'text-6xl text-green-400' : 'text-6xl text-green-600'}>{'✓'}</div>
<h2 className={['text-xl font-bold', dark ? 'text-green-400' : 'text-green-600'].join(' ')}>{t.paid}</h2>
<p className={['text-center text-sm', dark ? 'text-slate-400' : 'text-gray-500'].join(' ')}>
{t.paidCancelBlocked}
</p>
<button
onClick={onBack}
className="mt-4 w-full rounded-lg bg-blue-600 py-3 font-medium text-white hover:bg-blue-700"
className={[
'mt-4 w-full rounded-lg py-3 font-medium text-white',
dark ? 'bg-blue-600/90 hover:bg-blue-600' : 'bg-blue-600 hover:bg-blue-700',
].join(' ')}
>
{t.backToRecharge}
</button>
@@ -409,7 +412,7 @@ export default function PaymentQRCode({
return (
<div className="flex flex-col items-center space-y-4">
<div className="text-center">
<div className="text-4xl font-bold text-blue-600">
<div className={['text-4xl font-bold', dark ? 'text-blue-400' : 'text-blue-600'].join(' ')}>
{'¥'}
{displayAmount.toFixed(2)}
</div>
@@ -465,13 +468,16 @@ export default function PaymentQRCode({
].join(' ')}
/>
{stripeError && (
<div className="rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-600">
<div className={[
'rounded-lg border p-3 text-sm',
dark ? 'border-red-700/50 bg-red-900/30 text-red-400' : 'border-red-200 bg-red-50 text-red-600',
].join(' ')}>
{stripeError}
</div>
)}
{stripeSuccess ? (
<div className="text-center">
<div className="text-4xl text-green-600">{'✓'}</div>
<div className={dark ? 'text-4xl text-green-400' : 'text-4xl text-green-600'}>{'✓'}</div>
<p className={['mt-2 text-sm', dark ? 'text-slate-400' : 'text-gray-500'].join(' ')}>
{t.successProcessing}
</p>