feat(payments): 增加兑换码与支付适配框架 (#299)

* feat(payments): 增加兑换码与支付适配框架

* fix(ci): 对齐 Rust 1.95 lint 与格式要求

* fix(payments): harden redeem code wallet credits

---------

Co-authored-by: fawney19 <elky0401@gmail.com>
This commit is contained in:
Entropy.Xu
2026-04-17 10:07:52 +08:00
committed by GitHub
parent 54d77598ae
commit 6964729cb7
36 changed files with 5129 additions and 274 deletions

View File

@@ -39,6 +39,75 @@ export interface AdminPaymentCreditRequest {
gateway_response?: Record<string, unknown>
}
export interface RedeemCodeBatch {
id: string
name: string
amount_usd: number
currency: string
balance_bucket: string
total_count: number
redeemed_count: number
active_count: number
status: string
description?: string | null
created_by?: string | null
expires_at?: string | null
created_at: string | null
updated_at: string | null
}
export interface RedeemCodeRecord {
id: string
batch_id: string
batch_name?: string | null
code_prefix: string
code_suffix: string
masked_code: string
status: string
redeemed_by_user_id?: string | null
redeemed_by_user_name?: string | null
redeemed_wallet_id?: string | null
redeemed_payment_order_id?: string | null
redeemed_order_no?: string | null
redeemed_at?: string | null
disabled_by?: string | null
expires_at?: string | null
created_at: string | null
updated_at: string | null
}
export interface CreateRedeemCodeBatchRequest {
name: string
amount_usd: number
total_count: number
expires_at?: string
description?: string
}
export interface CreateRedeemCodeBatchResponse {
batch: RedeemCodeBatch
codes: Array<{
id: string
code: string
masked_code: string
}>
}
export interface RedeemCodeBatchListResponse {
items: RedeemCodeBatch[]
total: number
limit: number
offset: number
}
export interface RedeemCodeListResponse {
batch: RedeemCodeBatch
items: RedeemCodeRecord[]
total: number
limit: number
offset: number
}
export const adminPaymentsApi = {
async listOrders(params?: {
status?: string
@@ -90,4 +159,72 @@ export const adminPaymentsApi = {
const response = await apiClient.get<AdminPaymentCallbacksResponse>('/api/admin/payments/callbacks', { params })
return response.data
},
async listRedeemCodeBatches(params?: {
status?: string
limit?: number
offset?: number
}): Promise<RedeemCodeBatchListResponse> {
const response = await apiClient.get<RedeemCodeBatchListResponse>(
'/api/admin/payments/redeem-codes/batches',
{ params }
)
return response.data
},
async createRedeemCodeBatch(
payload: CreateRedeemCodeBatchRequest
): Promise<CreateRedeemCodeBatchResponse> {
const response = await apiClient.post<CreateRedeemCodeBatchResponse>(
'/api/admin/payments/redeem-codes/batches',
payload
)
return response.data
},
async getRedeemCodeBatch(batchId: string): Promise<{ batch: RedeemCodeBatch }> {
const response = await apiClient.get<{ batch: RedeemCodeBatch }>(
`/api/admin/payments/redeem-codes/batches/${batchId}`
)
return response.data
},
async listRedeemCodes(
batchId: string,
params?: {
status?: string
limit?: number
offset?: number
}
): Promise<RedeemCodeListResponse> {
const response = await apiClient.get<RedeemCodeListResponse>(
`/api/admin/payments/redeem-codes/batches/${batchId}/codes`,
{ params }
)
return response.data
},
async disableRedeemCodeBatch(batchId: string): Promise<{ batch: RedeemCodeBatch }> {
const response = await apiClient.post<{ batch: RedeemCodeBatch }>(
`/api/admin/payments/redeem-codes/batches/${batchId}/disable`,
{}
)
return response.data
},
async deleteRedeemCodeBatch(batchId: string): Promise<{ batch: RedeemCodeBatch }> {
const response = await apiClient.post<{ batch: RedeemCodeBatch }>(
`/api/admin/payments/redeem-codes/batches/${batchId}/delete`,
{}
)
return response.data
},
async disableRedeemCode(codeId: string): Promise<{ code: RedeemCodeRecord }> {
const response = await apiClient.post<{ code: RedeemCodeRecord }>(
`/api/admin/payments/redeem-codes/codes/${codeId}/disable`,
{}
)
return response.data
},
}

View File

@@ -150,6 +150,17 @@ export interface WalletRefundCreateRequest {
idempotency_key?: string
}
export interface WalletRedeemRequest {
code: string
}
export interface WalletRedeemResponse {
order: PaymentOrder
wallet: WalletSummary
amount_usd: number
batch_name: string
}
export const walletApi = {
async getBalance(): Promise<WalletBalanceResponse> {
const response = await apiClient.get<WalletBalanceResponse>('/api/wallet/balance')
@@ -213,4 +224,9 @@ export const walletApi = {
const response = await apiClient.post<RefundRequest>('/api/wallet/refunds', payload)
return response.data
},
async redeemCode(payload: WalletRedeemRequest): Promise<WalletRedeemResponse> {
const response = await apiClient.post<WalletRedeemResponse>('/api/wallet/redeem', payload)
return response.data
},
}