@djangocfg/ext-payments 1.0.14 → 1.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.cjs +5 -8
- package/dist/config.js +5 -8
- package/dist/index.cjs +1906 -1043
- package/dist/index.d.cts +644 -59
- package/dist/index.d.ts +644 -59
- package/dist/index.js +1886 -1040
- package/package.json +13 -16
- package/src/WalletPage.tsx +100 -0
- package/src/api/generated/ext_payments/CLAUDE.md +10 -4
- package/src/api/generated/ext_payments/_utils/fetchers/ext_payments__payments.ts +268 -5
- package/src/api/generated/ext_payments/_utils/hooks/ext_payments__payments.ts +102 -3
- package/src/api/generated/ext_payments/_utils/schemas/Balance.schema.ts +1 -1
- package/src/api/generated/ext_payments/_utils/schemas/PaginatedWithdrawalListList.schema.ts +24 -0
- package/src/api/generated/ext_payments/_utils/schemas/PaymentCreateRequest.schema.ts +21 -0
- package/src/api/generated/ext_payments/_utils/schemas/PaymentCreateResponse.schema.ts +22 -0
- package/src/api/generated/ext_payments/_utils/schemas/PaymentDetail.schema.ts +3 -3
- package/src/api/generated/ext_payments/_utils/schemas/PaymentList.schema.ts +2 -2
- package/src/api/generated/ext_payments/_utils/schemas/Transaction.schema.ts +1 -1
- package/src/api/generated/ext_payments/_utils/schemas/WithdrawalCancelResponse.schema.ts +22 -0
- package/src/api/generated/ext_payments/_utils/schemas/WithdrawalCreateRequest.schema.ts +21 -0
- package/src/api/generated/ext_payments/_utils/schemas/WithdrawalCreateResponse.schema.ts +22 -0
- package/src/api/generated/ext_payments/_utils/schemas/WithdrawalDetail.schema.ts +42 -0
- package/src/api/generated/ext_payments/_utils/schemas/WithdrawalList.schema.ts +29 -0
- package/src/api/generated/ext_payments/_utils/schemas/index.ts +8 -0
- package/src/api/generated/ext_payments/client.ts +1 -1
- package/src/api/generated/ext_payments/enums.ts +36 -0
- package/src/api/generated/ext_payments/ext_payments__payments/client.ts +104 -6
- package/src/api/generated/ext_payments/ext_payments__payments/models.ts +168 -8
- package/src/api/generated/ext_payments/index.ts +1 -1
- package/src/api/generated/ext_payments/schema.json +752 -42
- package/src/components/ActivityItem.tsx +118 -0
- package/src/components/ActivityList.tsx +93 -0
- package/src/components/AddFundsSheet.tsx +342 -0
- package/src/components/BalanceHero.tsx +102 -0
- package/src/components/CurrencyCombobox.tsx +49 -0
- package/src/components/PaymentSheet.tsx +352 -0
- package/src/components/WithdrawSheet.tsx +355 -0
- package/src/components/WithdrawalSheet.tsx +332 -0
- package/src/components/index.ts +11 -0
- package/src/config.ts +1 -0
- package/src/contexts/WalletContext.tsx +356 -0
- package/src/contexts/index.ts +13 -42
- package/src/contexts/types.ts +43 -37
- package/src/hooks/index.ts +3 -20
- package/src/hooks/useCurrencyOptions.ts +79 -0
- package/src/hooks/useEstimate.ts +113 -0
- package/src/hooks/useWithdrawalEstimate.ts +117 -0
- package/src/index.ts +9 -18
- package/src/types/index.ts +78 -0
- package/src/utils/errors.ts +36 -0
- package/src/utils/format.ts +65 -0
- package/src/utils/index.ts +3 -0
- package/src/contexts/BalancesContext.tsx +0 -63
- package/src/contexts/CurrenciesContext.tsx +0 -64
- package/src/contexts/OverviewContext.tsx +0 -173
- package/src/contexts/PaymentsContext.tsx +0 -122
- package/src/contexts/PaymentsExtensionProvider.tsx +0 -56
- package/src/contexts/README.md +0 -201
- package/src/contexts/RootPaymentsContext.tsx +0 -66
- package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +0 -90
- package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +0 -274
- package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +0 -287
- package/src/layouts/PaymentsLayout/components/index.ts +0 -2
- package/src/layouts/PaymentsLayout/events.ts +0 -47
- package/src/layouts/PaymentsLayout/index.ts +0 -16
- package/src/layouts/PaymentsLayout/types.ts +0 -6
- package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +0 -121
- package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +0 -139
- package/src/layouts/PaymentsLayout/views/overview/components/index.ts +0 -2
- package/src/layouts/PaymentsLayout/views/overview/index.tsx +0 -21
- package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +0 -279
- package/src/layouts/PaymentsLayout/views/payments/components/index.ts +0 -1
- package/src/layouts/PaymentsLayout/views/payments/index.tsx +0 -18
- package/src/layouts/PaymentsLayout/views/transactions/components/TransactionsList.tsx +0 -260
- package/src/layouts/PaymentsLayout/views/transactions/components/index.ts +0 -1
- package/src/layouts/PaymentsLayout/views/transactions/index.tsx +0 -18
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hook for fetching currency estimates with debounce
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useState, useEffect } from 'react';
|
|
8
|
+
import { apiPayments } from '../api';
|
|
9
|
+
import type { DepositEstimate } from '../types';
|
|
10
|
+
|
|
11
|
+
interface UseEstimateOptions {
|
|
12
|
+
/** Currency code to get estimate for */
|
|
13
|
+
currencyCode: string | undefined;
|
|
14
|
+
/** Amount in USD */
|
|
15
|
+
amountUsd: number;
|
|
16
|
+
/** Minimum amount required (default: 0) */
|
|
17
|
+
minAmount?: number;
|
|
18
|
+
/** Debounce delay in ms (default: 300) */
|
|
19
|
+
debounceMs?: number;
|
|
20
|
+
/** Whether to skip fetching (e.g., when conditions not met) */
|
|
21
|
+
skip?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Parsed deposit estimate data for UI */
|
|
25
|
+
interface DepositEstimateData {
|
|
26
|
+
estimatedAmount: number;
|
|
27
|
+
usdRate: number;
|
|
28
|
+
minAmountUsd: number | null;
|
|
29
|
+
isStablecoin: boolean;
|
|
30
|
+
amountToReceive: number;
|
|
31
|
+
serviceFeeUsd: number;
|
|
32
|
+
serviceFeePercent: number;
|
|
33
|
+
totalToPayUsd: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface UseEstimateResult {
|
|
37
|
+
estimate: DepositEstimateData | null;
|
|
38
|
+
isLoading: boolean;
|
|
39
|
+
error: Error | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Fetch currency estimate with debouncing
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```tsx
|
|
47
|
+
* const { estimate, isLoading } = useEstimate({
|
|
48
|
+
* currencyCode: 'USDTTRC20',
|
|
49
|
+
* amountUsd: 100,
|
|
50
|
+
* minAmount: 1,
|
|
51
|
+
* });
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export function useEstimate({
|
|
55
|
+
currencyCode,
|
|
56
|
+
amountUsd,
|
|
57
|
+
minAmount = 0,
|
|
58
|
+
debounceMs = 300,
|
|
59
|
+
skip = false,
|
|
60
|
+
}: UseEstimateOptions): UseEstimateResult {
|
|
61
|
+
const [estimate, setEstimate] = useState<DepositEstimateData | null>(null);
|
|
62
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
63
|
+
const [error, setError] = useState<Error | null>(null);
|
|
64
|
+
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
// Skip if conditions not met
|
|
67
|
+
if (skip || !currencyCode || amountUsd < minAmount) {
|
|
68
|
+
setEstimate(null);
|
|
69
|
+
setError(null);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const fetchEstimate = async () => {
|
|
74
|
+
setIsLoading(true);
|
|
75
|
+
setError(null);
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const response = await apiPayments.ext_payments_payments.currenciesEstimateRetrieve(
|
|
79
|
+
currencyCode,
|
|
80
|
+
{ amount: amountUsd }
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
if (response?.success && response?.estimated_amount) {
|
|
84
|
+
setEstimate({
|
|
85
|
+
estimatedAmount: parseFloat(response.estimated_amount),
|
|
86
|
+
usdRate: parseFloat(response.usd_rate) || 0,
|
|
87
|
+
minAmountUsd: response.min_amount_usd ? parseFloat(response.min_amount_usd) : null,
|
|
88
|
+
isStablecoin: response.is_stablecoin || false,
|
|
89
|
+
// New fee fields
|
|
90
|
+
amountToReceive: parseFloat(response.amount_to_receive) || amountUsd,
|
|
91
|
+
serviceFeeUsd: parseFloat(response.service_fee_usd) || 0,
|
|
92
|
+
serviceFeePercent: parseFloat(response.service_fee_percent) || 0,
|
|
93
|
+
totalToPayUsd: parseFloat(response.total_to_pay_usd) || amountUsd,
|
|
94
|
+
});
|
|
95
|
+
} else {
|
|
96
|
+
setEstimate(null);
|
|
97
|
+
}
|
|
98
|
+
} catch (err) {
|
|
99
|
+
console.error('Failed to fetch estimate:', err);
|
|
100
|
+
setEstimate(null);
|
|
101
|
+
setError(err instanceof Error ? err : new Error('Failed to fetch estimate'));
|
|
102
|
+
} finally {
|
|
103
|
+
setIsLoading(false);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Debounce the fetch
|
|
108
|
+
const timeoutId = setTimeout(fetchEstimate, debounceMs);
|
|
109
|
+
return () => clearTimeout(timeoutId);
|
|
110
|
+
}, [currencyCode, amountUsd, minAmount, debounceMs, skip]);
|
|
111
|
+
|
|
112
|
+
return { estimate, isLoading, error };
|
|
113
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hook for fetching withdrawal estimates with debounce
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useState, useEffect } from 'react';
|
|
8
|
+
import { apiPayments } from '../api';
|
|
9
|
+
import type { WithdrawalEstimate } from '../types';
|
|
10
|
+
|
|
11
|
+
interface UseWithdrawalEstimateOptions {
|
|
12
|
+
/** Currency code to get estimate for */
|
|
13
|
+
currencyCode: string | undefined;
|
|
14
|
+
/** Amount in USD to withdraw */
|
|
15
|
+
amountUsd: number;
|
|
16
|
+
/** Minimum amount required (default: 10) */
|
|
17
|
+
minAmount?: number;
|
|
18
|
+
/** Debounce delay in ms (default: 300) */
|
|
19
|
+
debounceMs?: number;
|
|
20
|
+
/** Whether to skip fetching */
|
|
21
|
+
skip?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Parsed withdrawal estimate data for UI */
|
|
25
|
+
interface WithdrawalEstimateData {
|
|
26
|
+
estimatedAmount: number;
|
|
27
|
+
usdRate: number;
|
|
28
|
+
minAmountUsd: number | null;
|
|
29
|
+
isStablecoin: boolean;
|
|
30
|
+
amountRequested: number;
|
|
31
|
+
serviceFeeUsd: number;
|
|
32
|
+
serviceFeePercent: number;
|
|
33
|
+
networkFeeUsd: number;
|
|
34
|
+
totalFeesUsd: number;
|
|
35
|
+
amountToReceive: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface UseWithdrawalEstimateResult {
|
|
39
|
+
estimate: WithdrawalEstimateData | null;
|
|
40
|
+
isLoading: boolean;
|
|
41
|
+
error: Error | null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Fetch withdrawal estimate with debouncing
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```tsx
|
|
49
|
+
* const { estimate, isLoading } = useWithdrawalEstimate({
|
|
50
|
+
* currencyCode: 'USDTTRC20',
|
|
51
|
+
* amountUsd: 100,
|
|
52
|
+
* minAmount: 10,
|
|
53
|
+
* });
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export function useWithdrawalEstimate({
|
|
57
|
+
currencyCode,
|
|
58
|
+
amountUsd,
|
|
59
|
+
minAmount = 10,
|
|
60
|
+
debounceMs = 300,
|
|
61
|
+
skip = false,
|
|
62
|
+
}: UseWithdrawalEstimateOptions): UseWithdrawalEstimateResult {
|
|
63
|
+
const [estimate, setEstimate] = useState<WithdrawalEstimateData | null>(null);
|
|
64
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
65
|
+
const [error, setError] = useState<Error | null>(null);
|
|
66
|
+
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
// Skip if conditions not met
|
|
69
|
+
if (skip || !currencyCode || amountUsd < minAmount) {
|
|
70
|
+
setEstimate(null);
|
|
71
|
+
setError(null);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const fetchEstimate = async () => {
|
|
76
|
+
setIsLoading(true);
|
|
77
|
+
setError(null);
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const response = await apiPayments.ext_payments_payments.currenciesWithdrawalEstimateRetrieve(
|
|
81
|
+
currencyCode,
|
|
82
|
+
{ amount: amountUsd }
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
if (response?.success && response?.estimated_amount) {
|
|
86
|
+
setEstimate({
|
|
87
|
+
estimatedAmount: parseFloat(response.estimated_amount),
|
|
88
|
+
usdRate: parseFloat(response.usd_rate) || 0,
|
|
89
|
+
minAmountUsd: response.min_amount_usd ? parseFloat(response.min_amount_usd) : null,
|
|
90
|
+
isStablecoin: response.is_stablecoin || false,
|
|
91
|
+
// Withdrawal-specific fields
|
|
92
|
+
amountRequested: parseFloat(response.amount_requested) || amountUsd,
|
|
93
|
+
serviceFeeUsd: parseFloat(response.service_fee_usd) || 0,
|
|
94
|
+
serviceFeePercent: parseFloat(response.service_fee_percent) || 0,
|
|
95
|
+
networkFeeUsd: parseFloat(response.network_fee_usd) || 0,
|
|
96
|
+
totalFeesUsd: parseFloat(response.total_fees_usd) || 0,
|
|
97
|
+
amountToReceive: parseFloat(response.amount_to_receive) || 0,
|
|
98
|
+
});
|
|
99
|
+
} else {
|
|
100
|
+
setEstimate(null);
|
|
101
|
+
}
|
|
102
|
+
} catch (err) {
|
|
103
|
+
console.error('Failed to fetch withdrawal estimate:', err);
|
|
104
|
+
setEstimate(null);
|
|
105
|
+
setError(err instanceof Error ? err : new Error('Failed to fetch withdrawal estimate'));
|
|
106
|
+
} finally {
|
|
107
|
+
setIsLoading(false);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Debounce the fetch
|
|
112
|
+
const timeoutId = setTimeout(fetchEstimate, debounceMs);
|
|
113
|
+
return () => clearTimeout(timeoutId);
|
|
114
|
+
}, [currencyCode, amountUsd, minAmount, debounceMs, skip]);
|
|
115
|
+
|
|
116
|
+
return { estimate, isLoading, error };
|
|
117
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,38 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Wallet Extension (Apple-style)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* This entry point is server-safe (no SWR/React hooks).
|
|
6
|
-
*
|
|
7
|
-
* For React hooks, import from '@djangocfg/ext-payments/hooks'
|
|
4
|
+
* Simplified payments system with single page UX.
|
|
8
5
|
*/
|
|
9
6
|
|
|
7
|
+
// Page component
|
|
8
|
+
export { WalletPage } from './WalletPage';
|
|
9
|
+
|
|
10
10
|
// API client (server-safe)
|
|
11
11
|
export * from './api/generated/ext_payments';
|
|
12
12
|
export { API } from './api/generated/ext_payments';
|
|
13
13
|
export { apiPayments } from './api';
|
|
14
14
|
|
|
15
|
-
//
|
|
16
|
-
export
|
|
17
|
-
export type { PaymentsLayoutProps } from './layouts/PaymentsLayout/PaymentsLayout';
|
|
18
|
-
|
|
19
|
-
// Events
|
|
20
|
-
export * from './layouts/PaymentsLayout/events';
|
|
21
|
-
|
|
22
|
-
// Types
|
|
23
|
-
export type { PaymentTab } from './layouts/PaymentsLayout/types';
|
|
15
|
+
// Components
|
|
16
|
+
export * from './components';
|
|
24
17
|
|
|
25
18
|
// Re-export API types for convenience
|
|
26
19
|
export type {
|
|
27
20
|
Balance,
|
|
28
|
-
Currency,
|
|
29
21
|
PaymentList,
|
|
30
22
|
PaymentDetail,
|
|
23
|
+
PaymentCreateRequest,
|
|
31
24
|
Transaction,
|
|
32
25
|
PaginatedPaymentListList,
|
|
33
26
|
} from './api/generated/ext_payments/_utils/schemas';
|
|
34
27
|
|
|
35
|
-
//
|
|
36
|
-
// Use: import { usePaymentsList } from '@djangocfg/ext-payments/hooks'
|
|
37
|
-
// Export config
|
|
28
|
+
// Config
|
|
38
29
|
export { extensionConfig } from './config';
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
|
|
2
|
+
// =============================================================================
|
|
3
|
+
// Estimate Types (not generated - defined manually for estimate endpoints)
|
|
4
|
+
// =============================================================================
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Deposit estimate response from API
|
|
8
|
+
*/
|
|
9
|
+
export interface DepositEstimate {
|
|
10
|
+
success: boolean;
|
|
11
|
+
/** Crypto amount to send (includes fees) */
|
|
12
|
+
estimated_amount: string;
|
|
13
|
+
/** USD rate (how much USD per 1 crypto) */
|
|
14
|
+
usd_rate: string;
|
|
15
|
+
/** Minimum amount in USD */
|
|
16
|
+
min_amount_usd: string | null;
|
|
17
|
+
/** Whether the currency is a stablecoin */
|
|
18
|
+
is_stablecoin: boolean;
|
|
19
|
+
/** Amount user will receive on balance (USD) */
|
|
20
|
+
amount_to_receive: string;
|
|
21
|
+
/** Service fee in USD */
|
|
22
|
+
service_fee_usd: string;
|
|
23
|
+
/** Service fee percentage (e.g., 2 for 2%) */
|
|
24
|
+
service_fee_percent: string;
|
|
25
|
+
/** Total USD amount to pay (including fees) */
|
|
26
|
+
total_to_pay_usd: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Withdrawal estimate response from API
|
|
31
|
+
*/
|
|
32
|
+
export interface WithdrawalEstimate {
|
|
33
|
+
success: boolean;
|
|
34
|
+
/** Crypto amount user will receive */
|
|
35
|
+
estimated_amount: string;
|
|
36
|
+
/** USD rate (how much USD per 1 crypto) */
|
|
37
|
+
usd_rate: string;
|
|
38
|
+
/** Minimum amount in USD */
|
|
39
|
+
min_amount_usd: string | null;
|
|
40
|
+
/** Whether the currency is a stablecoin */
|
|
41
|
+
is_stablecoin: boolean;
|
|
42
|
+
/** Amount user requested to withdraw */
|
|
43
|
+
amount_requested: string;
|
|
44
|
+
/** Service fee in USD */
|
|
45
|
+
service_fee_usd: string;
|
|
46
|
+
/** Service fee percentage (e.g., 1 for 1%) */
|
|
47
|
+
service_fee_percent: string;
|
|
48
|
+
/** Network fee in USD */
|
|
49
|
+
network_fee_usd: string;
|
|
50
|
+
/** Total fees in USD */
|
|
51
|
+
total_fees_usd: string;
|
|
52
|
+
/** Amount user will receive after fees (USD) */
|
|
53
|
+
amount_to_receive: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// =============================================================================
|
|
57
|
+
// UI Helper Types
|
|
58
|
+
// =============================================================================
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Currency option for combobox
|
|
62
|
+
*/
|
|
63
|
+
export interface CurrencyOption {
|
|
64
|
+
value: string;
|
|
65
|
+
label: string;
|
|
66
|
+
token: string;
|
|
67
|
+
network?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Formatted display data for crypto transactions
|
|
72
|
+
*/
|
|
73
|
+
export interface CryptoDisplayData {
|
|
74
|
+
token: string;
|
|
75
|
+
amount: string;
|
|
76
|
+
rate: string;
|
|
77
|
+
showRate: boolean;
|
|
78
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error handling utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Extract user-friendly error message from API error response
|
|
7
|
+
*
|
|
8
|
+
* Handles various error formats:
|
|
9
|
+
* - APIError with response.data
|
|
10
|
+
* - Axios-style errors
|
|
11
|
+
* - Plain Error objects
|
|
12
|
+
*/
|
|
13
|
+
export function extractErrorMessage(err: unknown, fallback = 'An error occurred'): string {
|
|
14
|
+
if (!err) return fallback;
|
|
15
|
+
|
|
16
|
+
// Type guard for error-like objects
|
|
17
|
+
const error = err as Record<string, unknown>;
|
|
18
|
+
|
|
19
|
+
// APIError / Axios style: err.response.data.error
|
|
20
|
+
const responseData = (error.response as Record<string, unknown>)?.data as Record<string, unknown> | undefined;
|
|
21
|
+
const response = responseData || error.response as Record<string, unknown> | undefined;
|
|
22
|
+
|
|
23
|
+
if (response) {
|
|
24
|
+
if (typeof response.error === 'string') return response.error;
|
|
25
|
+
if (typeof response.message === 'string') return response.message;
|
|
26
|
+
if (typeof response.detail === 'string') return response.detail;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// APIError getter style
|
|
30
|
+
if (typeof error.errorMessage === 'string') return error.errorMessage;
|
|
31
|
+
|
|
32
|
+
// Plain Error
|
|
33
|
+
if (typeof error.message === 'string') return error.message;
|
|
34
|
+
|
|
35
|
+
return fallback;
|
|
36
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formatting utilities for Payments module
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Format USD rate with adaptive decimal places based on value
|
|
7
|
+
*
|
|
8
|
+
* - >= $1: 2 decimals (e.g., $95,503.90)
|
|
9
|
+
* - $0.01-$0.99: up to 4 decimals (e.g., $0.0123)
|
|
10
|
+
* - $0.0001-$0.0099: up to 6 decimals (e.g., $0.000253)
|
|
11
|
+
* - < $0.0001: up to 8 decimals (e.g., $0.00000025)
|
|
12
|
+
*/
|
|
13
|
+
export function formatUsdRate(rate: number): string {
|
|
14
|
+
if (rate >= 1) {
|
|
15
|
+
return rate.toLocaleString(undefined, {
|
|
16
|
+
minimumFractionDigits: 2,
|
|
17
|
+
maximumFractionDigits: 2,
|
|
18
|
+
});
|
|
19
|
+
} else if (rate >= 0.01) {
|
|
20
|
+
return rate.toLocaleString(undefined, {
|
|
21
|
+
minimumFractionDigits: 2,
|
|
22
|
+
maximumFractionDigits: 4,
|
|
23
|
+
});
|
|
24
|
+
} else if (rate >= 0.0001) {
|
|
25
|
+
return rate.toLocaleString(undefined, {
|
|
26
|
+
minimumFractionDigits: 4,
|
|
27
|
+
maximumFractionDigits: 6,
|
|
28
|
+
});
|
|
29
|
+
} else {
|
|
30
|
+
return rate.toLocaleString(undefined, {
|
|
31
|
+
minimumFractionDigits: 6,
|
|
32
|
+
maximumFractionDigits: 8,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Format crypto amount with appropriate decimal places
|
|
39
|
+
*
|
|
40
|
+
* @param amount - The crypto amount to format
|
|
41
|
+
* @param isStablecoin - Whether the currency is a stablecoin (affects decimal places)
|
|
42
|
+
*/
|
|
43
|
+
export function formatCryptoAmount(amount: number, isStablecoin: boolean): string {
|
|
44
|
+
const decimals = isStablecoin ? 2 : 8;
|
|
45
|
+
return amount.toFixed(decimals);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Format USD amount for display
|
|
50
|
+
* Shows cents only if they exist (e.g., $100 not $100.00, but $99.50)
|
|
51
|
+
*/
|
|
52
|
+
export function formatUsdAmount(amount: number, alwaysShowCents = false): string {
|
|
53
|
+
if (alwaysShowCents) {
|
|
54
|
+
return amount.toFixed(2);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check if amount has cents
|
|
58
|
+
const hasCents = amount % 1 !== 0;
|
|
59
|
+
|
|
60
|
+
if (hasCents) {
|
|
61
|
+
return amount.toFixed(2);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return amount.toFixed(0);
|
|
65
|
+
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import React, { createContext, ReactNode, useContext } from 'react';
|
|
4
|
-
|
|
5
|
-
import { apiPayments } from '../api';
|
|
6
|
-
import { usePaymentsBalanceRetrieve } from '../api/generated/ext_payments/_utils/hooks';
|
|
7
|
-
|
|
8
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
9
|
-
// Context Type
|
|
10
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
11
|
-
|
|
12
|
-
export interface BalancesContextValue {
|
|
13
|
-
// Balance data - single endpoint returns balance info
|
|
14
|
-
balance: any | undefined;
|
|
15
|
-
isLoadingBalance: boolean;
|
|
16
|
-
balanceError: Error | undefined;
|
|
17
|
-
refreshBalance: () => Promise<void>;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
21
|
-
// Context
|
|
22
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
23
|
-
|
|
24
|
-
const BalancesContext = createContext<BalancesContextValue | undefined>(undefined);
|
|
25
|
-
|
|
26
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
27
|
-
// Provider
|
|
28
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
29
|
-
|
|
30
|
-
export function BalancesProvider({ children }: { children: ReactNode }) {
|
|
31
|
-
// Get balance from /cfg/payments/balance/
|
|
32
|
-
const {
|
|
33
|
-
data: balance,
|
|
34
|
-
error: balanceError,
|
|
35
|
-
isLoading: isLoadingBalance,
|
|
36
|
-
mutate: mutateBalance,
|
|
37
|
-
} = usePaymentsBalanceRetrieve(apiPayments);
|
|
38
|
-
|
|
39
|
-
const refreshBalance = async () => {
|
|
40
|
-
await mutateBalance();
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const value: BalancesContextValue = {
|
|
44
|
-
balance,
|
|
45
|
-
isLoadingBalance,
|
|
46
|
-
balanceError,
|
|
47
|
-
refreshBalance,
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
return <BalancesContext.Provider value={value}>{children}</BalancesContext.Provider>;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
54
|
-
// Hook
|
|
55
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
56
|
-
|
|
57
|
-
export function useBalancesContext(): BalancesContextValue {
|
|
58
|
-
const context = useContext(BalancesContext);
|
|
59
|
-
if (!context) {
|
|
60
|
-
throw new Error('useBalancesContext must be used within BalancesProvider');
|
|
61
|
-
}
|
|
62
|
-
return context;
|
|
63
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import React, { createContext, ReactNode, useContext } from 'react';
|
|
4
|
-
|
|
5
|
-
import { apiPayments } from '../api';
|
|
6
|
-
import { usePaymentsCurrenciesList } from '../api/generated/ext_payments/_utils/hooks';
|
|
7
|
-
|
|
8
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
9
|
-
// Context Type
|
|
10
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
11
|
-
|
|
12
|
-
export interface CurrenciesContextValue {
|
|
13
|
-
// Currencies data - returns raw response with currencies array
|
|
14
|
-
currencies: any | undefined;
|
|
15
|
-
isLoadingCurrencies: boolean;
|
|
16
|
-
currenciesError: Error | undefined;
|
|
17
|
-
refreshCurrencies: () => Promise<void>;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
21
|
-
// Context
|
|
22
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
23
|
-
|
|
24
|
-
const CurrenciesContext = createContext<CurrenciesContextValue | undefined>(undefined);
|
|
25
|
-
|
|
26
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
27
|
-
// Provider
|
|
28
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
29
|
-
|
|
30
|
-
export function CurrenciesProvider({ children }: { children: ReactNode }) {
|
|
31
|
-
// Get currencies list from /cfg/payments/currencies/
|
|
32
|
-
const {
|
|
33
|
-
data: currencies,
|
|
34
|
-
error: currenciesError,
|
|
35
|
-
isLoading: isLoadingCurrencies,
|
|
36
|
-
mutate: mutateCurrencies,
|
|
37
|
-
} = usePaymentsCurrenciesList(apiPayments);
|
|
38
|
-
|
|
39
|
-
const refreshCurrencies = async () => {
|
|
40
|
-
await mutateCurrencies();
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const value: CurrenciesContextValue = {
|
|
44
|
-
currencies,
|
|
45
|
-
isLoadingCurrencies,
|
|
46
|
-
currenciesError,
|
|
47
|
-
refreshCurrencies,
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
return <CurrenciesContext.Provider value={value}>{children}</CurrenciesContext.Provider>;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
54
|
-
// Hook
|
|
55
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
56
|
-
|
|
57
|
-
export function useCurrenciesContext(): CurrenciesContextValue {
|
|
58
|
-
const context = useContext(CurrenciesContext);
|
|
59
|
-
if (!context) {
|
|
60
|
-
throw new Error('useCurrenciesContext must be used within CurrenciesProvider');
|
|
61
|
-
}
|
|
62
|
-
return context;
|
|
63
|
-
}
|
|
64
|
-
|