diff --git a/src/__tests__/lib/alipay/provider.test.ts b/src/__tests__/lib/alipay/provider.test.ts
index be21b9b..f22ddb6 100644
--- a/src/__tests__/lib/alipay/provider.test.ts
+++ b/src/__tests__/lib/alipay/provider.test.ts
@@ -37,21 +37,21 @@ describe('AlipayProvider', () => {
});
describe('metadata', () => {
- it('should have name "alipay"', () => {
- expect(provider.name).toBe('alipay');
+ it('should have name "alipay-direct"', () => {
+ expect(provider.name).toBe('alipay-direct');
});
it('should have providerKey "alipay"', () => {
expect(provider.providerKey).toBe('alipay');
});
- it('should support "alipay" payment type', () => {
- expect(provider.supportedTypes).toEqual(['alipay']);
+ it('should support "alipay_direct" payment type', () => {
+ expect(provider.supportedTypes).toEqual(['alipay_direct']);
});
it('should have default limits', () => {
expect(provider.defaultLimits).toEqual({
- alipay: { singleMax: 1000, dailyMax: 10000 },
+ alipay_direct: { singleMax: 1000, dailyMax: 10000 },
});
});
});
diff --git a/src/app/api/orders/route.ts b/src/app/api/orders/route.ts
index b751cec..115e515 100644
--- a/src/app/api/orders/route.ts
+++ b/src/app/api/orders/route.ts
@@ -6,7 +6,7 @@ import { getEnv } from '@/lib/config';
const createOrderSchema = z.object({
user_id: z.number().int().positive(),
amount: z.number().positive(),
- payment_type: z.enum(['alipay', 'wxpay', 'stripe']),
+ payment_type: z.string().min(1),
src_host: z.string().max(253).optional(),
src_url: z.string().max(2048).optional(),
});
diff --git a/src/app/pay/page.tsx b/src/app/pay/page.tsx
index 1c8f73b..89e9e68 100644
--- a/src/app/pay/page.tsx
+++ b/src/app/pay/page.tsx
@@ -15,7 +15,7 @@ interface OrderResult {
amount: number;
payAmount?: number;
status: string;
- paymentType: 'alipay' | 'wxpay' | 'stripe';
+ paymentType: string;
payUrl?: string | null;
qrCode?: string | null;
clientSecret?: string | null;
diff --git a/src/components/PaymentForm.tsx b/src/components/PaymentForm.tsx
index 03915d5..f0def70 100644
--- a/src/components/PaymentForm.tsx
+++ b/src/components/PaymentForm.tsx
@@ -1,7 +1,7 @@
'use client';
import { useState } from 'react';
-import { PAYMENT_TYPE_META } from '@/lib/pay-utils';
+import { PAYMENT_TYPE_META, getPaymentIconType } from '@/lib/pay-utils';
export interface MethodLimitInfo {
available: boolean;
@@ -99,14 +99,15 @@ export default function PaymentForm({
};
const renderPaymentIcon = (type: string) => {
- if (type === 'alipay') {
+ const iconType = getPaymentIconType(type);
+ if (iconType === 'alipay') {
return (
支
);
}
- if (type === 'wxpay') {
+ if (iconType === 'wxpay') {
return (
);
}
- if (type === 'stripe') {
+ if (iconType === 'stripe') {
return (
-
- {order.paymentType === 'alipay'
- ? '支付宝'
- : order.paymentType === 'wechat'
- ? '微信支付'
- : order.paymentType === 'stripe'
- ? 'Stripe'
- : order.paymentType}
- |
+ {getPaymentTypeLabel(order.paymentType)} |
{order.srcHost || '-'} |
{new Date(order.createdAt).toLocaleString('zh-CN')} |
diff --git a/src/components/admin/PaymentMethodChart.tsx b/src/components/admin/PaymentMethodChart.tsx
index 0fe3bdf..df21ef1 100644
--- a/src/components/admin/PaymentMethodChart.tsx
+++ b/src/components/admin/PaymentMethodChart.tsx
@@ -1,5 +1,7 @@
'use client';
+import { getPaymentTypeLabel } from '@/lib/pay-utils';
+
interface PaymentMethod {
paymentType: string;
amount: number;
@@ -13,8 +15,10 @@ interface PaymentMethodChartProps {
}
const TYPE_CONFIG: Record = {
- alipay: { label: '支付宝', light: 'bg-blue-500', dark: 'bg-blue-400' },
- wechat: { label: '微信支付', light: 'bg-green-500', dark: 'bg-green-400' },
+ alipay: { label: '支付宝(易支付)', light: 'bg-cyan-500', dark: 'bg-cyan-400' },
+ alipay_direct: { label: '支付宝(官方)', light: 'bg-blue-500', dark: 'bg-blue-400' },
+ wxpay: { label: '微信支付(易支付)', light: 'bg-green-500', dark: 'bg-green-400' },
+ wxpay_direct: { label: '微信支付(官方)', light: 'bg-emerald-500', dark: 'bg-emerald-400' },
stripe: { label: 'Stripe', light: 'bg-purple-500', dark: 'bg-purple-400' },
};
@@ -48,7 +52,7 @@ export default function PaymentMethodChart({ data, dark }: PaymentMethodChartPro
{data.map((method) => {
const config = TYPE_CONFIG[method.paymentType] || {
- label: method.paymentType,
+ label: getPaymentTypeLabel(method.paymentType),
light: 'bg-gray-500',
dark: 'bg-gray-400',
};
diff --git a/src/lib/alipay/provider.ts b/src/lib/alipay/provider.ts
index 59e5a0e..ddacf53 100644
--- a/src/lib/alipay/provider.ts
+++ b/src/lib/alipay/provider.ts
@@ -14,11 +14,11 @@ import { getEnv } from '@/lib/config';
import type { AlipayTradeQueryResponse, AlipayTradeRefundResponse, AlipayTradeCloseResponse } from './types';
export class AlipayProvider implements PaymentProvider {
- readonly name = 'alipay';
+ readonly name = 'alipay-direct';
readonly providerKey = 'alipay';
- readonly supportedTypes: PaymentType[] = ['alipay'];
+ readonly supportedTypes: PaymentType[] = ['alipay_direct'];
readonly defaultLimits = {
- alipay: { singleMax: 1000, dailyMax: 10000 },
+ alipay_direct: { singleMax: 1000, dailyMax: 10000 },
};
async createPayment(request: CreatePaymentRequest): Promise {
diff --git a/src/lib/config.ts b/src/lib/config.ts
index 5382f1b..de8716a 100644
--- a/src/lib/config.ts
+++ b/src/lib/config.ts
@@ -67,6 +67,11 @@ const envSchema = z.object({
.optional()
.transform((v) => (v !== undefined ? Number(v) : undefined))
.pipe(z.number().min(0).optional()),
+ MAX_DAILY_AMOUNT_ALIPAY_DIRECT: z
+ .string()
+ .optional()
+ .transform((v) => (v !== undefined ? Number(v) : undefined))
+ .pipe(z.number().min(0).optional()),
MAX_DAILY_AMOUNT_WXPAY: z
.string()
.optional()
diff --git a/src/lib/easy-pay/client.ts b/src/lib/easy-pay/client.ts
index e3ab35a..d2d0de7 100644
--- a/src/lib/easy-pay/client.ts
+++ b/src/lib/easy-pay/client.ts
@@ -5,7 +5,7 @@ import type { EasyPayCreateResponse, EasyPayQueryResponse, EasyPayRefundResponse
export interface CreatePaymentOptions {
outTradeNo: string;
amount: string;
- paymentType: 'alipay' | 'wxpay';
+ paymentType: string;
clientIp: string;
productName: string;
}
@@ -20,7 +20,7 @@ function normalizeCidList(cid?: string): string | undefined {
return normalized || undefined;
}
-function resolveCid(paymentType: 'alipay' | 'wxpay'): string | undefined {
+function resolveCid(paymentType: string): string | undefined {
const env = getEnv();
if (paymentType === 'alipay') {
return normalizeCidList(env.EASY_PAY_CID_ALIPAY) || normalizeCidList(env.EASY_PAY_CID);
diff --git a/src/lib/pay-utils.ts b/src/lib/pay-utils.ts
index c019fe3..ac6b0e3 100644
--- a/src/lib/pay-utils.ts
+++ b/src/lib/pay-utils.ts
@@ -75,19 +75,36 @@ export interface PaymentTypeMeta {
export const PAYMENT_TYPE_META: Record = {
alipay: {
label: '支付宝',
- sublabel: 'ALIPAY',
+ sublabel: '易支付',
color: '#00AEEF',
selectedBorder: 'border-cyan-400',
selectedBg: 'bg-cyan-50',
iconBg: 'bg-[#00AEEF]',
},
+ alipay_direct: {
+ label: '支付宝',
+ sublabel: '官方直连',
+ color: '#1677FF',
+ selectedBorder: 'border-blue-500',
+ selectedBg: 'bg-blue-50',
+ iconBg: 'bg-[#1677FF]',
+ },
wxpay: {
label: '微信支付',
+ sublabel: '易支付',
color: '#2BB741',
selectedBorder: 'border-green-500',
selectedBg: 'bg-green-50',
iconBg: 'bg-[#2BB741]',
},
+ wxpay_direct: {
+ label: '微信支付',
+ sublabel: '官方直连',
+ color: '#07C160',
+ selectedBorder: 'border-green-600',
+ selectedBg: 'bg-green-50',
+ iconBg: 'bg-[#07C160]',
+ },
stripe: {
label: 'Stripe',
sublabel: '信用卡 / 借记卡',
@@ -98,6 +115,21 @@ export const PAYMENT_TYPE_META: Record = {
},
};
+/** 获取支付方式的显示名称(如 '支付宝(官方直连)') */
+export function getPaymentTypeLabel(type: string): string {
+ const meta = PAYMENT_TYPE_META[type];
+ if (!meta) return type;
+ return meta.sublabel ? `${meta.label}(${meta.sublabel})` : meta.label;
+}
+
+/** 获取基础支付方式图标类型(alipay_direct → alipay) */
+export function getPaymentIconType(type: string): string {
+ if (type.startsWith('alipay')) return 'alipay';
+ if (type.startsWith('wxpay')) return 'wxpay';
+ if (type.startsWith('stripe')) return 'stripe';
+ return type;
+}
+
export function getStatusBadgeClass(status: string, isDark: boolean): string {
if (['COMPLETED', 'PAID'].includes(status)) {
return isDark ? 'bg-emerald-500/20 text-emerald-200' : 'bg-emerald-100 text-emerald-700';
diff --git a/src/lib/payment/index.ts b/src/lib/payment/index.ts
index 16a85a7..d8ae284 100644
--- a/src/lib/payment/index.ts
+++ b/src/lib/payment/index.ts
@@ -36,7 +36,7 @@ export function initPaymentProviders(): void {
if (!env.ALIPAY_APP_ID || !env.ALIPAY_PRIVATE_KEY) {
throw new Error('PAYMENT_PROVIDERS 含 alipay,但缺少 ALIPAY_APP_ID 或 ALIPAY_PRIVATE_KEY');
}
- paymentRegistry.register(new AlipayProvider());
+ paymentRegistry.register(new AlipayProvider()); // 注册 alipay_direct
}
if (providers.includes('stripe')) {
diff --git a/src/lib/payment/types.ts b/src/lib/payment/types.ts
index 470a40b..21a2f2b 100644
--- a/src/lib/payment/types.ts
+++ b/src/lib/payment/types.ts
@@ -1,5 +1,16 @@
/** Unified payment method types across all providers */
-export type PaymentType = 'alipay' | 'wxpay' | 'stripe';
+export type PaymentType = string;
+
+/**
+ * 从复合 key 中提取基础支付方式(如 'alipay_direct' → 'alipay')
+ * 用于传给第三方 API 时映射回标准名称
+ */
+export function getBasePaymentType(type: string): string {
+ if (type.startsWith('alipay')) return 'alipay';
+ if (type.startsWith('wxpay')) return 'wxpay';
+ if (type.startsWith('stripe')) return 'stripe';
+ return type;
+}
/** Request to create a payment with any provider */
export interface CreatePaymentRequest {
|