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:
71
frontend/src/utils/apiError.ts
Normal file
71
frontend/src/utils/apiError.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Centralized API error message extraction
|
||||
*
|
||||
* The API client interceptor rejects with a plain object: { status, code, message, error }
|
||||
* This utility extracts the user-facing message from any error shape.
|
||||
*/
|
||||
|
||||
interface ApiErrorLike {
|
||||
status?: number
|
||||
code?: number | string
|
||||
message?: string
|
||||
error?: string
|
||||
reason?: string
|
||||
metadata?: Record<string, unknown>
|
||||
response?: {
|
||||
data?: {
|
||||
detail?: string
|
||||
message?: string
|
||||
code?: number | string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the error code from an API error object.
|
||||
*/
|
||||
export function extractApiErrorCode(err: unknown): string | undefined {
|
||||
if (!err || typeof err !== 'object') return undefined
|
||||
const e = err as ApiErrorLike
|
||||
const code = e.code ?? e.reason ?? e.response?.data?.code
|
||||
return code != null ? String(code) : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a displayable error message from an API error.
|
||||
*
|
||||
* @param err - The caught error (unknown type)
|
||||
* @param fallback - Fallback message if none can be extracted (use t('common.error') or similar)
|
||||
* @param i18nMap - Optional map of error codes to i18n translated strings
|
||||
*/
|
||||
export function extractApiErrorMessage(
|
||||
err: unknown,
|
||||
fallback = 'Unknown error',
|
||||
i18nMap?: Record<string, string>,
|
||||
): string {
|
||||
if (!err) return fallback
|
||||
|
||||
// Try i18n mapping by error code first
|
||||
if (i18nMap) {
|
||||
const code = extractApiErrorCode(err)
|
||||
if (code && i18nMap[code]) return i18nMap[code]
|
||||
}
|
||||
|
||||
// Plain object from API client interceptor (most common case)
|
||||
if (typeof err === 'object' && err !== null) {
|
||||
const e = err as ApiErrorLike
|
||||
// Interceptor shape: { message, error }
|
||||
if (e.message) return e.message
|
||||
if (e.error) return e.error
|
||||
// Legacy axios shape: { response.data.detail }
|
||||
if (e.response?.data?.detail) return e.response.data.detail
|
||||
if (e.response?.data?.message) return e.response.data.message
|
||||
}
|
||||
|
||||
// Standard Error
|
||||
if (err instanceof Error) return err.message
|
||||
|
||||
// Last resort
|
||||
const str = String(err)
|
||||
return str === '[object Object]' ? fallback : str
|
||||
}
|
||||
Reference in New Issue
Block a user