Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
- fix(industry): list_industries SQL参数编号错位 — count查询和items查询 共用WHERE子句但参数从$3开始,sqlx bind按$1/$2顺序绑定导致500 - feat(billing): 新增 PUT /admin/accounts/:id/subscription 端点 (super_admin) 验证目标计划 → 取消当前订阅 → 创建新订阅(30天) → 同步配额 - feat(admin-v2): Accounts.tsx 编辑弹窗新增「订阅计划」选择区 显示所有活跃计划,保存时调用admin switch plan API
99 lines
2.4 KiB
TypeScript
99 lines
2.4 KiB
TypeScript
import request, { withSignal } from './request'
|
|
|
|
// === Types ===
|
|
|
|
export interface BillingPlan {
|
|
id: string
|
|
name: string
|
|
display_name: string
|
|
description: string | null
|
|
price_cents: number
|
|
currency: string
|
|
interval: string
|
|
features: Record<string, unknown>
|
|
limits: Record<string, unknown>
|
|
is_default: boolean
|
|
sort_order: number
|
|
status: string
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
export interface Subscription {
|
|
id: string
|
|
account_id: string
|
|
plan_id: string
|
|
status: string
|
|
current_period_start: string
|
|
current_period_end: string
|
|
trial_end: string | null
|
|
canceled_at: string | null
|
|
cancel_at_period_end: boolean
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
export interface UsageQuota {
|
|
id: string
|
|
account_id: string
|
|
period_start: string
|
|
period_end: string
|
|
input_tokens: number
|
|
output_tokens: number
|
|
relay_requests: number
|
|
hand_executions: number
|
|
pipeline_runs: number
|
|
max_input_tokens: number | null
|
|
max_output_tokens: number | null
|
|
max_relay_requests: number | null
|
|
max_hand_executions: number | null
|
|
max_pipeline_runs: number | null
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
export interface SubscriptionInfo {
|
|
plan: BillingPlan
|
|
subscription: Subscription | null
|
|
usage: UsageQuota
|
|
}
|
|
|
|
export interface PaymentResult {
|
|
payment_id: string
|
|
trade_no: string
|
|
pay_url: string
|
|
amount_cents: number
|
|
}
|
|
|
|
export interface PaymentStatus {
|
|
id: string
|
|
method: string
|
|
amount_cents: number
|
|
currency: string
|
|
status: string
|
|
}
|
|
|
|
// === Service ===
|
|
|
|
export const billingService = {
|
|
listPlans: (signal?: AbortSignal) =>
|
|
request.get<BillingPlan[]>('/billing/plans', withSignal({}, signal))
|
|
.then((r) => r.data),
|
|
|
|
getSubscription: (signal?: AbortSignal) =>
|
|
request.get<SubscriptionInfo>('/billing/subscription', withSignal({}, signal))
|
|
.then((r) => r.data),
|
|
|
|
createPayment: (data: { plan_id: string; payment_method: 'alipay' | 'wechat' }) =>
|
|
request.post<PaymentResult>('/billing/payments', data).then((r) => r.data),
|
|
|
|
getPaymentStatus: (id: string, signal?: AbortSignal) =>
|
|
request.get<PaymentStatus>(`/billing/payments/${id}`, withSignal({}, signal))
|
|
.then((r) => r.data),
|
|
|
|
/** 管理员切换用户订阅计划 (super_admin only) */
|
|
adminSwitchPlan: (accountId: string, planId: string) =>
|
|
request.put<{ success: boolean; subscription: Subscription }>(`/admin/accounts/${accountId}/subscription`, { plan_id: planId })
|
|
.then((r) => r.data),
|
|
}
|