Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e170d5451e | ||
|
|
e5424e6c5e | ||
|
|
310fa1020f | ||
|
|
85239e97f8 |
BIN
0e10fd7fa68c9dda45b221f98145dd7a.jpg
Normal file
BIN
0e10fd7fa68c9dda45b221f98145dd7a.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 109 KiB |
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
app:
|
||||
image: sub2apipay:latest
|
||||
image: touwaeriol/sub2apipay:${IMAGE_TAG:-latest}
|
||||
container_name: sub2apipay
|
||||
ports:
|
||||
- '8087:3000'
|
||||
|
||||
@@ -27,6 +27,8 @@ export async function GET(request: NextRequest) {
|
||||
maxAmount: env.MAX_RECHARGE_AMOUNT,
|
||||
maxDailyAmount: env.MAX_DAILY_RECHARGE_AMOUNT,
|
||||
methodLimits,
|
||||
helpImageUrl: env.PAY_HELP_IMAGE_URL ?? null,
|
||||
helpText: env.PAY_HELP_TEXT ?? null,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -27,6 +27,8 @@ interface AppConfig {
|
||||
maxAmount: number;
|
||||
maxDailyAmount: number;
|
||||
methodLimits?: Record<string, MethodLimitInfo>;
|
||||
helpImageUrl?: string | null;
|
||||
helpText?: string | null;
|
||||
}
|
||||
|
||||
function PayContent() {
|
||||
@@ -59,12 +61,13 @@ function PayContent() {
|
||||
maxAmount: 1000,
|
||||
maxDailyAmount: 0,
|
||||
});
|
||||
const [userNotFound, setUserNotFound] = useState(false);
|
||||
|
||||
const effectiveUserId = resolvedUserId || userId;
|
||||
const isEmbedded = uiMode === 'embedded' && isIframeContext;
|
||||
const hasToken = token.length > 0;
|
||||
const helpImageUrl = (process.env.NEXT_PUBLIC_PAY_HELP_IMAGE_URL || '').trim();
|
||||
const helpText = (process.env.NEXT_PUBLIC_PAY_HELP_TEXT || '').trim();
|
||||
const helpImageUrl = (config.helpImageUrl || '').trim();
|
||||
const helpText = (config.helpText || '').trim();
|
||||
const hasHelpContent = Boolean(helpImageUrl || helpText);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -86,6 +89,7 @@ function PayContent() {
|
||||
const loadUserAndOrders = async () => {
|
||||
if (!userId || Number.isNaN(userId) || userId <= 0) return;
|
||||
|
||||
setUserNotFound(false);
|
||||
try {
|
||||
// 始终获取服务端配置(不含隐私信息)
|
||||
const cfgRes = await fetch(`/api/user?user_id=${userId}`);
|
||||
@@ -98,8 +102,13 @@ function PayContent() {
|
||||
maxAmount: cfgData.config.maxAmount ?? 1000,
|
||||
maxDailyAmount: cfgData.config.maxDailyAmount ?? 0,
|
||||
methodLimits: cfgData.config.methodLimits,
|
||||
helpImageUrl: cfgData.config.helpImageUrl ?? null,
|
||||
helpText: cfgData.config.helpText ?? null,
|
||||
});
|
||||
}
|
||||
} else if (cfgRes.status === 404) {
|
||||
setUserNotFound(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// 有 token 时才尝试获取用户详情和订单
|
||||
@@ -183,6 +192,17 @@ function PayContent() {
|
||||
);
|
||||
}
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
const buildScopedUrl = (path: string, forceOrdersTab = false) => {
|
||||
const params = new URLSearchParams();
|
||||
if (effectiveUserId) params.set('user_id', String(effectiveUserId));
|
||||
|
||||
@@ -94,14 +94,9 @@ export default function PaymentForm({
|
||||
if (type === 'wxpay') {
|
||||
return (
|
||||
<span className="flex h-8 w-8 items-center justify-center rounded-full bg-[#2BB741] text-white">
|
||||
<svg viewBox="0 0 24 24" className="h-5 w-5" fill="none">
|
||||
<path
|
||||
d="M5 12.5 10.2 17 19 8"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2.4"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<svg viewBox="0 0 24 24" className="h-5 w-5" fill="currentColor">
|
||||
<path d="M10 3C6.13 3 3 5.58 3 8.75c0 1.7.84 3.23 2.17 4.29l-.5 2.21 2.4-1.32c.61.17 1.25.27 1.93.27.22 0 .43-.01.64-.03C9.41 13.72 9 12.88 9 12c0-3.31 3.13-6 7-6 .26 0 .51.01.76.03C15.96 3.98 13.19 3 10 3z" />
|
||||
<path d="M16 8c-3.31 0-6 2.24-6 5s2.69 5 6 5c.67 0 1.31-.1 1.9-.28l2.1 1.15-.55-2.44C20.77 15.52 22 13.86 22 12c0-2.21-2.69-4-6-4z" />
|
||||
</svg>
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -12,7 +12,13 @@ const envSchema = z.object({
|
||||
SUB2API_BASE_URL: z.string().url(),
|
||||
SUB2API_ADMIN_API_KEY: z.string().min(1),
|
||||
|
||||
// ── Easy-Pay (optional when only using Stripe) ──
|
||||
// ── 支付服务商(显式声明启用哪些服务商,逗号分隔:easypay, stripe) ──
|
||||
PAYMENT_PROVIDERS: z
|
||||
.string()
|
||||
.default('')
|
||||
.transform((v) => v.split(',').map((s) => s.trim().toLowerCase()).filter(Boolean)),
|
||||
|
||||
// ── Easy-Pay(PAYMENT_PROVIDERS 含 easypay 时必填) ──
|
||||
EASY_PAY_PID: optionalTrimmedString,
|
||||
EASY_PAY_PKEY: optionalTrimmedString,
|
||||
EASY_PAY_API_BASE: optionalTrimmedString,
|
||||
@@ -22,10 +28,13 @@ const envSchema = z.object({
|
||||
EASY_PAY_CID_ALIPAY: optionalTrimmedString,
|
||||
EASY_PAY_CID_WXPAY: optionalTrimmedString,
|
||||
|
||||
// ── Stripe(PAYMENT_PROVIDERS 含 stripe 时必填) ──
|
||||
STRIPE_SECRET_KEY: optionalTrimmedString,
|
||||
STRIPE_PUBLISHABLE_KEY: optionalTrimmedString,
|
||||
STRIPE_WEBHOOK_SECRET: optionalTrimmedString,
|
||||
|
||||
// ── 启用的支付渠道(在已配置服务商支持的渠道中选择) ──
|
||||
// 易支付支持: alipay, wxpay;Stripe 支持: stripe
|
||||
ENABLED_PAYMENT_TYPES: z
|
||||
.string()
|
||||
.default('alipay,wxpay')
|
||||
@@ -47,8 +56,8 @@ const envSchema = z.object({
|
||||
ADMIN_TOKEN: z.string().min(1),
|
||||
|
||||
NEXT_PUBLIC_APP_URL: z.string().url(),
|
||||
NEXT_PUBLIC_PAY_HELP_IMAGE_URL: optionalTrimmedString,
|
||||
NEXT_PUBLIC_PAY_HELP_TEXT: optionalTrimmedString,
|
||||
PAY_HELP_IMAGE_URL: optionalTrimmedString,
|
||||
PAY_HELP_TEXT: optionalTrimmedString,
|
||||
});
|
||||
|
||||
export type Env = z.infer<typeof envSchema>;
|
||||
|
||||
@@ -19,10 +19,21 @@ let initialized = false;
|
||||
|
||||
export function initPaymentProviders(): void {
|
||||
if (initialized) return;
|
||||
paymentRegistry.register(new EasyPayProvider());
|
||||
|
||||
const env = getEnv();
|
||||
if (env.STRIPE_SECRET_KEY) {
|
||||
const providers = env.PAYMENT_PROVIDERS;
|
||||
|
||||
if (providers.includes('easypay')) {
|
||||
if (!env.EASY_PAY_PID || !env.EASY_PAY_PKEY) {
|
||||
throw new Error('PAYMENT_PROVIDERS 含 easypay,但缺少 EASY_PAY_PID 或 EASY_PAY_PKEY');
|
||||
}
|
||||
paymentRegistry.register(new EasyPayProvider());
|
||||
}
|
||||
|
||||
if (providers.includes('stripe')) {
|
||||
if (!env.STRIPE_SECRET_KEY) {
|
||||
throw new Error('PAYMENT_PROVIDERS 含 stripe,但缺少 STRIPE_SECRET_KEY');
|
||||
}
|
||||
paymentRegistry.register(new StripeProvider());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user