feat(payment): add complete payment system with multi-provider support
Add a full payment and subscription system supporting EasyPay (Alipay/WeChat), Stripe, and direct Alipay/WeChat Pay providers with multi-instance load balancing.
This commit is contained in:
@@ -106,6 +106,7 @@ export interface PublicSettings {
|
||||
hide_ccs_import_button: boolean
|
||||
purchase_subscription_enabled: boolean
|
||||
purchase_subscription_url: string
|
||||
payment_enabled: boolean
|
||||
custom_menu_items: CustomMenuItem[]
|
||||
custom_endpoints: CustomEndpoint[]
|
||||
linuxdo_oauth_enabled: boolean
|
||||
@@ -1629,3 +1630,6 @@ export interface UpdateScheduledTestPlanRequest {
|
||||
max_results?: number
|
||||
auto_recover?: boolean
|
||||
}
|
||||
|
||||
// Payment types
|
||||
export type { SubscriptionPlan, PaymentOrder, CheckoutInfoResponse } from './payment'
|
||||
|
||||
173
frontend/src/types/payment.ts
Normal file
173
frontend/src/types/payment.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
/**
|
||||
* Payment System Type Definitions
|
||||
*/
|
||||
|
||||
// ==================== Enums / Union Types ====================
|
||||
|
||||
export type OrderStatus =
|
||||
| 'PENDING'
|
||||
| 'PAID'
|
||||
| 'RECHARGING'
|
||||
| 'COMPLETED'
|
||||
| 'EXPIRED'
|
||||
| 'CANCELLED'
|
||||
| 'FAILED'
|
||||
| 'REFUND_REQUESTED'
|
||||
| 'REFUNDING'
|
||||
| 'PARTIALLY_REFUNDED'
|
||||
| 'REFUNDED'
|
||||
| 'REFUND_FAILED'
|
||||
|
||||
export type PaymentType = 'alipay' | 'wxpay' | 'alipay_direct' | 'wxpay_direct' | 'stripe' | 'easypay'
|
||||
|
||||
export type OrderType = 'balance' | 'subscription'
|
||||
|
||||
// ==================== Configuration ====================
|
||||
|
||||
export interface PaymentConfig {
|
||||
payment_enabled: boolean
|
||||
min_amount: number
|
||||
max_amount: number
|
||||
daily_limit: number
|
||||
max_pending_orders: number
|
||||
order_timeout_minutes: number
|
||||
balance_disabled: boolean
|
||||
enabled_payment_types: PaymentType[]
|
||||
help_image_url: string
|
||||
help_text: string
|
||||
stripe_publishable_key: string
|
||||
}
|
||||
|
||||
export interface MethodLimit {
|
||||
daily_limit: number
|
||||
daily_used: number
|
||||
daily_remaining: number
|
||||
single_min: number
|
||||
single_max: number
|
||||
fee_rate: number
|
||||
available: boolean
|
||||
}
|
||||
|
||||
/** Response from /payment/limits API */
|
||||
export interface MethodLimitsResponse {
|
||||
methods: Record<string, MethodLimit>
|
||||
global_min: number // widest min across all methods; 0 = no minimum
|
||||
global_max: number // widest max across all methods; 0 = no maximum
|
||||
}
|
||||
|
||||
/** Response from /payment/checkout-info API — single call for the payment page */
|
||||
export interface CheckoutInfoResponse {
|
||||
methods: Record<string, MethodLimit>
|
||||
global_min: number
|
||||
global_max: number
|
||||
plans: SubscriptionPlan[]
|
||||
balance_disabled: boolean
|
||||
help_text: string
|
||||
help_image_url: string
|
||||
stripe_publishable_key: string
|
||||
}
|
||||
|
||||
// ==================== Orders ====================
|
||||
|
||||
export interface PaymentOrder {
|
||||
id: number
|
||||
user_id: number
|
||||
amount: number
|
||||
pay_amount: number
|
||||
fee_rate: number
|
||||
payment_type: string
|
||||
out_trade_no: string
|
||||
status: OrderStatus
|
||||
order_type: OrderType
|
||||
created_at: string
|
||||
expires_at: string
|
||||
paid_at?: string
|
||||
completed_at?: string
|
||||
refund_amount: number
|
||||
refund_reason?: string
|
||||
refund_requested_at?: string
|
||||
refund_requested_by?: number
|
||||
refund_request_reason?: string
|
||||
plan_id?: number
|
||||
}
|
||||
|
||||
// ==================== Plans & Channels ====================
|
||||
|
||||
export interface SubscriptionPlan {
|
||||
id: number
|
||||
group_id: number
|
||||
group_platform?: string
|
||||
group_name?: string
|
||||
rate_multiplier?: number
|
||||
daily_limit_usd?: number | null
|
||||
weekly_limit_usd?: number | null
|
||||
monthly_limit_usd?: number | null
|
||||
supported_model_scopes?: string[]
|
||||
name: string
|
||||
description: string
|
||||
price: number
|
||||
original_price?: number
|
||||
validity_days: number
|
||||
validity_unit: string
|
||||
/** Stored as JSON string in backend; API layer should parse before use */
|
||||
features: string[]
|
||||
for_sale: boolean
|
||||
sort_order: number
|
||||
}
|
||||
|
||||
export interface PaymentChannel {
|
||||
id: number
|
||||
group_id?: number
|
||||
name: string
|
||||
platform: string
|
||||
rate_multiplier: number
|
||||
description: string
|
||||
models: string[]
|
||||
features: string[]
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
// ==================== Providers ====================
|
||||
|
||||
export interface ProviderInstance {
|
||||
id: number
|
||||
provider_key: string
|
||||
name: string
|
||||
config: Record<string, string>
|
||||
supported_types: string[]
|
||||
enabled: boolean
|
||||
payment_mode: string
|
||||
refund_enabled: boolean
|
||||
limits: string
|
||||
sort_order: number
|
||||
}
|
||||
|
||||
// ==================== Request / Response ====================
|
||||
|
||||
export interface CreateOrderRequest {
|
||||
amount: number
|
||||
payment_type: string
|
||||
order_type: string
|
||||
plan_id?: number
|
||||
}
|
||||
|
||||
export interface CreateOrderResult {
|
||||
order_id: number
|
||||
pay_url?: string
|
||||
qr_code?: string
|
||||
client_secret?: string
|
||||
pay_amount: number
|
||||
expires_at: string
|
||||
payment_mode?: string
|
||||
}
|
||||
|
||||
export interface DashboardStats {
|
||||
today_amount: number
|
||||
total_amount: number
|
||||
today_count: number
|
||||
total_count: number
|
||||
avg_amount: number
|
||||
daily_series: { date: string; amount: number; count: number }[]
|
||||
payment_methods: { type: string; amount: number; count: number }[]
|
||||
top_users: { user_id: number; email: string; amount: number }[]
|
||||
}
|
||||
Reference in New Issue
Block a user