@doujins/payments-ui 0.0.15 → 0.1.0
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/index.cjs +1008 -2320
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +248 -346
- package/dist/index.d.ts +248 -346
- package/dist/index.js +872 -2166
- package/dist/index.js.map +1 -1
- package/dist/styles.css +2 -1
- package/package.json +9 -4
package/dist/index.js
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { createContext, useMemo, useState, useEffect, useCallback, useRef, useContext } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { WalletModalProvider
|
|
1
|
+
import * as React17 from 'react';
|
|
2
|
+
import React17__default, { createContext, useMemo, useState, useEffect, useCallback, useRef, useContext } from 'react';
|
|
3
|
+
import { useQueryClient, useQuery, useMutation, QueryClient, QueryClientProvider, useInfiniteQuery } from '@tanstack/react-query';
|
|
4
|
+
import { ConnectionProvider, WalletProvider, useWallet, useConnection } from '@solana/wallet-adapter-react';
|
|
5
|
+
import { WalletModalProvider } from '@solana/wallet-adapter-react-ui';
|
|
6
6
|
import '@solana/wallet-adapter-react-ui/styles.css';
|
|
7
|
-
import {
|
|
7
|
+
import { WalletAdapterNetwork } from '@solana/wallet-adapter-base';
|
|
8
|
+
import { clusterApiUrl, PublicKey } from '@solana/web3.js';
|
|
8
9
|
import { PhantomWalletAdapter } from '@solana/wallet-adapter-phantom';
|
|
9
10
|
import { SolflareWalletAdapter } from '@solana/wallet-adapter-solflare';
|
|
10
11
|
import { TrustWalletAdapter } from '@solana/wallet-adapter-trust';
|
|
11
12
|
import { CoinbaseWalletAdapter } from '@solana/wallet-adapter-coinbase';
|
|
12
13
|
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
13
|
-
import { X, ChevronDown, ChevronUp, Check, User, MapPin, Loader2, CreditCard, WalletCards, Trash2, ArrowLeft, CheckCircle, AlertCircle, Wallet, Ban, TriangleAlert,
|
|
14
|
+
import { X, ChevronDown, ChevronUp, Check, User, MapPin, Loader2, CreditCard, WalletCards, Trash2, ArrowLeft, CheckCircle, AlertCircle, Wallet, Ban, TriangleAlert, Shield, UserRound, Calendar, KeyRound, Sparkles, XCircle, RotateCcw, RefreshCw } from 'lucide-react';
|
|
14
15
|
import { clsx } from 'clsx';
|
|
15
16
|
import { twMerge } from 'tailwind-merge';
|
|
16
17
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
17
18
|
import countryList from 'country-list';
|
|
19
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
18
20
|
import { cva } from 'class-variance-authority';
|
|
19
21
|
import * as LabelPrimitive from '@radix-ui/react-label';
|
|
20
22
|
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
21
23
|
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
|
|
22
24
|
import * as TabsPrimitive from '@radix-ui/react-tabs';
|
|
23
|
-
import { Buffer } from 'buffer';
|
|
24
|
-
import { getAssociatedTokenAddress, getAccount, TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
|
25
25
|
import QRCode from 'qrcode';
|
|
26
26
|
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
|
|
27
|
-
import bs58 from 'bs58';
|
|
28
27
|
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
|
|
28
|
+
import { getAssociatedTokenAddress, getAccount } from '@solana/spl-token';
|
|
29
29
|
|
|
30
30
|
// src/context/PaymentContext.tsx
|
|
31
31
|
|
|
@@ -57,531 +57,297 @@ var loadCollectJs = (tokenizationKey) => {
|
|
|
57
57
|
document.head.appendChild(script);
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
-
// src/
|
|
61
|
-
var
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
// src/lib/client.ts
|
|
61
|
+
var ClientApiError = class extends Error {
|
|
62
|
+
constructor(message, status, body, request) {
|
|
63
|
+
super(message);
|
|
64
|
+
this.name = "ClientApiError";
|
|
65
|
+
this.status = status;
|
|
66
|
+
this.body = body;
|
|
67
|
+
this.request = request;
|
|
67
68
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
});
|
|
74
|
-
if ([...queryString.keys()].length > 0) {
|
|
75
|
-
resolved = `${resolved}?${queryString.toString()}`;
|
|
76
|
-
}
|
|
69
|
+
};
|
|
70
|
+
var ensureFetch = (fetchImpl) => {
|
|
71
|
+
if (fetchImpl) return fetchImpl;
|
|
72
|
+
if (typeof globalThis.fetch === "function") {
|
|
73
|
+
return globalThis.fetch.bind(globalThis);
|
|
77
74
|
}
|
|
78
|
-
|
|
75
|
+
throw new Error("payments-ui: global fetch is not available");
|
|
79
76
|
};
|
|
80
|
-
var
|
|
77
|
+
var createClient = (config) => {
|
|
78
|
+
const fetchImpl = ensureFetch(config.fetch);
|
|
79
|
+
const normalizeBase = (value) => value.replace(/\/$/, "");
|
|
80
|
+
const normalizePath = (value, fallback = "/v1") => {
|
|
81
|
+
if (!value) return fallback;
|
|
82
|
+
return value.startsWith("/") ? value : `/${value}`;
|
|
83
|
+
};
|
|
84
|
+
const billingBaseUrl = normalizeBase(config.billingBaseUrl);
|
|
85
|
+
const accountBaseUrl = normalizeBase(
|
|
86
|
+
config.accountBaseUrl ?? config.billingBaseUrl
|
|
87
|
+
);
|
|
88
|
+
const billingBasePath = normalizePath(config.billingBasePath ?? "/v1");
|
|
89
|
+
const accountBasePath = normalizePath(
|
|
90
|
+
config.accountBasePath ?? config.billingBasePath ?? "/v1"
|
|
91
|
+
);
|
|
92
|
+
const defaultHeaders = config.defaultHeaders ?? {};
|
|
93
|
+
const resolveAuthToken = async () => {
|
|
94
|
+
if (!config.getAuthToken) return null;
|
|
95
|
+
try {
|
|
96
|
+
const result = config.getAuthToken();
|
|
97
|
+
return result instanceof Promise ? await result : result;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.warn("payments-ui: failed to resolve auth token", error);
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const buildUrl = (path, query, target) => {
|
|
104
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
105
|
+
const basePath = target === "account" ? accountBasePath : billingBasePath;
|
|
106
|
+
const baseUrl = target === "account" ? accountBaseUrl : billingBaseUrl;
|
|
107
|
+
const needsBasePrefix = !normalizedPath.startsWith(basePath);
|
|
108
|
+
const finalPath = needsBasePrefix ? `${basePath}${normalizedPath}` : normalizedPath;
|
|
109
|
+
const url = new URL(`${baseUrl}${finalPath}`);
|
|
110
|
+
if (query) {
|
|
111
|
+
Object.entries(query).forEach(([key, value]) => {
|
|
112
|
+
if (value === void 0 || value === null) return;
|
|
113
|
+
url.searchParams.append(key, String(value));
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return url.toString();
|
|
117
|
+
};
|
|
81
118
|
const request = async (method, path, options) => {
|
|
82
|
-
const
|
|
119
|
+
const target = options?.target ?? "billing";
|
|
120
|
+
const url = buildUrl(path, options?.query, target);
|
|
83
121
|
const headers = {
|
|
84
122
|
"Content-Type": "application/json",
|
|
85
|
-
...
|
|
123
|
+
...defaultHeaders,
|
|
86
124
|
...options?.headers ?? {}
|
|
87
125
|
};
|
|
88
126
|
const token = await resolveAuthToken();
|
|
89
127
|
if (token) {
|
|
90
128
|
headers.Authorization = `Bearer ${token}`;
|
|
91
129
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
body: options?.body ? JSON.stringify(options.body) : void 0
|
|
97
|
-
});
|
|
98
|
-
if (!response.ok) {
|
|
99
|
-
const message = await response.text();
|
|
100
|
-
console.error("payments-ui: API request failed", {
|
|
101
|
-
url,
|
|
102
|
-
method,
|
|
103
|
-
status: response.status,
|
|
104
|
-
message
|
|
105
|
-
});
|
|
106
|
-
throw new Error(message || `Request failed with status ${response.status}`);
|
|
107
|
-
}
|
|
108
|
-
if (response.status === 204) {
|
|
109
|
-
return void 0;
|
|
110
|
-
}
|
|
111
|
-
const data = await response.json();
|
|
112
|
-
return data;
|
|
113
|
-
} catch (error) {
|
|
114
|
-
console.error("payments-ui: API request error", { url, method, error });
|
|
115
|
-
throw error;
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
return {
|
|
119
|
-
request,
|
|
120
|
-
get: (path, options) => request("GET", path, options),
|
|
121
|
-
post: (path, options) => request("POST", path, options),
|
|
122
|
-
put: (path, options) => request("PUT", path, options),
|
|
123
|
-
patch: (path, options) => request("PATCH", path, options),
|
|
124
|
-
delete: (path, options) => request("DELETE", path, options)
|
|
125
|
-
};
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
// src/services/CardPaymentService.ts
|
|
129
|
-
var CardPaymentService = class {
|
|
130
|
-
constructor(config) {
|
|
131
|
-
this.config = config;
|
|
132
|
-
this.collectLoaded = false;
|
|
133
|
-
}
|
|
134
|
-
async ensureCollectLoaded() {
|
|
135
|
-
if (this.collectLoaded) return;
|
|
136
|
-
if (!this.config.collectJsKey) {
|
|
137
|
-
throw new Error("payments-ui: collect.js key missing");
|
|
138
|
-
}
|
|
139
|
-
loadCollectJs(this.config.collectJsKey);
|
|
140
|
-
this.collectLoaded = true;
|
|
141
|
-
}
|
|
142
|
-
buildCreatePayload(result) {
|
|
143
|
-
return {
|
|
144
|
-
payment_token: result.token,
|
|
145
|
-
first_name: result.billing.firstName,
|
|
146
|
-
last_name: result.billing.lastName,
|
|
147
|
-
address1: result.billing.address1,
|
|
148
|
-
address2: result.billing.address2,
|
|
149
|
-
city: result.billing.city,
|
|
150
|
-
state: result.billing.stateRegion,
|
|
151
|
-
zip: result.billing.postalCode,
|
|
152
|
-
country: result.billing.country,
|
|
153
|
-
email: result.billing.email,
|
|
154
|
-
provider: result.billing.provider
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
// src/services/PaymentMethodService.ts
|
|
160
|
-
var PaymentMethodService = class {
|
|
161
|
-
constructor(api) {
|
|
162
|
-
this.api = api;
|
|
163
|
-
}
|
|
164
|
-
async list(params) {
|
|
165
|
-
return this.api.get("/payment-methods", {
|
|
166
|
-
query: {
|
|
167
|
-
page: params?.page ?? 1,
|
|
168
|
-
page_size: params?.pageSize ?? 50
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
async create(payload) {
|
|
173
|
-
return this.api.post("/payment-methods", {
|
|
174
|
-
body: { ...payload }
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
async remove(id) {
|
|
178
|
-
await this.api.delete(`/payment-methods/${id}`);
|
|
179
|
-
}
|
|
180
|
-
async activate(id) {
|
|
181
|
-
await this.api.put(`/payment-methods/${id}/activate`);
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
// src/services/SolanaPaymentService.ts
|
|
186
|
-
var SolanaPaymentService = class {
|
|
187
|
-
constructor(api) {
|
|
188
|
-
this.api = api;
|
|
189
|
-
}
|
|
190
|
-
async generatePayment(priceId, token, userWallet) {
|
|
191
|
-
return this.api.post("/solana/generate", {
|
|
192
|
-
body: { price_id: priceId, token, user_wallet: userWallet }
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
async submitPayment(signedTransaction, priceId, intentId, memo) {
|
|
196
|
-
return this.api.post("/solana/submit", {
|
|
197
|
-
body: {
|
|
198
|
-
signed_transaction: signedTransaction,
|
|
199
|
-
price_id: priceId,
|
|
200
|
-
intent_id: intentId,
|
|
201
|
-
...memo ? { memo } : {}
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
async fetchSupportedTokens() {
|
|
206
|
-
const response = await this.api.get(
|
|
207
|
-
"/solana/tokens"
|
|
208
|
-
);
|
|
209
|
-
return response.tokens;
|
|
210
|
-
}
|
|
211
|
-
async getSupportedTokens() {
|
|
212
|
-
return this.fetchSupportedTokens();
|
|
213
|
-
}
|
|
214
|
-
async generateQrCode(priceId, token, userWallet) {
|
|
215
|
-
return this.api.post("/solana/qr", {
|
|
216
|
-
body: {
|
|
217
|
-
price_id: priceId,
|
|
218
|
-
token,
|
|
219
|
-
...userWallet ? { user_wallet: userWallet } : {}
|
|
220
|
-
}
|
|
130
|
+
const response = await fetchImpl(url, {
|
|
131
|
+
method,
|
|
132
|
+
headers,
|
|
133
|
+
body: options?.body ? JSON.stringify(options.body) : void 0
|
|
221
134
|
});
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
// src/services/TokenCatalog.ts
|
|
237
|
-
var TokenCatalog = class {
|
|
238
|
-
constructor(solanaService, options = {}) {
|
|
239
|
-
this.solanaService = solanaService;
|
|
240
|
-
this.options = options;
|
|
241
|
-
this.cache = [];
|
|
242
|
-
this.lastFetched = 0;
|
|
243
|
-
}
|
|
244
|
-
get ttl() {
|
|
245
|
-
return this.options.ttlMs ?? 5 * 60 * 1e3;
|
|
246
|
-
}
|
|
247
|
-
async getTokens(force = false) {
|
|
248
|
-
const isStale = Date.now() - this.lastFetched > this.ttl;
|
|
249
|
-
if (!force && this.cache.length > 0 && !isStale) {
|
|
250
|
-
return this.cache;
|
|
251
|
-
}
|
|
252
|
-
const tokens = await this.solanaService.fetchSupportedTokens();
|
|
253
|
-
this.cache = tokens;
|
|
254
|
-
this.lastFetched = Date.now();
|
|
255
|
-
return tokens;
|
|
256
|
-
}
|
|
257
|
-
getCached() {
|
|
258
|
-
return this.cache;
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
// src/services/WalletGateway.ts
|
|
263
|
-
var WalletGateway = class {
|
|
264
|
-
constructor() {
|
|
265
|
-
this.adapter = null;
|
|
266
|
-
}
|
|
267
|
-
setAdapter(adapter) {
|
|
268
|
-
this.adapter = adapter;
|
|
269
|
-
}
|
|
270
|
-
getPublicKey() {
|
|
271
|
-
if (!this.adapter?.publicKey) return null;
|
|
272
|
-
try {
|
|
273
|
-
return this.adapter.publicKey.toBase58();
|
|
274
|
-
} catch {
|
|
275
|
-
return null;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
async sign(transaction) {
|
|
279
|
-
if (!this.adapter) {
|
|
280
|
-
throw new Error("payments-ui: wallet adapter not set");
|
|
281
|
-
}
|
|
282
|
-
if (typeof this.adapter.signVersionedTransaction === "function") {
|
|
283
|
-
return this.adapter.signVersionedTransaction(transaction);
|
|
135
|
+
if (!response.ok) {
|
|
136
|
+
let payload = null;
|
|
137
|
+
try {
|
|
138
|
+
payload = await response.json();
|
|
139
|
+
} catch {
|
|
140
|
+
payload = await response.text();
|
|
141
|
+
}
|
|
142
|
+
throw new ClientApiError(
|
|
143
|
+
payload && typeof payload === "object" && "message" in payload ? String(payload.message) : response.statusText || "Request failed",
|
|
144
|
+
response.status,
|
|
145
|
+
payload,
|
|
146
|
+
{ method, url }
|
|
147
|
+
);
|
|
284
148
|
}
|
|
285
|
-
if (
|
|
286
|
-
return
|
|
149
|
+
if (response.status === 204) {
|
|
150
|
+
return void 0;
|
|
287
151
|
}
|
|
288
|
-
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
152
|
+
return await response.json();
|
|
153
|
+
};
|
|
154
|
+
const normalizeList = (payload) => {
|
|
155
|
+
const data = payload.data ?? [];
|
|
156
|
+
const total = payload.total ?? payload.total_items ?? data.length;
|
|
157
|
+
const limit = payload.limit ?? data.length;
|
|
158
|
+
const offset = payload.offset ?? 0;
|
|
159
|
+
const hasMore = typeof payload.has_more === "boolean" ? payload.has_more : offset + data.length < total;
|
|
160
|
+
return { data, total, limit, offset, hasMore };
|
|
161
|
+
};
|
|
162
|
+
return {
|
|
163
|
+
async listPaymentMethods(params) {
|
|
164
|
+
const result = await request(
|
|
165
|
+
"GET",
|
|
166
|
+
"/me/payment-methods",
|
|
167
|
+
{
|
|
168
|
+
query: {
|
|
169
|
+
limit: params?.limit,
|
|
170
|
+
offset: params?.offset,
|
|
171
|
+
include_inactive: params?.includeInactive
|
|
172
|
+
},
|
|
173
|
+
target: "account"
|
|
174
|
+
}
|
|
175
|
+
);
|
|
176
|
+
return normalizeList(result);
|
|
177
|
+
},
|
|
178
|
+
createPaymentMethod(payload) {
|
|
179
|
+
return request("POST", "/me/payment-methods", {
|
|
180
|
+
body: payload,
|
|
181
|
+
target: "account"
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
updatePaymentMethod(id, payload) {
|
|
185
|
+
return request("PUT", `/me/payment-methods/${id}`, {
|
|
186
|
+
body: payload,
|
|
187
|
+
target: "account"
|
|
188
|
+
});
|
|
189
|
+
},
|
|
190
|
+
deletePaymentMethod(id) {
|
|
191
|
+
return request("DELETE", `/me/payment-methods/${id}`, {
|
|
192
|
+
target: "account"
|
|
193
|
+
});
|
|
194
|
+
},
|
|
195
|
+
activatePaymentMethod(id) {
|
|
196
|
+
return request("PUT", `/me/payment-methods/${id}/activate`, {
|
|
197
|
+
target: "account"
|
|
198
|
+
});
|
|
199
|
+
},
|
|
200
|
+
checkout(payload) {
|
|
201
|
+
return request("POST", "/me/checkout", {
|
|
202
|
+
body: payload
|
|
203
|
+
});
|
|
204
|
+
},
|
|
205
|
+
cancelSubscription(feedback) {
|
|
206
|
+
return request("POST", "/me/subscriptions/cancel", {
|
|
207
|
+
body: feedback ? { feedback } : void 0
|
|
208
|
+
});
|
|
209
|
+
},
|
|
210
|
+
async getPaymentHistory(params) {
|
|
211
|
+
const result = await request("GET", "/me/payments", {
|
|
212
|
+
query: {
|
|
213
|
+
limit: params?.limit,
|
|
214
|
+
offset: params?.offset,
|
|
215
|
+
type: params?.type
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
return normalizeList(result);
|
|
219
|
+
},
|
|
220
|
+
async getSolanaTokens() {
|
|
221
|
+
const response = await request(
|
|
222
|
+
"GET",
|
|
223
|
+
"/solana/tokens"
|
|
224
|
+
);
|
|
225
|
+
if (Array.isArray(response)) {
|
|
226
|
+
return response;
|
|
303
227
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
return {
|
|
329
|
-
data: response?.data ?? [],
|
|
330
|
-
total_items: totalItems,
|
|
331
|
-
limit,
|
|
332
|
-
offset,
|
|
333
|
-
page: pageNumber,
|
|
334
|
-
page_size: pageSize,
|
|
335
|
-
total_pages: totalPages
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
async cancelSubscription(feedback) {
|
|
339
|
-
return this.api.post("/subscriptions/cancel", {
|
|
340
|
-
body: feedback ? { feedback } : void 0
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
serializePayload(platform, payload) {
|
|
344
|
-
if (platform === "nmi") {
|
|
345
|
-
const data2 = payload;
|
|
346
|
-
if (!data2.priceId) {
|
|
347
|
-
throw new Error("payments-ui: priceId is required for NMI subscriptions");
|
|
228
|
+
return response.tokens ?? [];
|
|
229
|
+
},
|
|
230
|
+
async createSolanaPayIntent(payload) {
|
|
231
|
+
const response = await request("POST", "/solana/pay", {
|
|
232
|
+
body: {
|
|
233
|
+
price_id: payload.priceId,
|
|
234
|
+
token: payload.token,
|
|
235
|
+
...payload.userWallet ? { user_wallet: payload.userWallet } : {}
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
return response;
|
|
239
|
+
},
|
|
240
|
+
async getSolanaPayStatus(reference) {
|
|
241
|
+
const response = await request(
|
|
242
|
+
"GET",
|
|
243
|
+
`/solana/pay/${reference}`
|
|
244
|
+
);
|
|
245
|
+
if (response.status === "confirmed") {
|
|
246
|
+
return {
|
|
247
|
+
status: "confirmed",
|
|
248
|
+
payment_id: response.payment_id ?? "",
|
|
249
|
+
transaction: response.signature ?? null,
|
|
250
|
+
intent_id: response.intent_id
|
|
251
|
+
};
|
|
348
252
|
}
|
|
349
|
-
if (
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
253
|
+
if (response.status === "expired") {
|
|
254
|
+
return {
|
|
255
|
+
status: "failed",
|
|
256
|
+
payment_id: response.payment_id ?? "",
|
|
257
|
+
transaction: response.signature ?? null,
|
|
258
|
+
intent_id: response.intent_id,
|
|
259
|
+
error_message: "Payment intent expired"
|
|
260
|
+
};
|
|
353
261
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
262
|
+
return {
|
|
263
|
+
status: "pending",
|
|
264
|
+
payment_id: response.payment_id ?? "",
|
|
265
|
+
transaction: response.signature ?? null,
|
|
266
|
+
intent_id: response.intent_id
|
|
358
267
|
};
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
if (data2.lastName) body.last_name = data2.lastName;
|
|
362
|
-
if (data2.address1) body.address1 = data2.address1;
|
|
363
|
-
if (data2.city) body.city = data2.city;
|
|
364
|
-
if (data2.state) body.state = data2.state;
|
|
365
|
-
if (data2.zipCode) body.zip = data2.zipCode;
|
|
366
|
-
if (data2.country) body.country = data2.country;
|
|
367
|
-
if (data2.paymentToken) body.payment_token = data2.paymentToken;
|
|
368
|
-
if (data2.paymentMethodId) body.payment_method_id = data2.paymentMethodId;
|
|
369
|
-
return body;
|
|
370
|
-
}
|
|
371
|
-
const data = payload;
|
|
372
|
-
if (!data.priceId) {
|
|
373
|
-
throw new Error("payments-ui: priceId is required for CCBill subscriptions");
|
|
374
|
-
}
|
|
375
|
-
return {
|
|
376
|
-
price_id: data.priceId,
|
|
377
|
-
processor: data.processor ?? "ccbill",
|
|
378
|
-
email: data.email,
|
|
379
|
-
first_name: data.firstName,
|
|
380
|
-
last_name: data.lastName,
|
|
381
|
-
zip: data.zipCode,
|
|
382
|
-
country: data.country
|
|
383
|
-
};
|
|
384
|
-
}
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
// src/services/SolanaWalletService.ts
|
|
388
|
-
var SolanaWalletService = class {
|
|
389
|
-
constructor(api) {
|
|
390
|
-
this.api = api;
|
|
391
|
-
}
|
|
392
|
-
async list() {
|
|
393
|
-
const response = await this.api.get(
|
|
394
|
-
"/wallet/solana"
|
|
395
|
-
);
|
|
396
|
-
if (Array.isArray(response)) {
|
|
397
|
-
return response;
|
|
398
|
-
}
|
|
399
|
-
if ("wallets" in response && Array.isArray(response.wallets)) {
|
|
400
|
-
return response.wallets;
|
|
401
|
-
}
|
|
402
|
-
if ("wallet" in response) {
|
|
403
|
-
return response.wallet ? [response.wallet] : [];
|
|
404
|
-
}
|
|
405
|
-
if ("address" in response) {
|
|
406
|
-
return [response];
|
|
407
|
-
}
|
|
408
|
-
return [];
|
|
409
|
-
}
|
|
410
|
-
async requestChallenge(wallet) {
|
|
411
|
-
return this.api.post("/wallet/solana/challenge", {
|
|
412
|
-
body: { wallet }
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
async verify(wallet, signature, nonce) {
|
|
416
|
-
const body = { wallet, signature };
|
|
417
|
-
if (nonce) {
|
|
418
|
-
body.nonce = nonce;
|
|
419
|
-
}
|
|
420
|
-
return this.api.post("/wallet/solana/verify", { body });
|
|
421
|
-
}
|
|
422
|
-
async remove(wallet) {
|
|
423
|
-
await this.api.delete("/wallet/solana", {
|
|
424
|
-
query: { wallet }
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
};
|
|
428
|
-
|
|
429
|
-
// src/core/PaymentApp.ts
|
|
430
|
-
var PaymentApp = class {
|
|
431
|
-
constructor(options) {
|
|
432
|
-
this.resolveAuthToken = async () => {
|
|
433
|
-
if (!this.config.getAuthToken) {
|
|
434
|
-
return null;
|
|
435
|
-
}
|
|
268
|
+
},
|
|
269
|
+
async getPaymentStatus(id) {
|
|
436
270
|
try {
|
|
437
|
-
|
|
438
|
-
if (result instanceof Promise) {
|
|
439
|
-
return await result ?? null;
|
|
440
|
-
}
|
|
441
|
-
return result ?? null;
|
|
271
|
+
return await request("GET", `/payment/status/${id}`);
|
|
442
272
|
} catch (error) {
|
|
443
|
-
|
|
444
|
-
|
|
273
|
+
if (error instanceof ClientApiError && error.status === 404) {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
throw error;
|
|
445
277
|
}
|
|
446
|
-
};
|
|
447
|
-
this.config = options.config;
|
|
448
|
-
this.fetcher = options.fetcher ?? options.config.fetcher ?? globalThis.fetch?.bind(globalThis);
|
|
449
|
-
if (!this.fetcher) {
|
|
450
|
-
throw new Error("payments-ui: fetch implementation is required");
|
|
451
|
-
}
|
|
452
|
-
this.services = this.createServices();
|
|
453
|
-
}
|
|
454
|
-
getConfig() {
|
|
455
|
-
return this.config;
|
|
456
|
-
}
|
|
457
|
-
getFetcher() {
|
|
458
|
-
return this.fetcher;
|
|
459
|
-
}
|
|
460
|
-
getServices() {
|
|
461
|
-
return this.services;
|
|
462
|
-
}
|
|
463
|
-
createServices() {
|
|
464
|
-
const billingApi = createApiClient(
|
|
465
|
-
this.config,
|
|
466
|
-
this.config.endpoints.billingBaseUrl,
|
|
467
|
-
this.fetcher,
|
|
468
|
-
this.resolveAuthToken
|
|
469
|
-
);
|
|
470
|
-
const accountBaseUrl = this.config.endpoints.accountBaseUrl ?? this.config.endpoints.billingBaseUrl;
|
|
471
|
-
const accountApi = createApiClient(
|
|
472
|
-
this.config,
|
|
473
|
-
accountBaseUrl,
|
|
474
|
-
this.fetcher,
|
|
475
|
-
this.resolveAuthToken
|
|
476
|
-
);
|
|
477
|
-
const solanaPayments = new SolanaPaymentService(billingApi);
|
|
478
|
-
const solanaWallets = new SolanaWalletService(billingApi);
|
|
479
|
-
const paymentMethods = new PaymentMethodService(accountApi);
|
|
480
|
-
const cardPayments = new CardPaymentService(this.config);
|
|
481
|
-
const walletGateway = new WalletGateway();
|
|
482
|
-
const tokenCatalog = new TokenCatalog(solanaPayments);
|
|
483
|
-
const subscriptions = new SubscriptionService(billingApi);
|
|
484
|
-
return {
|
|
485
|
-
cardPayments,
|
|
486
|
-
paymentMethods,
|
|
487
|
-
solanaPayments,
|
|
488
|
-
solanaWallets,
|
|
489
|
-
tokenCatalog,
|
|
490
|
-
walletGateway,
|
|
491
|
-
subscriptions,
|
|
492
|
-
billingApi,
|
|
493
|
-
accountApi
|
|
494
|
-
};
|
|
495
|
-
}
|
|
496
|
-
};
|
|
497
|
-
|
|
498
|
-
// src/runtime/PaymentsRuntime.ts
|
|
499
|
-
var createQueryClient = () => new QueryClient({
|
|
500
|
-
defaultOptions: {
|
|
501
|
-
queries: {
|
|
502
|
-
staleTime: 3e4,
|
|
503
|
-
gcTime: 5 * 6e4,
|
|
504
|
-
refetchOnWindowFocus: false,
|
|
505
|
-
retry: 1
|
|
506
|
-
},
|
|
507
|
-
mutations: {
|
|
508
|
-
retry: 1
|
|
509
278
|
}
|
|
510
|
-
}
|
|
511
|
-
});
|
|
512
|
-
var PaymentsRuntime = class {
|
|
513
|
-
constructor(config) {
|
|
514
|
-
this.config = config;
|
|
515
|
-
this.app = new PaymentApp({ config });
|
|
516
|
-
this.services = this.app.getServices();
|
|
517
|
-
this.queryClient = createQueryClient();
|
|
518
|
-
}
|
|
279
|
+
};
|
|
519
280
|
};
|
|
520
|
-
var createPaymentsRuntime = (config) => new PaymentsRuntime(config);
|
|
521
|
-
|
|
522
|
-
// node_modules/@solana/wallet-adapter-base/lib/esm/types.js
|
|
523
|
-
var WalletAdapterNetwork;
|
|
524
|
-
(function(WalletAdapterNetwork2) {
|
|
525
|
-
WalletAdapterNetwork2["Mainnet"] = "mainnet-beta";
|
|
526
|
-
WalletAdapterNetwork2["Testnet"] = "testnet";
|
|
527
|
-
WalletAdapterNetwork2["Devnet"] = "devnet";
|
|
528
|
-
})(WalletAdapterNetwork || (WalletAdapterNetwork = {}));
|
|
529
281
|
function cn(...inputs) {
|
|
530
282
|
return twMerge(clsx(inputs));
|
|
531
283
|
}
|
|
532
284
|
var Dialog = DialogPrimitive.Root;
|
|
533
|
-
var DialogPortal =
|
|
534
|
-
|
|
535
|
-
var DialogOverlay = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
285
|
+
var DialogPortal = DialogPrimitive.Portal;
|
|
286
|
+
var DialogOverlay = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
536
287
|
DialogPrimitive.Overlay,
|
|
537
288
|
{
|
|
538
289
|
ref,
|
|
539
290
|
className: cn(
|
|
540
|
-
"fixed inset-0 z-50 bg-black/80
|
|
291
|
+
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
541
292
|
className
|
|
542
293
|
),
|
|
543
294
|
...props
|
|
544
295
|
}
|
|
545
296
|
));
|
|
546
297
|
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
547
|
-
var DialogContent =
|
|
298
|
+
var DialogContent = React17.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsx(DialogPortal, { children: /* @__PURE__ */ jsxs("div", { className: "payments-ui-portal", children: [
|
|
548
299
|
/* @__PURE__ */ jsx(DialogOverlay, {}),
|
|
549
300
|
/* @__PURE__ */ jsxs(
|
|
550
301
|
DialogPrimitive.Content,
|
|
551
302
|
{
|
|
552
303
|
ref,
|
|
553
304
|
className: cn(
|
|
554
|
-
"fixed left-[50%] top-[50%] z-50 grid w-
|
|
305
|
+
"fixed left-[50%] top-[50%] z-50 grid w-[calc(100vw-40px)] sm:w-[calc(100vw-60px)] max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-white/10 bg-[#161b22] text-white p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] rounded-lg",
|
|
555
306
|
className
|
|
556
307
|
),
|
|
557
308
|
...props,
|
|
558
309
|
children: [
|
|
559
310
|
children,
|
|
560
|
-
/* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "absolute right-4 top-
|
|
561
|
-
/* @__PURE__ */ jsx(X, { className: "h-
|
|
311
|
+
/* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "absolute right-4 top-5 rounded-full bg-white/10 p-1 opacity-70 transition-opacity hover:opacity-100 hover:bg-white/20 focus:outline-none disabled:pointer-events-none", children: [
|
|
312
|
+
/* @__PURE__ */ jsx(X, { className: "h-3 w-3 text-white" }),
|
|
562
313
|
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
|
|
563
314
|
] })
|
|
564
315
|
]
|
|
565
316
|
}
|
|
566
317
|
)
|
|
567
|
-
] }));
|
|
318
|
+
] }) }));
|
|
568
319
|
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|
569
|
-
var DialogHeader = ({
|
|
320
|
+
var DialogHeader = ({
|
|
321
|
+
className,
|
|
322
|
+
...props
|
|
323
|
+
}) => /* @__PURE__ */ jsx(
|
|
324
|
+
"div",
|
|
325
|
+
{
|
|
326
|
+
className: cn(
|
|
327
|
+
"flex flex-col space-y-1.5 text-center sm:text-left",
|
|
328
|
+
className
|
|
329
|
+
),
|
|
330
|
+
...props
|
|
331
|
+
}
|
|
332
|
+
);
|
|
570
333
|
DialogHeader.displayName = "DialogHeader";
|
|
571
|
-
var DialogTitle =
|
|
334
|
+
var DialogTitle = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
572
335
|
DialogPrimitive.Title,
|
|
573
336
|
{
|
|
574
337
|
ref,
|
|
575
|
-
className: cn(
|
|
338
|
+
className: cn(
|
|
339
|
+
"text-lg font-semibold leading-none tracking-tight",
|
|
340
|
+
className
|
|
341
|
+
),
|
|
576
342
|
...props
|
|
577
343
|
}
|
|
578
344
|
));
|
|
579
345
|
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
580
|
-
var DialogDescription =
|
|
346
|
+
var DialogDescription = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
581
347
|
DialogPrimitive.Description,
|
|
582
348
|
{
|
|
583
349
|
ref,
|
|
584
|
-
className: cn("text-
|
|
350
|
+
className: cn("text-white/60 text-sm", className),
|
|
585
351
|
...props
|
|
586
352
|
}
|
|
587
353
|
));
|
|
@@ -601,131 +367,17 @@ var customCountries = [
|
|
|
601
367
|
];
|
|
602
368
|
countryList.overwrite(customCountries);
|
|
603
369
|
var countries = countryList.getData().sort((a, b) => a.name.localeCompare(b.name));
|
|
604
|
-
function setRef(ref, value) {
|
|
605
|
-
if (typeof ref === "function") {
|
|
606
|
-
return ref(value);
|
|
607
|
-
} else if (ref !== null && ref !== void 0) {
|
|
608
|
-
ref.current = value;
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
function composeRefs(...refs) {
|
|
612
|
-
return (node) => {
|
|
613
|
-
let hasCleanup = false;
|
|
614
|
-
const cleanups = refs.map((ref) => {
|
|
615
|
-
const cleanup = setRef(ref, node);
|
|
616
|
-
if (!hasCleanup && typeof cleanup == "function") {
|
|
617
|
-
hasCleanup = true;
|
|
618
|
-
}
|
|
619
|
-
return cleanup;
|
|
620
|
-
});
|
|
621
|
-
if (hasCleanup) {
|
|
622
|
-
return () => {
|
|
623
|
-
for (let i = 0; i < cleanups.length; i++) {
|
|
624
|
-
const cleanup = cleanups[i];
|
|
625
|
-
if (typeof cleanup == "function") {
|
|
626
|
-
cleanup();
|
|
627
|
-
} else {
|
|
628
|
-
setRef(refs[i], null);
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
};
|
|
632
|
-
}
|
|
633
|
-
};
|
|
634
|
-
}
|
|
635
|
-
// @__NO_SIDE_EFFECTS__
|
|
636
|
-
function createSlot(ownerName) {
|
|
637
|
-
const SlotClone = /* @__PURE__ */ createSlotClone(ownerName);
|
|
638
|
-
const Slot2 = React3.forwardRef((props, forwardedRef) => {
|
|
639
|
-
const { children, ...slotProps } = props;
|
|
640
|
-
const childrenArray = React3.Children.toArray(children);
|
|
641
|
-
const slottable = childrenArray.find(isSlottable);
|
|
642
|
-
if (slottable) {
|
|
643
|
-
const newElement = slottable.props.children;
|
|
644
|
-
const newChildren = childrenArray.map((child) => {
|
|
645
|
-
if (child === slottable) {
|
|
646
|
-
if (React3.Children.count(newElement) > 1) return React3.Children.only(null);
|
|
647
|
-
return React3.isValidElement(newElement) ? newElement.props.children : null;
|
|
648
|
-
} else {
|
|
649
|
-
return child;
|
|
650
|
-
}
|
|
651
|
-
});
|
|
652
|
-
return /* @__PURE__ */ jsx(SlotClone, { ...slotProps, ref: forwardedRef, children: React3.isValidElement(newElement) ? React3.cloneElement(newElement, void 0, newChildren) : null });
|
|
653
|
-
}
|
|
654
|
-
return /* @__PURE__ */ jsx(SlotClone, { ...slotProps, ref: forwardedRef, children });
|
|
655
|
-
});
|
|
656
|
-
Slot2.displayName = `${ownerName}.Slot`;
|
|
657
|
-
return Slot2;
|
|
658
|
-
}
|
|
659
|
-
var Slot = /* @__PURE__ */ createSlot("Slot");
|
|
660
|
-
// @__NO_SIDE_EFFECTS__
|
|
661
|
-
function createSlotClone(ownerName) {
|
|
662
|
-
const SlotClone = React3.forwardRef((props, forwardedRef) => {
|
|
663
|
-
const { children, ...slotProps } = props;
|
|
664
|
-
if (React3.isValidElement(children)) {
|
|
665
|
-
const childrenRef = getElementRef(children);
|
|
666
|
-
const props2 = mergeProps(slotProps, children.props);
|
|
667
|
-
if (children.type !== React3.Fragment) {
|
|
668
|
-
props2.ref = forwardedRef ? composeRefs(forwardedRef, childrenRef) : childrenRef;
|
|
669
|
-
}
|
|
670
|
-
return React3.cloneElement(children, props2);
|
|
671
|
-
}
|
|
672
|
-
return React3.Children.count(children) > 1 ? React3.Children.only(null) : null;
|
|
673
|
-
});
|
|
674
|
-
SlotClone.displayName = `${ownerName}.SlotClone`;
|
|
675
|
-
return SlotClone;
|
|
676
|
-
}
|
|
677
|
-
var SLOTTABLE_IDENTIFIER = Symbol("radix.slottable");
|
|
678
|
-
function isSlottable(child) {
|
|
679
|
-
return React3.isValidElement(child) && typeof child.type === "function" && "__radixId" in child.type && child.type.__radixId === SLOTTABLE_IDENTIFIER;
|
|
680
|
-
}
|
|
681
|
-
function mergeProps(slotProps, childProps) {
|
|
682
|
-
const overrideProps = { ...childProps };
|
|
683
|
-
for (const propName in childProps) {
|
|
684
|
-
const slotPropValue = slotProps[propName];
|
|
685
|
-
const childPropValue = childProps[propName];
|
|
686
|
-
const isHandler = /^on[A-Z]/.test(propName);
|
|
687
|
-
if (isHandler) {
|
|
688
|
-
if (slotPropValue && childPropValue) {
|
|
689
|
-
overrideProps[propName] = (...args) => {
|
|
690
|
-
const result = childPropValue(...args);
|
|
691
|
-
slotPropValue(...args);
|
|
692
|
-
return result;
|
|
693
|
-
};
|
|
694
|
-
} else if (slotPropValue) {
|
|
695
|
-
overrideProps[propName] = slotPropValue;
|
|
696
|
-
}
|
|
697
|
-
} else if (propName === "style") {
|
|
698
|
-
overrideProps[propName] = { ...slotPropValue, ...childPropValue };
|
|
699
|
-
} else if (propName === "className") {
|
|
700
|
-
overrideProps[propName] = [slotPropValue, childPropValue].filter(Boolean).join(" ");
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
return { ...slotProps, ...overrideProps };
|
|
704
|
-
}
|
|
705
|
-
function getElementRef(element) {
|
|
706
|
-
let getter = Object.getOwnPropertyDescriptor(element.props, "ref")?.get;
|
|
707
|
-
let mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
|
|
708
|
-
if (mayWarn) {
|
|
709
|
-
return element.ref;
|
|
710
|
-
}
|
|
711
|
-
getter = Object.getOwnPropertyDescriptor(element, "ref")?.get;
|
|
712
|
-
mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
|
|
713
|
-
if (mayWarn) {
|
|
714
|
-
return element.props.ref;
|
|
715
|
-
}
|
|
716
|
-
return element.props.ref || element.ref;
|
|
717
|
-
}
|
|
718
370
|
var buttonVariants = cva(
|
|
719
|
-
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-
|
|
371
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/20 disabled:pointer-events-none disabled:opacity-50",
|
|
720
372
|
{
|
|
721
373
|
variants: {
|
|
722
374
|
variant: {
|
|
723
|
-
default: "bg-
|
|
724
|
-
secondary: "bg-
|
|
725
|
-
outline: "border border-
|
|
726
|
-
ghost: "hover:bg-
|
|
727
|
-
destructive: "bg-
|
|
728
|
-
link: "text-
|
|
375
|
+
default: "bg-[#28a745] text-white shadow hover:bg-[#16a34a]",
|
|
376
|
+
secondary: "bg-white/10 text-white hover:bg-white/20",
|
|
377
|
+
outline: "border border-white/10 bg-transparent text-white hover:bg-white/10",
|
|
378
|
+
ghost: "text-white/80 hover:bg-white/10 hover:text-white",
|
|
379
|
+
destructive: "bg-red-600 text-white hover:bg-red-700",
|
|
380
|
+
link: "text-blue-400 underline-offset-4 hover:underline"
|
|
729
381
|
},
|
|
730
382
|
size: {
|
|
731
383
|
default: "h-10 px-4 py-2",
|
|
@@ -740,7 +392,7 @@ var buttonVariants = cva(
|
|
|
740
392
|
}
|
|
741
393
|
}
|
|
742
394
|
);
|
|
743
|
-
var Button =
|
|
395
|
+
var Button = React17.forwardRef(
|
|
744
396
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
745
397
|
const Comp = asChild ? Slot : "button";
|
|
746
398
|
return /* @__PURE__ */ jsx(
|
|
@@ -754,14 +406,14 @@ var Button = React3.forwardRef(
|
|
|
754
406
|
}
|
|
755
407
|
);
|
|
756
408
|
Button.displayName = "Button";
|
|
757
|
-
var Input =
|
|
409
|
+
var Input = React17.forwardRef(
|
|
758
410
|
({ className, type, ...props }, ref) => {
|
|
759
411
|
return /* @__PURE__ */ jsx(
|
|
760
412
|
"input",
|
|
761
413
|
{
|
|
762
414
|
type,
|
|
763
415
|
className: cn(
|
|
764
|
-
"flex h-10 w-full rounded-md border border-
|
|
416
|
+
"flex h-10 w-full rounded-md border border-white/10 bg-white/5 px-3 py-2 text-sm text-white placeholder:text-white/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/20 focus-visible:border-white/20 disabled:cursor-not-allowed disabled:opacity-50",
|
|
765
417
|
className
|
|
766
418
|
),
|
|
767
419
|
ref,
|
|
@@ -771,12 +423,12 @@ var Input = React3.forwardRef(
|
|
|
771
423
|
}
|
|
772
424
|
);
|
|
773
425
|
Input.displayName = "Input";
|
|
774
|
-
var Label =
|
|
426
|
+
var Label = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
775
427
|
LabelPrimitive.Root,
|
|
776
428
|
{
|
|
777
429
|
ref,
|
|
778
430
|
className: cn(
|
|
779
|
-
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
|
431
|
+
"text-sm font-medium leading-none text-white peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
|
780
432
|
className
|
|
781
433
|
),
|
|
782
434
|
...props
|
|
@@ -785,12 +437,12 @@ var Label = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
|
|
|
785
437
|
Label.displayName = LabelPrimitive.Root.displayName;
|
|
786
438
|
var Select = SelectPrimitive.Root;
|
|
787
439
|
var SelectValue = SelectPrimitive.Value;
|
|
788
|
-
var SelectTrigger =
|
|
440
|
+
var SelectTrigger = React17.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
789
441
|
SelectPrimitive.Trigger,
|
|
790
442
|
{
|
|
791
443
|
ref,
|
|
792
444
|
className: cn(
|
|
793
|
-
"flex h-10 w-full items-center justify-between rounded-md border border-
|
|
445
|
+
"flex h-10 w-full items-center justify-between rounded-md border border-white/10 bg-white/5 px-3 py-2 text-sm text-white placeholder:text-white/40 focus:outline-none focus:ring-2 focus:ring-white/20 disabled:cursor-not-allowed disabled:opacity-50",
|
|
794
446
|
className
|
|
795
447
|
),
|
|
796
448
|
...props,
|
|
@@ -801,39 +453,39 @@ var SelectTrigger = React3.forwardRef(({ className, children, ...props }, ref) =
|
|
|
801
453
|
}
|
|
802
454
|
));
|
|
803
455
|
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
804
|
-
var SelectContent =
|
|
456
|
+
var SelectContent = React17.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsx("div", { className: "payments-ui-portal", children: /* @__PURE__ */ jsxs(
|
|
805
457
|
SelectPrimitive.Content,
|
|
806
458
|
{
|
|
807
459
|
ref,
|
|
808
460
|
className: cn(
|
|
809
|
-
"relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-
|
|
461
|
+
"relative z-50 min-w-[8rem] overflow-hidden rounded-md border border-white/10 bg-[#1a1f26] text-white shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
810
462
|
className
|
|
811
463
|
),
|
|
812
464
|
position,
|
|
813
465
|
...props,
|
|
814
466
|
children: [
|
|
815
|
-
/* @__PURE__ */ jsx(SelectPrimitive.ScrollUpButton, { className: "flex cursor-default items-center justify-center py-1", children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4" }) }),
|
|
467
|
+
/* @__PURE__ */ jsx(SelectPrimitive.ScrollUpButton, { className: "flex cursor-default items-center justify-center py-1 text-white/60", children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4" }) }),
|
|
816
468
|
/* @__PURE__ */ jsx(SelectPrimitive.Viewport, { className: "p-1", children }),
|
|
817
|
-
/* @__PURE__ */ jsx(SelectPrimitive.ScrollDownButton, { className: "flex cursor-default items-center justify-center py-1", children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" }) })
|
|
469
|
+
/* @__PURE__ */ jsx(SelectPrimitive.ScrollDownButton, { className: "flex cursor-default items-center justify-center py-1 text-white/60", children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" }) })
|
|
818
470
|
]
|
|
819
471
|
}
|
|
820
|
-
) }));
|
|
472
|
+
) }) }));
|
|
821
473
|
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
822
|
-
var SelectLabel =
|
|
474
|
+
var SelectLabel = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
823
475
|
SelectPrimitive.Label,
|
|
824
476
|
{
|
|
825
477
|
ref,
|
|
826
|
-
className: cn("px-2 py-1.5 text-sm font-semibold text-
|
|
478
|
+
className: cn("px-2 py-1.5 text-sm font-semibold text-white/60", className),
|
|
827
479
|
...props
|
|
828
480
|
}
|
|
829
481
|
));
|
|
830
482
|
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
831
|
-
var SelectItem =
|
|
483
|
+
var SelectItem = React17.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
832
484
|
SelectPrimitive.Item,
|
|
833
485
|
{
|
|
834
486
|
ref,
|
|
835
487
|
className: cn(
|
|
836
|
-
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-
|
|
488
|
+
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm text-white outline-none focus:bg-white/10 focus:text-white data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
837
489
|
className
|
|
838
490
|
),
|
|
839
491
|
...props,
|
|
@@ -844,16 +496,18 @@ var SelectItem = React3.forwardRef(({ className, children, ...props }, ref) => /
|
|
|
844
496
|
}
|
|
845
497
|
));
|
|
846
498
|
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
847
|
-
var SelectSeparator =
|
|
499
|
+
var SelectSeparator = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
848
500
|
SelectPrimitive.Separator,
|
|
849
501
|
{
|
|
850
502
|
ref,
|
|
851
|
-
className: cn("mx-1 my-1 h-px bg-
|
|
503
|
+
className: cn("mx-1 my-1 h-px bg-white/10", className),
|
|
852
504
|
...props
|
|
853
505
|
}
|
|
854
506
|
));
|
|
855
507
|
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
856
|
-
|
|
508
|
+
|
|
509
|
+
// src/constants/billing.ts
|
|
510
|
+
var defaultBillingDetails = {
|
|
857
511
|
firstName: "",
|
|
858
512
|
lastName: "",
|
|
859
513
|
address1: "",
|
|
@@ -881,9 +535,9 @@ var CardDetailsForm = ({
|
|
|
881
535
|
const defaultValuesKey = useMemo(() => JSON.stringify(defaultValues ?? {}), [defaultValues]);
|
|
882
536
|
const mergedDefaults = useMemo(
|
|
883
537
|
() => ({
|
|
884
|
-
...
|
|
538
|
+
...defaultBillingDetails,
|
|
885
539
|
...defaultValues,
|
|
886
|
-
email: defaultValues?.email ?? config.defaultUser?.email ??
|
|
540
|
+
email: defaultValues?.email ?? config.defaultUser?.email ?? defaultBillingDetails.email
|
|
887
541
|
}),
|
|
888
542
|
[defaultValuesKey, config.defaultUser?.email]
|
|
889
543
|
);
|
|
@@ -1012,7 +666,7 @@ var CardDetailsForm = ({
|
|
|
1012
666
|
window.CollectJS.startPaymentRequest();
|
|
1013
667
|
};
|
|
1014
668
|
const errorMessage = localError ?? externalError;
|
|
1015
|
-
const collectFieldClass = "flex h-11 w-full items-center rounded-md border border-
|
|
669
|
+
const collectFieldClass = "flex h-11 w-full items-center rounded-md border border-white/10 bg-white/5 px-3 text-sm text-white";
|
|
1016
670
|
return /* @__PURE__ */ jsxs(
|
|
1017
671
|
"form",
|
|
1018
672
|
{
|
|
@@ -1020,10 +674,10 @@ var CardDetailsForm = ({
|
|
|
1020
674
|
onSubmit: handleSubmit,
|
|
1021
675
|
noValidate: true,
|
|
1022
676
|
children: [
|
|
1023
|
-
errorMessage && /* @__PURE__ */ jsx("div", { className: "rounded-md border border-
|
|
677
|
+
errorMessage && /* @__PURE__ */ jsx("div", { className: "rounded-md border border-red-500/40 bg-red-500/10 px-4 py-2 text-sm text-red-400", children: errorMessage }),
|
|
1024
678
|
/* @__PURE__ */ jsxs("div", { className: "grid gap-5 md:grid-cols-2", children: [
|
|
1025
679
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1026
|
-
/* @__PURE__ */ jsxs(Label, { htmlFor: "payments-first", className: "flex items-center gap-2 text-
|
|
680
|
+
/* @__PURE__ */ jsxs(Label, { htmlFor: "payments-first", className: "flex items-center gap-2 text-white/70", children: [
|
|
1027
681
|
/* @__PURE__ */ jsx(User, { className: "h-4 w-4" }),
|
|
1028
682
|
" First name"
|
|
1029
683
|
] }),
|
|
@@ -1038,7 +692,7 @@ var CardDetailsForm = ({
|
|
|
1038
692
|
)
|
|
1039
693
|
] }),
|
|
1040
694
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1041
|
-
/* @__PURE__ */ jsxs(Label, { htmlFor: "payments-last", className: "flex items-center gap-2 text-
|
|
695
|
+
/* @__PURE__ */ jsxs(Label, { htmlFor: "payments-last", className: "flex items-center gap-2 text-white/70", children: [
|
|
1042
696
|
/* @__PURE__ */ jsx(User, { className: "h-4 w-4" }),
|
|
1043
697
|
" Last name"
|
|
1044
698
|
] }),
|
|
@@ -1105,7 +759,7 @@ var CardDetailsForm = ({
|
|
|
1105
759
|
] }),
|
|
1106
760
|
/* @__PURE__ */ jsxs("div", { className: "grid gap-5 md:grid-cols-2", children: [
|
|
1107
761
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1108
|
-
/* @__PURE__ */ jsxs(Label, { htmlFor: "payments-postal", className: "flex items-center gap-2 text-
|
|
762
|
+
/* @__PURE__ */ jsxs(Label, { htmlFor: "payments-postal", className: "flex items-center gap-2 text-white/70", children: [
|
|
1109
763
|
/* @__PURE__ */ jsx(MapPin, { className: "h-4 w-4" }),
|
|
1110
764
|
" Postal code"
|
|
1111
765
|
] }),
|
|
@@ -1157,7 +811,7 @@ var CardDetailsForm = ({
|
|
|
1157
811
|
] })
|
|
1158
812
|
}
|
|
1159
813
|
),
|
|
1160
|
-
/* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-xs text-
|
|
814
|
+
/* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-xs text-white/50", children: [
|
|
1161
815
|
/* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4" }),
|
|
1162
816
|
" Your payment information is encrypted and processed securely."
|
|
1163
817
|
] })
|
|
@@ -1165,19 +819,24 @@ var CardDetailsForm = ({
|
|
|
1165
819
|
}
|
|
1166
820
|
);
|
|
1167
821
|
};
|
|
1168
|
-
var usePaymentMethodService = () => {
|
|
1169
|
-
const { services } = usePaymentContext();
|
|
1170
|
-
return useMemo(() => services.paymentMethods, [services]);
|
|
1171
|
-
};
|
|
1172
|
-
|
|
1173
|
-
// src/hooks/usePaymentMethods.ts
|
|
1174
822
|
var PAYMENT_METHODS_KEY = ["payments-ui", "payment-methods"];
|
|
1175
823
|
var usePaymentMethods = () => {
|
|
1176
|
-
const
|
|
824
|
+
const { client } = usePaymentContext();
|
|
1177
825
|
const queryClient = useQueryClient();
|
|
1178
826
|
const listQuery = useQuery({
|
|
1179
827
|
queryKey: PAYMENT_METHODS_KEY,
|
|
1180
|
-
queryFn: () =>
|
|
828
|
+
queryFn: async () => {
|
|
829
|
+
const response = await client.listPaymentMethods({ limit: 50 });
|
|
830
|
+
return {
|
|
831
|
+
data: response.data,
|
|
832
|
+
total_items: response.total,
|
|
833
|
+
limit: response.limit,
|
|
834
|
+
offset: response.offset,
|
|
835
|
+
page: response.limit > 0 ? Math.floor(response.offset / response.limit) + 1 : 1,
|
|
836
|
+
page_size: response.limit,
|
|
837
|
+
total_pages: response.limit > 0 ? Math.ceil(response.total / response.limit) : void 0
|
|
838
|
+
};
|
|
839
|
+
}
|
|
1181
840
|
});
|
|
1182
841
|
const createMutation = useMutation({
|
|
1183
842
|
mutationFn: ({ token, billing }) => {
|
|
@@ -1194,14 +853,14 @@ var usePaymentMethods = () => {
|
|
|
1194
853
|
email: billing.email,
|
|
1195
854
|
provider: billing.provider
|
|
1196
855
|
};
|
|
1197
|
-
return
|
|
856
|
+
return client.createPaymentMethod(payload);
|
|
1198
857
|
},
|
|
1199
858
|
onSuccess: () => {
|
|
1200
859
|
void queryClient.invalidateQueries({ queryKey: PAYMENT_METHODS_KEY });
|
|
1201
860
|
}
|
|
1202
861
|
});
|
|
1203
862
|
const deleteMutation = useMutation({
|
|
1204
|
-
mutationFn: ({ id }) =>
|
|
863
|
+
mutationFn: ({ id }) => client.deletePaymentMethod(id),
|
|
1205
864
|
onSuccess: () => {
|
|
1206
865
|
void queryClient.invalidateQueries({ queryKey: PAYMENT_METHODS_KEY });
|
|
1207
866
|
}
|
|
@@ -1217,10 +876,10 @@ var badgeVariants = cva(
|
|
|
1217
876
|
{
|
|
1218
877
|
variants: {
|
|
1219
878
|
variant: {
|
|
1220
|
-
default: "border-transparent bg-
|
|
1221
|
-
secondary: "border-transparent bg-
|
|
1222
|
-
outline: "text-
|
|
1223
|
-
destructive: "border-transparent bg-
|
|
879
|
+
default: "border-transparent bg-emerald-500/20 text-emerald-400",
|
|
880
|
+
secondary: "border-transparent bg-white/10 text-white/70",
|
|
881
|
+
outline: "border-white/20 text-white/80",
|
|
882
|
+
destructive: "border-transparent bg-red-500/20 text-red-400"
|
|
1224
883
|
}
|
|
1225
884
|
},
|
|
1226
885
|
defaultVariants: {
|
|
@@ -1231,7 +890,7 @@ var badgeVariants = cva(
|
|
|
1231
890
|
function Badge({ className, variant, ...props }) {
|
|
1232
891
|
return /* @__PURE__ */ jsx("div", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
1233
892
|
}
|
|
1234
|
-
var ScrollArea =
|
|
893
|
+
var ScrollArea = React17.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
1235
894
|
ScrollAreaPrimitive.Root,
|
|
1236
895
|
{
|
|
1237
896
|
ref,
|
|
@@ -1245,7 +904,7 @@ var ScrollArea = React3.forwardRef(({ className, children, ...props }, ref) => /
|
|
|
1245
904
|
}
|
|
1246
905
|
));
|
|
1247
906
|
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
|
|
1248
|
-
var ScrollBar =
|
|
907
|
+
var ScrollBar = React17.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1249
908
|
ScrollAreaPrimitive.ScrollAreaScrollbar,
|
|
1250
909
|
{
|
|
1251
910
|
ref,
|
|
@@ -1383,36 +1042,36 @@ var StoredPaymentMethods = ({
|
|
|
1383
1042
|
] });
|
|
1384
1043
|
};
|
|
1385
1044
|
var Tabs = TabsPrimitive.Root;
|
|
1386
|
-
var TabsList =
|
|
1045
|
+
var TabsList = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1387
1046
|
TabsPrimitive.List,
|
|
1388
1047
|
{
|
|
1389
1048
|
ref,
|
|
1390
1049
|
className: cn(
|
|
1391
|
-
"inline-flex h-10 items-center justify-center rounded-md bg-
|
|
1050
|
+
"inline-flex h-10 items-center justify-center rounded-md bg-white/5 border border-white/10 p-1 text-white/60",
|
|
1392
1051
|
className
|
|
1393
1052
|
),
|
|
1394
1053
|
...props
|
|
1395
1054
|
}
|
|
1396
1055
|
));
|
|
1397
1056
|
TabsList.displayName = TabsPrimitive.List.displayName;
|
|
1398
|
-
var TabsTrigger =
|
|
1057
|
+
var TabsTrigger = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1399
1058
|
TabsPrimitive.Trigger,
|
|
1400
1059
|
{
|
|
1401
1060
|
ref,
|
|
1402
1061
|
className: cn(
|
|
1403
|
-
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium
|
|
1062
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/20 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-white/10 data-[state=active]:text-white data-[state=active]:shadow",
|
|
1404
1063
|
className
|
|
1405
1064
|
),
|
|
1406
1065
|
...props
|
|
1407
1066
|
}
|
|
1408
1067
|
));
|
|
1409
1068
|
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
|
1410
|
-
var TabsContent =
|
|
1069
|
+
var TabsContent = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1411
1070
|
TabsPrimitive.Content,
|
|
1412
1071
|
{
|
|
1413
1072
|
ref,
|
|
1414
1073
|
className: cn(
|
|
1415
|
-
"mt-2
|
|
1074
|
+
"mt-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/20",
|
|
1416
1075
|
className
|
|
1417
1076
|
),
|
|
1418
1077
|
...props
|
|
@@ -1447,372 +1106,9 @@ var usePaymentNotifications = () => {
|
|
|
1447
1106
|
notifyError
|
|
1448
1107
|
};
|
|
1449
1108
|
};
|
|
1450
|
-
var useSolanaService = () => {
|
|
1451
|
-
const { services } = usePaymentContext();
|
|
1452
|
-
return useMemo(() => services.solanaPayments, [services]);
|
|
1453
|
-
};
|
|
1454
|
-
var getSolBalance = async (connection, publicKey) => {
|
|
1455
|
-
try {
|
|
1456
|
-
const lamports = await connection.getBalance(publicKey);
|
|
1457
|
-
return lamports / LAMPORTS_PER_SOL;
|
|
1458
|
-
} catch (error) {
|
|
1459
|
-
console.error("Failed to fetch SOL balance:", error);
|
|
1460
|
-
return 0;
|
|
1461
|
-
}
|
|
1462
|
-
};
|
|
1463
|
-
var fetchAllTokenBalances = async (connection, publicKey) => {
|
|
1464
|
-
const balances = /* @__PURE__ */ new Map();
|
|
1465
|
-
try {
|
|
1466
|
-
const tokenAccounts = await connection.getParsedProgramAccounts(
|
|
1467
|
-
TOKEN_PROGRAM_ID,
|
|
1468
|
-
{
|
|
1469
|
-
filters: [
|
|
1470
|
-
{
|
|
1471
|
-
dataSize: 165
|
|
1472
|
-
// Size of token account
|
|
1473
|
-
},
|
|
1474
|
-
{
|
|
1475
|
-
memcmp: {
|
|
1476
|
-
offset: 32,
|
|
1477
|
-
// Owner field offset
|
|
1478
|
-
bytes: publicKey.toString()
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
|
-
]
|
|
1482
|
-
}
|
|
1483
|
-
);
|
|
1484
|
-
for (const account of tokenAccounts) {
|
|
1485
|
-
const data = account.account.data;
|
|
1486
|
-
const parsed = data.parsed;
|
|
1487
|
-
const info = parsed?.info;
|
|
1488
|
-
if (info && info.tokenAmount) {
|
|
1489
|
-
const mintAddress = info.mint;
|
|
1490
|
-
const uiAmount = info.tokenAmount.uiAmount || 0;
|
|
1491
|
-
if (uiAmount > 0) {
|
|
1492
|
-
balances.set(mintAddress, uiAmount);
|
|
1493
|
-
}
|
|
1494
|
-
}
|
|
1495
|
-
}
|
|
1496
|
-
} catch (error) {
|
|
1497
|
-
console.error("Failed to fetch token balances:", error);
|
|
1498
|
-
}
|
|
1499
|
-
return balances;
|
|
1500
|
-
};
|
|
1501
|
-
var fetchSupportedTokenBalances = async (connection, publicKey, supportedTokens) => {
|
|
1502
|
-
const results = [];
|
|
1503
|
-
const solBalance = await getSolBalance(connection, publicKey);
|
|
1504
|
-
const solTokenMeta = supportedTokens.find((token) => token.is_native || token.symbol === "SOL") || {
|
|
1505
|
-
symbol: "SOL",
|
|
1506
|
-
name: "Solana",
|
|
1507
|
-
mint: "So11111111111111111111111111111111111111112",
|
|
1508
|
-
decimals: 9};
|
|
1509
|
-
results.push({
|
|
1510
|
-
mint: solTokenMeta.mint,
|
|
1511
|
-
symbol: solTokenMeta.symbol,
|
|
1512
|
-
name: solTokenMeta.name,
|
|
1513
|
-
balance: solBalance,
|
|
1514
|
-
uiBalance: solBalance.toFixed(solTokenMeta.decimals <= 4 ? solTokenMeta.decimals : 4),
|
|
1515
|
-
decimals: solTokenMeta.decimals,
|
|
1516
|
-
isNative: true
|
|
1517
|
-
});
|
|
1518
|
-
const tokenBalances = await fetchAllTokenBalances(connection, publicKey);
|
|
1519
|
-
const tokenMetaByMint = new Map(
|
|
1520
|
-
supportedTokens.filter((token) => !(token.is_native || token.symbol === "SOL")).map((token) => [token.mint, token])
|
|
1521
|
-
);
|
|
1522
|
-
for (const [mint, tokenMeta] of tokenMetaByMint.entries()) {
|
|
1523
|
-
const balance = tokenBalances.get(mint) || 0;
|
|
1524
|
-
results.push({
|
|
1525
|
-
mint,
|
|
1526
|
-
symbol: tokenMeta.symbol,
|
|
1527
|
-
name: tokenMeta.name,
|
|
1528
|
-
balance,
|
|
1529
|
-
uiBalance: balance.toFixed(tokenMeta.decimals <= 6 ? tokenMeta.decimals : 6),
|
|
1530
|
-
decimals: tokenMeta.decimals,
|
|
1531
|
-
isNative: false
|
|
1532
|
-
});
|
|
1533
|
-
}
|
|
1534
|
-
return results;
|
|
1535
|
-
};
|
|
1536
|
-
var hasSufficientBalance = (balance, requiredAmount, buffer = 0.05) => {
|
|
1537
|
-
const requiredWithBuffer = requiredAmount * (1 + buffer);
|
|
1538
|
-
return balance >= requiredWithBuffer;
|
|
1539
|
-
};
|
|
1540
|
-
|
|
1541
|
-
// src/hooks/useSolanaDirectPayment.ts
|
|
1542
|
-
var useSolanaDirectPayment = (options) => {
|
|
1543
|
-
const { priceId, tokenAmount, selectedToken, supportedTokens, onStart, onConfirming, onSuccess, onError } = options;
|
|
1544
|
-
const { connected, publicKey, wallet, signTransaction } = useWallet();
|
|
1545
|
-
const { config } = usePaymentContext();
|
|
1546
|
-
const solanaService = useSolanaService();
|
|
1547
|
-
const [tokenBalance, setTokenBalance] = useState(null);
|
|
1548
|
-
const [isBalanceLoading, setIsBalanceLoading] = useState(false);
|
|
1549
|
-
const [isProcessing, setIsProcessing] = useState(false);
|
|
1550
|
-
const connection = useMemo(() => {
|
|
1551
|
-
const rpc = config.solanaRpcUrl ?? "https://api.mainnet-beta.solana.com";
|
|
1552
|
-
return new Connection(rpc);
|
|
1553
|
-
}, [config.solanaRpcUrl]);
|
|
1554
|
-
const fetchTokenBalance = useCallback(async () => {
|
|
1555
|
-
if (!connected || !publicKey || !selectedToken) {
|
|
1556
|
-
setTokenBalance({ balance: 0, hasBalance: false });
|
|
1557
|
-
return;
|
|
1558
|
-
}
|
|
1559
|
-
try {
|
|
1560
|
-
setIsBalanceLoading(true);
|
|
1561
|
-
const balances = await fetchSupportedTokenBalances(
|
|
1562
|
-
connection,
|
|
1563
|
-
publicKey,
|
|
1564
|
-
supportedTokens
|
|
1565
|
-
);
|
|
1566
|
-
const balanceInfo = balances.find(
|
|
1567
|
-
(tb) => tb.symbol === selectedToken.symbol || tb.mint === selectedToken.mint
|
|
1568
|
-
);
|
|
1569
|
-
const balance = balanceInfo?.balance || 0;
|
|
1570
|
-
const hasBalanceFlag = hasSufficientBalance(balance, tokenAmount);
|
|
1571
|
-
setTokenBalance({ balance, hasBalance: hasBalanceFlag });
|
|
1572
|
-
console.log("payments-ui: Solana wallet balance", {
|
|
1573
|
-
token: selectedToken.symbol,
|
|
1574
|
-
balance,
|
|
1575
|
-
required: tokenAmount
|
|
1576
|
-
});
|
|
1577
|
-
} catch (error) {
|
|
1578
|
-
console.error("Failed to fetch token balance:", {
|
|
1579
|
-
token: selectedToken?.symbol,
|
|
1580
|
-
error
|
|
1581
|
-
});
|
|
1582
|
-
setTokenBalance({ balance: 0, hasBalance: false });
|
|
1583
|
-
} finally {
|
|
1584
|
-
setIsBalanceLoading(false);
|
|
1585
|
-
}
|
|
1586
|
-
}, [connected, publicKey, connection, selectedToken, tokenAmount, supportedTokens]);
|
|
1587
|
-
useEffect(() => {
|
|
1588
|
-
if (connected && publicKey && selectedToken) {
|
|
1589
|
-
void fetchTokenBalance();
|
|
1590
|
-
}
|
|
1591
|
-
}, [connected, publicKey, selectedToken, tokenAmount, fetchTokenBalance]);
|
|
1592
|
-
const decodeTransaction = useCallback((serialized) => {
|
|
1593
|
-
const buffer = Buffer.from(serialized, "base64");
|
|
1594
|
-
try {
|
|
1595
|
-
return VersionedTransaction.deserialize(buffer);
|
|
1596
|
-
} catch (err) {
|
|
1597
|
-
try {
|
|
1598
|
-
return Transaction.from(buffer);
|
|
1599
|
-
} catch (legacyErr) {
|
|
1600
|
-
console.error("Failed to deserialize transaction", legacyErr);
|
|
1601
|
-
throw new Error("Invalid transaction payload received from server");
|
|
1602
|
-
}
|
|
1603
|
-
}
|
|
1604
|
-
}, []);
|
|
1605
|
-
const isVersionedTransaction = (tx) => {
|
|
1606
|
-
return !!tx && typeof tx === "object" && "version" in tx;
|
|
1607
|
-
};
|
|
1608
|
-
const signWithWallet = useCallback(
|
|
1609
|
-
async (tx) => {
|
|
1610
|
-
if (!wallet) {
|
|
1611
|
-
throw new Error("Wallet adapter is not available");
|
|
1612
|
-
}
|
|
1613
|
-
const adapter = wallet.adapter;
|
|
1614
|
-
if (isVersionedTransaction(tx)) {
|
|
1615
|
-
if (adapter.supportedTransactionVersions) {
|
|
1616
|
-
const supported = adapter.supportedTransactionVersions;
|
|
1617
|
-
if (!supported.has(tx.version)) {
|
|
1618
|
-
throw new Error("Connected wallet does not support this transaction version");
|
|
1619
|
-
}
|
|
1620
|
-
}
|
|
1621
|
-
if (adapter.signVersionedTransaction) {
|
|
1622
|
-
return adapter.signVersionedTransaction(tx);
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
if (adapter.signTransaction) {
|
|
1626
|
-
return adapter.signTransaction(tx);
|
|
1627
|
-
}
|
|
1628
|
-
if (signTransaction) {
|
|
1629
|
-
return signTransaction(tx);
|
|
1630
|
-
}
|
|
1631
|
-
throw new Error("Connected wallet cannot sign transactions");
|
|
1632
|
-
},
|
|
1633
|
-
[wallet, signTransaction]
|
|
1634
|
-
);
|
|
1635
|
-
const pay = useCallback(async () => {
|
|
1636
|
-
if (!connected || !publicKey) {
|
|
1637
|
-
onError("Wallet not connected");
|
|
1638
|
-
return;
|
|
1639
|
-
}
|
|
1640
|
-
if (!selectedToken) {
|
|
1641
|
-
onError("No payment token selected");
|
|
1642
|
-
return;
|
|
1643
|
-
}
|
|
1644
|
-
if (!tokenBalance?.hasBalance) {
|
|
1645
|
-
console.warn("payments-ui: insufficient balance for Solana direct payment", {
|
|
1646
|
-
token: selectedToken.symbol
|
|
1647
|
-
});
|
|
1648
|
-
onError("Insufficient balance for this token");
|
|
1649
|
-
return;
|
|
1650
|
-
}
|
|
1651
|
-
try {
|
|
1652
|
-
setIsProcessing(true);
|
|
1653
|
-
onStart();
|
|
1654
|
-
console.log("payments-ui: initiating Solana direct payment", {
|
|
1655
|
-
priceId,
|
|
1656
|
-
token: selectedToken.symbol
|
|
1657
|
-
});
|
|
1658
|
-
const paymentData = await solanaService.generatePayment(
|
|
1659
|
-
priceId,
|
|
1660
|
-
selectedToken.symbol,
|
|
1661
|
-
publicKey.toBase58()
|
|
1662
|
-
);
|
|
1663
|
-
console.log("payments-ui: Solana payment intent created", {
|
|
1664
|
-
intentId: paymentData.intent_id
|
|
1665
|
-
});
|
|
1666
|
-
const transactionToSign = decodeTransaction(paymentData.transaction);
|
|
1667
|
-
console.log("payments-ui: requesting Solana wallet signature");
|
|
1668
|
-
const signedTx = await signWithWallet(transactionToSign);
|
|
1669
|
-
const signedSerialized = Buffer.from(signedTx.serialize()).toString("base64");
|
|
1670
|
-
onConfirming();
|
|
1671
|
-
console.log("payments-ui: submitting signed Solana transaction");
|
|
1672
|
-
const result = await solanaService.submitPayment(
|
|
1673
|
-
signedSerialized,
|
|
1674
|
-
priceId,
|
|
1675
|
-
paymentData.intent_id,
|
|
1676
|
-
`Payment for subscription - ${selectedToken.symbol}`
|
|
1677
|
-
);
|
|
1678
|
-
console.log("payments-ui: Solana direct payment confirmed", {
|
|
1679
|
-
transactionId: result.transaction_id
|
|
1680
|
-
});
|
|
1681
|
-
onSuccess(result, result.transaction_id);
|
|
1682
|
-
} catch (err) {
|
|
1683
|
-
console.error("Solana direct payment failed:", {
|
|
1684
|
-
priceId,
|
|
1685
|
-
token: selectedToken.symbol,
|
|
1686
|
-
error: err
|
|
1687
|
-
});
|
|
1688
|
-
let errorMessage = "Payment failed. Please try again.";
|
|
1689
|
-
const message = err instanceof Error ? err.message : typeof err === "string" ? err : "";
|
|
1690
|
-
if (message.includes("User rejected")) {
|
|
1691
|
-
errorMessage = "Payment cancelled by user";
|
|
1692
|
-
} else if (/insufficient\s+funds/i.test(message)) {
|
|
1693
|
-
errorMessage = "Insufficient balance for this token";
|
|
1694
|
-
} else if (message) {
|
|
1695
|
-
errorMessage = message;
|
|
1696
|
-
}
|
|
1697
|
-
onError(errorMessage);
|
|
1698
|
-
} finally {
|
|
1699
|
-
setIsProcessing(false);
|
|
1700
|
-
}
|
|
1701
|
-
}, [
|
|
1702
|
-
connected,
|
|
1703
|
-
publicKey,
|
|
1704
|
-
selectedToken,
|
|
1705
|
-
tokenBalance?.hasBalance,
|
|
1706
|
-
onError,
|
|
1707
|
-
onStart,
|
|
1708
|
-
solanaService,
|
|
1709
|
-
priceId,
|
|
1710
|
-
decodeTransaction,
|
|
1711
|
-
signWithWallet,
|
|
1712
|
-
onConfirming,
|
|
1713
|
-
onSuccess
|
|
1714
|
-
]);
|
|
1715
|
-
const balanceLabel = tokenBalance ? `${tokenBalance.balance.toFixed(4)} ${selectedToken?.symbol ?? ""}` : "--";
|
|
1716
|
-
return {
|
|
1717
|
-
isBalanceLoading,
|
|
1718
|
-
isProcessing,
|
|
1719
|
-
balanceLabel,
|
|
1720
|
-
canPay: Boolean(
|
|
1721
|
-
connected && tokenBalance?.hasBalance && !isProcessing
|
|
1722
|
-
),
|
|
1723
|
-
pay
|
|
1724
|
-
};
|
|
1725
|
-
};
|
|
1726
|
-
var Card = React3.forwardRef(
|
|
1727
|
-
({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1728
|
-
"div",
|
|
1729
|
-
{
|
|
1730
|
-
ref,
|
|
1731
|
-
className: cn("rounded-xl border bg-card text-card-foreground shadow", className),
|
|
1732
|
-
...props
|
|
1733
|
-
}
|
|
1734
|
-
)
|
|
1735
|
-
);
|
|
1736
|
-
Card.displayName = "Card";
|
|
1737
|
-
var CardHeader = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1738
|
-
"div",
|
|
1739
|
-
{
|
|
1740
|
-
ref,
|
|
1741
|
-
className: cn("flex flex-col space-y-1.5 p-6", className),
|
|
1742
|
-
...props
|
|
1743
|
-
}
|
|
1744
|
-
));
|
|
1745
|
-
CardHeader.displayName = "CardHeader";
|
|
1746
|
-
var CardTitle = React3.forwardRef(
|
|
1747
|
-
({ className, ...props }, ref) => /* @__PURE__ */ jsx("h3", { ref, className: cn("text-2xl font-semibold leading-none tracking-tight", className), ...props })
|
|
1748
|
-
);
|
|
1749
|
-
CardTitle.displayName = "CardTitle";
|
|
1750
|
-
var CardDescription = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("p", { ref, className: cn("text-sm text-muted-foreground", className), ...props }));
|
|
1751
|
-
CardDescription.displayName = "CardDescription";
|
|
1752
|
-
var CardContent = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
|
|
1753
|
-
CardContent.displayName = "CardContent";
|
|
1754
|
-
var CardFooter = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1755
|
-
"div",
|
|
1756
|
-
{
|
|
1757
|
-
ref,
|
|
1758
|
-
className: cn("flex items-center p-6 pt-0", className),
|
|
1759
|
-
...props
|
|
1760
|
-
}
|
|
1761
|
-
));
|
|
1762
|
-
CardFooter.displayName = "CardFooter";
|
|
1763
|
-
var DirectPayment = ({
|
|
1764
|
-
priceId,
|
|
1765
|
-
tokenAmount,
|
|
1766
|
-
selectedToken,
|
|
1767
|
-
supportedTokens,
|
|
1768
|
-
onPaymentStart,
|
|
1769
|
-
onPaymentConfirming,
|
|
1770
|
-
onPaymentSuccess,
|
|
1771
|
-
onPaymentError
|
|
1772
|
-
}) => {
|
|
1773
|
-
const { isBalanceLoading, balanceLabel, canPay, isProcessing, pay } = useSolanaDirectPayment({
|
|
1774
|
-
priceId,
|
|
1775
|
-
tokenAmount,
|
|
1776
|
-
selectedToken,
|
|
1777
|
-
supportedTokens,
|
|
1778
|
-
onStart: onPaymentStart,
|
|
1779
|
-
onConfirming: onPaymentConfirming,
|
|
1780
|
-
onSuccess: onPaymentSuccess,
|
|
1781
|
-
onError: onPaymentError
|
|
1782
|
-
});
|
|
1783
|
-
return /* @__PURE__ */ jsxs(Card, { className: "space-y-4 rounded-md border border-border/60 bg-background/80 shadow-none p-6", children: [
|
|
1784
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1785
|
-
/* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: [
|
|
1786
|
-
/* @__PURE__ */ jsx(Wallet, { className: "h-4 w-4" }),
|
|
1787
|
-
" Pay with connected wallet"
|
|
1788
|
-
] }),
|
|
1789
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Sign the transaction directly in your Solana wallet." })
|
|
1790
|
-
] }),
|
|
1791
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between rounded-lg border border-border/50 bg-muted/10 px-4 py-3 text-sm", children: [
|
|
1792
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Available balance" }),
|
|
1793
|
-
isBalanceLoading ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }) : /* @__PURE__ */ jsx("strong", { className: "text-foreground", children: balanceLabel })
|
|
1794
|
-
] }),
|
|
1795
|
-
/* @__PURE__ */ jsx(
|
|
1796
|
-
Button,
|
|
1797
|
-
{
|
|
1798
|
-
type: "button",
|
|
1799
|
-
className: "w-full",
|
|
1800
|
-
disabled: !canPay,
|
|
1801
|
-
onClick: pay,
|
|
1802
|
-
children: isProcessing ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1803
|
-
/* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
|
|
1804
|
-
" Processing\u2026"
|
|
1805
|
-
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1806
|
-
/* @__PURE__ */ jsx(Wallet, { className: "mr-2 h-4 w-4" }),
|
|
1807
|
-
" Pay with wallet"
|
|
1808
|
-
] })
|
|
1809
|
-
}
|
|
1810
|
-
)
|
|
1811
|
-
] });
|
|
1812
|
-
};
|
|
1813
1109
|
var useSolanaQrPayment = (options) => {
|
|
1814
1110
|
const { priceId, selectedToken, onSuccess, onError } = options;
|
|
1815
|
-
const
|
|
1111
|
+
const { client } = usePaymentContext();
|
|
1816
1112
|
const tokenSymbol = selectedToken?.symbol ?? null;
|
|
1817
1113
|
const onSuccessRef = useRef(onSuccess);
|
|
1818
1114
|
const onErrorRef = useRef(onError);
|
|
@@ -1874,14 +1170,16 @@ var useSolanaQrPayment = (options) => {
|
|
|
1874
1170
|
paymentId: status.payment_id,
|
|
1875
1171
|
intentId: status.intent_id
|
|
1876
1172
|
});
|
|
1877
|
-
|
|
1173
|
+
const paymentId = status.payment_id ?? void 0;
|
|
1174
|
+
const txId = status.transaction ?? void 0;
|
|
1175
|
+
onSuccessRef.current?.(paymentId, txId);
|
|
1878
1176
|
},
|
|
1879
1177
|
[clearTimers, resetState]
|
|
1880
1178
|
);
|
|
1881
1179
|
const pollStatus = useCallback(
|
|
1882
1180
|
async (reference) => {
|
|
1883
1181
|
try {
|
|
1884
|
-
const status = await
|
|
1182
|
+
const status = await client.getSolanaPayStatus(reference);
|
|
1885
1183
|
if (status.status === "confirmed") {
|
|
1886
1184
|
handleSuccess(status);
|
|
1887
1185
|
}
|
|
@@ -1895,7 +1193,7 @@ var useSolanaQrPayment = (options) => {
|
|
|
1895
1193
|
console.error("Failed to poll Solana Pay status:", err);
|
|
1896
1194
|
}
|
|
1897
1195
|
},
|
|
1898
|
-
[handleError, handleSuccess,
|
|
1196
|
+
[handleError, handleSuccess, client]
|
|
1899
1197
|
);
|
|
1900
1198
|
const startCountdown = useCallback(
|
|
1901
1199
|
(expiresAt, reference) => {
|
|
@@ -1944,10 +1242,10 @@ var useSolanaQrPayment = (options) => {
|
|
|
1944
1242
|
priceId,
|
|
1945
1243
|
token: tokenSymbol
|
|
1946
1244
|
});
|
|
1947
|
-
const nextIntent = await
|
|
1245
|
+
const nextIntent = await client.createSolanaPayIntent({
|
|
1948
1246
|
priceId,
|
|
1949
|
-
tokenSymbol
|
|
1950
|
-
);
|
|
1247
|
+
token: tokenSymbol
|
|
1248
|
+
});
|
|
1951
1249
|
if (cancelled) return;
|
|
1952
1250
|
setIntent(nextIntent);
|
|
1953
1251
|
setTimeRemaining(
|
|
@@ -1957,8 +1255,13 @@ var useSolanaQrPayment = (options) => {
|
|
|
1957
1255
|
console.log("[payments-ui] Solana Pay QR ready", {
|
|
1958
1256
|
reference: nextIntent.reference
|
|
1959
1257
|
});
|
|
1960
|
-
|
|
1961
|
-
|
|
1258
|
+
const reference = nextIntent.reference;
|
|
1259
|
+
if (typeof reference === "string" && reference.length > 0) {
|
|
1260
|
+
startCountdown(nextIntent.expires_at, reference);
|
|
1261
|
+
pollStatus(reference);
|
|
1262
|
+
} else {
|
|
1263
|
+
handleError("Payment reference missing from intent", true);
|
|
1264
|
+
}
|
|
1962
1265
|
} catch (err) {
|
|
1963
1266
|
if (cancelled) return;
|
|
1964
1267
|
console.error("Failed to generate Solana Pay QR intent:", err);
|
|
@@ -1982,7 +1285,7 @@ var useSolanaQrPayment = (options) => {
|
|
|
1982
1285
|
priceId,
|
|
1983
1286
|
renderQr,
|
|
1984
1287
|
resetState,
|
|
1985
|
-
|
|
1288
|
+
client,
|
|
1986
1289
|
startCountdown,
|
|
1987
1290
|
tokenSymbol,
|
|
1988
1291
|
refreshNonce
|
|
@@ -1999,17 +1302,65 @@ var useSolanaQrPayment = (options) => {
|
|
|
1999
1302
|
refresh
|
|
2000
1303
|
};
|
|
2001
1304
|
};
|
|
1305
|
+
var Card = React17.forwardRef(
|
|
1306
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1307
|
+
"div",
|
|
1308
|
+
{
|
|
1309
|
+
ref,
|
|
1310
|
+
className: cn("rounded-xl border border-white/10 bg-[#161b22] text-white shadow", className),
|
|
1311
|
+
...props
|
|
1312
|
+
}
|
|
1313
|
+
)
|
|
1314
|
+
);
|
|
1315
|
+
Card.displayName = "Card";
|
|
1316
|
+
var CardHeader = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1317
|
+
"div",
|
|
1318
|
+
{
|
|
1319
|
+
ref,
|
|
1320
|
+
className: cn("flex flex-col space-y-1.5 p-6", className),
|
|
1321
|
+
...props
|
|
1322
|
+
}
|
|
1323
|
+
));
|
|
1324
|
+
CardHeader.displayName = "CardHeader";
|
|
1325
|
+
var CardTitle = React17.forwardRef(
|
|
1326
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx("h3", { ref, className: cn("text-xl font-semibold leading-none tracking-tight text-white", className), ...props })
|
|
1327
|
+
);
|
|
1328
|
+
CardTitle.displayName = "CardTitle";
|
|
1329
|
+
var CardDescription = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("p", { ref, className: cn("text-sm text-white/60", className), ...props }));
|
|
1330
|
+
CardDescription.displayName = "CardDescription";
|
|
1331
|
+
var CardContent = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
|
|
1332
|
+
CardContent.displayName = "CardContent";
|
|
1333
|
+
var CardFooter = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1334
|
+
"div",
|
|
1335
|
+
{
|
|
1336
|
+
ref,
|
|
1337
|
+
className: cn("flex items-center p-6 pt-0", className),
|
|
1338
|
+
...props
|
|
1339
|
+
}
|
|
1340
|
+
));
|
|
1341
|
+
CardFooter.displayName = "CardFooter";
|
|
2002
1342
|
var QRCodePayment = ({
|
|
2003
1343
|
priceId,
|
|
2004
1344
|
selectedToken,
|
|
2005
1345
|
onPaymentError,
|
|
2006
1346
|
onPaymentSuccess
|
|
2007
1347
|
}) => {
|
|
1348
|
+
const handleQrSuccess = React17__default.useCallback(
|
|
1349
|
+
(paymentId, txId) => {
|
|
1350
|
+
if (!paymentId && !txId) {
|
|
1351
|
+
onPaymentError("Missing payment confirmation details");
|
|
1352
|
+
return;
|
|
1353
|
+
}
|
|
1354
|
+
const resolvedResult = paymentId ?? txId ?? "";
|
|
1355
|
+
onPaymentSuccess(resolvedResult, txId);
|
|
1356
|
+
},
|
|
1357
|
+
[onPaymentError, onPaymentSuccess]
|
|
1358
|
+
);
|
|
2008
1359
|
const { intent, qrDataUri, isLoading, error, timeRemaining, refresh } = useSolanaQrPayment({
|
|
2009
1360
|
priceId,
|
|
2010
1361
|
selectedToken,
|
|
2011
1362
|
onError: onPaymentError,
|
|
2012
|
-
onSuccess:
|
|
1363
|
+
onSuccess: handleQrSuccess
|
|
2013
1364
|
});
|
|
2014
1365
|
if (!selectedToken) {
|
|
2015
1366
|
return /* @__PURE__ */ jsx("div", { className: "rounded-md border border-dashed border-border/60 bg-muted/10 px-4 py-6 text-center text-sm text-muted-foreground", children: "Select a token to continue." });
|
|
@@ -2102,7 +1453,7 @@ var PaymentStatus = ({
|
|
|
2102
1453
|
return null;
|
|
2103
1454
|
};
|
|
2104
1455
|
var useSupportedTokens = () => {
|
|
2105
|
-
const
|
|
1456
|
+
const { client } = usePaymentContext();
|
|
2106
1457
|
const [tokens, setTokens] = useState([]);
|
|
2107
1458
|
const [isLoading, setIsLoading] = useState(false);
|
|
2108
1459
|
const [error, setError] = useState(null);
|
|
@@ -2120,7 +1471,7 @@ var useSupportedTokens = () => {
|
|
|
2120
1471
|
setError(null);
|
|
2121
1472
|
try {
|
|
2122
1473
|
console.log("payments-ui: fetching supported Solana tokens");
|
|
2123
|
-
const tokens2 = await
|
|
1474
|
+
const tokens2 = await client.getSolanaTokens();
|
|
2124
1475
|
const sortedTokens = [...tokens2].sort(
|
|
2125
1476
|
(a, b) => a.symbol.localeCompare(b.symbol)
|
|
2126
1477
|
);
|
|
@@ -2138,7 +1489,7 @@ var useSupportedTokens = () => {
|
|
|
2138
1489
|
} finally {
|
|
2139
1490
|
setIsLoading(false);
|
|
2140
1491
|
}
|
|
2141
|
-
}, [
|
|
1492
|
+
}, [client]);
|
|
2142
1493
|
useEffect(() => {
|
|
2143
1494
|
if (tokens.length === 0) {
|
|
2144
1495
|
fetchSupportedTokens();
|
|
@@ -2250,9 +1601,7 @@ var SolanaPaymentView = ({
|
|
|
2250
1601
|
onError,
|
|
2251
1602
|
onClose
|
|
2252
1603
|
}) => {
|
|
2253
|
-
const { connected } = useWallet();
|
|
2254
1604
|
const { notifyStatus, notifyError, notifySuccess } = usePaymentNotifications();
|
|
2255
|
-
const [activeTab, setActiveTab] = useState("wallet");
|
|
2256
1605
|
const [paymentState, setPaymentState] = useState("selecting");
|
|
2257
1606
|
const [errorMessage, setErrorMessage] = useState(null);
|
|
2258
1607
|
const [transactionId, setTransactionId] = useState(null);
|
|
@@ -2274,14 +1623,6 @@ var SolanaPaymentView = ({
|
|
|
2274
1623
|
setSelectedTokenSymbol(defaultToken.symbol);
|
|
2275
1624
|
}
|
|
2276
1625
|
}, [tokens, selectedTokenSymbol]);
|
|
2277
|
-
const handlePaymentStart = useCallback(() => {
|
|
2278
|
-
setPaymentState("processing");
|
|
2279
|
-
setErrorMessage(null);
|
|
2280
|
-
notifyStatus("processing", { source: "solana" });
|
|
2281
|
-
}, [notifyStatus]);
|
|
2282
|
-
const handlePaymentConfirming = useCallback(() => {
|
|
2283
|
-
setPaymentState("confirming");
|
|
2284
|
-
}, []);
|
|
2285
1626
|
const handlePaymentSuccess = useCallback(
|
|
2286
1627
|
(result, txId) => {
|
|
2287
1628
|
const resolvedTx = txId || (typeof result === "string" ? result : result.transaction_id);
|
|
@@ -2350,16 +1691,6 @@ var SolanaPaymentView = ({
|
|
|
2350
1691
|
const handleTokenChange = useCallback((value) => {
|
|
2351
1692
|
setSelectedTokenSymbol(value);
|
|
2352
1693
|
}, []);
|
|
2353
|
-
const wasConnectedRef = useRef(connected);
|
|
2354
|
-
useEffect(() => {
|
|
2355
|
-
if (connected && !wasConnectedRef.current) {
|
|
2356
|
-
setActiveTab("wallet");
|
|
2357
|
-
}
|
|
2358
|
-
if (!connected && wasConnectedRef.current) {
|
|
2359
|
-
setActiveTab("qr");
|
|
2360
|
-
}
|
|
2361
|
-
wasConnectedRef.current = connected;
|
|
2362
|
-
}, [connected]);
|
|
2363
1694
|
const renderBody = () => {
|
|
2364
1695
|
if (paymentState !== "selecting") {
|
|
2365
1696
|
return /* @__PURE__ */ jsx(
|
|
@@ -2414,51 +1745,15 @@ var SolanaPaymentView = ({
|
|
|
2414
1745
|
] }, token.symbol)) })
|
|
2415
1746
|
] })
|
|
2416
1747
|
] }),
|
|
2417
|
-
/* @__PURE__ */ jsx(
|
|
2418
|
-
|
|
1748
|
+
/* @__PURE__ */ jsx(
|
|
1749
|
+
QRCodePayment,
|
|
2419
1750
|
{
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
/* @__PURE__ */ jsxs(TabsList, { className: "grid w-full grid-cols-2 bg-muted/10", children: [
|
|
2425
|
-
/* @__PURE__ */ jsxs(TabsTrigger, { value: "wallet", disabled: !connected, children: [
|
|
2426
|
-
/* @__PURE__ */ jsx(Wallet, { className: "mr-2 h-4 w-4" }),
|
|
2427
|
-
" Wallet"
|
|
2428
|
-
] }),
|
|
2429
|
-
/* @__PURE__ */ jsxs(TabsTrigger, { value: "qr", children: [
|
|
2430
|
-
/* @__PURE__ */ jsx(CreditCard, { className: "mr-2 h-4 w-4" }),
|
|
2431
|
-
" QR Code"
|
|
2432
|
-
] })
|
|
2433
|
-
] }),
|
|
2434
|
-
/* @__PURE__ */ jsxs(TabsContent, { value: "wallet", className: "space-y-4", children: [
|
|
2435
|
-
activeTab === "wallet" && /* @__PURE__ */ jsx(
|
|
2436
|
-
DirectPayment,
|
|
2437
|
-
{
|
|
2438
|
-
priceId,
|
|
2439
|
-
tokenAmount,
|
|
2440
|
-
selectedToken,
|
|
2441
|
-
supportedTokens: tokens,
|
|
2442
|
-
onPaymentStart: handlePaymentStart,
|
|
2443
|
-
onPaymentConfirming: handlePaymentConfirming,
|
|
2444
|
-
onPaymentSuccess: handlePaymentSuccess,
|
|
2445
|
-
onPaymentError: handlePaymentError
|
|
2446
|
-
}
|
|
2447
|
-
),
|
|
2448
|
-
!connected && /* @__PURE__ */ jsx("div", { className: "text-sm text-amber-100", children: "Connect your Solana wallet to continue or switch to QR mode." })
|
|
2449
|
-
] }),
|
|
2450
|
-
/* @__PURE__ */ jsx(TabsContent, { value: "qr", children: activeTab === "qr" && /* @__PURE__ */ jsx(
|
|
2451
|
-
QRCodePayment,
|
|
2452
|
-
{
|
|
2453
|
-
priceId,
|
|
2454
|
-
selectedToken,
|
|
2455
|
-
onPaymentError: handlePaymentError,
|
|
2456
|
-
onPaymentSuccess: handlePaymentSuccess
|
|
2457
|
-
}
|
|
2458
|
-
) })
|
|
2459
|
-
]
|
|
1751
|
+
priceId,
|
|
1752
|
+
selectedToken,
|
|
1753
|
+
onPaymentError: handlePaymentError,
|
|
1754
|
+
onPaymentSuccess: handlePaymentSuccess
|
|
2460
1755
|
}
|
|
2461
|
-
)
|
|
1756
|
+
)
|
|
2462
1757
|
] });
|
|
2463
1758
|
};
|
|
2464
1759
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
@@ -2466,7 +1761,7 @@ var SolanaPaymentView = ({
|
|
|
2466
1761
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
2467
1762
|
/* @__PURE__ */ jsx("p", { className: "text-xs uppercase tracking-wide text-muted-foreground", children: "Solana Pay checkout" }),
|
|
2468
1763
|
/* @__PURE__ */ jsx("p", { className: "text-2xl font-semibold text-foreground", children: "Pay with Solana" }),
|
|
2469
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Choose a supported token and send the payment
|
|
1764
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Choose a supported token and send the payment via Solana Pay QR code." })
|
|
2470
1765
|
] }),
|
|
2471
1766
|
onClose && /* @__PURE__ */ jsxs(
|
|
2472
1767
|
Button,
|
|
@@ -2487,176 +1782,31 @@ var SolanaPaymentView = ({
|
|
|
2487
1782
|
renderBody()
|
|
2488
1783
|
] });
|
|
2489
1784
|
};
|
|
2490
|
-
var
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
paymentToken,
|
|
2509
|
-
processor,
|
|
2510
|
-
provider,
|
|
2511
|
-
email: billing.email,
|
|
2512
|
-
firstName: billing.firstName,
|
|
2513
|
-
lastName: billing.lastName,
|
|
2514
|
-
address1: billing.address1,
|
|
2515
|
-
city: billing.city,
|
|
2516
|
-
state: billing.stateRegion,
|
|
2517
|
-
zipCode: billing.postalCode,
|
|
2518
|
-
country: billing.country
|
|
2519
|
-
};
|
|
2520
|
-
return services.subscriptions.subscribe("nmi", payload);
|
|
2521
|
-
},
|
|
2522
|
-
[services]
|
|
2523
|
-
);
|
|
2524
|
-
const subscribeWithSavedMethod = useCallback(
|
|
2525
|
-
async ({
|
|
2526
|
-
priceId,
|
|
2527
|
-
processor = "nmi",
|
|
2528
|
-
provider,
|
|
2529
|
-
paymentMethodId,
|
|
2530
|
-
email
|
|
2531
|
-
}) => {
|
|
2532
|
-
const payload = {
|
|
2533
|
-
priceId: ensurePrice(priceId),
|
|
2534
|
-
paymentMethodId,
|
|
2535
|
-
processor,
|
|
2536
|
-
provider,
|
|
2537
|
-
email
|
|
2538
|
-
};
|
|
2539
|
-
return services.subscriptions.subscribe("nmi", payload);
|
|
2540
|
-
},
|
|
2541
|
-
[services]
|
|
2542
|
-
);
|
|
2543
|
-
const subscribeWithCCBill = useCallback(
|
|
2544
|
-
async ({
|
|
2545
|
-
priceId,
|
|
2546
|
-
email,
|
|
2547
|
-
firstName,
|
|
2548
|
-
lastName,
|
|
2549
|
-
zipCode,
|
|
2550
|
-
country,
|
|
2551
|
-
processor = "ccbill"
|
|
2552
|
-
}) => {
|
|
2553
|
-
const payload = {
|
|
2554
|
-
priceId: ensurePrice(priceId),
|
|
2555
|
-
email,
|
|
2556
|
-
firstName,
|
|
2557
|
-
lastName,
|
|
2558
|
-
zipCode,
|
|
2559
|
-
country,
|
|
2560
|
-
processor
|
|
2561
|
-
};
|
|
2562
|
-
return services.subscriptions.subscribe("ccbill", payload);
|
|
2563
|
-
},
|
|
2564
|
-
[services]
|
|
2565
|
-
);
|
|
2566
|
-
const generateFlexFormUrl = useCallback(
|
|
2567
|
-
async ({
|
|
2568
|
-
priceId,
|
|
2569
|
-
firstName,
|
|
2570
|
-
lastName,
|
|
2571
|
-
address1,
|
|
2572
|
-
city,
|
|
2573
|
-
state,
|
|
2574
|
-
zipCode,
|
|
2575
|
-
country
|
|
2576
|
-
}) => {
|
|
2577
|
-
const payload = {
|
|
2578
|
-
price_id: ensurePrice(priceId),
|
|
2579
|
-
first_name: firstName,
|
|
2580
|
-
last_name: lastName,
|
|
2581
|
-
address1,
|
|
2582
|
-
city,
|
|
2583
|
-
state,
|
|
2584
|
-
zip_code: zipCode,
|
|
2585
|
-
country
|
|
2586
|
-
};
|
|
2587
|
-
return services.subscriptions.generateFlexFormUrl(payload);
|
|
2588
|
-
},
|
|
2589
|
-
[services]
|
|
2590
|
-
);
|
|
2591
|
-
return {
|
|
2592
|
-
subscribeWithCard,
|
|
2593
|
-
subscribeWithSavedMethod,
|
|
2594
|
-
subscribeWithCCBill,
|
|
2595
|
-
generateFlexFormUrl
|
|
2596
|
-
};
|
|
2597
|
-
};
|
|
2598
|
-
|
|
2599
|
-
// src/hooks/useAlternativePaymentProvider.ts
|
|
2600
|
-
var useAlternativePaymentProvider = () => {
|
|
2601
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
2602
|
-
const [error, setError] = useState(null);
|
|
2603
|
-
const { generateFlexFormUrl } = useSubscriptionActions();
|
|
2604
|
-
const openFlexForm = useCallback(
|
|
2605
|
-
async (payload) => {
|
|
2606
|
-
setIsLoading(true);
|
|
2607
|
-
setError(null);
|
|
2608
|
-
try {
|
|
2609
|
-
const response = await generateFlexFormUrl(payload);
|
|
2610
|
-
if (response?.iframe_url) {
|
|
2611
|
-
window.location.href = response.iframe_url;
|
|
2612
|
-
} else {
|
|
2613
|
-
throw new Error("Unable to launch payment provider.");
|
|
2614
|
-
}
|
|
2615
|
-
} catch (err) {
|
|
2616
|
-
const message = err instanceof Error ? err.message : "Failed to open payment provider.";
|
|
2617
|
-
setError(message);
|
|
2618
|
-
console.error("[payments-ui] failed to open alternative payment provider", err);
|
|
2619
|
-
} finally {
|
|
2620
|
-
setIsLoading(false);
|
|
2621
|
-
}
|
|
2622
|
-
},
|
|
2623
|
-
[generateFlexFormUrl]
|
|
2624
|
-
);
|
|
2625
|
-
return { openFlexForm, isLoading, error };
|
|
2626
|
-
};
|
|
2627
|
-
var PaymentExperience = ({
|
|
2628
|
-
priceId,
|
|
2629
|
-
usdAmount,
|
|
2630
|
-
onNewCardPayment,
|
|
2631
|
-
onSavedMethodPayment,
|
|
2632
|
-
enableNewCard = true,
|
|
2633
|
-
enableStoredMethods = true,
|
|
2634
|
-
enableSolanaPay = true,
|
|
2635
|
-
enableAlternativePayments = true,
|
|
2636
|
-
onSolanaSuccess,
|
|
2637
|
-
onSolanaError,
|
|
2638
|
-
initialMode = "cards"
|
|
2639
|
-
}) => {
|
|
2640
|
-
const showNewCard = enableNewCard && Boolean(onNewCardPayment);
|
|
2641
|
-
const showStored = enableStoredMethods && Boolean(onSavedMethodPayment);
|
|
2642
|
-
const defaultTab = showStored ? "saved" : "new";
|
|
2643
|
-
const [activeTab, setActiveTab] = useState(defaultTab);
|
|
2644
|
-
const [mode, setMode] = useState(
|
|
2645
|
-
() => initialMode === "solana" && enableSolanaPay ? "solana" : "cards"
|
|
1785
|
+
var PaymentExperience = ({
|
|
1786
|
+
priceId,
|
|
1787
|
+
usdAmount,
|
|
1788
|
+
onNewCardPayment,
|
|
1789
|
+
onSavedMethodPayment,
|
|
1790
|
+
enableNewCard = true,
|
|
1791
|
+
enableStoredMethods = true,
|
|
1792
|
+
enableSolanaPay = true,
|
|
1793
|
+
onSolanaSuccess,
|
|
1794
|
+
onSolanaError,
|
|
1795
|
+
initialMode = "cards"
|
|
1796
|
+
}) => {
|
|
1797
|
+
const showNewCard = enableNewCard && Boolean(onNewCardPayment);
|
|
1798
|
+
const showStored = enableStoredMethods && Boolean(onSavedMethodPayment);
|
|
1799
|
+
const defaultTab = showStored ? "saved" : "new";
|
|
1800
|
+
const [activeTab, setActiveTab] = useState(defaultTab);
|
|
1801
|
+
const [mode, setMode] = useState(
|
|
1802
|
+
() => initialMode === "solana" && enableSolanaPay ? "solana" : "cards"
|
|
2646
1803
|
);
|
|
2647
1804
|
const [selectedMethodId, setSelectedMethodId] = useState(null);
|
|
2648
1805
|
const [savedStatus, setSavedStatus] = useState("idle");
|
|
2649
1806
|
const [savedError, setSavedError] = useState(null);
|
|
2650
1807
|
const [newCardStatus, setNewCardStatus] = useState("idle");
|
|
2651
1808
|
const [newCardError, setNewCardError] = useState(null);
|
|
2652
|
-
const [billingDetails, setBillingDetails] = useState(null);
|
|
2653
|
-
const [alternativePaymentError, setAlternativePaymentError] = useState(null);
|
|
2654
1809
|
const { notifyStatus, notifySuccess, notifyError } = usePaymentNotifications();
|
|
2655
|
-
const {
|
|
2656
|
-
openFlexForm,
|
|
2657
|
-
isLoading: flexFormLoading,
|
|
2658
|
-
error: flexFormError
|
|
2659
|
-
} = useAlternativePaymentProvider();
|
|
2660
1810
|
useEffect(() => {
|
|
2661
1811
|
setActiveTab(showStored ? "saved" : "new");
|
|
2662
1812
|
}, [showStored]);
|
|
@@ -2736,48 +1886,6 @@ var PaymentExperience = ({
|
|
|
2736
1886
|
},
|
|
2737
1887
|
[onSolanaError]
|
|
2738
1888
|
);
|
|
2739
|
-
const handleAlternativePayment = useCallback(() => {
|
|
2740
|
-
if (!enableAlternativePayments || !priceId) {
|
|
2741
|
-
return;
|
|
2742
|
-
}
|
|
2743
|
-
if (!billingDetails) {
|
|
2744
|
-
setAlternativePaymentError("Enter your billing details first.");
|
|
2745
|
-
return;
|
|
2746
|
-
}
|
|
2747
|
-
const requiredFields = [
|
|
2748
|
-
"firstName",
|
|
2749
|
-
"lastName",
|
|
2750
|
-
"address1",
|
|
2751
|
-
"city",
|
|
2752
|
-
"stateRegion",
|
|
2753
|
-
"postalCode",
|
|
2754
|
-
"country"
|
|
2755
|
-
];
|
|
2756
|
-
const missingField = requiredFields.find((field) => {
|
|
2757
|
-
const value = billingDetails[field];
|
|
2758
|
-
return typeof value !== "string" || value.trim().length === 0;
|
|
2759
|
-
});
|
|
2760
|
-
if (missingField) {
|
|
2761
|
-
setAlternativePaymentError("Please complete your billing address before continuing.");
|
|
2762
|
-
return;
|
|
2763
|
-
}
|
|
2764
|
-
setAlternativePaymentError(null);
|
|
2765
|
-
openFlexForm({
|
|
2766
|
-
priceId,
|
|
2767
|
-
firstName: billingDetails.firstName,
|
|
2768
|
-
lastName: billingDetails.lastName,
|
|
2769
|
-
address1: billingDetails.address1,
|
|
2770
|
-
city: billingDetails.city,
|
|
2771
|
-
state: billingDetails.stateRegion ?? "",
|
|
2772
|
-
zipCode: billingDetails.postalCode,
|
|
2773
|
-
country: billingDetails.country
|
|
2774
|
-
});
|
|
2775
|
-
}, [
|
|
2776
|
-
billingDetails,
|
|
2777
|
-
enableAlternativePayments,
|
|
2778
|
-
openFlexForm,
|
|
2779
|
-
priceId
|
|
2780
|
-
]);
|
|
2781
1889
|
const renderSavedTab = () => {
|
|
2782
1890
|
if (!showStored) {
|
|
2783
1891
|
return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Saved payment methods are unavailable right now. Add a new card to get started." });
|
|
@@ -2816,8 +1924,7 @@ var PaymentExperience = ({
|
|
|
2816
1924
|
submitLabel: "Pay now",
|
|
2817
1925
|
externalError: newCardError,
|
|
2818
1926
|
onTokenize: handleNewCardTokenize,
|
|
2819
|
-
submitting: newCardStatus === "processing"
|
|
2820
|
-
onBillingChange: setBillingDetails
|
|
1927
|
+
submitting: newCardStatus === "processing"
|
|
2821
1928
|
}
|
|
2822
1929
|
);
|
|
2823
1930
|
};
|
|
@@ -2845,19 +1952,6 @@ var PaymentExperience = ({
|
|
|
2845
1952
|
] });
|
|
2846
1953
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-6 pt-4", children: [
|
|
2847
1954
|
mode === "cards" && renderCardExperience(),
|
|
2848
|
-
mode === "cards" && enableAlternativePayments && /* @__PURE__ */ jsxs("div", { className: "space-y-2 text-center text-sm text-muted-foreground", children: [
|
|
2849
|
-
/* @__PURE__ */ jsx(
|
|
2850
|
-
"button",
|
|
2851
|
-
{
|
|
2852
|
-
type: "button",
|
|
2853
|
-
className: "text-primary underline-offset-4 hover:underline disabled:opacity-60",
|
|
2854
|
-
onClick: handleAlternativePayment,
|
|
2855
|
-
disabled: flexFormLoading || !priceId,
|
|
2856
|
-
children: flexFormLoading ? "Preparing alternative checkout\u2026" : "Prefer a different processor? Pay with CCBill"
|
|
2857
|
-
}
|
|
2858
|
-
),
|
|
2859
|
-
(alternativePaymentError || flexFormError) && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: alternativePaymentError || flexFormError })
|
|
2860
|
-
] }),
|
|
2861
1955
|
mode === "solana" && enableSolanaPay && /* @__PURE__ */ jsx(
|
|
2862
1956
|
SolanaPaymentView,
|
|
2863
1957
|
{
|
|
@@ -2898,6 +1992,88 @@ var SubscriptionSuccessDialog = ({
|
|
|
2898
1992
|
/* @__PURE__ */ jsx("div", { className: "px-6 py-6", children: /* @__PURE__ */ jsx(Button, { className: "w-full", onClick: onClose, children: "Continue exploring" }) })
|
|
2899
1993
|
] }) });
|
|
2900
1994
|
};
|
|
1995
|
+
var useSubscriptionActions = () => {
|
|
1996
|
+
const { client } = usePaymentContext();
|
|
1997
|
+
const ensurePrice = (priceId) => {
|
|
1998
|
+
if (!priceId) {
|
|
1999
|
+
throw new Error("payments-ui: priceId is required for subscription actions");
|
|
2000
|
+
}
|
|
2001
|
+
return priceId;
|
|
2002
|
+
};
|
|
2003
|
+
const subscribeWithCard = useCallback(
|
|
2004
|
+
async ({
|
|
2005
|
+
priceId,
|
|
2006
|
+
processor = "nmi",
|
|
2007
|
+
provider,
|
|
2008
|
+
paymentToken,
|
|
2009
|
+
billing
|
|
2010
|
+
}) => {
|
|
2011
|
+
const payload = {
|
|
2012
|
+
price_id: ensurePrice(priceId),
|
|
2013
|
+
processor,
|
|
2014
|
+
provider,
|
|
2015
|
+
payment_token: paymentToken,
|
|
2016
|
+
email: billing.email,
|
|
2017
|
+
first_name: billing.firstName,
|
|
2018
|
+
last_name: billing.lastName,
|
|
2019
|
+
address1: billing.address1,
|
|
2020
|
+
city: billing.city,
|
|
2021
|
+
state: billing.stateRegion,
|
|
2022
|
+
zip: billing.postalCode,
|
|
2023
|
+
country: billing.country
|
|
2024
|
+
};
|
|
2025
|
+
return client.checkout(payload);
|
|
2026
|
+
},
|
|
2027
|
+
[client]
|
|
2028
|
+
);
|
|
2029
|
+
const subscribeWithSavedMethod = useCallback(
|
|
2030
|
+
async ({
|
|
2031
|
+
priceId,
|
|
2032
|
+
processor = "nmi",
|
|
2033
|
+
provider,
|
|
2034
|
+
paymentMethodId,
|
|
2035
|
+
email
|
|
2036
|
+
}) => {
|
|
2037
|
+
const payload = {
|
|
2038
|
+
price_id: ensurePrice(priceId),
|
|
2039
|
+
processor,
|
|
2040
|
+
provider,
|
|
2041
|
+
payment_method_id: paymentMethodId,
|
|
2042
|
+
email
|
|
2043
|
+
};
|
|
2044
|
+
return client.checkout(payload);
|
|
2045
|
+
},
|
|
2046
|
+
[client]
|
|
2047
|
+
);
|
|
2048
|
+
const subscribeWithCCBill = useCallback(
|
|
2049
|
+
async ({
|
|
2050
|
+
priceId,
|
|
2051
|
+
email,
|
|
2052
|
+
firstName,
|
|
2053
|
+
lastName,
|
|
2054
|
+
zipCode,
|
|
2055
|
+
country,
|
|
2056
|
+
processor = "ccbill"
|
|
2057
|
+
}) => {
|
|
2058
|
+
const payload = {
|
|
2059
|
+
price_id: ensurePrice(priceId),
|
|
2060
|
+
processor,
|
|
2061
|
+
email,
|
|
2062
|
+
first_name: firstName,
|
|
2063
|
+
last_name: lastName,
|
|
2064
|
+
zip: zipCode,
|
|
2065
|
+
country
|
|
2066
|
+
};
|
|
2067
|
+
return client.checkout(payload);
|
|
2068
|
+
},
|
|
2069
|
+
[client]
|
|
2070
|
+
);
|
|
2071
|
+
return {
|
|
2072
|
+
subscribeWithCard,
|
|
2073
|
+
subscribeWithSavedMethod,
|
|
2074
|
+
subscribeWithCCBill
|
|
2075
|
+
};
|
|
2076
|
+
};
|
|
2901
2077
|
var SubscriptionCheckoutModal = ({
|
|
2902
2078
|
open,
|
|
2903
2079
|
onOpenChange,
|
|
@@ -2934,22 +2110,32 @@ var SubscriptionCheckoutModal = ({
|
|
|
2934
2110
|
console.debug("[payments-ui] subscription success", result);
|
|
2935
2111
|
}
|
|
2936
2112
|
};
|
|
2113
|
+
const assertCheckoutSuccess = (status, message) => {
|
|
2114
|
+
if (status === "blocked") {
|
|
2115
|
+
throw new Error(message || "This subscription cannot be completed right now.");
|
|
2116
|
+
}
|
|
2117
|
+
if (status === "redirect_required") {
|
|
2118
|
+
throw new Error(message || "Additional action required in an alternate flow.");
|
|
2119
|
+
}
|
|
2120
|
+
};
|
|
2937
2121
|
const handleNewCardPayment = async ({ token, billing }) => {
|
|
2938
|
-
await subscribeWithCard({
|
|
2122
|
+
const response = await subscribeWithCard({
|
|
2939
2123
|
priceId: ensurePrice(),
|
|
2940
2124
|
provider,
|
|
2941
2125
|
paymentToken: token,
|
|
2942
2126
|
billing
|
|
2943
2127
|
});
|
|
2128
|
+
assertCheckoutSuccess(response.status, response.message);
|
|
2944
2129
|
notifySuccess();
|
|
2945
2130
|
};
|
|
2946
2131
|
const handleSavedMethodPayment = async ({ paymentMethodId }) => {
|
|
2947
|
-
await subscribeWithSavedMethod({
|
|
2132
|
+
const response = await subscribeWithSavedMethod({
|
|
2948
2133
|
priceId: ensurePrice(),
|
|
2949
2134
|
provider,
|
|
2950
2135
|
paymentMethodId,
|
|
2951
2136
|
email: userEmail ?? ""
|
|
2952
2137
|
});
|
|
2138
|
+
assertCheckoutSuccess(response.status, response.message);
|
|
2953
2139
|
notifySuccess();
|
|
2954
2140
|
};
|
|
2955
2141
|
const solanaSuccess = (result) => {
|
|
@@ -2961,27 +2147,33 @@ var SubscriptionCheckoutModal = ({
|
|
|
2961
2147
|
onSolanaError?.(error);
|
|
2962
2148
|
};
|
|
2963
2149
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2964
|
-
/* @__PURE__ */ jsx(Dialog, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsx(
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
"
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2150
|
+
/* @__PURE__ */ jsx(Dialog, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsx(
|
|
2151
|
+
DialogContent,
|
|
2152
|
+
{
|
|
2153
|
+
className: "max-w-3xl max-h-[90vh] overflow-y-auto p-0 [&::-webkit-scrollbar]:hidden",
|
|
2154
|
+
children: /* @__PURE__ */ jsxs("div", { className: "p-6 space-y-6", children: [
|
|
2155
|
+
!priceId && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: [
|
|
2156
|
+
/* @__PURE__ */ jsx(AlertCircle, { className: "h-4 w-4" }),
|
|
2157
|
+
" Select a subscription plan to continue."
|
|
2158
|
+
] }),
|
|
2159
|
+
/* @__PURE__ */ jsx(
|
|
2160
|
+
PaymentExperience,
|
|
2161
|
+
{
|
|
2162
|
+
usdAmount,
|
|
2163
|
+
priceId: priceId ?? "",
|
|
2164
|
+
onSolanaSuccess: solanaSuccess,
|
|
2165
|
+
onSolanaError: solanaError,
|
|
2166
|
+
enableNewCard: Boolean(priceId),
|
|
2167
|
+
enableStoredMethods: Boolean(priceId),
|
|
2168
|
+
enableSolanaPay: enableSolanaPay && Boolean(priceId),
|
|
2169
|
+
onNewCardPayment: priceId ? handleNewCardPayment : void 0,
|
|
2170
|
+
onSavedMethodPayment: priceId ? handleSavedMethodPayment : void 0,
|
|
2171
|
+
initialMode
|
|
2172
|
+
}
|
|
2173
|
+
)
|
|
2174
|
+
] })
|
|
2175
|
+
}
|
|
2176
|
+
) }),
|
|
2985
2177
|
/* @__PURE__ */ jsx(
|
|
2986
2178
|
SubscriptionSuccessDialog,
|
|
2987
2179
|
{
|
|
@@ -3136,13 +2328,50 @@ var usePaymentDialogs = () => {
|
|
|
3136
2328
|
var PaymentContext = createContext(void 0);
|
|
3137
2329
|
var PaymentProvider = ({
|
|
3138
2330
|
config,
|
|
3139
|
-
runtime: runtimeProp,
|
|
3140
2331
|
children
|
|
3141
2332
|
}) => {
|
|
3142
|
-
const
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
2333
|
+
const queryClient = useMemo(() => {
|
|
2334
|
+
return new QueryClient({
|
|
2335
|
+
defaultOptions: {
|
|
2336
|
+
queries: {
|
|
2337
|
+
staleTime: 3e4,
|
|
2338
|
+
gcTime: 5 * 6e4,
|
|
2339
|
+
refetchOnWindowFocus: false,
|
|
2340
|
+
retry: 1
|
|
2341
|
+
},
|
|
2342
|
+
mutations: {
|
|
2343
|
+
retry: 1
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
});
|
|
2347
|
+
}, []);
|
|
2348
|
+
const client = useMemo(() => {
|
|
2349
|
+
const authProvider = config.getAuthToken ? async () => {
|
|
2350
|
+
try {
|
|
2351
|
+
const result = config.getAuthToken?.();
|
|
2352
|
+
if (result instanceof Promise) {
|
|
2353
|
+
return await result ?? null;
|
|
2354
|
+
}
|
|
2355
|
+
return result ?? null;
|
|
2356
|
+
} catch (error) {
|
|
2357
|
+
console.warn("payments-ui: failed to resolve auth token", error);
|
|
2358
|
+
return null;
|
|
2359
|
+
}
|
|
2360
|
+
} : void 0;
|
|
2361
|
+
const wrappedFetch = config.fetcher ? ((input, init) => {
|
|
2362
|
+
const normalizedInput = input instanceof URL ? input.toString() : input;
|
|
2363
|
+
return config.fetcher(normalizedInput, init);
|
|
2364
|
+
}) : void 0;
|
|
2365
|
+
return createClient({
|
|
2366
|
+
billingBaseUrl: config.endpoints.billingBaseUrl,
|
|
2367
|
+
billingBasePath: config.endpoints.billingBasePath,
|
|
2368
|
+
accountBaseUrl: config.endpoints.accountBaseUrl,
|
|
2369
|
+
accountBasePath: config.endpoints.accountBasePath,
|
|
2370
|
+
getAuthToken: authProvider,
|
|
2371
|
+
defaultHeaders: config.defaultHeaders,
|
|
2372
|
+
fetch: wrappedFetch
|
|
2373
|
+
});
|
|
2374
|
+
}, [config]);
|
|
3146
2375
|
const solanaEndpoint = useMemo(() => {
|
|
3147
2376
|
if (config.solana?.endpoint) return config.solana.endpoint;
|
|
3148
2377
|
const network = config.solana?.network ?? WalletAdapterNetwork.Mainnet;
|
|
@@ -3162,19 +2391,16 @@ var PaymentProvider = ({
|
|
|
3162
2391
|
const autoConnect = config.solana?.autoConnect ?? true;
|
|
3163
2392
|
const value = useMemo(() => {
|
|
3164
2393
|
return {
|
|
3165
|
-
config
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
app: runtime.app,
|
|
3169
|
-
services: runtime.services,
|
|
3170
|
-
queryClient: runtime.queryClient
|
|
2394
|
+
config,
|
|
2395
|
+
client,
|
|
2396
|
+
queryClient
|
|
3171
2397
|
};
|
|
3172
|
-
}, [
|
|
2398
|
+
}, [client, config, queryClient]);
|
|
3173
2399
|
useEffect(() => {
|
|
3174
2400
|
if (!config.collectJsKey) return;
|
|
3175
2401
|
loadCollectJs(config.collectJsKey);
|
|
3176
2402
|
}, [config.collectJsKey]);
|
|
3177
|
-
return /* @__PURE__ */ jsx(PaymentContext.Provider, { value, children: /* @__PURE__ */ jsx(QueryClientProvider, { client:
|
|
2403
|
+
return /* @__PURE__ */ jsx(PaymentContext.Provider, { value, children: /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx(ConnectionProvider, { endpoint: solanaEndpoint, config: { commitment: "confirmed" }, children: /* @__PURE__ */ jsx(WalletProvider, { wallets: walletAdapters, autoConnect, children: /* @__PURE__ */ jsx(WalletModalProvider, { children: /* @__PURE__ */ jsx(PaymentsDialogProvider, { children }) }) }) }) }) });
|
|
3178
2404
|
};
|
|
3179
2405
|
var usePaymentContext = () => {
|
|
3180
2406
|
const context = useContext(PaymentContext);
|
|
@@ -3183,6 +2409,40 @@ var usePaymentContext = () => {
|
|
|
3183
2409
|
}
|
|
3184
2410
|
return context;
|
|
3185
2411
|
};
|
|
2412
|
+
var PaymentsUIRoot = ({
|
|
2413
|
+
children,
|
|
2414
|
+
className,
|
|
2415
|
+
dark = false
|
|
2416
|
+
}) => {
|
|
2417
|
+
return /* @__PURE__ */ jsx(
|
|
2418
|
+
"div",
|
|
2419
|
+
{
|
|
2420
|
+
className: cn(
|
|
2421
|
+
"payments-ui-root",
|
|
2422
|
+
dark && "dark",
|
|
2423
|
+
className
|
|
2424
|
+
),
|
|
2425
|
+
children
|
|
2426
|
+
}
|
|
2427
|
+
);
|
|
2428
|
+
};
|
|
2429
|
+
var PaymentsUIPortalRoot = ({
|
|
2430
|
+
children,
|
|
2431
|
+
className,
|
|
2432
|
+
dark = false
|
|
2433
|
+
}) => {
|
|
2434
|
+
return /* @__PURE__ */ jsx(
|
|
2435
|
+
"div",
|
|
2436
|
+
{
|
|
2437
|
+
className: cn(
|
|
2438
|
+
"payments-ui-portal",
|
|
2439
|
+
dark && "dark",
|
|
2440
|
+
className
|
|
2441
|
+
),
|
|
2442
|
+
children
|
|
2443
|
+
}
|
|
2444
|
+
);
|
|
2445
|
+
};
|
|
3186
2446
|
var SolanaPaymentSelector = ({
|
|
3187
2447
|
isOpen,
|
|
3188
2448
|
onClose,
|
|
@@ -3190,7 +2450,7 @@ var SolanaPaymentSelector = ({
|
|
|
3190
2450
|
}) => {
|
|
3191
2451
|
return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (value) => value ? void 0 : onClose(), children: /* @__PURE__ */ jsx(DialogContent, { className: "w-full max-w-2xl max-h-[90vh] overflow-y-auto rounded-md border border-border/70 bg-background/95 p-0 shadow-2xl [&::-webkit-scrollbar]:hidden", children: /* @__PURE__ */ jsx(SolanaPaymentView, { ...props, onClose }) }) });
|
|
3192
2452
|
};
|
|
3193
|
-
var Table =
|
|
2453
|
+
var Table = React17.forwardRef(
|
|
3194
2454
|
({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3195
2455
|
"table",
|
|
3196
2456
|
{
|
|
@@ -3201,15 +2461,15 @@ var Table = React3.forwardRef(
|
|
|
3201
2461
|
)
|
|
3202
2462
|
);
|
|
3203
2463
|
Table.displayName = "Table";
|
|
3204
|
-
var TableHeader =
|
|
2464
|
+
var TableHeader = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("thead", { ref, className: cn("[&_tr]:border-b", className), ...props }));
|
|
3205
2465
|
TableHeader.displayName = "TableHeader";
|
|
3206
|
-
var TableBody =
|
|
2466
|
+
var TableBody = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("tbody", { ref, className: cn("[&_tr:last-child]:border-0", className), ...props }));
|
|
3207
2467
|
TableBody.displayName = "TableBody";
|
|
3208
|
-
var TableFooter =
|
|
2468
|
+
var TableFooter = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("tfoot", { ref, className: cn("bg-muted/50 font-medium text-muted-foreground", className), ...props }));
|
|
3209
2469
|
TableFooter.displayName = "TableFooter";
|
|
3210
|
-
var TableRow =
|
|
2470
|
+
var TableRow = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("tr", { ref, className: cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className), ...props }));
|
|
3211
2471
|
TableRow.displayName = "TableRow";
|
|
3212
|
-
var TableHead =
|
|
2472
|
+
var TableHead = React17.forwardRef(
|
|
3213
2473
|
({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3214
2474
|
"th",
|
|
3215
2475
|
{
|
|
@@ -3223,7 +2483,7 @@ var TableHead = React3.forwardRef(
|
|
|
3223
2483
|
)
|
|
3224
2484
|
);
|
|
3225
2485
|
TableHead.displayName = "TableHead";
|
|
3226
|
-
var TableCell =
|
|
2486
|
+
var TableCell = React17.forwardRef(
|
|
3227
2487
|
({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3228
2488
|
"td",
|
|
3229
2489
|
{
|
|
@@ -3234,37 +2494,37 @@ var TableCell = React3.forwardRef(
|
|
|
3234
2494
|
)
|
|
3235
2495
|
);
|
|
3236
2496
|
TableCell.displayName = "TableCell";
|
|
3237
|
-
var TableCaption =
|
|
2497
|
+
var TableCaption = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("caption", { ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props }));
|
|
3238
2498
|
TableCaption.displayName = "TableCaption";
|
|
3239
2499
|
var AlertDialog = AlertDialogPrimitive.Root;
|
|
3240
2500
|
var AlertDialogTrigger = AlertDialogPrimitive.Trigger;
|
|
3241
2501
|
var AlertDialogPortal = AlertDialogPrimitive.Portal;
|
|
3242
|
-
var AlertDialogOverlay =
|
|
2502
|
+
var AlertDialogOverlay = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3243
2503
|
AlertDialogPrimitive.Overlay,
|
|
3244
2504
|
{
|
|
3245
2505
|
ref,
|
|
3246
2506
|
className: cn(
|
|
3247
|
-
"fixed inset-0 z-50 bg-
|
|
2507
|
+
"fixed inset-0 z-50 bg-black/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0",
|
|
3248
2508
|
className
|
|
3249
2509
|
),
|
|
3250
2510
|
...props
|
|
3251
2511
|
}
|
|
3252
2512
|
));
|
|
3253
2513
|
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
|
|
3254
|
-
var AlertDialogContent =
|
|
2514
|
+
var AlertDialogContent = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(AlertDialogPortal, { children: /* @__PURE__ */ jsxs("div", { className: "payments-ui-portal", children: [
|
|
3255
2515
|
/* @__PURE__ */ jsx(AlertDialogOverlay, {}),
|
|
3256
2516
|
/* @__PURE__ */ jsx(
|
|
3257
2517
|
AlertDialogPrimitive.Content,
|
|
3258
2518
|
{
|
|
3259
2519
|
ref,
|
|
3260
2520
|
className: cn(
|
|
3261
|
-
"fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-
|
|
2521
|
+
"fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-white/10 bg-[#161b22] text-white p-6 shadow-lg rounded-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
3262
2522
|
className
|
|
3263
2523
|
),
|
|
3264
2524
|
...props
|
|
3265
2525
|
}
|
|
3266
2526
|
)
|
|
3267
|
-
] }));
|
|
2527
|
+
] }) }));
|
|
3268
2528
|
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
|
|
3269
2529
|
var AlertDialogHeader = ({
|
|
3270
2530
|
className,
|
|
@@ -3282,13 +2542,13 @@ var AlertDialogFooter = ({
|
|
|
3282
2542
|
}
|
|
3283
2543
|
);
|
|
3284
2544
|
AlertDialogFooter.displayName = "AlertDialogFooter";
|
|
3285
|
-
var AlertDialogTitle =
|
|
2545
|
+
var AlertDialogTitle = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(AlertDialogPrimitive.Title, { ref, className: cn("text-lg font-semibold text-white", className), ...props }));
|
|
3286
2546
|
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
|
|
3287
|
-
var AlertDialogDescription =
|
|
2547
|
+
var AlertDialogDescription = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(AlertDialogPrimitive.Description, { ref, className: cn("text-sm text-white/60", className), ...props }));
|
|
3288
2548
|
AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
|
|
3289
|
-
var AlertDialogAction =
|
|
2549
|
+
var AlertDialogAction = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(AlertDialogPrimitive.Action, { ref, className: cn(buttonVariants(), className), ...props }));
|
|
3290
2550
|
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
|
|
3291
|
-
var AlertDialogCancel =
|
|
2551
|
+
var AlertDialogCancel = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3292
2552
|
AlertDialogPrimitive.Cancel,
|
|
3293
2553
|
{
|
|
3294
2554
|
ref,
|
|
@@ -3297,7 +2557,7 @@ var AlertDialogCancel = React3.forwardRef(({ className, ...props }, ref) => /* @
|
|
|
3297
2557
|
}
|
|
3298
2558
|
));
|
|
3299
2559
|
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
|
|
3300
|
-
var Textarea =
|
|
2560
|
+
var Textarea = React17.forwardRef(
|
|
3301
2561
|
({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3302
2562
|
"textarea",
|
|
3303
2563
|
{
|
|
@@ -3315,13 +2575,33 @@ var notifyDefault = (payload) => {
|
|
|
3315
2575
|
const level = payload.status === "destructive" ? "error" : "info";
|
|
3316
2576
|
console[level === "error" ? "error" : "log"]("[payments-ui] cancellation", payload);
|
|
3317
2577
|
};
|
|
2578
|
+
var defaultTranslations = {
|
|
2579
|
+
buttonLabel: "Cancel Membership",
|
|
2580
|
+
title: "Confirm Membership Cancellation",
|
|
2581
|
+
description: "You are about to cancel your membership. Please review the consequences:",
|
|
2582
|
+
consequence1: "You will immediately lose access to premium features upon confirmation.",
|
|
2583
|
+
consequence2: "Your benefits remain active until the end of the billing cycle.",
|
|
2584
|
+
consequence3: "Your account will revert to the free plan afterwards.",
|
|
2585
|
+
reasonLabel: "Please provide a reason for cancellation (required):",
|
|
2586
|
+
reasonPlaceholder: "Your feedback helps us improve...",
|
|
2587
|
+
reasonError: "Reason must be at least {min} characters long.",
|
|
2588
|
+
reasonHint: "Minimum {min} characters required.",
|
|
2589
|
+
keepMembership: "Keep Membership",
|
|
2590
|
+
confirmCancellation: "Confirm Cancellation",
|
|
2591
|
+
cancelling: "Cancelling...",
|
|
2592
|
+
membershipCancelled: "Membership cancelled",
|
|
2593
|
+
cancellationSuccess: "Your subscription has been cancelled successfully.",
|
|
2594
|
+
cancellationFailed: "Cancellation failed"
|
|
2595
|
+
};
|
|
3318
2596
|
var CancelMembershipDialog = ({
|
|
3319
2597
|
minReasonLength = 15,
|
|
3320
2598
|
onCancelled,
|
|
3321
|
-
onNotify
|
|
2599
|
+
onNotify,
|
|
2600
|
+
translations: customTranslations
|
|
3322
2601
|
}) => {
|
|
3323
|
-
const {
|
|
2602
|
+
const { client } = usePaymentContext();
|
|
3324
2603
|
const notify = onNotify ?? notifyDefault;
|
|
2604
|
+
const t = { ...defaultTranslations, ...customTranslations };
|
|
3325
2605
|
const [cancelReason, setCancelReason] = useState("");
|
|
3326
2606
|
const [isOpen, setIsOpen] = useState(false);
|
|
3327
2607
|
const [isReasonValid, setIsReasonValid] = useState(false);
|
|
@@ -3353,17 +2633,17 @@ var CancelMembershipDialog = ({
|
|
|
3353
2633
|
}
|
|
3354
2634
|
setIsSubmitting(true);
|
|
3355
2635
|
try {
|
|
3356
|
-
await
|
|
2636
|
+
await client.cancelSubscription(cancelReason.trim());
|
|
3357
2637
|
notify({
|
|
3358
|
-
title:
|
|
3359
|
-
description:
|
|
2638
|
+
title: t.membershipCancelled,
|
|
2639
|
+
description: t.cancellationSuccess,
|
|
3360
2640
|
status: "success"
|
|
3361
2641
|
});
|
|
3362
2642
|
onCancelled?.();
|
|
3363
2643
|
handleOpenChange(false);
|
|
3364
2644
|
} catch (error) {
|
|
3365
2645
|
const message = error instanceof Error ? error.message : "Unable to cancel membership";
|
|
3366
|
-
notify({ title:
|
|
2646
|
+
notify({ title: t.cancellationFailed, description: message, status: "destructive" });
|
|
3367
2647
|
} finally {
|
|
3368
2648
|
setIsSubmitting(false);
|
|
3369
2649
|
}
|
|
@@ -3372,32 +2652,34 @@ var CancelMembershipDialog = ({
|
|
|
3372
2652
|
return /* @__PURE__ */ jsxs(AlertDialog, { open: isOpen, onOpenChange: handleOpenChange, children: [
|
|
3373
2653
|
/* @__PURE__ */ jsx(AlertDialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", className: "border-destructive/50 text-destructive", children: [
|
|
3374
2654
|
/* @__PURE__ */ jsx(Ban, { className: "mr-2 h-4 w-4" }),
|
|
3375
|
-
"
|
|
2655
|
+
" ",
|
|
2656
|
+
t.buttonLabel
|
|
3376
2657
|
] }) }),
|
|
3377
2658
|
/* @__PURE__ */ jsxs(AlertDialogContent, { className: "max-h-[90vh] overflow-y-auto rounded-md border border-border bg-background", children: [
|
|
3378
2659
|
/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
|
|
3379
2660
|
/* @__PURE__ */ jsxs(AlertDialogTitle, { className: "flex items-center gap-2 text-lg font-semibold", children: [
|
|
3380
2661
|
/* @__PURE__ */ jsx(TriangleAlert, { className: "h-5 w-5 text-destructive" }),
|
|
3381
|
-
"
|
|
2662
|
+
" ",
|
|
2663
|
+
t.title
|
|
3382
2664
|
] }),
|
|
3383
2665
|
/* @__PURE__ */ jsxs(AlertDialogDescription, { className: "mt-3 space-y-3 text-muted-foreground", children: [
|
|
3384
|
-
/* @__PURE__ */ jsx("p", { children:
|
|
2666
|
+
/* @__PURE__ */ jsx("p", { children: t.description }),
|
|
3385
2667
|
/* @__PURE__ */ jsxs("ul", { className: "list-disc space-y-1 pl-5 text-sm", children: [
|
|
3386
|
-
/* @__PURE__ */ jsx("li", { children:
|
|
3387
|
-
/* @__PURE__ */ jsx("li", { children:
|
|
3388
|
-
/* @__PURE__ */ jsx("li", { children:
|
|
2668
|
+
/* @__PURE__ */ jsx("li", { children: t.consequence1 }),
|
|
2669
|
+
/* @__PURE__ */ jsx("li", { children: t.consequence2 }),
|
|
2670
|
+
/* @__PURE__ */ jsx("li", { children: t.consequence3 })
|
|
3389
2671
|
] })
|
|
3390
2672
|
] })
|
|
3391
2673
|
] }),
|
|
3392
2674
|
/* @__PURE__ */ jsxs("div", { className: "my-4 space-y-2 py-2", children: [
|
|
3393
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "cancelReason", className: "text-sm font-medium", children:
|
|
2675
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "cancelReason", className: "text-sm font-medium", children: t.reasonLabel }),
|
|
3394
2676
|
/* @__PURE__ */ jsx(
|
|
3395
2677
|
Textarea,
|
|
3396
2678
|
{
|
|
3397
2679
|
id: "cancelReason",
|
|
3398
2680
|
value: cancelReason,
|
|
3399
2681
|
onChange: handleReasonChange,
|
|
3400
|
-
placeholder:
|
|
2682
|
+
placeholder: t.reasonPlaceholder,
|
|
3401
2683
|
className: cn(
|
|
3402
2684
|
"w-full resize-none border-border bg-background",
|
|
3403
2685
|
showError && "border-destructive"
|
|
@@ -3412,19 +2694,19 @@ var CancelMembershipDialog = ({
|
|
|
3412
2694
|
{
|
|
3413
2695
|
id: "reason-hint",
|
|
3414
2696
|
className: `text-xs ${showError ? "text-destructive" : "text-muted-foreground"}`,
|
|
3415
|
-
children: showError ?
|
|
2697
|
+
children: showError ? t.reasonError.replace("{min}", String(minReasonLength)) : t.reasonHint.replace("{min}", String(minReasonLength))
|
|
3416
2698
|
}
|
|
3417
2699
|
)
|
|
3418
2700
|
] }),
|
|
3419
2701
|
/* @__PURE__ */ jsxs(AlertDialogFooter, { className: "mt-6 gap-2", children: [
|
|
3420
|
-
/* @__PURE__ */ jsx(AlertDialogCancel, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", className: "border-border text-muted-foreground", children:
|
|
2702
|
+
/* @__PURE__ */ jsx(AlertDialogCancel, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", className: "border-border text-muted-foreground", children: t.keepMembership }) }),
|
|
3421
2703
|
/* @__PURE__ */ jsx(AlertDialogAction, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
3422
2704
|
Button,
|
|
3423
2705
|
{
|
|
3424
2706
|
className: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
3425
2707
|
onClick: handleConfirm,
|
|
3426
2708
|
disabled: !isReasonValid || isSubmitting,
|
|
3427
|
-
children: isSubmitting ?
|
|
2709
|
+
children: isSubmitting ? t.cancelling : t.confirmCancellation
|
|
3428
2710
|
}
|
|
3429
2711
|
) })
|
|
3430
2712
|
] })
|
|
@@ -3435,14 +2717,29 @@ var notifyDefault2 = (payload) => {
|
|
|
3435
2717
|
const level = payload.status === "destructive" ? "error" : "info";
|
|
3436
2718
|
console[level === "error" ? "error" : "log"]("[payments-ui] billing", payload);
|
|
3437
2719
|
};
|
|
2720
|
+
var defaultTranslations2 = {
|
|
2721
|
+
title: "Transaction History",
|
|
2722
|
+
description: "Record of billing history",
|
|
2723
|
+
reviewActivity: "Review your account activity below",
|
|
2724
|
+
loading: "Loading...",
|
|
2725
|
+
error: "Error loading billing history.",
|
|
2726
|
+
loadingMore: "Loading more...",
|
|
2727
|
+
reference: "Reference",
|
|
2728
|
+
date: "Date",
|
|
2729
|
+
amount: "Amount",
|
|
2730
|
+
processor: "Processor",
|
|
2731
|
+
status: "Status"
|
|
2732
|
+
};
|
|
3438
2733
|
var BillingHistory = ({
|
|
3439
2734
|
pageSize = 10,
|
|
3440
2735
|
initialPage = 1,
|
|
3441
2736
|
enableCancel = true,
|
|
3442
|
-
onNotify
|
|
2737
|
+
onNotify,
|
|
2738
|
+
translations: customTranslations
|
|
3443
2739
|
}) => {
|
|
3444
|
-
const {
|
|
2740
|
+
const { client } = usePaymentContext();
|
|
3445
2741
|
const notify = onNotify ?? notifyDefault2;
|
|
2742
|
+
const t = { ...defaultTranslations2, ...customTranslations };
|
|
3446
2743
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
3447
2744
|
const observerRef = useRef(null);
|
|
3448
2745
|
const loadMoreRef = useRef(null);
|
|
@@ -3450,7 +2747,17 @@ var BillingHistory = ({
|
|
|
3450
2747
|
queryKey: ["payments-ui", "billing-history", pageSize],
|
|
3451
2748
|
queryFn: async ({ pageParam = initialPage }) => {
|
|
3452
2749
|
const offset = (pageParam - 1) * pageSize;
|
|
3453
|
-
return
|
|
2750
|
+
return client.getPaymentHistory({ limit: pageSize, offset, type: void 0 }).then(
|
|
2751
|
+
(response) => ({
|
|
2752
|
+
data: response.data,
|
|
2753
|
+
total_items: response.total,
|
|
2754
|
+
limit: response.limit,
|
|
2755
|
+
offset: response.offset,
|
|
2756
|
+
page: response.limit > 0 ? Math.floor(response.offset / response.limit) + 1 : 1,
|
|
2757
|
+
page_size: response.limit,
|
|
2758
|
+
total_pages: response.limit > 0 ? Math.ceil(response.total / response.limit) : void 0
|
|
2759
|
+
})
|
|
2760
|
+
);
|
|
3454
2761
|
},
|
|
3455
2762
|
initialPageParam: initialPage,
|
|
3456
2763
|
getNextPageParam: (lastPage) => {
|
|
@@ -3499,8 +2806,8 @@ var BillingHistory = ({
|
|
|
3499
2806
|
return /* @__PURE__ */ jsx(Card, { className: "border-0 bg-background/5 shadow-lg", children: /* @__PURE__ */ jsxs("div", { className: "p-4 sm:p-6", children: [
|
|
3500
2807
|
/* @__PURE__ */ jsxs("div", { className: "flex cursor-pointer items-center justify-between", onClick: () => setIsExpanded((prev) => !prev), children: [
|
|
3501
2808
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
3502
|
-
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl font-semibold", children:
|
|
3503
|
-
/* @__PURE__ */ jsx(CardDescription, { children:
|
|
2809
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl font-semibold", children: t.title }),
|
|
2810
|
+
/* @__PURE__ */ jsx(CardDescription, { children: t.description })
|
|
3504
2811
|
] }),
|
|
3505
2812
|
/* @__PURE__ */ jsx(ChevronDown, { className: cn("h-5 w-5 text-muted-foreground transition-transform", isExpanded && "rotate-180") })
|
|
3506
2813
|
] }),
|
|
@@ -3513,16 +2820,16 @@ var BillingHistory = ({
|
|
|
3513
2820
|
),
|
|
3514
2821
|
children: /* @__PURE__ */ jsx(CardContent, { className: "p-0 pt-4", children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
3515
2822
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between", children: [
|
|
3516
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children:
|
|
2823
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t.reviewActivity }),
|
|
3517
2824
|
enableCancel && /* @__PURE__ */ jsx(CancelMembershipDialog, { onNotify: notify })
|
|
3518
2825
|
] }),
|
|
3519
|
-
/* @__PURE__ */ jsx("div", { className: "max-h-[300px] overflow-y-auto rounded-lg border border-border/70", children: /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: historyQuery.isLoading ? /* @__PURE__ */ jsx("p", { className: "p-4 text-center text-sm text-muted-foreground", children:
|
|
2826
|
+
/* @__PURE__ */ jsx("div", { className: "max-h-[300px] overflow-y-auto rounded-lg border border-border/70", children: /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: historyQuery.isLoading ? /* @__PURE__ */ jsx("p", { className: "p-4 text-center text-sm text-muted-foreground", children: t.loading }) : historyQuery.isError ? /* @__PURE__ */ jsx("p", { className: "p-4 text-center text-sm text-destructive", children: t.error }) : /* @__PURE__ */ jsxs(Table, { children: [
|
|
3520
2827
|
/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { className: "border-border/60", children: [
|
|
3521
|
-
/* @__PURE__ */ jsx(TableHead, { children:
|
|
3522
|
-
/* @__PURE__ */ jsx(TableHead, { children:
|
|
3523
|
-
/* @__PURE__ */ jsx(TableHead, { children:
|
|
3524
|
-
/* @__PURE__ */ jsx(TableHead, { children:
|
|
3525
|
-
/* @__PURE__ */ jsx(TableHead, { children:
|
|
2828
|
+
/* @__PURE__ */ jsx(TableHead, { children: t.reference }),
|
|
2829
|
+
/* @__PURE__ */ jsx(TableHead, { children: t.date }),
|
|
2830
|
+
/* @__PURE__ */ jsx(TableHead, { children: t.amount }),
|
|
2831
|
+
/* @__PURE__ */ jsx(TableHead, { children: t.processor }),
|
|
2832
|
+
/* @__PURE__ */ jsx(TableHead, { children: t.status })
|
|
3526
2833
|
] }) }),
|
|
3527
2834
|
/* @__PURE__ */ jsx(TableBody, { children: payments.map(
|
|
3528
2835
|
(page) => page.data.map((payment) => /* @__PURE__ */ jsxs(TableRow, { className: "border-border/40", children: [
|
|
@@ -3534,7 +2841,7 @@ var BillingHistory = ({
|
|
|
3534
2841
|
] }, payment.id))
|
|
3535
2842
|
) })
|
|
3536
2843
|
] }) }) }),
|
|
3537
|
-
/* @__PURE__ */ jsx("div", { ref: loadMoreRef, className: "h-10 w-full", children: historyQuery.isFetchingNextPage && /* @__PURE__ */ jsx("p", { className: "text-center text-sm text-muted-foreground", children:
|
|
2844
|
+
/* @__PURE__ */ jsx("div", { ref: loadMoreRef, className: "h-10 w-full", children: historyQuery.isFetchingNextPage && /* @__PURE__ */ jsx("p", { className: "text-center text-sm text-muted-foreground", children: t.loadingMore }) })
|
|
3538
2845
|
] }) })
|
|
3539
2846
|
}
|
|
3540
2847
|
)
|
|
@@ -3549,19 +2856,56 @@ var notifyDefault3 = (payload) => {
|
|
|
3549
2856
|
const level = payload.status === "destructive" ? "error" : "info";
|
|
3550
2857
|
console[level === "error" ? "error" : "log"]("[payments-ui] notification", payload);
|
|
3551
2858
|
};
|
|
2859
|
+
var defaultTranslations3 = {
|
|
2860
|
+
title: "Payment Methods",
|
|
2861
|
+
description: "Manage your saved billing cards",
|
|
2862
|
+
addCard: "Add card",
|
|
2863
|
+
loadingCards: "Loading cards...",
|
|
2864
|
+
noPaymentMethods: "No saved payment methods yet.",
|
|
2865
|
+
addedOn: "Added on",
|
|
2866
|
+
active: "Active",
|
|
2867
|
+
inactive: "Inactive",
|
|
2868
|
+
replaceCard: "Replace card",
|
|
2869
|
+
makeDefault: "Make default",
|
|
2870
|
+
defaultMethod: "Default method",
|
|
2871
|
+
remove: "Remove",
|
|
2872
|
+
addNewCard: "Add a new card",
|
|
2873
|
+
addNewCardDescription: "Your card details are tokenized securely via our payment provider.",
|
|
2874
|
+
saveCard: "Save card",
|
|
2875
|
+
replaceCardTitle: "Replace card",
|
|
2876
|
+
replaceCardDescription: "Update this card with new billing details.",
|
|
2877
|
+
cardAddedSuccess: "Card added successfully",
|
|
2878
|
+
unableToAddCard: "Unable to add card",
|
|
2879
|
+
cardRemoved: "Card removed",
|
|
2880
|
+
unableToRemoveCard: "Unable to remove card",
|
|
2881
|
+
cardUpdated: "Card updated",
|
|
2882
|
+
unableToReplaceCard: "Unable to replace card",
|
|
2883
|
+
defaultPaymentMethodUpdated: "Default payment method updated",
|
|
2884
|
+
unableToSetDefault: "Unable to set default payment method"
|
|
2885
|
+
};
|
|
3552
2886
|
var PaymentMethodsSection = ({
|
|
3553
2887
|
isAuthenticated = true,
|
|
3554
2888
|
userEmail,
|
|
3555
2889
|
provider = "mobius",
|
|
3556
2890
|
defaultCountry = "US",
|
|
3557
2891
|
collectPrefix = "account-card",
|
|
3558
|
-
onNotify
|
|
2892
|
+
onNotify,
|
|
2893
|
+
translations: customTranslations
|
|
3559
2894
|
}) => {
|
|
3560
|
-
const
|
|
2895
|
+
const { client } = usePaymentContext();
|
|
3561
2896
|
const queryClient = useQueryClient();
|
|
2897
|
+
const paymentMethods = {
|
|
2898
|
+
list: (params) => client.listPaymentMethods({ limit: params.pageSize }),
|
|
2899
|
+
create: (payload) => client.createPaymentMethod(payload),
|
|
2900
|
+
update: (id, payload) => client.updatePaymentMethod(id, payload),
|
|
2901
|
+
remove: (id) => client.deletePaymentMethod(id),
|
|
2902
|
+
activate: (id) => client.activatePaymentMethod(id)
|
|
2903
|
+
};
|
|
3562
2904
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
3563
2905
|
const [deletingId, setDeletingId] = useState(null);
|
|
2906
|
+
const [methodBeingReplaced, setMethodBeingReplaced] = useState(null);
|
|
3564
2907
|
const notify = onNotify ?? notifyDefault3;
|
|
2908
|
+
const t = { ...defaultTranslations3, ...customTranslations };
|
|
3565
2909
|
const queryKey = ["payments-ui", "payment-methods"];
|
|
3566
2910
|
const paymentQuery = useQuery({
|
|
3567
2911
|
queryKey,
|
|
@@ -3572,13 +2916,13 @@ var PaymentMethodsSection = ({
|
|
|
3572
2916
|
const createMutation = useMutation({
|
|
3573
2917
|
mutationFn: (payload) => paymentMethods.create(payload),
|
|
3574
2918
|
onSuccess: () => {
|
|
3575
|
-
notify({ title:
|
|
2919
|
+
notify({ title: t.cardAddedSuccess, status: "success" });
|
|
3576
2920
|
setIsModalOpen(false);
|
|
3577
2921
|
void queryClient.invalidateQueries({ queryKey });
|
|
3578
2922
|
},
|
|
3579
2923
|
onError: (error) => {
|
|
3580
2924
|
notify({
|
|
3581
|
-
title:
|
|
2925
|
+
title: t.unableToAddCard,
|
|
3582
2926
|
description: error.message,
|
|
3583
2927
|
status: "destructive"
|
|
3584
2928
|
});
|
|
@@ -3588,18 +2932,47 @@ var PaymentMethodsSection = ({
|
|
|
3588
2932
|
mutationFn: (id) => paymentMethods.remove(id),
|
|
3589
2933
|
onMutate: (id) => setDeletingId(id),
|
|
3590
2934
|
onSuccess: () => {
|
|
3591
|
-
notify({ title:
|
|
2935
|
+
notify({ title: t.cardRemoved, status: "success" });
|
|
3592
2936
|
void queryClient.invalidateQueries({ queryKey });
|
|
3593
2937
|
},
|
|
3594
2938
|
onError: (error) => {
|
|
3595
2939
|
notify({
|
|
3596
|
-
title:
|
|
2940
|
+
title: t.unableToRemoveCard,
|
|
3597
2941
|
description: error.message,
|
|
3598
2942
|
status: "destructive"
|
|
3599
2943
|
});
|
|
3600
2944
|
},
|
|
3601
2945
|
onSettled: () => setDeletingId(null)
|
|
3602
2946
|
});
|
|
2947
|
+
const replaceMutation = useMutation({
|
|
2948
|
+
mutationFn: ({ id, payload }) => paymentMethods.update(id, payload),
|
|
2949
|
+
onSuccess: () => {
|
|
2950
|
+
notify({ title: t.cardUpdated, status: "success" });
|
|
2951
|
+
setMethodBeingReplaced(null);
|
|
2952
|
+
void queryClient.invalidateQueries({ queryKey });
|
|
2953
|
+
},
|
|
2954
|
+
onError: (error) => {
|
|
2955
|
+
notify({
|
|
2956
|
+
title: t.unableToReplaceCard,
|
|
2957
|
+
description: error.message,
|
|
2958
|
+
status: "destructive"
|
|
2959
|
+
});
|
|
2960
|
+
}
|
|
2961
|
+
});
|
|
2962
|
+
const activateMutation = useMutation({
|
|
2963
|
+
mutationFn: (id) => paymentMethods.activate(id),
|
|
2964
|
+
onSuccess: () => {
|
|
2965
|
+
notify({ title: t.defaultPaymentMethodUpdated, status: "success" });
|
|
2966
|
+
void queryClient.invalidateQueries({ queryKey });
|
|
2967
|
+
},
|
|
2968
|
+
onError: (error) => {
|
|
2969
|
+
notify({
|
|
2970
|
+
title: t.unableToSetDefault,
|
|
2971
|
+
description: error.message,
|
|
2972
|
+
status: "destructive"
|
|
2973
|
+
});
|
|
2974
|
+
}
|
|
2975
|
+
});
|
|
3603
2976
|
useEffect(() => {
|
|
3604
2977
|
if (!isModalOpen) {
|
|
3605
2978
|
createMutation.reset();
|
|
@@ -3607,49 +2980,56 @@ var PaymentMethodsSection = ({
|
|
|
3607
2980
|
}, [createMutation, isModalOpen]);
|
|
3608
2981
|
const payments = useMemo(() => paymentQuery.data?.data ?? [], [paymentQuery.data]);
|
|
3609
2982
|
const loading = paymentQuery.isLoading || paymentQuery.isFetching;
|
|
2983
|
+
const buildPayload = (token, billing) => ({
|
|
2984
|
+
payment_token: token,
|
|
2985
|
+
first_name: billing.firstName,
|
|
2986
|
+
last_name: billing.lastName,
|
|
2987
|
+
address1: billing.address1,
|
|
2988
|
+
address2: billing.address2,
|
|
2989
|
+
city: billing.city,
|
|
2990
|
+
state: billing.stateRegion,
|
|
2991
|
+
zip: billing.postalCode,
|
|
2992
|
+
country: billing.country,
|
|
2993
|
+
email: billing.email,
|
|
2994
|
+
provider: billing.provider
|
|
2995
|
+
});
|
|
3610
2996
|
const handleCardTokenize = (token, billing) => {
|
|
3611
|
-
|
|
3612
|
-
payment_token: token,
|
|
3613
|
-
first_name: billing.firstName,
|
|
3614
|
-
last_name: billing.lastName,
|
|
3615
|
-
address1: billing.address1,
|
|
3616
|
-
address2: billing.address2,
|
|
3617
|
-
city: billing.city,
|
|
3618
|
-
state: billing.stateRegion,
|
|
3619
|
-
zip: billing.postalCode,
|
|
3620
|
-
country: billing.country,
|
|
3621
|
-
email: billing.email,
|
|
3622
|
-
provider: billing.provider
|
|
3623
|
-
};
|
|
3624
|
-
createMutation.mutate(payload);
|
|
2997
|
+
createMutation.mutate(buildPayload(token, billing));
|
|
3625
2998
|
};
|
|
3626
|
-
|
|
2999
|
+
const handleReplaceTokenize = (token, billing) => {
|
|
3000
|
+
if (!methodBeingReplaced) return;
|
|
3001
|
+
replaceMutation.mutate({ id: methodBeingReplaced.id, payload: buildPayload(token, billing) });
|
|
3002
|
+
};
|
|
3003
|
+
return /* @__PURE__ */ jsxs(Card, { children: [
|
|
3627
3004
|
/* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-col gap-4 md:flex-row md:items-center md:justify-between", children: [
|
|
3628
3005
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
3629
|
-
/* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2
|
|
3630
|
-
/* @__PURE__ */ jsx(WalletCards, { className: "h-5 w-5 text-
|
|
3631
|
-
"
|
|
3006
|
+
/* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2", children: [
|
|
3007
|
+
/* @__PURE__ */ jsx(WalletCards, { className: "h-5 w-5 text-emerald-400" }),
|
|
3008
|
+
" ",
|
|
3009
|
+
t.title
|
|
3632
3010
|
] }),
|
|
3633
|
-
/* @__PURE__ */ jsx(CardDescription, { children:
|
|
3011
|
+
/* @__PURE__ */ jsx(CardDescription, { children: t.description })
|
|
3634
3012
|
] }),
|
|
3635
3013
|
/* @__PURE__ */ jsxs(Button, { onClick: () => setIsModalOpen(true), children: [
|
|
3636
3014
|
/* @__PURE__ */ jsx(CreditCard, { className: "mr-2 h-4 w-4" }),
|
|
3637
|
-
"
|
|
3015
|
+
" ",
|
|
3016
|
+
t.addCard
|
|
3638
3017
|
] })
|
|
3639
3018
|
] }),
|
|
3640
|
-
/* @__PURE__ */ jsx(CardContent, { className: "space-y-4", children: loading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center py-10 text-
|
|
3019
|
+
/* @__PURE__ */ jsx(CardContent, { className: "space-y-4", children: loading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center py-10 text-white/60", children: [
|
|
3641
3020
|
/* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" }),
|
|
3642
|
-
"
|
|
3643
|
-
|
|
3021
|
+
" ",
|
|
3022
|
+
t.loadingCards
|
|
3023
|
+
] }) : payments.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-dashed border-white/20 bg-white/5 p-6 text-sm text-white/60", children: t.noPaymentMethods }) : /* @__PURE__ */ jsx("div", { className: "space-y-3", children: payments.map((method) => /* @__PURE__ */ jsxs(
|
|
3644
3024
|
"div",
|
|
3645
3025
|
{
|
|
3646
|
-
className: "rounded-lg border border-
|
|
3026
|
+
className: "rounded-lg border border-white/10 bg-white/5 p-4 shadow-sm",
|
|
3647
3027
|
children: [
|
|
3648
3028
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 md:flex-row md:items-center md:justify-between", children: [
|
|
3649
3029
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
3650
|
-
/* @__PURE__ */ jsx("h4", { className: "text-base font-medium text-
|
|
3651
|
-
/* @__PURE__ */ jsxs("p", { className: "text-sm text-
|
|
3652
|
-
|
|
3030
|
+
/* @__PURE__ */ jsx("h4", { className: "text-base font-medium text-white", children: formatCardLabel2(method) }),
|
|
3031
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm text-white/60", children: [
|
|
3032
|
+
t.addedOn,
|
|
3653
3033
|
" ",
|
|
3654
3034
|
method.created_at ? new Date(method.created_at).toLocaleDateString() : "unknown date"
|
|
3655
3035
|
] })
|
|
@@ -3659,34 +3039,60 @@ var PaymentMethodsSection = ({
|
|
|
3659
3039
|
Badge,
|
|
3660
3040
|
{
|
|
3661
3041
|
variant: method.is_active ? "default" : "secondary",
|
|
3662
|
-
|
|
3663
|
-
children: method.is_active ? "Active" : "Inactive"
|
|
3042
|
+
children: method.is_active ? t.active : t.inactive
|
|
3664
3043
|
}
|
|
3665
3044
|
),
|
|
3666
3045
|
method.failure_reason && /* @__PURE__ */ jsx(Badge, { variant: "destructive", children: method.failure_reason })
|
|
3667
3046
|
] })
|
|
3668
3047
|
] }),
|
|
3669
|
-
/* @__PURE__ */
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3048
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap gap-2", children: [
|
|
3049
|
+
/* @__PURE__ */ jsxs(
|
|
3050
|
+
Button,
|
|
3051
|
+
{
|
|
3052
|
+
variant: "ghost",
|
|
3053
|
+
className: "text-blue-400",
|
|
3054
|
+
disabled: replaceMutation.isPending && methodBeingReplaced?.id === method.id,
|
|
3055
|
+
onClick: () => setMethodBeingReplaced(method),
|
|
3056
|
+
children: [
|
|
3057
|
+
replaceMutation.isPending && methodBeingReplaced?.id === method.id ? /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(CreditCard, { className: "mr-2 h-4 w-4" }),
|
|
3058
|
+
t.replaceCard
|
|
3059
|
+
]
|
|
3060
|
+
}
|
|
3061
|
+
),
|
|
3062
|
+
/* @__PURE__ */ jsxs(
|
|
3063
|
+
Button,
|
|
3064
|
+
{
|
|
3065
|
+
variant: "outline",
|
|
3066
|
+
disabled: method.is_active || activateMutation.isPending,
|
|
3067
|
+
onClick: () => activateMutation.mutate(method.id),
|
|
3068
|
+
children: [
|
|
3069
|
+
activateMutation.isPending ? /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null,
|
|
3070
|
+
method.is_active ? t.defaultMethod : t.makeDefault
|
|
3071
|
+
]
|
|
3072
|
+
}
|
|
3073
|
+
),
|
|
3074
|
+
/* @__PURE__ */ jsxs(
|
|
3075
|
+
Button,
|
|
3076
|
+
{
|
|
3077
|
+
variant: "ghost",
|
|
3078
|
+
className: "text-red-400 hover:text-red-300",
|
|
3079
|
+
disabled: deletingId === method.id && deleteMutation.isPending,
|
|
3080
|
+
onClick: () => deleteMutation.mutate(method.id),
|
|
3081
|
+
children: [
|
|
3082
|
+
deletingId === method.id && deleteMutation.isPending ? /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Trash2, { className: "mr-2 h-4 w-4" }),
|
|
3083
|
+
t.remove
|
|
3084
|
+
]
|
|
3085
|
+
}
|
|
3086
|
+
)
|
|
3087
|
+
] })
|
|
3682
3088
|
]
|
|
3683
3089
|
},
|
|
3684
3090
|
method.id
|
|
3685
3091
|
)) }) }),
|
|
3686
|
-
/* @__PURE__ */ jsx(Dialog, { open: isModalOpen, onOpenChange: setIsModalOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-h-[95vh] overflow-y-auto
|
|
3092
|
+
/* @__PURE__ */ jsx(Dialog, { open: isModalOpen, onOpenChange: setIsModalOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-h-[95vh] overflow-y-auto", children: [
|
|
3687
3093
|
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
3688
|
-
/* @__PURE__ */ jsx(DialogTitle, { children:
|
|
3689
|
-
/* @__PURE__ */ jsx(DialogDescription, { children:
|
|
3094
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: t.addNewCard }),
|
|
3095
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: t.addNewCardDescription })
|
|
3690
3096
|
] }),
|
|
3691
3097
|
/* @__PURE__ */ jsx(
|
|
3692
3098
|
CardDetailsForm,
|
|
@@ -3694,7 +3100,7 @@ var PaymentMethodsSection = ({
|
|
|
3694
3100
|
visible: isModalOpen,
|
|
3695
3101
|
collectPrefix,
|
|
3696
3102
|
submitting: createMutation.isPending,
|
|
3697
|
-
submitLabel:
|
|
3103
|
+
submitLabel: t.saveCard,
|
|
3698
3104
|
defaultValues: {
|
|
3699
3105
|
email: userEmail ?? "",
|
|
3700
3106
|
country: defaultCountry,
|
|
@@ -3702,640 +3108,36 @@ var PaymentMethodsSection = ({
|
|
|
3702
3108
|
},
|
|
3703
3109
|
externalError: createMutation.error?.message ?? null,
|
|
3704
3110
|
onTokenize: handleCardTokenize,
|
|
3705
|
-
className: "rounded-2xl border border-
|
|
3111
|
+
className: "rounded-2xl border border-white/10 bg-white/5 p-6"
|
|
3706
3112
|
}
|
|
3707
3113
|
)
|
|
3708
|
-
] }) })
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
const [state, setState] = useState({
|
|
3714
|
-
wallets: [],
|
|
3715
|
-
isLoading: false,
|
|
3716
|
-
error: null
|
|
3717
|
-
});
|
|
3718
|
-
const fetchWallets = useCallback(async () => {
|
|
3719
|
-
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
3720
|
-
try {
|
|
3721
|
-
const wallets2 = await services.solanaWallets.list();
|
|
3722
|
-
setState({ wallets: wallets2, isLoading: false, error: null });
|
|
3723
|
-
return wallets2;
|
|
3724
|
-
} catch (error) {
|
|
3725
|
-
const message = error instanceof Error ? error.message : "Failed to load wallets";
|
|
3726
|
-
console.error("payments-ui: wallet list fetch failed", error);
|
|
3727
|
-
setState((prev) => ({ ...prev, isLoading: false, error: message }));
|
|
3728
|
-
throw error;
|
|
3729
|
-
}
|
|
3730
|
-
}, [services.solanaWallets]);
|
|
3731
|
-
const deleteWallet = useCallback(
|
|
3732
|
-
async (walletIdOrAddress) => {
|
|
3733
|
-
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
3734
|
-
try {
|
|
3735
|
-
await services.solanaWallets.remove(walletIdOrAddress);
|
|
3736
|
-
setState((prev) => ({
|
|
3737
|
-
...prev,
|
|
3738
|
-
wallets: prev.wallets.filter(
|
|
3739
|
-
(wallet) => wallet.id !== walletIdOrAddress && wallet.address !== walletIdOrAddress
|
|
3740
|
-
),
|
|
3741
|
-
isLoading: false
|
|
3742
|
-
}));
|
|
3743
|
-
} catch (error) {
|
|
3744
|
-
const message = error instanceof Error ? error.message : "Failed to remove wallet";
|
|
3745
|
-
console.error("payments-ui: wallet removal failed", error);
|
|
3746
|
-
setState((prev) => ({ ...prev, isLoading: false, error: message }));
|
|
3747
|
-
throw error;
|
|
3748
|
-
}
|
|
3749
|
-
},
|
|
3750
|
-
[services.solanaWallets]
|
|
3751
|
-
);
|
|
3752
|
-
const addWallet = useCallback((wallet) => {
|
|
3753
|
-
setState((prev) => ({ ...prev, wallets: [...prev.wallets, wallet] }));
|
|
3754
|
-
}, []);
|
|
3755
|
-
const updateWallet = useCallback((walletId, updates) => {
|
|
3756
|
-
setState((prev) => ({
|
|
3757
|
-
...prev,
|
|
3758
|
-
wallets: prev.wallets.map(
|
|
3759
|
-
(wallet) => wallet.id === walletId ? { ...wallet, ...updates } : wallet
|
|
3760
|
-
)
|
|
3761
|
-
}));
|
|
3762
|
-
}, []);
|
|
3763
|
-
const clearError = useCallback(() => {
|
|
3764
|
-
setState((prev) => ({ ...prev, error: null }));
|
|
3765
|
-
}, []);
|
|
3766
|
-
const findWalletByAddress = useCallback(
|
|
3767
|
-
(address) => state.wallets.find((wallet) => wallet.address === address),
|
|
3768
|
-
[state.wallets]
|
|
3769
|
-
);
|
|
3770
|
-
const getVerifiedWallets = useCallback(
|
|
3771
|
-
() => state.wallets.filter((wallet) => wallet.is_verified),
|
|
3772
|
-
[state.wallets]
|
|
3773
|
-
);
|
|
3774
|
-
useEffect(() => {
|
|
3775
|
-
if (options.autoFetch !== false) {
|
|
3776
|
-
fetchWallets().catch(() => {
|
|
3777
|
-
});
|
|
3778
|
-
}
|
|
3779
|
-
}, [fetchWallets, options.autoFetch]);
|
|
3780
|
-
return {
|
|
3781
|
-
wallets: state.wallets,
|
|
3782
|
-
isLoading: state.isLoading,
|
|
3783
|
-
error: state.error,
|
|
3784
|
-
fetchWallets,
|
|
3785
|
-
deleteWallet,
|
|
3786
|
-
addWallet,
|
|
3787
|
-
updateWallet,
|
|
3788
|
-
clearError,
|
|
3789
|
-
findWalletByAddress,
|
|
3790
|
-
getVerifiedWallets,
|
|
3791
|
-
hasVerifiedWallets: state.wallets.some((w) => w.is_verified),
|
|
3792
|
-
totalWallets: state.wallets.length
|
|
3793
|
-
};
|
|
3794
|
-
};
|
|
3795
|
-
var useWalletVerification = () => {
|
|
3796
|
-
const { services } = usePaymentContext();
|
|
3797
|
-
const { publicKey, signMessage } = useWallet();
|
|
3798
|
-
const [state, setState] = useState({
|
|
3799
|
-
isVerifying: false,
|
|
3800
|
-
isVerified: false,
|
|
3801
|
-
error: null
|
|
3802
|
-
});
|
|
3803
|
-
const signAndVerifyWallet = useCallback(
|
|
3804
|
-
async (walletAddress, message, nonce) => {
|
|
3805
|
-
if (!publicKey || !signMessage) {
|
|
3806
|
-
throw new Error("Wallet not connected or signing unavailable");
|
|
3807
|
-
}
|
|
3808
|
-
if (publicKey.toBase58() !== walletAddress) {
|
|
3809
|
-
throw new Error("Connected wallet does not match target wallet");
|
|
3810
|
-
}
|
|
3811
|
-
setState((prev) => ({ ...prev, isVerifying: true, error: null }));
|
|
3812
|
-
try {
|
|
3813
|
-
const encodedMessage = new TextEncoder().encode(message);
|
|
3814
|
-
const signature = await signMessage(encodedMessage);
|
|
3815
|
-
const signatureBase58 = bs58.encode(signature);
|
|
3816
|
-
const response = await services.solanaWallets.verify(
|
|
3817
|
-
walletAddress,
|
|
3818
|
-
signatureBase58,
|
|
3819
|
-
nonce
|
|
3820
|
-
);
|
|
3821
|
-
setState({ isVerifying: false, isVerified: response.verified, error: null });
|
|
3822
|
-
return response;
|
|
3823
|
-
} catch (error) {
|
|
3824
|
-
const message2 = error instanceof Error ? error.message : "Wallet verification failed";
|
|
3825
|
-
console.error("payments-ui: wallet verification failed", error);
|
|
3826
|
-
setState({ isVerifying: false, isVerified: false, error: message2 });
|
|
3827
|
-
throw error;
|
|
3828
|
-
}
|
|
3829
|
-
},
|
|
3830
|
-
[publicKey, signMessage, services.solanaWallets]
|
|
3831
|
-
);
|
|
3832
|
-
const clearError = useCallback(() => {
|
|
3833
|
-
setState((prev) => ({ ...prev, error: null }));
|
|
3834
|
-
}, []);
|
|
3835
|
-
const resetVerification = useCallback(() => {
|
|
3836
|
-
setState({ isVerifying: false, isVerified: false, error: null });
|
|
3837
|
-
}, []);
|
|
3838
|
-
const autoVerifyWallet = useCallback(
|
|
3839
|
-
async (walletAddress, message, nonce) => {
|
|
3840
|
-
try {
|
|
3841
|
-
return await signAndVerifyWallet(walletAddress, message, nonce);
|
|
3842
|
-
} catch (error) {
|
|
3843
|
-
console.warn("payments-ui: auto-verification skipped", error);
|
|
3844
|
-
return null;
|
|
3845
|
-
}
|
|
3846
|
-
},
|
|
3847
|
-
[signAndVerifyWallet]
|
|
3848
|
-
);
|
|
3849
|
-
return {
|
|
3850
|
-
...state,
|
|
3851
|
-
signAndVerifyWallet,
|
|
3852
|
-
autoVerifyWallet,
|
|
3853
|
-
clearError,
|
|
3854
|
-
resetVerification
|
|
3855
|
-
};
|
|
3856
|
-
};
|
|
3857
|
-
var useWalletConnection = () => {
|
|
3858
|
-
const { services } = usePaymentContext();
|
|
3859
|
-
const { publicKey, connected, connecting, disconnect, wallet } = useWallet();
|
|
3860
|
-
const [state, setState] = useState({
|
|
3861
|
-
isConnected: false,
|
|
3862
|
-
isConnecting: false,
|
|
3863
|
-
publicKey: null,
|
|
3864
|
-
wallets: [],
|
|
3865
|
-
isLoading: false,
|
|
3866
|
-
error: null
|
|
3867
|
-
});
|
|
3868
|
-
useEffect(() => {
|
|
3869
|
-
setState((prev) => ({
|
|
3870
|
-
...prev,
|
|
3871
|
-
isConnected: connected,
|
|
3872
|
-
isConnecting: connecting,
|
|
3873
|
-
publicKey: publicKey?.toBase58() ?? null
|
|
3874
|
-
}));
|
|
3875
|
-
}, [connected, connecting, publicKey]);
|
|
3876
|
-
const connectWalletToBackend = useCallback(
|
|
3877
|
-
async (address) => {
|
|
3878
|
-
if (!address) {
|
|
3879
|
-
throw new Error("Wallet address is required");
|
|
3880
|
-
}
|
|
3881
|
-
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
3882
|
-
try {
|
|
3883
|
-
const challenge = await services.solanaWallets.requestChallenge(address);
|
|
3884
|
-
setState((prev) => ({ ...prev, isLoading: false }));
|
|
3885
|
-
return challenge;
|
|
3886
|
-
} catch (error) {
|
|
3887
|
-
const message = error instanceof Error ? error.message : "Wallet challenge failed";
|
|
3888
|
-
console.error("payments-ui: wallet challenge failed", error);
|
|
3889
|
-
setState((prev) => ({ ...prev, isLoading: false, error: message }));
|
|
3890
|
-
throw error;
|
|
3891
|
-
}
|
|
3892
|
-
},
|
|
3893
|
-
[services.solanaWallets]
|
|
3894
|
-
);
|
|
3895
|
-
const connectWallet = useCallback(async () => {
|
|
3896
|
-
if (!wallet) {
|
|
3897
|
-
throw new Error("No wallet adapter selected");
|
|
3898
|
-
}
|
|
3899
|
-
setState((prev) => ({ ...prev, isConnecting: true, error: null }));
|
|
3900
|
-
try {
|
|
3901
|
-
await wallet.adapter.connect();
|
|
3902
|
-
if (publicKey) {
|
|
3903
|
-
await connectWalletToBackend(publicKey.toBase58());
|
|
3904
|
-
}
|
|
3905
|
-
} catch (error) {
|
|
3906
|
-
const message = error instanceof Error ? error.message : "Failed to connect wallet";
|
|
3907
|
-
console.error("payments-ui: wallet connection failed", error);
|
|
3908
|
-
setState((prev) => ({ ...prev, isConnecting: false, error: message }));
|
|
3909
|
-
throw error;
|
|
3910
|
-
} finally {
|
|
3911
|
-
setState((prev) => ({ ...prev, isConnecting: false }));
|
|
3912
|
-
}
|
|
3913
|
-
}, [wallet, publicKey, connectWalletToBackend]);
|
|
3914
|
-
const disconnectWallet = useCallback(async () => {
|
|
3915
|
-
try {
|
|
3916
|
-
await disconnect();
|
|
3917
|
-
setState((prev) => ({
|
|
3918
|
-
...prev,
|
|
3919
|
-
isConnected: false,
|
|
3920
|
-
publicKey: null,
|
|
3921
|
-
wallets: [],
|
|
3922
|
-
error: null
|
|
3923
|
-
}));
|
|
3924
|
-
} catch (error) {
|
|
3925
|
-
const message = error instanceof Error ? error.message : "Failed to disconnect wallet";
|
|
3926
|
-
console.error("payments-ui: wallet disconnect failed", error);
|
|
3927
|
-
setState((prev) => ({ ...prev, error: message }));
|
|
3928
|
-
throw error;
|
|
3929
|
-
}
|
|
3930
|
-
}, [disconnect]);
|
|
3931
|
-
const clearError = useCallback(() => {
|
|
3932
|
-
setState((prev) => ({ ...prev, error: null }));
|
|
3933
|
-
}, []);
|
|
3934
|
-
return {
|
|
3935
|
-
...state,
|
|
3936
|
-
walletName: wallet?.adapter.name,
|
|
3937
|
-
walletIcon: wallet?.adapter.icon,
|
|
3938
|
-
connectWallet,
|
|
3939
|
-
disconnectWallet,
|
|
3940
|
-
connectWalletToBackend,
|
|
3941
|
-
clearError
|
|
3942
|
-
};
|
|
3943
|
-
};
|
|
3944
|
-
var notifyDefault4 = (payload) => {
|
|
3945
|
-
console.log("[payments-ui] wallet-card", payload);
|
|
3946
|
-
};
|
|
3947
|
-
var WalletCard = ({
|
|
3948
|
-
wallet,
|
|
3949
|
-
isPrimary = false,
|
|
3950
|
-
isConnected = false,
|
|
3951
|
-
balance,
|
|
3952
|
-
onSetPrimary,
|
|
3953
|
-
onVerify,
|
|
3954
|
-
onDelete,
|
|
3955
|
-
isVerifying = false,
|
|
3956
|
-
isDeleting = false,
|
|
3957
|
-
notify = notifyDefault4
|
|
3958
|
-
}) => {
|
|
3959
|
-
const [isCopying, setIsCopying] = useState(false);
|
|
3960
|
-
const formatAddress = (address) => address.length <= 12 ? address : `${address.slice(0, 4)}...${address.slice(-4)}`;
|
|
3961
|
-
const copyToClipboard = async (text) => {
|
|
3962
|
-
setIsCopying(true);
|
|
3963
|
-
try {
|
|
3964
|
-
await navigator.clipboard.writeText(text);
|
|
3965
|
-
notify({ title: "Copied", description: "Wallet address copied to clipboard" });
|
|
3966
|
-
} catch (error) {
|
|
3967
|
-
notify({ title: "Failed to copy", description: error.message, status: "destructive" });
|
|
3968
|
-
} finally {
|
|
3969
|
-
setTimeout(() => setIsCopying(false), 500);
|
|
3970
|
-
}
|
|
3971
|
-
};
|
|
3972
|
-
const openExplorer = () => {
|
|
3973
|
-
window.open(`https://solscan.io/account/${wallet.address}`, "_blank", "noopener,noreferrer");
|
|
3974
|
-
};
|
|
3975
|
-
return /* @__PURE__ */ jsxs(
|
|
3976
|
-
"div",
|
|
3977
|
-
{
|
|
3978
|
-
className: cn(
|
|
3979
|
-
"rounded-lg border bg-background/40 p-4 transition-shadow",
|
|
3980
|
-
isPrimary && "border-primary/50 shadow-lg",
|
|
3981
|
-
isConnected && "ring-1 ring-primary"
|
|
3982
|
-
),
|
|
3983
|
-
children: [
|
|
3984
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
|
|
3985
|
-
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
3986
|
-
/* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-full bg-primary/20", children: /* @__PURE__ */ jsx("div", { className: "h-5 w-5 rounded-full bg-gradient-to-br from-primary to-primary/60" }) }),
|
|
3987
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
3988
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
3989
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono text-sm font-medium", children: formatAddress(wallet.address) }),
|
|
3990
|
-
isPrimary && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 rounded-full bg-yellow-500/20 px-2 py-0.5 text-xs text-yellow-500", children: [
|
|
3991
|
-
/* @__PURE__ */ jsx(Star, { className: "h-3 w-3 fill-yellow-500 text-yellow-500" }),
|
|
3992
|
-
" Primary"
|
|
3993
|
-
] }),
|
|
3994
|
-
wallet.is_verified ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs text-emerald-400", children: [
|
|
3995
|
-
/* @__PURE__ */ jsx(CheckCircle, { className: "h-3.5 w-3.5" }),
|
|
3996
|
-
" Verified"
|
|
3997
|
-
] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs text-amber-400", children: [
|
|
3998
|
-
/* @__PURE__ */ jsx(AlertCircle, { className: "h-3.5 w-3.5" }),
|
|
3999
|
-
" Unverified"
|
|
4000
|
-
] }),
|
|
4001
|
-
isConnected && /* @__PURE__ */ jsx("span", { className: "rounded bg-blue-500/20 px-2 py-0.5 text-xs text-blue-400", children: "Connected" })
|
|
4002
|
-
] }),
|
|
4003
|
-
balance !== null && typeof balance !== "undefined" && /* @__PURE__ */ jsxs("p", { className: "mt-1 text-sm text-muted-foreground", children: [
|
|
4004
|
-
"Balance: ",
|
|
4005
|
-
balance.toFixed(4),
|
|
4006
|
-
" SOL"
|
|
4007
|
-
] }),
|
|
4008
|
-
/* @__PURE__ */ jsxs("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [
|
|
4009
|
-
"Added ",
|
|
4010
|
-
new Date(wallet.created_at).toLocaleDateString()
|
|
4011
|
-
] })
|
|
4012
|
-
] })
|
|
4013
|
-
] }) }),
|
|
4014
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
4015
|
-
/* @__PURE__ */ jsx(
|
|
4016
|
-
Button,
|
|
4017
|
-
{
|
|
4018
|
-
variant: "ghost",
|
|
4019
|
-
size: "icon",
|
|
4020
|
-
className: "h-8 w-8",
|
|
4021
|
-
onClick: () => copyToClipboard(wallet.address),
|
|
4022
|
-
disabled: isCopying,
|
|
4023
|
-
children: isCopying ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Copy, { className: "h-4 w-4" })
|
|
4024
|
-
}
|
|
4025
|
-
),
|
|
4026
|
-
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", className: "h-8 w-8", onClick: openExplorer, children: /* @__PURE__ */ jsx(ExternalLink, { className: "h-4 w-4" }) })
|
|
4027
|
-
] })
|
|
4028
|
-
] }),
|
|
4029
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-2", children: [
|
|
4030
|
-
!isPrimary && onSetPrimary && wallet.is_verified && /* @__PURE__ */ jsxs(
|
|
4031
|
-
Button,
|
|
4032
|
-
{
|
|
4033
|
-
variant: "outline",
|
|
4034
|
-
size: "sm",
|
|
4035
|
-
className: "border-primary/40 text-primary",
|
|
4036
|
-
onClick: () => onSetPrimary(wallet.id),
|
|
4037
|
-
children: [
|
|
4038
|
-
/* @__PURE__ */ jsx(Star, { className: "mr-1 h-3 w-3" }),
|
|
4039
|
-
" Set Primary"
|
|
4040
|
-
]
|
|
4041
|
-
}
|
|
4042
|
-
),
|
|
4043
|
-
!wallet.is_verified && onVerify && isConnected && /* @__PURE__ */ jsx(
|
|
4044
|
-
Button,
|
|
4045
|
-
{
|
|
4046
|
-
variant: "outline",
|
|
4047
|
-
size: "sm",
|
|
4048
|
-
className: "border-emerald-500/40 text-emerald-400",
|
|
4049
|
-
onClick: () => onVerify(wallet),
|
|
4050
|
-
disabled: isVerifying,
|
|
4051
|
-
children: isVerifying ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4052
|
-
/* @__PURE__ */ jsx(Loader2, { className: "mr-1 h-3 w-3 animate-spin" }),
|
|
4053
|
-
" Verifying..."
|
|
4054
|
-
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4055
|
-
/* @__PURE__ */ jsx(Shield, { className: "mr-1 h-3 w-3" }),
|
|
4056
|
-
" Verify"
|
|
4057
|
-
] })
|
|
4058
|
-
}
|
|
4059
|
-
),
|
|
4060
|
-
onDelete && /* @__PURE__ */ jsx(
|
|
4061
|
-
Button,
|
|
4062
|
-
{
|
|
4063
|
-
variant: "outline",
|
|
4064
|
-
size: "sm",
|
|
4065
|
-
className: "ml-auto border-destructive/40 text-destructive",
|
|
4066
|
-
onClick: () => onDelete(wallet.id),
|
|
4067
|
-
disabled: isDeleting,
|
|
4068
|
-
children: isDeleting ? /* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 animate-spin" }) : /* @__PURE__ */ jsx(Trash2, { className: "h-3 w-3" })
|
|
4069
|
-
}
|
|
4070
|
-
)
|
|
4071
|
-
] })
|
|
4072
|
-
]
|
|
4073
|
-
}
|
|
4074
|
-
);
|
|
4075
|
-
};
|
|
4076
|
-
var EmptyWalletState = () => /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center", children: [
|
|
4077
|
-
/* @__PURE__ */ jsx(Wallet, { className: "mb-4 h-12 w-12 text-muted-foreground" }),
|
|
4078
|
-
/* @__PURE__ */ jsx("h3", { className: "mb-2 text-lg font-medium", children: "No wallets connected" }),
|
|
4079
|
-
/* @__PURE__ */ jsx("p", { className: "mb-6 text-sm text-muted-foreground", children: "Connect your Solana wallet to get started" }),
|
|
4080
|
-
/* @__PURE__ */ jsx(WalletMultiButton, { className: "!bg-primary text-primary-foreground hover:!bg-primary/90" })
|
|
4081
|
-
] });
|
|
4082
|
-
var notifyDefault5 = (payload) => {
|
|
4083
|
-
console.log("[payments-ui] solana-wallets", payload);
|
|
4084
|
-
};
|
|
4085
|
-
var SolanaWalletSection = ({
|
|
4086
|
-
onNotify,
|
|
4087
|
-
rpcUrl
|
|
4088
|
-
}) => {
|
|
4089
|
-
const notify = onNotify ?? notifyDefault5;
|
|
4090
|
-
const { config } = usePaymentContext();
|
|
4091
|
-
const { connected, publicKey, disconnect } = useWallet();
|
|
4092
|
-
const { wallets: wallets2, isLoading, deleteWallet, fetchWallets } = useWalletList();
|
|
4093
|
-
const { signAndVerifyWallet } = useWalletVerification();
|
|
4094
|
-
const walletConnection = useWalletConnection();
|
|
4095
|
-
const [primaryWalletId, setPrimaryWalletId] = useState(null);
|
|
4096
|
-
const [showOtherWallets, setShowOtherWallets] = useState(false);
|
|
4097
|
-
const [balances, setBalances] = useState({});
|
|
4098
|
-
const [verifyingWalletId, setVerifyingWalletId] = useState(null);
|
|
4099
|
-
const [deletingWalletId, setDeletingWalletId] = useState(null);
|
|
4100
|
-
const [walletToDelete, setWalletToDelete] = useState(null);
|
|
4101
|
-
const [isRegistering, setIsRegistering] = useState(false);
|
|
4102
|
-
const rpcEndpoint = rpcUrl || config.solanaRpcUrl || "https://api.mainnet-beta.solana.com";
|
|
4103
|
-
const connection = useMemo(() => new Connection(rpcEndpoint), [rpcEndpoint]);
|
|
4104
|
-
const primaryWallet = wallets2.find((w) => w.id === primaryWalletId) ?? wallets2.find((w) => w.is_verified) ?? null;
|
|
4105
|
-
const otherWallets = wallets2.filter((w) => w.id !== primaryWallet?.id);
|
|
4106
|
-
const fetchBalance = useCallback(
|
|
4107
|
-
async (address) => {
|
|
4108
|
-
try {
|
|
4109
|
-
const pubkey = new PublicKey(address);
|
|
4110
|
-
const balance = await connection.getBalance(pubkey);
|
|
4111
|
-
return balance / LAMPORTS_PER_SOL;
|
|
4112
|
-
} catch (error) {
|
|
4113
|
-
console.error("payments-ui: failed to fetch balance", error);
|
|
4114
|
-
return null;
|
|
4115
|
-
}
|
|
4116
|
-
},
|
|
4117
|
-
[connection]
|
|
4118
|
-
);
|
|
4119
|
-
const fetchAllBalances = useCallback(async () => {
|
|
4120
|
-
const results = await Promise.all(
|
|
4121
|
-
wallets2.map(async (wallet) => ({ address: wallet.address, balance: await fetchBalance(wallet.address) }))
|
|
4122
|
-
);
|
|
4123
|
-
const next = {};
|
|
4124
|
-
results.forEach(({ address, balance }) => {
|
|
4125
|
-
if (balance !== null && typeof balance !== "undefined") {
|
|
4126
|
-
next[address] = balance;
|
|
4127
|
-
}
|
|
4128
|
-
});
|
|
4129
|
-
setBalances(next);
|
|
4130
|
-
}, [wallets2, fetchBalance]);
|
|
4131
|
-
useEffect(() => {
|
|
4132
|
-
if (wallets2.length > 0) {
|
|
4133
|
-
fetchAllBalances().catch(() => void 0);
|
|
4134
|
-
}
|
|
4135
|
-
}, [wallets2, fetchAllBalances]);
|
|
4136
|
-
useEffect(() => {
|
|
4137
|
-
const verifiedWallet = wallets2.find((w) => w.is_verified);
|
|
4138
|
-
if (verifiedWallet && !primaryWalletId) {
|
|
4139
|
-
setPrimaryWalletId(verifiedWallet.id);
|
|
4140
|
-
}
|
|
4141
|
-
}, [wallets2, primaryWalletId]);
|
|
4142
|
-
const registerWallet = useCallback(async () => {
|
|
4143
|
-
if (!connected || !publicKey || isRegistering) return;
|
|
4144
|
-
setIsRegistering(true);
|
|
4145
|
-
try {
|
|
4146
|
-
const challenge = await walletConnection.connectWalletToBackend(publicKey.toBase58());
|
|
4147
|
-
if (!challenge?.message) {
|
|
4148
|
-
throw new Error("Failed to retrieve wallet verification challenge");
|
|
4149
|
-
}
|
|
4150
|
-
await signAndVerifyWallet(challenge.wallet, challenge.message, challenge.nonce);
|
|
4151
|
-
await fetchWallets();
|
|
4152
|
-
notify({
|
|
4153
|
-
title: "Wallet verified",
|
|
4154
|
-
description: "Your wallet has been linked successfully.",
|
|
4155
|
-
status: "success"
|
|
4156
|
-
});
|
|
4157
|
-
} catch (error) {
|
|
4158
|
-
notify({
|
|
4159
|
-
title: "Wallet verification failed",
|
|
4160
|
-
description: error instanceof Error ? error.message : "Failed to verify wallet",
|
|
4161
|
-
status: "destructive"
|
|
4162
|
-
});
|
|
4163
|
-
} finally {
|
|
4164
|
-
setIsRegistering(false);
|
|
4165
|
-
}
|
|
4166
|
-
}, [connected, publicKey, isRegistering, walletConnection, signAndVerifyWallet, fetchWallets, notify]);
|
|
4167
|
-
useEffect(() => {
|
|
4168
|
-
if (connected && publicKey && !walletConnection.isConnecting && wallets2.length === 0) {
|
|
4169
|
-
registerWallet().catch(() => void 0);
|
|
4170
|
-
}
|
|
4171
|
-
}, [connected, publicKey, walletConnection.isConnecting, wallets2.length, registerWallet]);
|
|
4172
|
-
const handleVerifyWallet = useCallback(
|
|
4173
|
-
async (wallet) => {
|
|
4174
|
-
if (!connected || publicKey?.toBase58() !== wallet.address) {
|
|
4175
|
-
notify({
|
|
4176
|
-
title: "Connect wallet first",
|
|
4177
|
-
description: "Please connect the wallet you want to verify.",
|
|
4178
|
-
status: "destructive"
|
|
4179
|
-
});
|
|
4180
|
-
return;
|
|
4181
|
-
}
|
|
4182
|
-
setVerifyingWalletId(wallet.id);
|
|
4183
|
-
try {
|
|
4184
|
-
const challenge = await walletConnection.connectWalletToBackend(wallet.address);
|
|
4185
|
-
if (!challenge?.message) {
|
|
4186
|
-
throw new Error("Failed to retrieve verification challenge");
|
|
4187
|
-
}
|
|
4188
|
-
await signAndVerifyWallet(challenge.wallet, challenge.message, challenge.nonce);
|
|
4189
|
-
await fetchWallets();
|
|
4190
|
-
notify({ title: "Wallet verified", status: "success" });
|
|
4191
|
-
} catch (error) {
|
|
4192
|
-
notify({
|
|
4193
|
-
title: "Verification failed",
|
|
4194
|
-
description: error instanceof Error ? error.message : "Failed to verify wallet",
|
|
4195
|
-
status: "destructive"
|
|
4196
|
-
});
|
|
4197
|
-
} finally {
|
|
4198
|
-
setVerifyingWalletId(null);
|
|
4199
|
-
}
|
|
4200
|
-
},
|
|
4201
|
-
[connected, publicKey, walletConnection, signAndVerifyWallet, fetchWallets, notify]
|
|
4202
|
-
);
|
|
4203
|
-
const handleDeleteWallet = useCallback(
|
|
4204
|
-
async (walletId) => {
|
|
4205
|
-
setDeletingWalletId(walletId);
|
|
4206
|
-
try {
|
|
4207
|
-
await deleteWallet(walletId);
|
|
4208
|
-
if (primaryWalletId === walletId) {
|
|
4209
|
-
setPrimaryWalletId(null);
|
|
4210
|
-
}
|
|
4211
|
-
if (connected && publicKey) {
|
|
4212
|
-
const deletedWallet = wallets2.find((w) => w.id === walletId);
|
|
4213
|
-
if (deletedWallet?.address === publicKey.toBase58()) {
|
|
4214
|
-
await disconnect();
|
|
4215
|
-
}
|
|
4216
|
-
}
|
|
4217
|
-
notify({ title: "Wallet removed", status: "success" });
|
|
4218
|
-
} catch (error) {
|
|
4219
|
-
notify({
|
|
4220
|
-
title: "Failed to remove wallet",
|
|
4221
|
-
description: error instanceof Error ? error.message : "Please try again",
|
|
4222
|
-
status: "destructive"
|
|
4223
|
-
});
|
|
4224
|
-
} finally {
|
|
4225
|
-
setDeletingWalletId(null);
|
|
4226
|
-
setWalletToDelete(null);
|
|
4227
|
-
}
|
|
4228
|
-
},
|
|
4229
|
-
[deleteWallet, primaryWalletId, connected, publicKey, wallets2, disconnect, notify]
|
|
4230
|
-
);
|
|
4231
|
-
const handleSetPrimary = (walletId) => {
|
|
4232
|
-
setPrimaryWalletId(walletId);
|
|
4233
|
-
notify({ title: "Primary wallet updated", status: "success" });
|
|
4234
|
-
};
|
|
4235
|
-
const isWalletRegistered = connected && publicKey && wallets2.some((w) => w.address === publicKey.toBase58());
|
|
4236
|
-
if (isLoading && wallets2.length === 0) {
|
|
4237
|
-
return /* @__PURE__ */ jsxs(Card, { className: "border-0 bg-background/5", children: [
|
|
4238
|
-
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsx(CardTitle, { className: "text-xl", children: "Solana Wallets" }) }),
|
|
4239
|
-
/* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center py-8", children: [
|
|
4240
|
-
/* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-6 w-6 animate-spin text-muted-foreground" }),
|
|
4241
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Loading wallets..." })
|
|
4242
|
-
] }) })
|
|
4243
|
-
] });
|
|
4244
|
-
}
|
|
4245
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4246
|
-
/* @__PURE__ */ jsxs(Card, { className: "border-0 bg-background/5", children: [
|
|
4247
|
-
/* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
4248
|
-
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl", children: "Solana Wallets" }),
|
|
4249
|
-
/* @__PURE__ */ jsx(CardDescription, { children: "Connect and manage your Solana wallets for payments" })
|
|
4250
|
-
] }),
|
|
4251
|
-
/* @__PURE__ */ jsx(CardContent, { className: "space-y-4", children: wallets2.length === 0 && !connected ? /* @__PURE__ */ jsx(EmptyWalletState, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4252
|
-
primaryWallet && /* @__PURE__ */ jsxs("div", { children: [
|
|
4253
|
-
/* @__PURE__ */ jsx("h3", { className: "mb-3 text-sm font-medium uppercase tracking-wide text-muted-foreground", children: "Primary Wallet" }),
|
|
4254
|
-
/* @__PURE__ */ jsx(
|
|
4255
|
-
WalletCard,
|
|
4256
|
-
{
|
|
4257
|
-
wallet: primaryWallet,
|
|
4258
|
-
isPrimary: true,
|
|
4259
|
-
isConnected: connected && publicKey?.toBase58() === primaryWallet.address,
|
|
4260
|
-
balance: balances[primaryWallet.address],
|
|
4261
|
-
onVerify: handleVerifyWallet,
|
|
4262
|
-
onDelete: (id) => {
|
|
4263
|
-
const target = wallets2.find((w) => w.id === id);
|
|
4264
|
-
if (target) setWalletToDelete(target);
|
|
4265
|
-
},
|
|
4266
|
-
isVerifying: verifyingWalletId === primaryWallet.id,
|
|
4267
|
-
isDeleting: deletingWalletId === primaryWallet.id,
|
|
4268
|
-
notify
|
|
4269
|
-
}
|
|
4270
|
-
)
|
|
4271
|
-
] }),
|
|
4272
|
-
otherWallets.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
4273
|
-
/* @__PURE__ */ jsxs(
|
|
4274
|
-
"button",
|
|
4275
|
-
{
|
|
4276
|
-
className: "mb-3 flex w-full items-center justify-between text-left",
|
|
4277
|
-
onClick: () => setShowOtherWallets((prev) => !prev),
|
|
4278
|
-
children: [
|
|
4279
|
-
/* @__PURE__ */ jsxs("h3", { className: "text-sm font-medium uppercase tracking-wide text-muted-foreground", children: [
|
|
4280
|
-
"Other Wallets (",
|
|
4281
|
-
otherWallets.length,
|
|
4282
|
-
")"
|
|
4283
|
-
] }),
|
|
4284
|
-
showOtherWallets ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4 text-muted-foreground" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground" })
|
|
4285
|
-
]
|
|
4286
|
-
}
|
|
4287
|
-
),
|
|
4288
|
-
showOtherWallets && /* @__PURE__ */ jsx("div", { className: "space-y-2", children: otherWallets.map((wallet) => /* @__PURE__ */ jsx(
|
|
4289
|
-
WalletCard,
|
|
4290
|
-
{
|
|
4291
|
-
wallet,
|
|
4292
|
-
isConnected: connected && publicKey?.toBase58() === wallet.address,
|
|
4293
|
-
balance: balances[wallet.address],
|
|
4294
|
-
onSetPrimary: handleSetPrimary,
|
|
4295
|
-
onVerify: handleVerifyWallet,
|
|
4296
|
-
onDelete: (id) => {
|
|
4297
|
-
const target = wallets2.find((w) => w.id === id);
|
|
4298
|
-
if (target) setWalletToDelete(target);
|
|
4299
|
-
},
|
|
4300
|
-
isVerifying: verifyingWalletId === wallet.id,
|
|
4301
|
-
isDeleting: deletingWalletId === wallet.id,
|
|
4302
|
-
notify
|
|
4303
|
-
},
|
|
4304
|
-
wallet.id
|
|
4305
|
-
)) })
|
|
4306
|
-
] }),
|
|
4307
|
-
/* @__PURE__ */ jsx("div", { className: "pt-4", children: connected && publicKey ? isWalletRegistered ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
4308
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-emerald-500/40 bg-emerald-500/10 p-3 text-sm text-emerald-200", children: [
|
|
4309
|
-
/* @__PURE__ */ jsx(CheckCircle, { className: "h-4 w-4" }),
|
|
4310
|
-
" Connected wallet is verified and linked to your account."
|
|
4311
|
-
] }),
|
|
4312
|
-
/* @__PURE__ */ jsx(WalletMultiButton, { className: "w-full !bg-primary text-primary-foreground" })
|
|
4313
|
-
] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
4314
|
-
/* @__PURE__ */ jsx("div", { className: "rounded-lg border border-amber-500/40 bg-amber-500/10 p-3 text-sm text-amber-200", children: "Your connected wallet is not registered with your account." }),
|
|
4315
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
4316
|
-
/* @__PURE__ */ jsx(Button, { onClick: registerWallet, disabled: isRegistering || walletConnection.isConnecting, className: "flex-1 bg-primary text-primary-foreground", children: isRegistering ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4317
|
-
/* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
|
|
4318
|
-
" Registering..."
|
|
4319
|
-
] }) : "Register This Wallet" }),
|
|
4320
|
-
/* @__PURE__ */ jsx(Button, { onClick: () => disconnect(), variant: "outline", className: "border-border", children: "Disconnect" })
|
|
4321
|
-
] })
|
|
4322
|
-
] }) : /* @__PURE__ */ jsx(WalletMultiButton, { className: "w-full !bg-primary text-primary-foreground" }) })
|
|
4323
|
-
] }) })
|
|
4324
|
-
] }),
|
|
4325
|
-
/* @__PURE__ */ jsx(AlertDialog, { open: !!walletToDelete, onOpenChange: () => setWalletToDelete(null), children: /* @__PURE__ */ jsxs(AlertDialogContent, { children: [
|
|
4326
|
-
/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
|
|
4327
|
-
/* @__PURE__ */ jsx(AlertDialogTitle, { children: "Remove Wallet" }),
|
|
4328
|
-
/* @__PURE__ */ jsx(AlertDialogDescription, { children: "Are you sure you want to remove this wallet from your account? This action cannot be undone." })
|
|
3114
|
+
] }) }),
|
|
3115
|
+
/* @__PURE__ */ jsx(Dialog, { open: Boolean(methodBeingReplaced), onOpenChange: (open) => !open && setMethodBeingReplaced(null), children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-h-[95vh] overflow-y-auto", children: [
|
|
3116
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
3117
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: t.replaceCardTitle }),
|
|
3118
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: t.replaceCardDescription })
|
|
4329
3119
|
] }),
|
|
4330
|
-
/* @__PURE__ */
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
3120
|
+
methodBeingReplaced && /* @__PURE__ */ jsx(
|
|
3121
|
+
CardDetailsForm,
|
|
3122
|
+
{
|
|
3123
|
+
visible: true,
|
|
3124
|
+
collectPrefix: `${collectPrefix}-replace-${methodBeingReplaced.id}`,
|
|
3125
|
+
submitting: replaceMutation.isPending,
|
|
3126
|
+
submitLabel: t.replaceCard,
|
|
3127
|
+
defaultValues: {
|
|
3128
|
+
email: userEmail ?? "",
|
|
3129
|
+
country: defaultCountry,
|
|
3130
|
+
provider
|
|
3131
|
+
},
|
|
3132
|
+
externalError: replaceMutation.error?.message ?? null,
|
|
3133
|
+
onTokenize: handleReplaceTokenize,
|
|
3134
|
+
className: "rounded-2xl border border-white/10 bg-white/5 p-6"
|
|
3135
|
+
}
|
|
3136
|
+
)
|
|
4334
3137
|
] }) })
|
|
4335
3138
|
] });
|
|
4336
3139
|
};
|
|
4337
|
-
var
|
|
4338
|
-
var Checkbox = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3140
|
+
var Checkbox = React17.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
4339
3141
|
CheckboxPrimitive.Root,
|
|
4340
3142
|
{
|
|
4341
3143
|
ref,
|
|
@@ -4532,7 +3334,7 @@ var useTokenBalance = (tokens) => {
|
|
|
4532
3334
|
},
|
|
4533
3335
|
[balances]
|
|
4534
3336
|
);
|
|
4535
|
-
const
|
|
3337
|
+
const hasSufficientBalance = useCallback(
|
|
4536
3338
|
(tokenSymbol, requiredAmount) => {
|
|
4537
3339
|
const balance = getTokenBalance(tokenSymbol);
|
|
4538
3340
|
return balance ? balance.uiAmount >= requiredAmount : false;
|
|
@@ -4593,106 +3395,16 @@ var useTokenBalance = (tokens) => {
|
|
|
4593
3395
|
error,
|
|
4594
3396
|
refreshBalances,
|
|
4595
3397
|
getTokenBalance,
|
|
4596
|
-
hasSufficientBalance
|
|
3398
|
+
hasSufficientBalance,
|
|
4597
3399
|
getFormattedBalance,
|
|
4598
3400
|
getTotalValue,
|
|
4599
3401
|
hasAnyBalance: positiveBalances.length > 0,
|
|
4600
3402
|
isConnected: !!publicKey
|
|
4601
3403
|
};
|
|
4602
3404
|
};
|
|
4603
|
-
var useDirectWalletPayment = () => {
|
|
4604
|
-
const { publicKey, signTransaction, connected } = useWallet();
|
|
4605
|
-
const solanaService = useSolanaService();
|
|
4606
|
-
const [paymentState, setPaymentState] = useState({
|
|
4607
|
-
loading: false,
|
|
4608
|
-
error: null,
|
|
4609
|
-
success: false,
|
|
4610
|
-
transactionId: null
|
|
4611
|
-
});
|
|
4612
|
-
const resetPayment = useCallback(() => {
|
|
4613
|
-
setPaymentState({
|
|
4614
|
-
loading: false,
|
|
4615
|
-
error: null,
|
|
4616
|
-
success: false,
|
|
4617
|
-
transactionId: null
|
|
4618
|
-
});
|
|
4619
|
-
}, []);
|
|
4620
|
-
const payWithWallet = useCallback(
|
|
4621
|
-
async (token, priceId) => {
|
|
4622
|
-
if (!connected || !publicKey || !signTransaction) {
|
|
4623
|
-
setPaymentState((prev) => ({
|
|
4624
|
-
...prev,
|
|
4625
|
-
error: "Wallet not connected. Please connect your wallet first."
|
|
4626
|
-
}));
|
|
4627
|
-
return;
|
|
4628
|
-
}
|
|
4629
|
-
setPaymentState({
|
|
4630
|
-
loading: true,
|
|
4631
|
-
error: null,
|
|
4632
|
-
success: false,
|
|
4633
|
-
transactionId: null
|
|
4634
|
-
});
|
|
4635
|
-
try {
|
|
4636
|
-
console.log("Generating payment transaction...", {
|
|
4637
|
-
token: token.symbol,
|
|
4638
|
-
priceId
|
|
4639
|
-
});
|
|
4640
|
-
const paymentData = await solanaService.generatePayment(
|
|
4641
|
-
priceId,
|
|
4642
|
-
token.symbol,
|
|
4643
|
-
publicKey.toBase58()
|
|
4644
|
-
);
|
|
4645
|
-
const transactionBuffer = Buffer.from(paymentData.transaction, "base64");
|
|
4646
|
-
const transaction = Transaction.from(transactionBuffer);
|
|
4647
|
-
console.log("Requesting wallet signature...");
|
|
4648
|
-
const signedTransaction = await signTransaction(transaction);
|
|
4649
|
-
const signedTransactionBase64 = signedTransaction.serialize().toString("base64");
|
|
4650
|
-
console.log("Submitting signed transaction...");
|
|
4651
|
-
const submitResult = await solanaService.submitPayment(
|
|
4652
|
-
signedTransactionBase64,
|
|
4653
|
-
priceId,
|
|
4654
|
-
paymentData.intent_id
|
|
4655
|
-
);
|
|
4656
|
-
setPaymentState({
|
|
4657
|
-
loading: false,
|
|
4658
|
-
error: null,
|
|
4659
|
-
success: true,
|
|
4660
|
-
transactionId: submitResult.transaction_id
|
|
4661
|
-
});
|
|
4662
|
-
console.log("Payment successful!", submitResult);
|
|
4663
|
-
} catch (err) {
|
|
4664
|
-
console.error("Payment failed:", err);
|
|
4665
|
-
let errorMessage = "Payment failed. Please try again.";
|
|
4666
|
-
const message = err instanceof Error ? err.message : typeof err === "string" ? err : "";
|
|
4667
|
-
if (message.includes("User rejected")) {
|
|
4668
|
-
errorMessage = "Payment cancelled by user.";
|
|
4669
|
-
} else if (/insufficient\s+funds/i.test(message)) {
|
|
4670
|
-
errorMessage = `Insufficient ${token.symbol} balance.`;
|
|
4671
|
-
} else if (/network/i.test(message)) {
|
|
4672
|
-
errorMessage = "Network error. Please check your connection.";
|
|
4673
|
-
} else if (message) {
|
|
4674
|
-
errorMessage = message;
|
|
4675
|
-
}
|
|
4676
|
-
setPaymentState({
|
|
4677
|
-
loading: false,
|
|
4678
|
-
error: errorMessage,
|
|
4679
|
-
success: false,
|
|
4680
|
-
transactionId: null
|
|
4681
|
-
});
|
|
4682
|
-
}
|
|
4683
|
-
},
|
|
4684
|
-
[connected, publicKey, signTransaction, solanaService]
|
|
4685
|
-
);
|
|
4686
|
-
return {
|
|
4687
|
-
paymentState,
|
|
4688
|
-
payWithWallet,
|
|
4689
|
-
resetPayment
|
|
4690
|
-
};
|
|
4691
|
-
};
|
|
4692
3405
|
var usePaymentStatus = (options = {}) => {
|
|
4693
3406
|
const { connection } = useConnection();
|
|
4694
|
-
const {
|
|
4695
|
-
const billingApi = services.billingApi;
|
|
3407
|
+
const { client } = usePaymentContext();
|
|
4696
3408
|
const {
|
|
4697
3409
|
transactionId,
|
|
4698
3410
|
purchaseId,
|
|
@@ -4765,14 +3477,8 @@ var usePaymentStatus = (options = {}) => {
|
|
|
4765
3477
|
const checkPaymentStatus = useCallback(
|
|
4766
3478
|
async (id) => {
|
|
4767
3479
|
try {
|
|
4768
|
-
|
|
4769
|
-
`/payment/status/${id}`
|
|
4770
|
-
);
|
|
4771
|
-
return data;
|
|
3480
|
+
return await client.getPaymentStatus(id);
|
|
4772
3481
|
} catch (error2) {
|
|
4773
|
-
if (error2?.status === 404) {
|
|
4774
|
-
return null;
|
|
4775
|
-
}
|
|
4776
3482
|
console.error("Failed to check payment status:", {
|
|
4777
3483
|
purchaseId: id,
|
|
4778
3484
|
error: error2
|
|
@@ -4780,7 +3486,7 @@ var usePaymentStatus = (options = {}) => {
|
|
|
4780
3486
|
return null;
|
|
4781
3487
|
}
|
|
4782
3488
|
},
|
|
4783
|
-
[
|
|
3489
|
+
[client]
|
|
4784
3490
|
);
|
|
4785
3491
|
const startMonitoring = useCallback(async () => {
|
|
4786
3492
|
if (isMonitoringRef.current || !transactionId && !purchaseId) {
|
|
@@ -4946,6 +3652,6 @@ var usePaymentStatus = (options = {}) => {
|
|
|
4946
3652
|
};
|
|
4947
3653
|
};
|
|
4948
3654
|
|
|
4949
|
-
export { BillingHistory, CancelMembershipDialog, CardDetailsForm,
|
|
3655
|
+
export { BillingHistory, CancelMembershipDialog, CardDetailsForm, ClientApiError, PaymentContext, PaymentExperience, PaymentMethodsSection, PaymentProvider, PaymentsDialogProvider, PaymentsUIPortalRoot, PaymentsUIRoot, SolanaPaymentSelector, SolanaPaymentView, StoredPaymentMethods, SubscriptionCheckoutModal, SubscriptionSuccessDialog, WalletDialog, WalletModal, createClient, usePaymentContext, usePaymentDialogs, usePaymentMethods, usePaymentNotifications, usePaymentStatus, useSolanaQrPayment, useSubscriptionActions, useSupportedTokens, useTokenBalance };
|
|
4950
3656
|
//# sourceMappingURL=index.js.map
|
|
4951
3657
|
//# sourceMappingURL=index.js.map
|