mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
feat(wallet): 钱包系统替代配额系统,新增支付与退款机制
- 新增钱包余额管理、充值、扣费、退款完整流程 - 新增支付网关抽象层(支持手动/支付宝/微信) - 用量计费从配额系统迁移到钱包余额扣费 - 新增管理员钱包管理与支付订单管理页面 - 新增用户钱包中心页面 - 移除独立 Key 锁定机制,统一由钱包余额控制 - 新增相关 API 路由、序列化器与数据库迁移 - 新增钱包、支付、退款相关测试
This commit is contained in:
93
frontend/src/api/admin-payments.ts
Normal file
93
frontend/src/api/admin-payments.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import apiClient from './client'
|
||||
import type { PaymentOrder } from './wallet'
|
||||
|
||||
export interface PaymentCallbackRecord {
|
||||
id: string
|
||||
payment_order_id: string | null
|
||||
payment_method: string
|
||||
callback_key: string
|
||||
order_no: string | null
|
||||
gateway_order_id: string | null
|
||||
payload_hash: string | null
|
||||
signature_valid: boolean
|
||||
status: string
|
||||
payload: Record<string, unknown> | null
|
||||
error_message: string | null
|
||||
created_at: string
|
||||
processed_at: string | null
|
||||
}
|
||||
|
||||
export interface AdminPaymentOrderListResponse {
|
||||
items: PaymentOrder[]
|
||||
total: number
|
||||
limit: number
|
||||
offset: number
|
||||
}
|
||||
|
||||
export interface AdminPaymentCallbacksResponse {
|
||||
items: PaymentCallbackRecord[]
|
||||
total: number
|
||||
limit: number
|
||||
offset: number
|
||||
}
|
||||
|
||||
export interface AdminPaymentCreditRequest {
|
||||
gateway_order_id?: string
|
||||
pay_amount?: number
|
||||
pay_currency?: string
|
||||
exchange_rate?: number
|
||||
gateway_response?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export const adminPaymentsApi = {
|
||||
async listOrders(params?: {
|
||||
status?: string
|
||||
payment_method?: string
|
||||
limit?: number
|
||||
offset?: number
|
||||
}): Promise<AdminPaymentOrderListResponse> {
|
||||
const response = await apiClient.get<AdminPaymentOrderListResponse>('/api/admin/payments/orders', { params })
|
||||
return response.data
|
||||
},
|
||||
|
||||
async getOrder(orderId: string): Promise<{ order: PaymentOrder }> {
|
||||
const response = await apiClient.get<{ order: PaymentOrder }>(`/api/admin/payments/orders/${orderId}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
async expireOrder(orderId: string): Promise<{ order: PaymentOrder; expired: boolean }> {
|
||||
const response = await apiClient.post<{ order: PaymentOrder; expired: boolean }>(
|
||||
`/api/admin/payments/orders/${orderId}/expire`,
|
||||
{}
|
||||
)
|
||||
return response.data
|
||||
},
|
||||
|
||||
async failOrder(orderId: string): Promise<{ order: PaymentOrder }> {
|
||||
const response = await apiClient.post<{ order: PaymentOrder }>(
|
||||
`/api/admin/payments/orders/${orderId}/fail`,
|
||||
{}
|
||||
)
|
||||
return response.data
|
||||
},
|
||||
|
||||
async creditOrder(
|
||||
orderId: string,
|
||||
payload: AdminPaymentCreditRequest
|
||||
): Promise<{ order: PaymentOrder; credited: boolean }> {
|
||||
const response = await apiClient.post<{ order: PaymentOrder; credited: boolean }>(
|
||||
`/api/admin/payments/orders/${orderId}/credit`,
|
||||
payload
|
||||
)
|
||||
return response.data
|
||||
},
|
||||
|
||||
async listCallbacks(params?: {
|
||||
payment_method?: string
|
||||
limit?: number
|
||||
offset?: number
|
||||
}): Promise<AdminPaymentCallbacksResponse> {
|
||||
const response = await apiClient.get<AdminPaymentCallbacksResponse>('/api/admin/payments/callbacks', { params })
|
||||
return response.data
|
||||
},
|
||||
}
|
||||
247
frontend/src/api/admin-wallets.ts
Normal file
247
frontend/src/api/admin-wallets.ts
Normal file
@@ -0,0 +1,247 @@
|
||||
import apiClient from './client'
|
||||
import type { RefundRequest, WalletSummary, WalletTransaction } from './wallet'
|
||||
|
||||
export interface AdminWallet extends WalletSummary {
|
||||
user_id: string | null
|
||||
api_key_id: string | null
|
||||
owner_type: 'user' | 'api_key'
|
||||
owner_name: string | null
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export interface AdminWalletListResponse {
|
||||
items: AdminWallet[]
|
||||
total: number
|
||||
limit: number
|
||||
offset: number
|
||||
}
|
||||
|
||||
export interface AdminWalletDetailResponse extends AdminWallet {
|
||||
pending_refund_count: number
|
||||
}
|
||||
|
||||
export interface AdminWalletTransactionsResponse {
|
||||
wallet: AdminWallet
|
||||
items: WalletTransaction[]
|
||||
total: number
|
||||
limit: number
|
||||
offset: number
|
||||
}
|
||||
|
||||
export interface AdminWalletRefundsResponse {
|
||||
wallet: AdminWallet
|
||||
items: RefundRequest[]
|
||||
total: number
|
||||
limit: number
|
||||
offset: number
|
||||
}
|
||||
|
||||
export interface AdminLedgerTransaction extends WalletTransaction {
|
||||
wallet_id: string
|
||||
owner_type: 'user' | 'api_key'
|
||||
owner_name: string | null
|
||||
wallet_status?: string | null
|
||||
}
|
||||
|
||||
export interface AdminGlobalRefund extends RefundRequest {
|
||||
wallet_id: string
|
||||
owner_type: 'user' | 'api_key'
|
||||
owner_name: string | null
|
||||
wallet_status?: string | null
|
||||
}
|
||||
|
||||
export interface AdminLedgerResponse {
|
||||
items: AdminLedgerTransaction[]
|
||||
total: number
|
||||
limit: number
|
||||
offset: number
|
||||
}
|
||||
|
||||
export interface AdminGlobalRefundsListResponse {
|
||||
items: AdminGlobalRefund[]
|
||||
total: number
|
||||
limit: number
|
||||
offset: number
|
||||
}
|
||||
|
||||
export interface ManualRechargeRequest {
|
||||
amount_usd: number
|
||||
payment_method?: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
export interface WalletAdjustRequest {
|
||||
amount_usd: number
|
||||
balance_type?: 'recharge' | 'gift'
|
||||
description?: string
|
||||
}
|
||||
|
||||
export interface RefundFailRequest {
|
||||
reason: string
|
||||
}
|
||||
|
||||
export interface RefundCompleteRequest {
|
||||
gateway_refund_id?: string
|
||||
payout_reference?: string
|
||||
payout_proof?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export const adminWalletApi = {
|
||||
async listWallets(params?: {
|
||||
status?: string
|
||||
limit?: number
|
||||
offset?: number
|
||||
}): Promise<AdminWalletListResponse> {
|
||||
const response = await apiClient.get<AdminWalletListResponse>('/api/admin/wallets', { params })
|
||||
return response.data
|
||||
},
|
||||
|
||||
async listAllWallets(params?: {
|
||||
status?: string
|
||||
}): Promise<AdminWallet[]> {
|
||||
const items: AdminWallet[] = []
|
||||
const limit = 200
|
||||
const maxPages = 200
|
||||
let offset = 0
|
||||
let page = 0
|
||||
|
||||
while (page < maxPages) {
|
||||
const response = await apiClient.get<AdminWalletListResponse>('/api/admin/wallets', {
|
||||
params: {
|
||||
...params,
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
})
|
||||
const data = response.data
|
||||
items.push(...data.items)
|
||||
|
||||
if (items.length >= data.total || data.items.length < limit) {
|
||||
break
|
||||
}
|
||||
|
||||
const nextOffset = offset + data.items.length
|
||||
if (nextOffset <= offset) {
|
||||
throw new Error('分页游标未前进,终止全量钱包拉取以避免死循环')
|
||||
}
|
||||
offset = nextOffset
|
||||
page += 1
|
||||
}
|
||||
|
||||
if (page >= maxPages) {
|
||||
throw new Error(`钱包列表分页超过最大页数 ${maxPages},已中止请求`)
|
||||
}
|
||||
|
||||
return items
|
||||
},
|
||||
|
||||
async getWalletDetail(walletId: string): Promise<AdminWalletDetailResponse> {
|
||||
const response = await apiClient.get<AdminWalletDetailResponse>(`/api/admin/wallets/${walletId}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
async listLedger(params?: {
|
||||
category?: string
|
||||
reason_code?: string
|
||||
owner_type?: string
|
||||
limit?: number
|
||||
offset?: number
|
||||
}): Promise<AdminLedgerResponse> {
|
||||
const response = await apiClient.get<AdminLedgerResponse>('/api/admin/wallets/ledger', { params })
|
||||
return response.data
|
||||
},
|
||||
|
||||
async listGlobalRefunds(params?: {
|
||||
status?: string
|
||||
owner_type?: string
|
||||
limit?: number
|
||||
offset?: number
|
||||
}): Promise<AdminGlobalRefundsListResponse> {
|
||||
const response = await apiClient.get<AdminGlobalRefundsListResponse>('/api/admin/wallets/refund-requests', {
|
||||
params,
|
||||
})
|
||||
return response.data
|
||||
},
|
||||
|
||||
async getWalletTransactions(
|
||||
walletId: string,
|
||||
params?: { limit?: number; offset?: number }
|
||||
): Promise<AdminWalletTransactionsResponse> {
|
||||
const response = await apiClient.get<AdminWalletTransactionsResponse>(
|
||||
`/api/admin/wallets/${walletId}/transactions`,
|
||||
{ params }
|
||||
)
|
||||
return response.data
|
||||
},
|
||||
|
||||
async getWalletRefunds(
|
||||
walletId: string,
|
||||
params?: { limit?: number; offset?: number }
|
||||
): Promise<AdminWalletRefundsResponse> {
|
||||
const response = await apiClient.get<AdminWalletRefundsResponse>(
|
||||
`/api/admin/wallets/${walletId}/refunds`,
|
||||
{ params }
|
||||
)
|
||||
return response.data
|
||||
},
|
||||
|
||||
async rechargeWallet(walletId: string, payload: ManualRechargeRequest): Promise<{
|
||||
wallet: AdminWallet
|
||||
payment_order: {
|
||||
id: string
|
||||
order_no: string
|
||||
amount_usd: number
|
||||
payment_method: string
|
||||
status: string
|
||||
created_at: string
|
||||
credited_at: string | null
|
||||
}
|
||||
}> {
|
||||
const response = await apiClient.post(`/api/admin/wallets/${walletId}/recharge`, payload)
|
||||
return response.data
|
||||
},
|
||||
|
||||
async adjustWallet(walletId: string, payload: WalletAdjustRequest): Promise<{
|
||||
wallet: AdminWallet
|
||||
transaction: WalletTransaction
|
||||
}> {
|
||||
const response = await apiClient.post(`/api/admin/wallets/${walletId}/adjust`, payload)
|
||||
return response.data
|
||||
},
|
||||
|
||||
async processRefund(walletId: string, refundId: string): Promise<{
|
||||
wallet: AdminWallet
|
||||
refund: RefundRequest
|
||||
transaction: WalletTransaction
|
||||
}> {
|
||||
const response = await apiClient.post(
|
||||
`/api/admin/wallets/${walletId}/refunds/${refundId}/process`,
|
||||
{}
|
||||
)
|
||||
return response.data
|
||||
},
|
||||
|
||||
async failRefund(walletId: string, refundId: string, payload: RefundFailRequest): Promise<{
|
||||
wallet: AdminWallet
|
||||
refund: RefundRequest
|
||||
transaction: WalletTransaction | null
|
||||
}> {
|
||||
const response = await apiClient.post(
|
||||
`/api/admin/wallets/${walletId}/refunds/${refundId}/fail`,
|
||||
payload
|
||||
)
|
||||
return response.data
|
||||
},
|
||||
|
||||
async completeRefund(
|
||||
walletId: string,
|
||||
refundId: string,
|
||||
payload: RefundCompleteRequest
|
||||
): Promise<{ refund: RefundRequest }> {
|
||||
const response = await apiClient.post(
|
||||
`/api/admin/wallets/${walletId}/refunds/${refundId}/complete`,
|
||||
payload
|
||||
)
|
||||
return response.data
|
||||
},
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import apiClient from './client'
|
||||
import { cachedRequest, buildCacheKey } from '@/utils/cache'
|
||||
import type { BillingSummary } from './auth'
|
||||
|
||||
// LDAP 配置导出结构
|
||||
export interface LDAPConfigExport {
|
||||
@@ -69,9 +70,8 @@ export interface UserExport {
|
||||
allowed_api_formats?: string[] | null
|
||||
allowed_models?: string[] | null
|
||||
model_capability_settings?: Record<string, Record<string, boolean>>
|
||||
quota_usd?: number | null
|
||||
used_usd?: number
|
||||
total_usd?: number
|
||||
unlimited?: boolean
|
||||
wallet?: BillingSummary | null
|
||||
is_active: boolean
|
||||
api_keys: UserApiKeyExport[]
|
||||
}
|
||||
@@ -82,8 +82,6 @@ export interface UserApiKeyExport {
|
||||
key_encrypted?: string | null
|
||||
name?: string | null
|
||||
is_standalone: boolean
|
||||
balance_used_usd?: number
|
||||
current_balance_usd?: number | null
|
||||
allowed_providers?: string[] | null
|
||||
allowed_api_formats?: string[] | null
|
||||
allowed_models?: string[] | null
|
||||
@@ -329,10 +327,7 @@ export interface AdminApiKey {
|
||||
name?: string
|
||||
key_display?: string // 脱敏后的密钥显示
|
||||
is_active: boolean
|
||||
is_locked: boolean // 管理员锁定标志
|
||||
is_standalone: boolean // 是否为独立余额Key
|
||||
balance_used_usd?: number // 已使用余额(仅独立Key)
|
||||
current_balance_usd?: number | null // 当前余额(独立Key预付费模式,null表示无限制)
|
||||
total_requests?: number
|
||||
total_tokens?: number
|
||||
total_cost_usd?: number
|
||||
@@ -354,7 +349,8 @@ export interface CreateStandaloneApiKeyRequest {
|
||||
allowed_models?: string[] | null
|
||||
rate_limit?: number | null // null = 无限制
|
||||
expires_at?: string | null // ISO 日期字符串,如 "2025-12-31",null = 永不过期
|
||||
initial_balance_usd: number // 初始余额,必须设置
|
||||
initial_balance_usd: number | null // 初始余额,null = 无限制
|
||||
unlimited_balance?: boolean | null // 编辑时仅切换额度模式,不调整余额数值
|
||||
auto_delete_on_expiry?: boolean // 过期后是否自动删除
|
||||
}
|
||||
|
||||
@@ -502,7 +498,10 @@ export const adminApi = {
|
||||
},
|
||||
|
||||
// 更新独立余额Key
|
||||
async updateApiKey(keyId: string, data: Partial<CreateStandaloneApiKeyRequest>): Promise<AdminApiKey & { message: string }> {
|
||||
async updateApiKey(
|
||||
keyId: string,
|
||||
data: Partial<CreateStandaloneApiKeyRequest>
|
||||
): Promise<AdminApiKey & { message: string }> {
|
||||
const response = await apiClient.put<AdminApiKey & { message: string }>(
|
||||
`/api/admin/api-keys/${keyId}`,
|
||||
data
|
||||
@@ -526,27 +525,10 @@ export const adminApi = {
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 切换API密钥锁定状态(锁定/解锁)
|
||||
async toggleLockApiKey(keyId: string): Promise<ApiKeyLockResponse> {
|
||||
// 切换用户普通 API Key 锁定状态(锁定/解锁)
|
||||
async toggleUserApiKeyLock(userId: string, keyId: string): Promise<ApiKeyLockResponse> {
|
||||
const response = await apiClient.patch<ApiKeyLockResponse>(
|
||||
`/api/admin/api-keys/${keyId}/lock`
|
||||
)
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 为独立余额Key调整余额
|
||||
async addApiKeyBalance(keyId: string, amountUsd: number): Promise<AdminApiKey & { message: string }> {
|
||||
const response = await apiClient.patch<AdminApiKey & { message: string }>(
|
||||
`/api/admin/api-keys/${keyId}/balance`,
|
||||
{ amount_usd: amountUsd }
|
||||
)
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 重置独立余额Key的已使用额度
|
||||
async resetApiKeyUsage(keyId: string): Promise<AdminApiKey & { message: string }> {
|
||||
const response = await apiClient.patch<AdminApiKey & { message: string }>(
|
||||
`/api/admin/api-keys/${keyId}/reset-usage`
|
||||
`/api/admin/users/${userId}/api-keys/${keyId}/lock`
|
||||
)
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -89,15 +89,30 @@ export interface AuthSettingsResponse {
|
||||
ldap_exclusive: boolean
|
||||
}
|
||||
|
||||
export interface BillingSummary {
|
||||
id?: string | null
|
||||
balance: number
|
||||
recharge_balance: number
|
||||
gift_balance: number
|
||||
refundable_balance: number
|
||||
currency: string
|
||||
status: string
|
||||
limit_mode: 'finite' | 'unlimited'
|
||||
unlimited: boolean
|
||||
total_recharged: number
|
||||
total_consumed: number
|
||||
total_refunded: number
|
||||
total_adjusted: number
|
||||
updated_at?: string | null
|
||||
}
|
||||
|
||||
export interface User {
|
||||
id: string // UUID
|
||||
username: string
|
||||
email?: string
|
||||
role: string // 'admin' or 'user'
|
||||
is_active: boolean
|
||||
quota_usd?: number | null
|
||||
used_usd?: number
|
||||
total_usd?: number
|
||||
billing?: BillingSummary
|
||||
allowed_providers?: string[] | null // 允许使用的提供商 ID 列表
|
||||
allowed_api_formats?: string[] | null // 允许使用的 API 格式列表
|
||||
allowed_models?: string[] | null // 允许使用的模型名称列表
|
||||
|
||||
@@ -2,6 +2,7 @@ import apiClient from './client'
|
||||
import type { ActivityHeatmap } from '@/types/activity'
|
||||
import type { TieredPricingConfig } from './endpoints/types'
|
||||
import { cachedRequest, buildCacheKey } from '@/utils/cache'
|
||||
import type { BillingSummary } from './auth'
|
||||
|
||||
export interface Profile {
|
||||
id: string // UUID
|
||||
@@ -9,9 +10,7 @@ export interface Profile {
|
||||
username: string
|
||||
role: string
|
||||
is_active: boolean
|
||||
quota_usd: number | null
|
||||
used_usd: number
|
||||
total_usd?: number // 累积消费总额
|
||||
billing: BillingSummary
|
||||
created_at: string
|
||||
updated_at?: string
|
||||
last_login_at?: string
|
||||
@@ -103,8 +102,7 @@ export interface UsageResponse {
|
||||
total_cost: number // 官方费率
|
||||
total_actual_cost?: number // 倍率消耗(仅管理员可见)
|
||||
avg_response_time: number
|
||||
quota_usd: number | null
|
||||
used_usd: number
|
||||
billing: BillingSummary
|
||||
summary_by_model: ModelSummary[]
|
||||
summary_by_provider?: ProviderSummary[]
|
||||
pagination?: {
|
||||
|
||||
@@ -6,14 +6,13 @@ export interface User {
|
||||
email: string
|
||||
role: 'admin' | 'user'
|
||||
is_active: boolean
|
||||
quota_usd: number | null
|
||||
used_usd: number
|
||||
total_usd: number
|
||||
unlimited: boolean
|
||||
allowed_providers: string[] | null // 允许使用的提供商 ID 列表
|
||||
allowed_api_formats: string[] | null // 允许使用的 API 格式列表
|
||||
allowed_models: string[] | null // 允许使用的模型名称列表
|
||||
created_at: string
|
||||
updated_at?: string
|
||||
last_login_at?: string | null
|
||||
}
|
||||
|
||||
export interface CreateUserRequest {
|
||||
@@ -21,7 +20,7 @@ export interface CreateUserRequest {
|
||||
password: string
|
||||
email: string
|
||||
role?: 'admin' | 'user'
|
||||
quota_usd?: number | null
|
||||
initial_gift_usd?: number | null
|
||||
unlimited?: boolean
|
||||
allowed_providers?: string[] | null
|
||||
allowed_api_formats?: string[] | null
|
||||
@@ -32,7 +31,7 @@ export interface UpdateUserRequest {
|
||||
email?: string
|
||||
is_active?: boolean
|
||||
role?: 'admin' | 'user'
|
||||
quota_usd?: number | null
|
||||
unlimited?: boolean
|
||||
password?: string
|
||||
allowed_providers?: string[] | null
|
||||
allowed_api_formats?: string[] | null
|
||||
@@ -50,8 +49,6 @@ export interface ApiKey {
|
||||
is_active: boolean
|
||||
is_locked: boolean // 管理员锁定标志
|
||||
is_standalone: boolean // 是否为独立余额Key
|
||||
balance_used_usd?: number // 已使用余额(仅独立Key)
|
||||
current_balance_usd?: number | null // 当前余额(独立Key预付费模式,null表示无限制)
|
||||
rate_limit?: number // 速率限制(请求/分钟)
|
||||
total_requests?: number // 总请求数
|
||||
total_cost_usd?: number // 总费用
|
||||
@@ -96,10 +93,12 @@ export const usersApi = {
|
||||
await apiClient.delete(`/api/admin/users/${userId}/api-keys/${keyId}`)
|
||||
},
|
||||
|
||||
async resetUserQuota(userId: string): Promise<void> {
|
||||
await apiClient.patch(`/api/admin/users/${userId}/quota`)
|
||||
async getFullApiKey(userId: string, keyId: string): Promise<{ key: string }> {
|
||||
const response = await apiClient.get<{ key: string }>(
|
||||
`/api/admin/users/${userId}/api-keys/${keyId}/full-key`
|
||||
)
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 管理员统计
|
||||
async getUsageStats(): Promise<Record<string, unknown>> {
|
||||
const response = await apiClient.get('/api/admin/usage/stats')
|
||||
|
||||
176
frontend/src/api/wallet.ts
Normal file
176
frontend/src/api/wallet.ts
Normal file
@@ -0,0 +1,176 @@
|
||||
import apiClient from './client'
|
||||
|
||||
export interface WalletSummary {
|
||||
id: string
|
||||
// balance = 总可用余额(充值余额 + 赠款余额)
|
||||
balance: number
|
||||
recharge_balance: number
|
||||
gift_balance: number
|
||||
refundable_balance: number
|
||||
currency: string
|
||||
status: string
|
||||
limit_mode?: 'finite' | 'unlimited'
|
||||
unlimited?: boolean
|
||||
total_recharged: number
|
||||
total_consumed: number
|
||||
total_refunded: number
|
||||
total_adjusted: number
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface WalletBalanceResponse {
|
||||
wallet: WalletSummary | null
|
||||
unlimited: boolean
|
||||
limit_mode: 'finite' | 'unlimited'
|
||||
// balance = 总可用余额(充值余额 + 赠款余额)
|
||||
balance: number | null
|
||||
recharge_balance?: number | null
|
||||
gift_balance?: number | null
|
||||
refundable_balance?: number | null
|
||||
currency: string
|
||||
pending_refund_count?: number
|
||||
}
|
||||
|
||||
export interface WalletTransaction {
|
||||
id: string
|
||||
category: string
|
||||
reason_code: string
|
||||
amount: number
|
||||
// 总可用余额(充值+赠款)快照
|
||||
balance_before: number
|
||||
balance_after: number
|
||||
// 分账户快照
|
||||
recharge_balance_before: number
|
||||
recharge_balance_after: number
|
||||
gift_balance_before: number
|
||||
gift_balance_after: number
|
||||
link_type?: string | null
|
||||
link_id?: string | null
|
||||
operator_id?: string | null
|
||||
operator_name?: string | null
|
||||
operator_email?: string | null
|
||||
description?: string | null
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export interface WalletTransactionsResponse extends WalletBalanceResponse {
|
||||
items: WalletTransaction[]
|
||||
total: number
|
||||
limit: number
|
||||
offset: number
|
||||
}
|
||||
|
||||
export interface PaymentOrder {
|
||||
id: string
|
||||
order_no: string
|
||||
wallet_id: string
|
||||
user_id: string | null
|
||||
amount_usd: number
|
||||
pay_amount: number | null
|
||||
pay_currency: string | null
|
||||
exchange_rate: number | null
|
||||
refunded_amount_usd: number
|
||||
refundable_amount_usd: number
|
||||
payment_method: string
|
||||
gateway_order_id: string | null
|
||||
gateway_response: Record<string, unknown> | null
|
||||
status: string
|
||||
created_at: string
|
||||
paid_at: string | null
|
||||
credited_at: string | null
|
||||
expires_at: string | null
|
||||
}
|
||||
|
||||
export interface RefundRequest {
|
||||
id: string
|
||||
refund_no: string
|
||||
payment_order_id: string | null
|
||||
source_type: string
|
||||
source_id: string | null
|
||||
refund_mode: string
|
||||
amount_usd: number
|
||||
status: string
|
||||
reason: string | null
|
||||
failure_reason: string | null
|
||||
gateway_refund_id: string | null
|
||||
payout_method: string | null
|
||||
payout_reference: string | null
|
||||
payout_proof: Record<string, unknown> | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
processed_at: string | null
|
||||
completed_at: string | null
|
||||
}
|
||||
|
||||
export interface WalletRechargeCreateRequest {
|
||||
amount_usd: number
|
||||
payment_method: string
|
||||
pay_amount?: number
|
||||
pay_currency?: string
|
||||
exchange_rate?: number
|
||||
}
|
||||
|
||||
export interface WalletRefundCreateRequest {
|
||||
amount_usd: number
|
||||
payment_order_id?: string
|
||||
source_type?: string
|
||||
source_id?: string
|
||||
refund_mode?: string
|
||||
reason?: string
|
||||
idempotency_key?: string
|
||||
}
|
||||
|
||||
export const walletApi = {
|
||||
async getBalance(): Promise<WalletBalanceResponse> {
|
||||
const response = await apiClient.get<WalletBalanceResponse>('/api/wallet/balance')
|
||||
return response.data
|
||||
},
|
||||
|
||||
async getTransactions(params?: { limit?: number; offset?: number }): Promise<WalletTransactionsResponse> {
|
||||
const response = await apiClient.get<WalletTransactionsResponse>('/api/wallet/transactions', { params })
|
||||
return response.data
|
||||
},
|
||||
|
||||
async createRechargeOrder(payload: WalletRechargeCreateRequest): Promise<{
|
||||
order: PaymentOrder
|
||||
payment_instructions: Record<string, unknown>
|
||||
}> {
|
||||
const response = await apiClient.post('/api/wallet/recharge', payload)
|
||||
return response.data
|
||||
},
|
||||
|
||||
async listRechargeOrders(params?: { limit?: number; offset?: number }): Promise<{
|
||||
items: PaymentOrder[]
|
||||
total: number
|
||||
limit: number
|
||||
offset: number
|
||||
}> {
|
||||
const response = await apiClient.get('/api/wallet/recharge', { params })
|
||||
return response.data
|
||||
},
|
||||
|
||||
async getRechargeOrder(orderId: string): Promise<{ order: PaymentOrder }> {
|
||||
const response = await apiClient.get(`/api/wallet/recharge/${orderId}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
async listRefunds(params?: { limit?: number; offset?: number }): Promise<{
|
||||
items: RefundRequest[]
|
||||
total: number
|
||||
limit: number
|
||||
offset: number
|
||||
}> {
|
||||
const response = await apiClient.get('/api/wallet/refunds', { params })
|
||||
return response.data
|
||||
},
|
||||
|
||||
async getRefund(refundId: string): Promise<RefundRequest> {
|
||||
const response = await apiClient.get<RefundRequest>(`/api/wallet/refunds/${refundId}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
async createRefund(payload: WalletRefundCreateRequest): Promise<RefundRequest> {
|
||||
const response = await apiClient.post<RefundRequest>('/api/wallet/refunds', payload)
|
||||
return response.data
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user