2026-03-01 03:04:24 +08:00
|
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
|
|
import { useSearchParams } from 'next/navigation';
|
2026-03-01 17:58:08 +08:00
|
|
|
|
import { useState, useEffect, Suspense } from 'react';
|
2026-03-01 03:04:24 +08:00
|
|
|
|
import PaymentForm from '@/components/PaymentForm';
|
|
|
|
|
|
import PaymentQRCode from '@/components/PaymentQRCode';
|
|
|
|
|
|
import OrderStatus from '@/components/OrderStatus';
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
import PayPageLayout from '@/components/PayPageLayout';
|
|
|
|
|
|
import MobileOrderList from '@/components/MobileOrderList';
|
|
|
|
|
|
import { detectDeviceIsMobile, type UserInfo, type MyOrder } from '@/lib/pay-utils';
|
2026-03-01 21:53:09 +08:00
|
|
|
|
import type { MethodLimitInfo } from '@/components/PaymentForm';
|
2026-03-01 03:04:24 +08:00
|
|
|
|
|
|
|
|
|
|
interface OrderResult {
|
|
|
|
|
|
orderId: string;
|
|
|
|
|
|
amount: number;
|
2026-03-03 22:00:44 +08:00
|
|
|
|
payAmount?: number;
|
2026-03-01 03:04:24 +08:00
|
|
|
|
status: string;
|
2026-03-06 15:33:22 +08:00
|
|
|
|
paymentType: string;
|
2026-03-01 03:04:24 +08:00
|
|
|
|
payUrl?: string | null;
|
|
|
|
|
|
qrCode?: string | null;
|
2026-03-04 10:58:07 +08:00
|
|
|
|
clientSecret?: string | null;
|
2026-03-01 03:04:24 +08:00
|
|
|
|
expiresAt: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interface AppConfig {
|
|
|
|
|
|
enabledPaymentTypes: string[];
|
|
|
|
|
|
minAmount: number;
|
|
|
|
|
|
maxAmount: number;
|
2026-03-01 19:41:44 +08:00
|
|
|
|
maxDailyAmount: number;
|
2026-03-01 21:53:09 +08:00
|
|
|
|
methodLimits?: Record<string, MethodLimitInfo>;
|
2026-03-02 02:46:51 +08:00
|
|
|
|
helpImageUrl?: string | null;
|
|
|
|
|
|
helpText?: string | null;
|
2026-03-04 10:58:07 +08:00
|
|
|
|
stripePublishableKey?: string | null;
|
2026-03-01 03:04:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function PayContent() {
|
|
|
|
|
|
const searchParams = useSearchParams();
|
|
|
|
|
|
const userId = Number(searchParams.get('user_id'));
|
|
|
|
|
|
const token = (searchParams.get('token') || '').trim();
|
|
|
|
|
|
const theme = searchParams.get('theme') === 'dark' ? 'dark' : 'light';
|
|
|
|
|
|
const uiMode = searchParams.get('ui_mode') || 'standalone';
|
|
|
|
|
|
const tab = searchParams.get('tab');
|
2026-03-02 20:40:16 +08:00
|
|
|
|
const srcHost = searchParams.get('src_host') || undefined;
|
|
|
|
|
|
const srcUrl = searchParams.get('src_url') || undefined;
|
2026-03-01 03:04:24 +08:00
|
|
|
|
const isDark = theme === 'dark';
|
|
|
|
|
|
|
|
|
|
|
|
const [isIframeContext, setIsIframeContext] = useState(true);
|
|
|
|
|
|
const [isMobile, setIsMobile] = useState(false);
|
|
|
|
|
|
const [step, setStep] = useState<'form' | 'paying' | 'result'>('form');
|
|
|
|
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
|
|
const [error, setError] = useState('');
|
|
|
|
|
|
const [orderResult, setOrderResult] = useState<OrderResult | null>(null);
|
|
|
|
|
|
const [finalStatus, setFinalStatus] = useState('');
|
|
|
|
|
|
const [userInfo, setUserInfo] = useState<UserInfo | null>(null);
|
|
|
|
|
|
const [resolvedUserId, setResolvedUserId] = useState<number | null>(null);
|
|
|
|
|
|
const [myOrders, setMyOrders] = useState<MyOrder[]>([]);
|
2026-03-01 20:12:32 +08:00
|
|
|
|
const [ordersPage, setOrdersPage] = useState(1);
|
|
|
|
|
|
const [ordersHasMore, setOrdersHasMore] = useState(false);
|
|
|
|
|
|
const [ordersLoadingMore, setOrdersLoadingMore] = useState(false);
|
2026-03-01 03:04:24 +08:00
|
|
|
|
const [activeMobileTab, setActiveMobileTab] = useState<'pay' | 'orders'>('pay');
|
|
|
|
|
|
|
2026-03-01 19:25:14 +08:00
|
|
|
|
const [config, setConfig] = useState<AppConfig>({
|
2026-03-04 10:58:07 +08:00
|
|
|
|
enabledPaymentTypes: [],
|
2026-03-01 03:04:24 +08:00
|
|
|
|
minAmount: 1,
|
2026-03-01 21:53:09 +08:00
|
|
|
|
maxAmount: 1000,
|
2026-03-01 19:41:44 +08:00
|
|
|
|
maxDailyAmount: 0,
|
2026-03-01 03:04:24 +08:00
|
|
|
|
});
|
2026-03-02 01:05:01 +08:00
|
|
|
|
const [userNotFound, setUserNotFound] = useState(false);
|
2026-03-02 03:39:49 +08:00
|
|
|
|
const [helpImageOpen, setHelpImageOpen] = useState(false);
|
2026-03-01 03:04:24 +08:00
|
|
|
|
|
|
|
|
|
|
const effectiveUserId = resolvedUserId || userId;
|
|
|
|
|
|
const isEmbedded = uiMode === 'embedded' && isIframeContext;
|
|
|
|
|
|
const hasToken = token.length > 0;
|
2026-03-02 02:46:51 +08:00
|
|
|
|
const helpImageUrl = (config.helpImageUrl || '').trim();
|
|
|
|
|
|
const helpText = (config.helpText || '').trim();
|
2026-03-01 03:04:24 +08:00
|
|
|
|
const hasHelpContent = Boolean(helpImageUrl || helpText);
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (typeof window === 'undefined') return;
|
|
|
|
|
|
|
|
|
|
|
|
setIsIframeContext(window.self !== window.top);
|
|
|
|
|
|
setIsMobile(detectDeviceIsMobile());
|
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (!isMobile || step !== 'form') return;
|
|
|
|
|
|
if (tab === 'orders') {
|
|
|
|
|
|
setActiveMobileTab('orders');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
setActiveMobileTab('pay');
|
|
|
|
|
|
}, [isMobile, step, tab]);
|
|
|
|
|
|
|
|
|
|
|
|
const loadUserAndOrders = async () => {
|
|
|
|
|
|
if (!userId || Number.isNaN(userId) || userId <= 0) return;
|
|
|
|
|
|
|
2026-03-02 01:23:04 +08:00
|
|
|
|
setUserNotFound(false);
|
2026-03-01 03:04:24 +08:00
|
|
|
|
try {
|
2026-03-01 19:25:14 +08:00
|
|
|
|
// 始终获取服务端配置(不含隐私信息)
|
|
|
|
|
|
const cfgRes = await fetch(`/api/user?user_id=${userId}`);
|
|
|
|
|
|
if (cfgRes.ok) {
|
|
|
|
|
|
const cfgData = await cfgRes.json();
|
|
|
|
|
|
if (cfgData.config) {
|
2026-03-01 21:53:09 +08:00
|
|
|
|
setConfig({
|
|
|
|
|
|
enabledPaymentTypes: cfgData.config.enabledPaymentTypes ?? ['alipay', 'wxpay'],
|
|
|
|
|
|
minAmount: cfgData.config.minAmount ?? 1,
|
|
|
|
|
|
maxAmount: cfgData.config.maxAmount ?? 1000,
|
|
|
|
|
|
maxDailyAmount: cfgData.config.maxDailyAmount ?? 0,
|
|
|
|
|
|
methodLimits: cfgData.config.methodLimits,
|
2026-03-02 02:46:51 +08:00
|
|
|
|
helpImageUrl: cfgData.config.helpImageUrl ?? null,
|
|
|
|
|
|
helpText: cfgData.config.helpText ?? null,
|
2026-03-04 10:58:07 +08:00
|
|
|
|
stripePublishableKey: cfgData.config.stripePublishableKey ?? null,
|
2026-03-01 21:53:09 +08:00
|
|
|
|
});
|
2026-03-01 19:25:14 +08:00
|
|
|
|
}
|
2026-03-02 01:05:01 +08:00
|
|
|
|
} else if (cfgRes.status === 404) {
|
|
|
|
|
|
setUserNotFound(true);
|
|
|
|
|
|
return;
|
2026-03-01 19:25:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 有 token 时才尝试获取用户详情和订单
|
2026-03-01 03:04:24 +08:00
|
|
|
|
if (token) {
|
|
|
|
|
|
const meRes = await fetch(`/api/orders/my?token=${encodeURIComponent(token)}`);
|
|
|
|
|
|
if (meRes.ok) {
|
|
|
|
|
|
const meData = await meRes.json();
|
|
|
|
|
|
const meUser = meData.user || {};
|
|
|
|
|
|
const meId = Number(meUser.id);
|
|
|
|
|
|
if (Number.isInteger(meId) && meId > 0) {
|
|
|
|
|
|
setResolvedUserId(meId);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setUserInfo({
|
|
|
|
|
|
id: Number.isInteger(meId) && meId > 0 ? meId : userId,
|
|
|
|
|
|
username:
|
|
|
|
|
|
(typeof meUser.displayName === 'string' && meUser.displayName.trim()) ||
|
|
|
|
|
|
(typeof meUser.username === 'string' && meUser.username.trim()) ||
|
|
|
|
|
|
`用户 #${userId}`,
|
2026-03-01 21:24:11 +08:00
|
|
|
|
balance: typeof meUser.balance === 'number' ? meUser.balance : undefined,
|
2026-03-01 03:04:24 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (Array.isArray(meData.orders)) {
|
|
|
|
|
|
setMyOrders(meData.orders);
|
2026-03-01 20:12:32 +08:00
|
|
|
|
setOrdersPage(1);
|
|
|
|
|
|
setOrdersHasMore((meData.total_pages ?? 1) > 1);
|
2026-03-01 03:04:24 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
setMyOrders([]);
|
2026-03-01 20:12:32 +08:00
|
|
|
|
setOrdersPage(1);
|
|
|
|
|
|
setOrdersHasMore(false);
|
2026-03-01 03:04:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-01 21:24:11 +08:00
|
|
|
|
// 无 token 或 token 失效:只显示用户 ID,不展示隐私信息(不显示余额)
|
|
|
|
|
|
setUserInfo({ id: userId, username: `用户 #${userId}` });
|
2026-03-01 03:04:24 +08:00
|
|
|
|
setMyOrders([]);
|
2026-03-01 20:12:32 +08:00
|
|
|
|
setOrdersPage(1);
|
|
|
|
|
|
setOrdersHasMore(false);
|
2026-03-01 03:04:24 +08:00
|
|
|
|
} catch {
|
|
|
|
|
|
// ignore and keep page usable
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-01 20:12:32 +08:00
|
|
|
|
const loadMoreOrders = async () => {
|
|
|
|
|
|
if (!token || ordersLoadingMore || !ordersHasMore) return;
|
|
|
|
|
|
const nextPage = ordersPage + 1;
|
|
|
|
|
|
setOrdersLoadingMore(true);
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await fetch(`/api/orders/my?token=${encodeURIComponent(token)}&page=${nextPage}&page_size=20`);
|
|
|
|
|
|
if (!res.ok) return;
|
|
|
|
|
|
const data = await res.json();
|
|
|
|
|
|
if (Array.isArray(data.orders) && data.orders.length > 0) {
|
|
|
|
|
|
setMyOrders((prev) => [...prev, ...data.orders]);
|
|
|
|
|
|
setOrdersPage(nextPage);
|
|
|
|
|
|
setOrdersHasMore(nextPage < (data.total_pages ?? 1));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
setOrdersHasMore(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
// ignore
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setOrdersLoadingMore(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-01 03:04:24 +08:00
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
loadUserAndOrders();
|
|
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
|
|
}, [userId, token]);
|
|
|
|
|
|
|
2026-03-05 23:08:48 +08:00
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (step !== 'result' || finalStatus !== 'COMPLETED') return;
|
|
|
|
|
|
// 立即在后台刷新余额,2.2s 显示结果页后再切回表单(届时余额已更新)
|
|
|
|
|
|
loadUserAndOrders();
|
|
|
|
|
|
const timer = setTimeout(() => {
|
|
|
|
|
|
setStep('form');
|
|
|
|
|
|
setOrderResult(null);
|
|
|
|
|
|
setFinalStatus('');
|
|
|
|
|
|
setError('');
|
|
|
|
|
|
}, 2200);
|
|
|
|
|
|
return () => clearTimeout(timer);
|
|
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
|
|
}, [step, finalStatus]);
|
|
|
|
|
|
|
2026-03-01 03:04:24 +08:00
|
|
|
|
if (!effectiveUserId || Number.isNaN(effectiveUserId) || effectiveUserId <= 0) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<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">无效的用户 ID</p>
|
|
|
|
|
|
<p className="mt-2 text-sm text-gray-500">请从 Sub2API 平台正确访问充值页面</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 01:05:01 +08:00
|
|
|
|
if (userNotFound) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<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">用户不存在</p>
|
|
|
|
|
|
<p className="mt-2 text-sm text-gray-500">请检查链接是否正确,或联系管理员</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-01 03:04:24 +08:00
|
|
|
|
const buildScopedUrl = (path: string, forceOrdersTab = false) => {
|
|
|
|
|
|
const params = new URLSearchParams();
|
|
|
|
|
|
if (effectiveUserId) params.set('user_id', String(effectiveUserId));
|
|
|
|
|
|
if (token) params.set('token', token);
|
|
|
|
|
|
params.set('theme', theme);
|
|
|
|
|
|
params.set('ui_mode', uiMode);
|
|
|
|
|
|
if (forceOrdersTab) params.set('tab', 'orders');
|
|
|
|
|
|
return `${path}?${params.toString()}`;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const pcOrdersUrl = buildScopedUrl('/pay/orders');
|
|
|
|
|
|
const mobileOrdersUrl = buildScopedUrl('/pay', true);
|
|
|
|
|
|
const ordersUrl = isMobile ? mobileOrdersUrl : pcOrdersUrl;
|
|
|
|
|
|
|
|
|
|
|
|
const handleSubmit = async (amount: number, paymentType: string) => {
|
|
|
|
|
|
setLoading(true);
|
|
|
|
|
|
setError('');
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await fetch('/api/orders', {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
|
user_id: effectiveUserId,
|
|
|
|
|
|
amount,
|
|
|
|
|
|
payment_type: paymentType,
|
2026-03-02 20:40:16 +08:00
|
|
|
|
src_host: srcHost,
|
|
|
|
|
|
src_url: srcUrl,
|
2026-03-01 03:04:24 +08:00
|
|
|
|
}),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const data = await res.json();
|
|
|
|
|
|
|
|
|
|
|
|
if (!res.ok) {
|
2026-03-01 19:25:14 +08:00
|
|
|
|
const codeMessages: Record<string, string> = {
|
|
|
|
|
|
USER_INACTIVE: '账户已被禁用,无法充值,请联系管理员',
|
|
|
|
|
|
TOO_MANY_PENDING: '您有过多待支付订单,请先完成或取消现有订单后再试',
|
|
|
|
|
|
USER_NOT_FOUND: '用户不存在,请检查链接是否正确',
|
2026-03-01 19:41:44 +08:00
|
|
|
|
DAILY_LIMIT_EXCEEDED: data.error,
|
2026-03-01 21:53:09 +08:00
|
|
|
|
METHOD_DAILY_LIMIT_EXCEEDED: data.error,
|
2026-03-01 19:56:41 +08:00
|
|
|
|
PAYMENT_GATEWAY_ERROR: data.error,
|
2026-03-01 19:25:14 +08:00
|
|
|
|
};
|
|
|
|
|
|
setError(codeMessages[data.code] || data.error || '创建订单失败');
|
2026-03-01 03:04:24 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setOrderResult({
|
|
|
|
|
|
orderId: data.orderId,
|
|
|
|
|
|
amount: data.amount,
|
2026-03-03 22:00:44 +08:00
|
|
|
|
payAmount: data.payAmount,
|
2026-03-01 03:04:24 +08:00
|
|
|
|
status: data.status,
|
|
|
|
|
|
paymentType: data.paymentType || paymentType,
|
|
|
|
|
|
payUrl: data.payUrl,
|
|
|
|
|
|
qrCode: data.qrCode,
|
2026-03-04 10:58:07 +08:00
|
|
|
|
clientSecret: data.clientSecret,
|
2026-03-01 03:04:24 +08:00
|
|
|
|
expiresAt: data.expiresAt,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
setStep('paying');
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
setError('网络错误,请稍后重试');
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setLoading(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleStatusChange = (status: string) => {
|
|
|
|
|
|
setFinalStatus(status);
|
|
|
|
|
|
setStep('result');
|
|
|
|
|
|
if (isMobile) {
|
|
|
|
|
|
setActiveMobileTab('orders');
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleBack = () => {
|
|
|
|
|
|
setStep('form');
|
|
|
|
|
|
setOrderResult(null);
|
|
|
|
|
|
setFinalStatus('');
|
|
|
|
|
|
setError('');
|
|
|
|
|
|
};
|
|
|
|
|
|
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
return (
|
|
|
|
|
|
<PayPageLayout
|
|
|
|
|
|
isDark={isDark}
|
|
|
|
|
|
isEmbedded={isEmbedded}
|
2026-03-03 05:54:46 +08:00
|
|
|
|
maxWidth={isMobile ? 'sm' : 'lg'}
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
title="Sub2API 余额充值"
|
|
|
|
|
|
subtitle="安全支付,自动到账"
|
2026-03-05 23:08:48 +08:00
|
|
|
|
actions={
|
|
|
|
|
|
!isMobile ? (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
onClick={loadUserAndOrders}
|
|
|
|
|
|
className={[
|
|
|
|
|
|
'inline-flex items-center rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors',
|
|
|
|
|
|
isDark
|
|
|
|
|
|
? 'border-slate-600 text-slate-200 hover:bg-slate-800'
|
|
|
|
|
|
: 'border-slate-300 text-slate-700 hover:bg-slate-100',
|
|
|
|
|
|
].join(' ')}
|
|
|
|
|
|
>
|
|
|
|
|
|
刷新
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<a
|
|
|
|
|
|
href={ordersUrl}
|
|
|
|
|
|
className={[
|
|
|
|
|
|
'inline-flex items-center rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors',
|
|
|
|
|
|
isDark
|
|
|
|
|
|
? 'border-slate-600 text-slate-200 hover:bg-slate-800'
|
|
|
|
|
|
: 'border-slate-300 text-slate-700 hover:bg-slate-100',
|
|
|
|
|
|
].join(' ')}
|
|
|
|
|
|
>
|
|
|
|
|
|
我的订单
|
|
|
|
|
|
</a>
|
|
|
|
|
|
</>
|
|
|
|
|
|
) : undefined
|
|
|
|
|
|
}
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
>
|
2026-03-05 23:08:48 +08:00
|
|
|
|
{error && <div className="mb-4 rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-600">{error}</div>}
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
|
|
|
|
|
|
{step === 'form' && isMobile && (
|
2026-03-01 03:04:24 +08:00
|
|
|
|
<div
|
|
|
|
|
|
className={[
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
'mb-4 grid grid-cols-2 rounded-xl border p-1',
|
|
|
|
|
|
isDark ? 'border-slate-700 bg-slate-800/70' : 'border-slate-300 bg-slate-100/90',
|
2026-03-01 03:04:24 +08:00
|
|
|
|
].join(' ')}
|
|
|
|
|
|
>
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
onClick={() => setActiveMobileTab('pay')}
|
2026-03-01 03:04:24 +08:00
|
|
|
|
className={[
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
'rounded-lg px-3 py-2 text-sm font-semibold transition-all duration-200',
|
|
|
|
|
|
activeMobileTab === 'pay'
|
2026-03-05 23:08:48 +08:00
|
|
|
|
? isDark
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
? 'bg-indigo-500/30 text-indigo-100 ring-1 ring-indigo-300/35 shadow-sm'
|
2026-03-05 23:08:48 +08:00
|
|
|
|
: 'bg-white text-slate-900 ring-1 ring-slate-300 shadow-md shadow-slate-300/50'
|
|
|
|
|
|
: isDark
|
|
|
|
|
|
? 'text-slate-400 hover:text-slate-200'
|
|
|
|
|
|
: 'text-slate-500 hover:text-slate-700',
|
2026-03-01 03:04:24 +08:00
|
|
|
|
].join(' ')}
|
|
|
|
|
|
>
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
充值
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
onClick={() => setActiveMobileTab('orders')}
|
|
|
|
|
|
className={[
|
|
|
|
|
|
'rounded-lg px-3 py-2 text-sm font-semibold transition-all duration-200',
|
|
|
|
|
|
activeMobileTab === 'orders'
|
2026-03-05 23:08:48 +08:00
|
|
|
|
? isDark
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
? 'bg-indigo-500/30 text-indigo-100 ring-1 ring-indigo-300/35 shadow-sm'
|
2026-03-05 23:08:48 +08:00
|
|
|
|
: 'bg-white text-slate-900 ring-1 ring-slate-300 shadow-md shadow-slate-300/50'
|
|
|
|
|
|
: isDark
|
|
|
|
|
|
? 'text-slate-400 hover:text-slate-200'
|
|
|
|
|
|
: 'text-slate-500 hover:text-slate-700',
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
].join(' ')}
|
|
|
|
|
|
>
|
|
|
|
|
|
我的订单
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
2026-03-04 10:58:07 +08:00
|
|
|
|
{step === 'form' && config.enabledPaymentTypes.length === 0 && (
|
|
|
|
|
|
<div className="flex items-center justify-center py-12">
|
|
|
|
|
|
<div className="h-6 w-6 animate-spin rounded-full border-2 border-blue-500 border-t-transparent" />
|
2026-03-05 23:08:48 +08:00
|
|
|
|
<span className={['ml-3 text-sm', isDark ? 'text-slate-400' : 'text-gray-500'].join(' ')}>加载中...</span>
|
2026-03-04 10:58:07 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{step === 'form' && config.enabledPaymentTypes.length > 0 && (
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
<>
|
|
|
|
|
|
{isMobile ? (
|
|
|
|
|
|
activeMobileTab === 'pay' ? (
|
|
|
|
|
|
<PaymentForm
|
|
|
|
|
|
userId={effectiveUserId}
|
|
|
|
|
|
userName={userInfo?.username}
|
|
|
|
|
|
userBalance={userInfo?.balance}
|
|
|
|
|
|
enabledPaymentTypes={config.enabledPaymentTypes}
|
2026-03-01 21:53:09 +08:00
|
|
|
|
methodLimits={config.methodLimits}
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
minAmount={config.minAmount}
|
|
|
|
|
|
maxAmount={config.maxAmount}
|
|
|
|
|
|
onSubmit={handleSubmit}
|
|
|
|
|
|
loading={loading}
|
|
|
|
|
|
dark={isDark}
|
|
|
|
|
|
/>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<MobileOrderList
|
|
|
|
|
|
isDark={isDark}
|
|
|
|
|
|
hasToken={hasToken}
|
|
|
|
|
|
orders={myOrders}
|
2026-03-01 20:12:32 +08:00
|
|
|
|
hasMore={ordersHasMore}
|
|
|
|
|
|
loadingMore={ordersLoadingMore}
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
onRefresh={loadUserAndOrders}
|
2026-03-01 20:12:32 +08:00
|
|
|
|
onLoadMore={loadMoreOrders}
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
/>
|
|
|
|
|
|
)
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<div className="grid gap-5 lg:grid-cols-[minmax(0,1.45fr)_minmax(300px,0.8fr)]">
|
|
|
|
|
|
<div className="min-w-0">
|
2026-03-01 03:04:24 +08:00
|
|
|
|
<PaymentForm
|
|
|
|
|
|
userId={effectiveUserId}
|
|
|
|
|
|
userName={userInfo?.username}
|
|
|
|
|
|
userBalance={userInfo?.balance}
|
|
|
|
|
|
enabledPaymentTypes={config.enabledPaymentTypes}
|
2026-03-03 22:00:44 +08:00
|
|
|
|
methodLimits={config.methodLimits}
|
2026-03-01 03:04:24 +08:00
|
|
|
|
minAmount={config.minAmount}
|
|
|
|
|
|
maxAmount={config.maxAmount}
|
|
|
|
|
|
onSubmit={handleSubmit}
|
|
|
|
|
|
loading={loading}
|
|
|
|
|
|
dark={isDark}
|
|
|
|
|
|
/>
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div className="space-y-4">
|
2026-03-05 23:08:48 +08:00
|
|
|
|
<div
|
|
|
|
|
|
className={[
|
|
|
|
|
|
'rounded-2xl border p-4',
|
|
|
|
|
|
isDark ? 'border-slate-700 bg-slate-800/70' : 'border-slate-200 bg-slate-50',
|
|
|
|
|
|
].join(' ')}
|
|
|
|
|
|
>
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
<div className={['text-xs', isDark ? 'text-slate-400' : 'text-slate-500'].join(' ')}>支付说明</div>
|
|
|
|
|
|
<ul className={['mt-2 space-y-1 text-sm', isDark ? 'text-slate-300' : 'text-slate-600'].join(' ')}>
|
|
|
|
|
|
<li>订单完成后会自动到账</li>
|
2026-03-05 23:08:48 +08:00
|
|
|
|
<li>如需历史记录请查看「我的订单」</li>
|
|
|
|
|
|
{config.maxDailyAmount > 0 && <li>每日最大充值 ¥{config.maxDailyAmount.toFixed(2)}</li>}
|
|
|
|
|
|
{!hasToken && (
|
|
|
|
|
|
<li className={isDark ? 'text-amber-200' : 'text-amber-700'}>当前链接无 token,订单查询受限</li>
|
2026-03-01 19:41:44 +08:00
|
|
|
|
)}
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
</ul>
|
2026-03-01 03:04:24 +08:00
|
|
|
|
</div>
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
|
|
|
|
|
|
{hasHelpContent && (
|
2026-03-05 23:08:48 +08:00
|
|
|
|
<div
|
|
|
|
|
|
className={[
|
|
|
|
|
|
'rounded-2xl border p-4',
|
|
|
|
|
|
isDark ? 'border-slate-700 bg-slate-800/70' : 'border-slate-200 bg-slate-50',
|
|
|
|
|
|
].join(' ')}
|
|
|
|
|
|
>
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
<div className={['text-xs', isDark ? 'text-slate-400' : 'text-slate-500'].join(' ')}>Support</div>
|
|
|
|
|
|
{helpImageUrl && (
|
|
|
|
|
|
<img
|
|
|
|
|
|
src={helpImageUrl}
|
2026-03-05 23:08:48 +08:00
|
|
|
|
alt="help"
|
2026-03-02 03:39:49 +08:00
|
|
|
|
onClick={() => setHelpImageOpen(true)}
|
2026-03-05 23:08:48 +08:00
|
|
|
|
className="mt-3 max-h-40 w-full cursor-zoom-in rounded-lg object-contain bg-white/70 p-2"
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{helpText && (
|
2026-03-05 23:08:48 +08:00
|
|
|
|
<div
|
|
|
|
|
|
className={[
|
|
|
|
|
|
'mt-3 space-y-1 text-sm leading-6',
|
|
|
|
|
|
isDark ? 'text-slate-300' : 'text-slate-600',
|
|
|
|
|
|
].join(' ')}
|
|
|
|
|
|
>
|
2026-03-02 04:17:51 +08:00
|
|
|
|
{helpText.split('\\n').map((line, i) => (
|
|
|
|
|
|
<p key={i}>{line}</p>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</div>
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
)}
|
2026-03-01 03:04:24 +08:00
|
|
|
|
</div>
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
)}
|
2026-03-01 03:04:24 +08:00
|
|
|
|
</div>
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{step === 'paying' && orderResult && (
|
|
|
|
|
|
<PaymentQRCode
|
|
|
|
|
|
orderId={orderResult.orderId}
|
2026-03-01 19:25:14 +08:00
|
|
|
|
token={token || undefined}
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
payUrl={orderResult.payUrl}
|
|
|
|
|
|
qrCode={orderResult.qrCode}
|
2026-03-04 10:58:07 +08:00
|
|
|
|
clientSecret={orderResult.clientSecret}
|
|
|
|
|
|
stripePublishableKey={config.stripePublishableKey}
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
paymentType={orderResult.paymentType}
|
|
|
|
|
|
amount={orderResult.amount}
|
2026-03-03 22:00:44 +08:00
|
|
|
|
payAmount={orderResult.payAmount}
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
expiresAt={orderResult.expiresAt}
|
|
|
|
|
|
onStatusChange={handleStatusChange}
|
|
|
|
|
|
onBack={handleBack}
|
|
|
|
|
|
dark={isDark}
|
2026-03-04 10:58:07 +08:00
|
|
|
|
isEmbedded={isEmbedded}
|
2026-03-05 16:21:12 +08:00
|
|
|
|
isMobile={isMobile}
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
2026-03-05 23:08:48 +08:00
|
|
|
|
{step === 'result' && <OrderStatus status={finalStatus} onBack={handleBack} dark={isDark} />}
|
2026-03-02 03:39:49 +08:00
|
|
|
|
|
|
|
|
|
|
{helpImageOpen && helpImageUrl && (
|
|
|
|
|
|
<div
|
|
|
|
|
|
className="fixed inset-0 z-50 flex items-center justify-center bg-black/75 p-4 backdrop-blur-sm"
|
|
|
|
|
|
onClick={() => setHelpImageOpen(false)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<img
|
|
|
|
|
|
src={helpImageUrl}
|
2026-03-05 23:08:48 +08:00
|
|
|
|
alt="help"
|
|
|
|
|
|
className="max-h-[90vh] max-w-full rounded-xl object-contain shadow-2xl"
|
2026-03-02 03:39:49 +08:00
|
|
|
|
onClick={(e) => e.stopPropagation()}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
refactor: extract pay page components and migrate zpay → easypay
Components:
- Add PayPageLayout, OrderFilterBar, MobileOrderList, OrderTable, OrderSummaryCards
- Extract shared pay-utils (types, constants, helper functions)
- Simplify pay/page.tsx and orders/page.tsx
EasyPay migration:
- Remove src/lib/zpay/, api/zpay/notify, zpay test, zpay.md
- Simplify config.ts: single envSchema, no ZPAY_* fallback
- Rename DB field zpay_trade_no → payment_trade_no (migration added)
- Update OrderDetail label: ZPAY订单号 → 支付单号
- Update CLAUDE.md project structure
2026-03-01 15:55:43 +08:00
|
|
|
|
</PayPageLayout>
|
2026-03-01 03:04:24 +08:00
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default function PayPage() {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Suspense
|
|
|
|
|
|
fallback={
|
|
|
|
|
|
<div className="flex min-h-screen items-center justify-center">
|
|
|
|
|
|
<div className="text-gray-500">加载中...</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
}
|
|
|
|
|
|
>
|
|
|
|
|
|
<PayContent />
|
|
|
|
|
|
</Suspense>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|