@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,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API client wrapper for custom backend integration
|
|
3
|
+
* This allows you to proxy Diviswap API calls through your own backend
|
|
4
|
+
* for additional security and custom logic
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
interface ApiOptions extends RequestInit {
|
|
8
|
+
params?: Record<string, any>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class ApiClient {
|
|
12
|
+
private baseUrl: string;
|
|
13
|
+
|
|
14
|
+
constructor(baseUrl = '/api') {
|
|
15
|
+
this.baseUrl = baseUrl;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private async request<T = any>(endpoint: string, options: ApiOptions = {}): Promise<T> {
|
|
19
|
+
const { params, ...fetchOptions } = options;
|
|
20
|
+
|
|
21
|
+
let url = `${this.baseUrl}${endpoint}`;
|
|
22
|
+
if (params) {
|
|
23
|
+
const searchParams = new URLSearchParams(params);
|
|
24
|
+
url += `?${searchParams.toString()}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const response = await fetch(url, {
|
|
28
|
+
headers: {
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
...fetchOptions.headers,
|
|
31
|
+
},
|
|
32
|
+
...fetchOptions,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
const error = await response.json().catch(() => ({ message: 'Request failed' }));
|
|
37
|
+
throw new Error(error.message || `HTTP ${response.status}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return response.json();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Diviswap-specific endpoints
|
|
44
|
+
diviswap = {
|
|
45
|
+
// Authentication
|
|
46
|
+
login: (email: string, password: string) =>
|
|
47
|
+
this.request('/diviswap', {
|
|
48
|
+
method: 'POST',
|
|
49
|
+
body: JSON.stringify({ action: 'login', email, password }),
|
|
50
|
+
}),
|
|
51
|
+
|
|
52
|
+
logout: () =>
|
|
53
|
+
this.request('/diviswap', {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
body: JSON.stringify({ action: 'logout' }),
|
|
56
|
+
}),
|
|
57
|
+
|
|
58
|
+
getSession: () =>
|
|
59
|
+
this.request('/diviswap', { params: { resource: 'session' } }),
|
|
60
|
+
|
|
61
|
+
// Transactions
|
|
62
|
+
createTransaction: (data: any) =>
|
|
63
|
+
this.request('/diviswap', {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
body: JSON.stringify({ action: 'createTransaction', ...data }),
|
|
66
|
+
}),
|
|
67
|
+
|
|
68
|
+
getTransactions: (filters?: any) =>
|
|
69
|
+
this.request('/diviswap', { params: { resource: 'transactions', ...filters } }),
|
|
70
|
+
|
|
71
|
+
// Payees
|
|
72
|
+
createPayee: (data: any) =>
|
|
73
|
+
this.request('/diviswap', {
|
|
74
|
+
method: 'POST',
|
|
75
|
+
body: JSON.stringify({ action: 'createPayee', ...data }),
|
|
76
|
+
}),
|
|
77
|
+
|
|
78
|
+
getPayees: () =>
|
|
79
|
+
this.request('/diviswap', { params: { resource: 'payees' } }),
|
|
80
|
+
|
|
81
|
+
deletePayee: (id: string) =>
|
|
82
|
+
this.request('/diviswap', {
|
|
83
|
+
method: 'DELETE',
|
|
84
|
+
params: { resource: 'payee', id },
|
|
85
|
+
}),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const apiClient = new ApiClient();
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { DiviswapProvider, useDiviswap } from '@diviswap/sdk/react';
|
|
3
|
+
import { apiClient } from './api-client';
|
|
4
|
+
|
|
5
|
+
function TransactionDemo() {
|
|
6
|
+
const {
|
|
7
|
+
user,
|
|
8
|
+
loading,
|
|
9
|
+
error,
|
|
10
|
+
login,
|
|
11
|
+
logout,
|
|
12
|
+
createTransaction,
|
|
13
|
+
getTransactions
|
|
14
|
+
} = useDiviswap();
|
|
15
|
+
|
|
16
|
+
const handleLogin = async () => {
|
|
17
|
+
try {
|
|
18
|
+
await login('user@example.com', 'password');
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.error('Login failed:', error);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const handleOnramp = async () => {
|
|
25
|
+
try {
|
|
26
|
+
const transaction = await createTransaction({
|
|
27
|
+
amount: 100,
|
|
28
|
+
currency: 'USD',
|
|
29
|
+
address: '0x...',
|
|
30
|
+
chain: 'ethereum'
|
|
31
|
+
});
|
|
32
|
+
console.log('Transaction created:', transaction);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error('Transaction failed:', error);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
if (loading) {
|
|
39
|
+
return <div>Loading...</div>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (error) {
|
|
43
|
+
return <div>Error: {error.message}</div>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div className="diviswap-demo">
|
|
48
|
+
<h1>Diviswap Demo</h1>
|
|
49
|
+
|
|
50
|
+
{!user ? (
|
|
51
|
+
<button onClick={handleLogin}>Login</button>
|
|
52
|
+
) : (
|
|
53
|
+
<div>
|
|
54
|
+
<p>Welcome, {user.email}!</p>
|
|
55
|
+
<button onClick={handleOnramp}>Create Onramp Transaction</button>
|
|
56
|
+
<button onClick={logout}>Logout</button>
|
|
57
|
+
</div>
|
|
58
|
+
)}
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export default function App() {
|
|
64
|
+
return (
|
|
65
|
+
<DiviswapProvider>
|
|
66
|
+
<TransactionDemo />
|
|
67
|
+
</DiviswapProvider>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example of using TanStack Query with Diviswap API routes
|
|
3
|
+
* This provides automatic caching, background refetching, and optimistic updates
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
7
|
+
import { apiClient } from './api-client';
|
|
8
|
+
|
|
9
|
+
// Query keys for cache invalidation
|
|
10
|
+
const queryKeys = {
|
|
11
|
+
user: ['diviswap', 'user'],
|
|
12
|
+
transactions: (filters?: any) => ['diviswap', 'transactions', filters],
|
|
13
|
+
payees: ['diviswap', 'payees'],
|
|
14
|
+
fees: (amount: number) => ['diviswap', 'fees', amount],
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// User hooks
|
|
18
|
+
export function useUser() {
|
|
19
|
+
return useQuery({
|
|
20
|
+
queryKey: queryKeys.user,
|
|
21
|
+
queryFn: () => apiClient.diviswap.getSession(),
|
|
22
|
+
retry: false,
|
|
23
|
+
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function useLogin() {
|
|
28
|
+
const queryClient = useQueryClient();
|
|
29
|
+
|
|
30
|
+
return useMutation({
|
|
31
|
+
mutationFn: ({ email, password }: { email: string; password: string }) =>
|
|
32
|
+
apiClient.diviswap.login(email, password),
|
|
33
|
+
onSuccess: (data) => {
|
|
34
|
+
queryClient.setQueryData(queryKeys.user, data.user);
|
|
35
|
+
queryClient.invalidateQueries({ queryKey: queryKeys.transactions() });
|
|
36
|
+
queryClient.invalidateQueries({ queryKey: queryKeys.payees });
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function useLogout() {
|
|
42
|
+
const queryClient = useQueryClient();
|
|
43
|
+
|
|
44
|
+
return useMutation({
|
|
45
|
+
mutationFn: () => apiClient.diviswap.logout(),
|
|
46
|
+
onSuccess: () => {
|
|
47
|
+
queryClient.clear(); // Clear all cached data
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Transaction hooks
|
|
53
|
+
export function useTransactions(filters?: any) {
|
|
54
|
+
const { data: user } = useUser();
|
|
55
|
+
|
|
56
|
+
return useQuery({
|
|
57
|
+
queryKey: queryKeys.transactions(filters),
|
|
58
|
+
queryFn: () => apiClient.diviswap.getTransactions(filters),
|
|
59
|
+
enabled: !!user, // Only fetch if user is authenticated
|
|
60
|
+
staleTime: 30 * 1000, // 30 seconds
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function useCreateTransaction() {
|
|
65
|
+
const queryClient = useQueryClient();
|
|
66
|
+
|
|
67
|
+
return useMutation({
|
|
68
|
+
mutationFn: (data: any) => apiClient.diviswap.createTransaction(data),
|
|
69
|
+
onSuccess: () => {
|
|
70
|
+
// Invalidate and refetch transactions
|
|
71
|
+
queryClient.invalidateQueries({ queryKey: queryKeys.transactions() });
|
|
72
|
+
},
|
|
73
|
+
onError: (error) => {
|
|
74
|
+
console.error('Transaction failed:', error);
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Payee hooks
|
|
80
|
+
export function usePayees() {
|
|
81
|
+
const { data: user } = useUser();
|
|
82
|
+
|
|
83
|
+
return useQuery({
|
|
84
|
+
queryKey: queryKeys.payees,
|
|
85
|
+
queryFn: () => apiClient.diviswap.getPayees(),
|
|
86
|
+
enabled: !!user,
|
|
87
|
+
staleTime: 60 * 1000, // 1 minute
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function useCreatePayee() {
|
|
92
|
+
const queryClient = useQueryClient();
|
|
93
|
+
|
|
94
|
+
return useMutation({
|
|
95
|
+
mutationFn: (data: any) => apiClient.diviswap.createPayee(data),
|
|
96
|
+
onSuccess: (newPayee) => {
|
|
97
|
+
// Optimistically add the new payee to the cache
|
|
98
|
+
queryClient.setQueryData(queryKeys.payees, (old: any[] = []) => {
|
|
99
|
+
return [...old, newPayee];
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function useDeletePayee() {
|
|
106
|
+
const queryClient = useQueryClient();
|
|
107
|
+
|
|
108
|
+
return useMutation({
|
|
109
|
+
mutationFn: (id: string) => apiClient.diviswap.deletePayee(id),
|
|
110
|
+
onMutate: async (deletedId) => {
|
|
111
|
+
// Cancel in-flight queries
|
|
112
|
+
await queryClient.cancelQueries({ queryKey: queryKeys.payees });
|
|
113
|
+
|
|
114
|
+
// Snapshot current value
|
|
115
|
+
const previousPayees = queryClient.getQueryData(queryKeys.payees);
|
|
116
|
+
|
|
117
|
+
// Optimistically remove the payee
|
|
118
|
+
queryClient.setQueryData(queryKeys.payees, (old: any[] = []) => {
|
|
119
|
+
return old.filter(payee => payee.id !== deletedId);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return { previousPayees };
|
|
123
|
+
},
|
|
124
|
+
onError: (err, deletedId, context) => {
|
|
125
|
+
// Rollback on error
|
|
126
|
+
if (context?.previousPayees) {
|
|
127
|
+
queryClient.setQueryData(queryKeys.payees, context.previousPayees);
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
onSettled: () => {
|
|
131
|
+
// Always refetch after error or success
|
|
132
|
+
queryClient.invalidateQueries({ queryKey: queryKeys.payees });
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
{{#if (includes features "fees")}}
|
|
138
|
+
// Fee calculation hooks
|
|
139
|
+
export function useFeeCalculation(amount: number, enabled = true) {
|
|
140
|
+
return useQuery({
|
|
141
|
+
queryKey: queryKeys.fees(amount),
|
|
142
|
+
queryFn: () => apiClient.diviswap.calculateFees(amount),
|
|
143
|
+
enabled: enabled && amount > 0,
|
|
144
|
+
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
145
|
+
gcTime: 10 * 60 * 1000, // 10 minutes (formerly cacheTime)
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
{{/if}}
|
|
149
|
+
|
|
150
|
+
// Example usage in a component:
|
|
151
|
+
/*
|
|
152
|
+
function MyComponent() {
|
|
153
|
+
const { data: user, isLoading: userLoading } = useUser();
|
|
154
|
+
const { data: transactions, isLoading: txLoading } = useTransactions({ limit: 10 });
|
|
155
|
+
const createTx = useCreateTransaction();
|
|
156
|
+
|
|
157
|
+
const handleCreateTransaction = async () => {
|
|
158
|
+
try {
|
|
159
|
+
await createTx.mutateAsync({
|
|
160
|
+
amount: 100,
|
|
161
|
+
type: 'onramp',
|
|
162
|
+
// ... other params
|
|
163
|
+
});
|
|
164
|
+
// Success! Transactions will automatically refetch
|
|
165
|
+
} catch (error) {
|
|
166
|
+
// Error handled by mutation
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
if (userLoading || txLoading) return <div>Loading...</div>;
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<div>
|
|
174
|
+
<h1>Welcome {user?.email}</h1>
|
|
175
|
+
<button
|
|
176
|
+
onClick={handleCreateTransaction}
|
|
177
|
+
disabled={createTx.isPending}
|
|
178
|
+
>
|
|
179
|
+
{createTx.isPending ? 'Creating...' : 'Create Transaction'}
|
|
180
|
+
</button>
|
|
181
|
+
{transactions?.map(tx => <div key={tx.id}>{tx.amount}</div>)}
|
|
182
|
+
</div>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
*/
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export class LiberExClient {
|
|
2
|
+
constructor(private baseUrl: string = '/api/liberex') {}
|
|
3
|
+
|
|
4
|
+
private async request(endpoint: string, options: RequestInit = {}) {
|
|
5
|
+
const response = await fetch(`${this.baseUrl}${endpoint}`, {
|
|
6
|
+
...options,
|
|
7
|
+
headers: {
|
|
8
|
+
'Content-Type': 'application/json',
|
|
9
|
+
...options.headers,
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
if (!response.ok) {
|
|
14
|
+
const error = await response.json();
|
|
15
|
+
throw new Error(error.message || 'Request failed');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return response.json();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async login(email: string, password: string) {
|
|
22
|
+
return this.request('', {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
body: JSON.stringify({ action: 'login', email, password }),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async logout() {
|
|
29
|
+
return this.request('', {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
body: JSON.stringify({ action: 'logout' }),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async register(data: any) {
|
|
36
|
+
return this.request('', {
|
|
37
|
+
method: 'POST',
|
|
38
|
+
body: JSON.stringify({ action: 'register', ...data }),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async checkSession() {
|
|
43
|
+
try {
|
|
44
|
+
return await this.request('?resource=session');
|
|
45
|
+
} catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async createTransaction(data: any) {
|
|
51
|
+
return this.request('', {
|
|
52
|
+
method: 'POST',
|
|
53
|
+
body: JSON.stringify({ action: 'createTransaction', ...data }),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async getPayees() {
|
|
58
|
+
return this.request('?resource=payees');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async getTransactions(params?: { limit?: number; offset?: number }) {
|
|
62
|
+
const searchParams = new URLSearchParams({
|
|
63
|
+
resource: 'transactions',
|
|
64
|
+
...(params && {
|
|
65
|
+
limit: params.limit?.toString() || '10',
|
|
66
|
+
offset: params.offset?.toString() || '0',
|
|
67
|
+
}),
|
|
68
|
+
});
|
|
69
|
+
return this.request(`?${searchParams}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async calculateFees(data: { amount: number; type: 'onramp' | 'offramp' }) {
|
|
73
|
+
return this.request('', {
|
|
74
|
+
method: 'POST',
|
|
75
|
+
body: JSON.stringify({ action: 'calculateFees', ...data }),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import crypto from 'crypto';
|
|
3
|
+
|
|
4
|
+
// Verify webhook signature
|
|
5
|
+
function verifyWebhookSignature(
|
|
6
|
+
payload: string,
|
|
7
|
+
signature: string,
|
|
8
|
+
secret: string
|
|
9
|
+
): boolean {
|
|
10
|
+
const expectedSignature = crypto
|
|
11
|
+
.createHmac('sha256', secret)
|
|
12
|
+
.update(payload)
|
|
13
|
+
.digest('hex');
|
|
14
|
+
|
|
15
|
+
return crypto.timingSafeEqual(
|
|
16
|
+
Buffer.from(signature),
|
|
17
|
+
Buffer.from(expectedSignature)
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function POST(request: NextRequest) {
|
|
22
|
+
try {
|
|
23
|
+
// Get the raw body
|
|
24
|
+
const rawBody = await request.text();
|
|
25
|
+
|
|
26
|
+
// Get signature from headers
|
|
27
|
+
const signature = request.headers.get('x-diviswap-signature');
|
|
28
|
+
|
|
29
|
+
if (!signature) {
|
|
30
|
+
return NextResponse.json(
|
|
31
|
+
{ error: 'Missing webhook signature' },
|
|
32
|
+
{ status: 401 }
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Verify signature
|
|
37
|
+
const webhookSecret = process.env.DIVISWAP_WEBHOOK_SECRET;
|
|
38
|
+
if (!webhookSecret) {
|
|
39
|
+
console.error('DIVISWAP_WEBHOOK_SECRET not configured');
|
|
40
|
+
return NextResponse.json(
|
|
41
|
+
{ error: 'Webhook secret not configured' },
|
|
42
|
+
{ status: 500 }
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const isValid = verifyWebhookSignature(rawBody, signature, webhookSecret);
|
|
47
|
+
if (!isValid) {
|
|
48
|
+
return NextResponse.json(
|
|
49
|
+
{ error: 'Invalid webhook signature' },
|
|
50
|
+
{ status: 401 }
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Parse the webhook payload
|
|
55
|
+
const webhook = JSON.parse(rawBody);
|
|
56
|
+
|
|
57
|
+
// Handle different webhook events
|
|
58
|
+
switch (webhook.event) {
|
|
59
|
+
case 'transaction.created':
|
|
60
|
+
console.log('Transaction created:', webhook.data);
|
|
61
|
+
// Handle new transaction
|
|
62
|
+
break;
|
|
63
|
+
|
|
64
|
+
case 'transaction.updated':
|
|
65
|
+
console.log('Transaction updated:', webhook.data);
|
|
66
|
+
// Handle transaction update
|
|
67
|
+
break;
|
|
68
|
+
|
|
69
|
+
case 'transaction.completed':
|
|
70
|
+
console.log('Transaction completed:', webhook.data);
|
|
71
|
+
// Handle completed transaction
|
|
72
|
+
break;
|
|
73
|
+
|
|
74
|
+
case 'transaction.failed':
|
|
75
|
+
console.log('Transaction failed:', webhook.data);
|
|
76
|
+
// Handle failed transaction
|
|
77
|
+
break;
|
|
78
|
+
|
|
79
|
+
case 'kyc.updated':
|
|
80
|
+
console.log('KYC status updated:', webhook.data);
|
|
81
|
+
// Handle KYC update
|
|
82
|
+
break;
|
|
83
|
+
|
|
84
|
+
default:
|
|
85
|
+
console.log('Unknown webhook event:', webhook.event);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Return success response
|
|
89
|
+
return NextResponse.json({ received: true });
|
|
90
|
+
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.error('Webhook error:', error);
|
|
93
|
+
return NextResponse.json(
|
|
94
|
+
{ error: 'Webhook processing failed' },
|
|
95
|
+
{ status: 500 }
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}
|