@diviswap/sdk 1.7.6
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/LICENSE +21 -0
- package/README.md +510 -0
- package/bin/create-diviswap-app.js +25 -0
- package/bin/diviswap-sdk.js +4 -0
- package/dist/cli/index.js +1888 -0
- package/dist/cli/templates/nextjs-app/actions.ts.hbs +259 -0
- package/dist/cli/templates/nextjs-app/api-hooks.ts.hbs +439 -0
- package/dist/cli/templates/nextjs-app/api-route.ts.hbs +502 -0
- package/dist/cli/templates/nextjs-app/auth-context.tsx.hbs +59 -0
- package/dist/cli/templates/nextjs-app/client.ts.hbs +116 -0
- package/dist/cli/templates/nextjs-app/dashboard-hooks.ts.hbs +180 -0
- package/dist/cli/templates/nextjs-app/example-page.tsx.hbs +276 -0
- package/dist/cli/templates/nextjs-app/hooks.ts.hbs +252 -0
- package/dist/cli/templates/nextjs-app/kyc-hooks.ts.hbs +87 -0
- package/dist/cli/templates/nextjs-app/kyc-wizard.css.hbs +433 -0
- package/dist/cli/templates/nextjs-app/kyc-wizard.tsx.hbs +711 -0
- package/dist/cli/templates/nextjs-app/layout-wrapper.tsx.hbs +13 -0
- package/dist/cli/templates/nextjs-app/layout.tsx.hbs +13 -0
- package/dist/cli/templates/nextjs-app/middleware.ts.hbs +49 -0
- package/dist/cli/templates/nextjs-app/provider-wrapper.tsx.hbs +8 -0
- package/dist/cli/templates/nextjs-app/provider.tsx.hbs +408 -0
- package/dist/cli/templates/nextjs-app/setup-provider.tsx.hbs +25 -0
- package/dist/cli/templates/nextjs-app/types.ts.hbs +159 -0
- package/dist/cli/templates/react/api-client-wrapper.ts.hbs +89 -0
- package/dist/cli/templates/react/example.tsx.hbs +69 -0
- package/dist/cli/templates/react/tanstack-hooks.ts.hbs +185 -0
- package/dist/cli/templates/webhooks/nextjs.hbs +98 -0
- package/dist/index.d.mts +91 -0
- package/dist/index.d.ts +91 -0
- package/dist/index.js +2339 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2313 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react/index.d.mts +192 -0
- package/dist/react/index.d.ts +192 -0
- package/dist/react/index.js +1083 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/index.mjs +1064 -0
- package/dist/react/index.mjs.map +1 -0
- package/dist/wallet-BEGvzNtB.d.mts +1614 -0
- package/dist/wallet-BEGvzNtB.d.ts +1614 -0
- package/package.json +102 -0
- package/src/cli/templates/index.ts +65 -0
- package/src/cli/templates/nextjs-app/actions.ts.hbs +259 -0
- package/src/cli/templates/nextjs-app/api-hooks.ts.hbs +439 -0
- package/src/cli/templates/nextjs-app/api-route.ts.hbs +502 -0
- package/src/cli/templates/nextjs-app/auth-context.tsx.hbs +59 -0
- package/src/cli/templates/nextjs-app/client.ts.hbs +116 -0
- package/src/cli/templates/nextjs-app/dashboard-hooks.ts.hbs +180 -0
- package/src/cli/templates/nextjs-app/example-page.tsx.hbs +276 -0
- package/src/cli/templates/nextjs-app/hooks.ts.hbs +252 -0
- package/src/cli/templates/nextjs-app/kyc-hooks.ts.hbs +87 -0
- package/src/cli/templates/nextjs-app/kyc-wizard.css.hbs +433 -0
- package/src/cli/templates/nextjs-app/kyc-wizard.tsx.hbs +711 -0
- package/src/cli/templates/nextjs-app/layout-wrapper.tsx.hbs +13 -0
- package/src/cli/templates/nextjs-app/layout.tsx.hbs +13 -0
- package/src/cli/templates/nextjs-app/middleware.ts.hbs +49 -0
- package/src/cli/templates/nextjs-app/provider-wrapper.tsx.hbs +8 -0
- package/src/cli/templates/nextjs-app/provider.tsx.hbs +408 -0
- package/src/cli/templates/nextjs-app/setup-provider.tsx.hbs +25 -0
- package/src/cli/templates/nextjs-app/types.ts.hbs +159 -0
- package/src/cli/templates/react/api-client-wrapper.ts.hbs +89 -0
- package/src/cli/templates/react/example.tsx.hbs +69 -0
- package/src/cli/templates/react/tanstack-hooks.ts.hbs +185 -0
- package/src/cli/templates/shared/client.ts +78 -0
- package/src/cli/templates/webhooks/nextjs.hbs +98 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
import { Diviswap } from '@diviswap/sdk';
|
|
4
|
+
import type { Transaction, Payee } from '@diviswap/sdk';
|
|
5
|
+
import { revalidatePath } from 'next/cache';
|
|
6
|
+
import { cookies } from 'next/headers';
|
|
7
|
+
|
|
8
|
+
// Initialize Diviswap SDK
|
|
9
|
+
function getDiviswap() {
|
|
10
|
+
return Diviswap.init({
|
|
11
|
+
apiKey: process.env.DIVISWAP_API_KEY!,
|
|
12
|
+
clientId: process.env.DIVISWAP_CLIENT_ID!,
|
|
13
|
+
environment: (process.env.NEXT_PUBLIC_DIVISWAP_ENV as 'production' | 'sandbox') || 'production',
|
|
14
|
+
debug: true // Enable debug logging
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Helper to get session token from cookies
|
|
19
|
+
async function getSessionToken() {
|
|
20
|
+
const cookieStore = await cookies();
|
|
21
|
+
const session = cookieStore.get('diviswap_session');
|
|
22
|
+
return session?.value;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Transaction Server Actions
|
|
26
|
+
export async function createTransactionAction(data: {
|
|
27
|
+
type: 'onramp' | 'offramp';
|
|
28
|
+
amount: number;
|
|
29
|
+
currency?: string;
|
|
30
|
+
payeeId?: string;
|
|
31
|
+
paymentMethodId?: string;
|
|
32
|
+
// Required for offramp
|
|
33
|
+
fromAddress?: string;
|
|
34
|
+
chain?: string;
|
|
35
|
+
}) {
|
|
36
|
+
try {
|
|
37
|
+
const sessionToken = await getSessionToken();
|
|
38
|
+
if (!sessionToken) {
|
|
39
|
+
return { success: false, error: 'Not authenticated' };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const diviswap = getDiviswap();
|
|
43
|
+
|
|
44
|
+
// Use the appropriate method based on transaction type
|
|
45
|
+
let transaction;
|
|
46
|
+
if (data.type === 'onramp') {
|
|
47
|
+
// Note: Onramp is not yet available in v1 API
|
|
48
|
+
return { success: false, error: 'Onramp transactions are not yet available' };
|
|
49
|
+
} else {
|
|
50
|
+
transaction = await diviswap.transactions.offramp({
|
|
51
|
+
amount: data.amount,
|
|
52
|
+
currency: data.currency || 'USD',
|
|
53
|
+
payeeId: data.payeeId!,
|
|
54
|
+
fromAddress: data.fromAddress!, // Required for offramp
|
|
55
|
+
chain: data.chain || 'ethereum'
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
revalidatePath('/diviswap');
|
|
60
|
+
return { success: true, transaction };
|
|
61
|
+
} catch (error) {
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
error: error instanceof Error ? error.message : 'Transaction creation failed'
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function getTransactionsAction(filters?: {
|
|
70
|
+
limit?: number;
|
|
71
|
+
offset?: number;
|
|
72
|
+
status?: 'pending' | 'processing' | 'completed' | 'failed';
|
|
73
|
+
type?: 'onramp' | 'offramp';
|
|
74
|
+
}) {
|
|
75
|
+
try {
|
|
76
|
+
const sessionToken = await getSessionToken();
|
|
77
|
+
if (!sessionToken) {
|
|
78
|
+
return { success: false, error: 'Not authenticated' };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const diviswap = getDiviswap();
|
|
82
|
+
const transactions = await diviswap.transactions.list(filters);
|
|
83
|
+
|
|
84
|
+
return { success: true, transactions };
|
|
85
|
+
} catch (error) {
|
|
86
|
+
return {
|
|
87
|
+
success: false,
|
|
88
|
+
error: error instanceof Error ? error.message : 'Failed to fetch transactions'
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Payee Server Actions
|
|
94
|
+
export async function getPayeesAction() {
|
|
95
|
+
try {
|
|
96
|
+
const sessionToken = await getSessionToken();
|
|
97
|
+
if (!sessionToken) {
|
|
98
|
+
return { success: false, error: 'Not authenticated' };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const diviswap = getDiviswap();
|
|
102
|
+
const payees = await diviswap.payees.list();
|
|
103
|
+
|
|
104
|
+
return { success: true, payees };
|
|
105
|
+
} catch (error) {
|
|
106
|
+
return {
|
|
107
|
+
success: false,
|
|
108
|
+
error: error instanceof Error ? error.message : 'Failed to fetch payees'
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export async function createPayeeAction(data: {
|
|
114
|
+
nickname: string;
|
|
115
|
+
accountNumber: string;
|
|
116
|
+
routingNumber: string;
|
|
117
|
+
accountType: 'checking' | 'savings';
|
|
118
|
+
}) {
|
|
119
|
+
try {
|
|
120
|
+
const sessionToken = await getSessionToken();
|
|
121
|
+
if (!sessionToken) {
|
|
122
|
+
return { success: false, error: 'Not authenticated' };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const diviswap = getDiviswap();
|
|
126
|
+
const payee = await diviswap.payees.create({
|
|
127
|
+
nickname: data.nickname,
|
|
128
|
+
accountNumber: data.accountNumber,
|
|
129
|
+
routingNumber: data.routingNumber,
|
|
130
|
+
accountType: data.accountType,
|
|
131
|
+
setAsDefault: false
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
revalidatePath('/diviswap');
|
|
135
|
+
return { success: true, payee };
|
|
136
|
+
} catch (error) {
|
|
137
|
+
return {
|
|
138
|
+
success: false,
|
|
139
|
+
error: error instanceof Error ? error.message : 'Payee creation failed'
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export async function deletePayeeAction(payeeId: string) {
|
|
145
|
+
try {
|
|
146
|
+
const sessionToken = await getSessionToken();
|
|
147
|
+
if (!sessionToken) {
|
|
148
|
+
return { success: false, error: 'Not authenticated' };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const diviswap = getDiviswap();
|
|
152
|
+
await diviswap.payees.delete(payeeId);
|
|
153
|
+
|
|
154
|
+
revalidatePath('/diviswap');
|
|
155
|
+
return { success: true };
|
|
156
|
+
} catch (error) {
|
|
157
|
+
return {
|
|
158
|
+
success: false,
|
|
159
|
+
error: error instanceof Error ? error.message : 'Failed to delete payee'
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
{{#if (includes features "fees")}}
|
|
165
|
+
// Fee Calculation Server Actions
|
|
166
|
+
export async function calculateFeesAction(data: {
|
|
167
|
+
amount: number;
|
|
168
|
+
type: 'onramp' | 'offramp';
|
|
169
|
+
currency?: string;
|
|
170
|
+
}) {
|
|
171
|
+
try {
|
|
172
|
+
const diviswap = getDiviswap();
|
|
173
|
+
const fees = await diviswap.fees.calculateFees({
|
|
174
|
+
amount: data.amount,
|
|
175
|
+
userId: undefined // Optional: for per-user fees
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
return { success: true, fees };
|
|
179
|
+
} catch (error) {
|
|
180
|
+
return {
|
|
181
|
+
success: false,
|
|
182
|
+
error: error instanceof Error ? error.message : 'Fee calculation failed'
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export async function getIntegratorFeesAction() {
|
|
188
|
+
try {
|
|
189
|
+
const sessionToken = await getSessionToken();
|
|
190
|
+
if (!sessionToken) {
|
|
191
|
+
return { success: false, error: 'Not authenticated' };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const diviswap = getDiviswap();
|
|
195
|
+
const fees = await diviswap.fees.getFees();
|
|
196
|
+
|
|
197
|
+
return { success: true, fees };
|
|
198
|
+
} catch (error) {
|
|
199
|
+
return {
|
|
200
|
+
success: false,
|
|
201
|
+
error: error instanceof Error ? error.message : 'Failed to fetch integrator fees'
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export async function updateIntegratorFeeAction(data: {
|
|
207
|
+
percentage: number;
|
|
208
|
+
userId?: string;
|
|
209
|
+
}) {
|
|
210
|
+
try {
|
|
211
|
+
const sessionToken = await getSessionToken();
|
|
212
|
+
if (!sessionToken) {
|
|
213
|
+
return { success: false, error: 'Not authenticated' };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const diviswap = getDiviswap();
|
|
217
|
+
const fee = await diviswap.fees.setFee(data);
|
|
218
|
+
|
|
219
|
+
revalidatePath('/diviswap');
|
|
220
|
+
return { success: true, fee };
|
|
221
|
+
} catch (error) {
|
|
222
|
+
return {
|
|
223
|
+
success: false,
|
|
224
|
+
error: error instanceof Error ? error.message : 'Failed to update fee'
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
{{/if}}
|
|
229
|
+
|
|
230
|
+
// Authentication Server Actions (for server components that need auth status)
|
|
231
|
+
export async function getCurrentUserAction() {
|
|
232
|
+
try {
|
|
233
|
+
const sessionToken = await getSessionToken();
|
|
234
|
+
if (!sessionToken) {
|
|
235
|
+
return { success: false, user: null };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const diviswap = getDiviswap();
|
|
239
|
+
const user = await diviswap.auth.getProfile();
|
|
240
|
+
|
|
241
|
+
return { success: true, user };
|
|
242
|
+
} catch (error) {
|
|
243
|
+
return { success: false, user: null };
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Utility action for checking API status
|
|
248
|
+
export async function checkApiStatusAction() {
|
|
249
|
+
try {
|
|
250
|
+
const diviswap = getDiviswap();
|
|
251
|
+
// Note: Health check endpoint not available in SDK
|
|
252
|
+
return { success: true, status: 'OK' };
|
|
253
|
+
} catch (error) {
|
|
254
|
+
return {
|
|
255
|
+
success: false,
|
|
256
|
+
error: error instanceof Error ? error.message : 'API health check failed'
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
}
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
// Authentication context hook (implement based on your auth system)
|
|
4
|
+
export function useAuth() {
|
|
5
|
+
// Replace this with your actual authentication logic
|
|
6
|
+
// This could be from a context provider, session storage, etc.
|
|
7
|
+
const [customerId, setCustomerId] = useState<string | null>(null);
|
|
8
|
+
const [customerEmail, setCustomerEmail] = useState<string | null>(null);
|
|
9
|
+
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
10
|
+
const [loading, setLoading] = useState(true);
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
// Initialize auth state from your auth system
|
|
14
|
+
// Example: get from session, context, or other auth provider
|
|
15
|
+
const initAuth = async () => {
|
|
16
|
+
try {
|
|
17
|
+
// Your auth initialization logic here
|
|
18
|
+
// Example:
|
|
19
|
+
// const session = await getSession();
|
|
20
|
+
// if (session) {
|
|
21
|
+
// setCustomerId(session.customerId);
|
|
22
|
+
// setCustomerEmail(session.customerEmail);
|
|
23
|
+
// setIsAuthenticated(true);
|
|
24
|
+
// }
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error('Auth initialization failed:', error);
|
|
27
|
+
} finally {
|
|
28
|
+
setLoading(false);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
initAuth();
|
|
33
|
+
}, []);
|
|
34
|
+
|
|
35
|
+
const setCustomer = useCallback(async (customerId: string, customerEmail: string) => {
|
|
36
|
+
try {
|
|
37
|
+
const response = await fetch('/api/diviswap', {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: { 'Content-Type': 'application/json' },
|
|
40
|
+
body: JSON.stringify({
|
|
41
|
+
action: 'setCustomer',
|
|
42
|
+
customerId,
|
|
43
|
+
customerEmail
|
|
44
|
+
})
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (response.ok) {
|
|
48
|
+
setCustomerId(customerId);
|
|
49
|
+
setCustomerEmail(customerEmail);
|
|
50
|
+
setIsAuthenticated(true);
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error('Failed to set customer context:', error);
|
|
54
|
+
}
|
|
55
|
+
}, []);
|
|
56
|
+
|
|
57
|
+
const clearCustomer = useCallback(async () => {
|
|
58
|
+
try {
|
|
59
|
+
await fetch('/api/diviswap', {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: { 'Content-Type': 'application/json' },
|
|
62
|
+
body: JSON.stringify({ action: 'clearCustomer' })
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
setCustomerId(null);
|
|
66
|
+
setCustomerEmail(null);
|
|
67
|
+
setIsAuthenticated(false);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error('Failed to clear customer context:', error);
|
|
70
|
+
}
|
|
71
|
+
}, []);
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
customerId,
|
|
75
|
+
customerEmail,
|
|
76
|
+
isAuthenticated,
|
|
77
|
+
loading,
|
|
78
|
+
setCustomer,
|
|
79
|
+
clearCustomer
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Hook for managing transactions
|
|
84
|
+
export function useTransactions() {
|
|
85
|
+
const { customerId, customerEmail, isAuthenticated } = useAuth();
|
|
86
|
+
const [transactions, setTransactions] = useState<any[]>([]);
|
|
87
|
+
const [loading, setLoading] = useState(false);
|
|
88
|
+
const [error, setError] = useState<Error | null>(null);
|
|
89
|
+
|
|
90
|
+
const fetchTransactions = useCallback(async (filters?: {
|
|
91
|
+
limit?: number;
|
|
92
|
+
offset?: number;
|
|
93
|
+
status?: string;
|
|
94
|
+
type?: 'onramp' | 'offramp';
|
|
95
|
+
}) => {
|
|
96
|
+
if (!isAuthenticated || !customerId) return [];
|
|
97
|
+
|
|
98
|
+
setLoading(true);
|
|
99
|
+
setError(null);
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
const queryParams = new URLSearchParams({
|
|
103
|
+
resource: 'transactions',
|
|
104
|
+
customerId,
|
|
105
|
+
customerEmail,
|
|
106
|
+
...filters
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const response = await fetch(`/api/diviswap?${queryParams}`);
|
|
110
|
+
if (!response.ok) throw new Error('Failed to fetch transactions');
|
|
111
|
+
|
|
112
|
+
const data = await response.json();
|
|
113
|
+
setTransactions(data);
|
|
114
|
+
return data;
|
|
115
|
+
} catch (err) {
|
|
116
|
+
const error = err as Error;
|
|
117
|
+
setError(error);
|
|
118
|
+
console.error('Failed to fetch transactions:', error);
|
|
119
|
+
throw error;
|
|
120
|
+
} finally {
|
|
121
|
+
setLoading(false);
|
|
122
|
+
}
|
|
123
|
+
}, [customerId, customerEmail, isAuthenticated]);
|
|
124
|
+
|
|
125
|
+
const createTransaction = useCallback(async (data: any) => {
|
|
126
|
+
if (!isAuthenticated || !customerId) throw new Error('Not authenticated');
|
|
127
|
+
|
|
128
|
+
setLoading(true);
|
|
129
|
+
setError(null);
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const response = await fetch('/api/diviswap', {
|
|
133
|
+
method: 'POST',
|
|
134
|
+
headers: { 'Content-Type': 'application/json' },
|
|
135
|
+
body: JSON.stringify({
|
|
136
|
+
action: 'createTransaction',
|
|
137
|
+
customerId,
|
|
138
|
+
customerEmail,
|
|
139
|
+
...data
|
|
140
|
+
})
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
if (!response.ok) throw new Error('Failed to create transaction');
|
|
144
|
+
|
|
145
|
+
const transaction = await response.json();
|
|
146
|
+
// Refresh transactions list
|
|
147
|
+
await fetchTransactions();
|
|
148
|
+
return transaction;
|
|
149
|
+
} catch (err) {
|
|
150
|
+
const error = err as Error;
|
|
151
|
+
setError(error);
|
|
152
|
+
console.error('Failed to create transaction:', error);
|
|
153
|
+
throw error;
|
|
154
|
+
} finally {
|
|
155
|
+
setLoading(false);
|
|
156
|
+
}
|
|
157
|
+
}, [customerId, customerEmail, isAuthenticated, fetchTransactions]);
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
transactions,
|
|
161
|
+
loading,
|
|
162
|
+
error,
|
|
163
|
+
fetchTransactions,
|
|
164
|
+
createTransaction,
|
|
165
|
+
refresh: fetchTransactions,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Hook for managing payees
|
|
170
|
+
export function usePayees() {
|
|
171
|
+
const { customerId, customerEmail, isAuthenticated } = useAuth();
|
|
172
|
+
const [payees, setPayees] = useState<any[]>([]);
|
|
173
|
+
const [loading, setLoading] = useState(false);
|
|
174
|
+
const [error, setError] = useState<Error | null>(null);
|
|
175
|
+
|
|
176
|
+
const fetchPayees = useCallback(async () => {
|
|
177
|
+
if (!isAuthenticated || !customerId) return [];
|
|
178
|
+
|
|
179
|
+
setLoading(true);
|
|
180
|
+
setError(null);
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
const response = await fetch(`/api/diviswap?resource=payees&customerId=${customerId}&customerEmail=${customerEmail}`);
|
|
184
|
+
if (!response.ok) throw new Error('Failed to fetch payees');
|
|
185
|
+
|
|
186
|
+
const data = await response.json();
|
|
187
|
+
setPayees(data);
|
|
188
|
+
return data;
|
|
189
|
+
} catch (err) {
|
|
190
|
+
const error = err as Error;
|
|
191
|
+
setError(error);
|
|
192
|
+
console.error('Failed to fetch payees:', error);
|
|
193
|
+
throw error;
|
|
194
|
+
} finally {
|
|
195
|
+
setLoading(false);
|
|
196
|
+
}
|
|
197
|
+
}, [customerId, customerEmail, isAuthenticated]);
|
|
198
|
+
|
|
199
|
+
const createPayee = useCallback(async (data: any) => {
|
|
200
|
+
if (!isAuthenticated || !customerId) throw new Error('Not authenticated');
|
|
201
|
+
|
|
202
|
+
setLoading(true);
|
|
203
|
+
setError(null);
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
const response = await fetch('/api/diviswap', {
|
|
207
|
+
method: 'POST',
|
|
208
|
+
headers: { 'Content-Type': 'application/json' },
|
|
209
|
+
body: JSON.stringify({
|
|
210
|
+
action: 'createPayee',
|
|
211
|
+
customerId,
|
|
212
|
+
customerEmail,
|
|
213
|
+
...data
|
|
214
|
+
})
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
if (!response.ok) throw new Error('Failed to create payee');
|
|
218
|
+
|
|
219
|
+
const payee = await response.json();
|
|
220
|
+
// Refresh payees list
|
|
221
|
+
await fetchPayees();
|
|
222
|
+
return payee;
|
|
223
|
+
} catch (err) {
|
|
224
|
+
const error = err as Error;
|
|
225
|
+
setError(error);
|
|
226
|
+
console.error('Failed to create payee:', error);
|
|
227
|
+
throw error;
|
|
228
|
+
} finally {
|
|
229
|
+
setLoading(false);
|
|
230
|
+
}
|
|
231
|
+
}, [customerId, customerEmail, isAuthenticated, fetchPayees]);
|
|
232
|
+
|
|
233
|
+
const deletePayee = useCallback(async (id: string) => {
|
|
234
|
+
if (!isAuthenticated || !customerId) throw new Error('Not authenticated');
|
|
235
|
+
|
|
236
|
+
setLoading(true);
|
|
237
|
+
setError(null);
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
const response = await fetch(`/api/diviswap?resource=payee&id=${id}&customerId=${customerId}&customerEmail=${customerEmail}`, {
|
|
241
|
+
method: 'DELETE'
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
if (!response.ok) throw new Error('Failed to delete payee');
|
|
245
|
+
|
|
246
|
+
// Refresh payees list
|
|
247
|
+
await fetchPayees();
|
|
248
|
+
} catch (err) {
|
|
249
|
+
const error = err as Error;
|
|
250
|
+
setError(error);
|
|
251
|
+
console.error('Failed to delete payee:', error);
|
|
252
|
+
throw error;
|
|
253
|
+
} finally {
|
|
254
|
+
setLoading(false);
|
|
255
|
+
}
|
|
256
|
+
}, [customerId, customerEmail, isAuthenticated, fetchPayees]);
|
|
257
|
+
|
|
258
|
+
// Auto-load payees when authenticated
|
|
259
|
+
useEffect(() => {
|
|
260
|
+
if (isAuthenticated && customerId) {
|
|
261
|
+
fetchPayees();
|
|
262
|
+
}
|
|
263
|
+
}, [isAuthenticated, customerId, fetchPayees]);
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
payees,
|
|
267
|
+
loading,
|
|
268
|
+
error,
|
|
269
|
+
createPayee,
|
|
270
|
+
deletePayee,
|
|
271
|
+
refresh: fetchPayees,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Hook for KYC status and submission
|
|
276
|
+
export function useKYC() {
|
|
277
|
+
const { customerId, customerEmail, isAuthenticated } = useAuth();
|
|
278
|
+
const [kycStatus, setKycStatus] = useState<any>(null);
|
|
279
|
+
const [loading, setLoading] = useState(false);
|
|
280
|
+
const [error, setError] = useState<Error | null>(null);
|
|
281
|
+
|
|
282
|
+
const fetchKYCStatus = useCallback(async () => {
|
|
283
|
+
if (!isAuthenticated || !customerId) return null;
|
|
284
|
+
|
|
285
|
+
setLoading(true);
|
|
286
|
+
setError(null);
|
|
287
|
+
|
|
288
|
+
try {
|
|
289
|
+
const response = await fetch(`/api/diviswap?resource=kycStatus&customerId=${customerId}&customerEmail=${customerEmail}`);
|
|
290
|
+
if (!response.ok) throw new Error('Failed to fetch KYC status');
|
|
291
|
+
|
|
292
|
+
const data = await response.json();
|
|
293
|
+
setKycStatus(data);
|
|
294
|
+
return data;
|
|
295
|
+
} catch (err) {
|
|
296
|
+
const error = err as Error;
|
|
297
|
+
setError(error);
|
|
298
|
+
console.error('Failed to fetch KYC status:', error);
|
|
299
|
+
throw error;
|
|
300
|
+
} finally {
|
|
301
|
+
setLoading(false);
|
|
302
|
+
}
|
|
303
|
+
}, [customerId, customerEmail, isAuthenticated]);
|
|
304
|
+
|
|
305
|
+
const submitKYC = useCallback(async (kycData: any) => {
|
|
306
|
+
if (!isAuthenticated || !customerId) throw new Error('Not authenticated');
|
|
307
|
+
|
|
308
|
+
setLoading(true);
|
|
309
|
+
setError(null);
|
|
310
|
+
|
|
311
|
+
try {
|
|
312
|
+
const response = await fetch('/api/diviswap', {
|
|
313
|
+
method: 'POST',
|
|
314
|
+
headers: { 'Content-Type': 'application/json' },
|
|
315
|
+
body: JSON.stringify({
|
|
316
|
+
action: 'submitKYC',
|
|
317
|
+
customerId,
|
|
318
|
+
customerEmail,
|
|
319
|
+
...kycData
|
|
320
|
+
})
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
if (!response.ok) throw new Error('Failed to submit KYC');
|
|
324
|
+
|
|
325
|
+
const result = await response.json();
|
|
326
|
+
// Refresh KYC status
|
|
327
|
+
await fetchKYCStatus();
|
|
328
|
+
return result;
|
|
329
|
+
} catch (err) {
|
|
330
|
+
const error = err as Error;
|
|
331
|
+
setError(error);
|
|
332
|
+
console.error('Failed to submit KYC:', error);
|
|
333
|
+
throw error;
|
|
334
|
+
} finally {
|
|
335
|
+
setLoading(false);
|
|
336
|
+
}
|
|
337
|
+
}, [customerId, customerEmail, isAuthenticated, fetchKYCStatus]);
|
|
338
|
+
|
|
339
|
+
// Auto-load KYC status when authenticated
|
|
340
|
+
useEffect(() => {
|
|
341
|
+
if (isAuthenticated && customerId) {
|
|
342
|
+
fetchKYCStatus();
|
|
343
|
+
}
|
|
344
|
+
}, [isAuthenticated, customerId, fetchKYCStatus]);
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
kycStatus,
|
|
348
|
+
loading,
|
|
349
|
+
error,
|
|
350
|
+
submitKYC,
|
|
351
|
+
refresh: fetchKYCStatus,
|
|
352
|
+
isKycApproved: kycStatus?.kycStatus === 'APPROVED',
|
|
353
|
+
needsKyc: kycStatus?.requiresAction && kycStatus?.nextStep === 'COMPLETE_KYC'
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
{{#if (includes features "fees")}}
|
|
358
|
+
// Hook for fee calculations
|
|
359
|
+
export function useFeeCalculator() {
|
|
360
|
+
const { customerId, customerEmail, isAuthenticated } = useAuth();
|
|
361
|
+
const [fees, setFees] = useState<any>(null);
|
|
362
|
+
const [loading, setLoading] = useState(false);
|
|
363
|
+
const [error, setError] = useState<Error | null>(null);
|
|
364
|
+
|
|
365
|
+
const calculateFees = useCallback(async (amount: number) => {
|
|
366
|
+
if (!isAuthenticated || !customerId || amount <= 0) {
|
|
367
|
+
setFees(null);
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
setLoading(true);
|
|
372
|
+
setError(null);
|
|
373
|
+
|
|
374
|
+
try {
|
|
375
|
+
const response = await fetch('/api/diviswap', {
|
|
376
|
+
method: 'POST',
|
|
377
|
+
headers: { 'Content-Type': 'application/json' },
|
|
378
|
+
body: JSON.stringify({
|
|
379
|
+
action: 'calculateFees',
|
|
380
|
+
amount,
|
|
381
|
+
customerId,
|
|
382
|
+
customerEmail
|
|
383
|
+
})
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
if (!response.ok) throw new Error('Failed to calculate fees');
|
|
387
|
+
|
|
388
|
+
const calculatedFees = await response.json();
|
|
389
|
+
setFees(calculatedFees);
|
|
390
|
+
return calculatedFees;
|
|
391
|
+
} catch (err) {
|
|
392
|
+
const error = err as Error;
|
|
393
|
+
setError(error);
|
|
394
|
+
setFees(null);
|
|
395
|
+
console.error('Failed to calculate fees:', error);
|
|
396
|
+
throw error;
|
|
397
|
+
} finally {
|
|
398
|
+
setLoading(false);
|
|
399
|
+
}
|
|
400
|
+
}, [customerId, customerEmail, isAuthenticated]);
|
|
401
|
+
|
|
402
|
+
return {
|
|
403
|
+
fees,
|
|
404
|
+
loading,
|
|
405
|
+
error,
|
|
406
|
+
calculateFees,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
{{/if}}
|
|
410
|
+
|
|
411
|
+
// Hook for handling async operations with loading states
|
|
412
|
+
export function useAsyncOperation<T extends (...args: any[]) => Promise<any>>() {
|
|
413
|
+
const [loading, setLoading] = useState(false);
|
|
414
|
+
const [error, setError] = useState<Error | null>(null);
|
|
415
|
+
|
|
416
|
+
const execute = useCallback(async <TResult>(
|
|
417
|
+
operation: T,
|
|
418
|
+
...args: Parameters<T>
|
|
419
|
+
): Promise<TResult | undefined> => {
|
|
420
|
+
setLoading(true);
|
|
421
|
+
setError(null);
|
|
422
|
+
|
|
423
|
+
try {
|
|
424
|
+
const result = await operation(...args);
|
|
425
|
+
return result as TResult;
|
|
426
|
+
} catch (err) {
|
|
427
|
+
setError(err as Error);
|
|
428
|
+
throw err;
|
|
429
|
+
} finally {
|
|
430
|
+
setLoading(false);
|
|
431
|
+
}
|
|
432
|
+
}, []);
|
|
433
|
+
|
|
434
|
+
return {
|
|
435
|
+
loading,
|
|
436
|
+
error,
|
|
437
|
+
execute,
|
|
438
|
+
};
|
|
439
|
+
}
|