@doujins/payments-ui 0.0.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 ADDED
@@ -0,0 +1,2861 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var vanilla = require('zustand/vanilla');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var lucideReact = require('lucide-react');
7
+ var countryList = require('country-list');
8
+ var clsx = require('clsx');
9
+ var Dialog2 = require('@radix-ui/react-dialog');
10
+ var reactQuery = require('@tanstack/react-query');
11
+ var walletAdapterReact = require('@solana/wallet-adapter-react');
12
+ var buffer = require('buffer');
13
+ var web3_js = require('@solana/web3.js');
14
+ var splToken = require('@solana/spl-token');
15
+ var QRCode = require('qrcode');
16
+ var zustand = require('zustand');
17
+
18
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
19
+
20
+ function _interopNamespace(e) {
21
+ if (e && e.__esModule) return e;
22
+ var n = Object.create(null);
23
+ if (e) {
24
+ Object.keys(e).forEach(function (k) {
25
+ if (k !== 'default') {
26
+ var d = Object.getOwnPropertyDescriptor(e, k);
27
+ Object.defineProperty(n, k, d.get ? d : {
28
+ enumerable: true,
29
+ get: function () { return e[k]; }
30
+ });
31
+ }
32
+ });
33
+ }
34
+ n.default = e;
35
+ return Object.freeze(n);
36
+ }
37
+
38
+ var countryList__default = /*#__PURE__*/_interopDefault(countryList);
39
+ var clsx__default = /*#__PURE__*/_interopDefault(clsx);
40
+ var Dialog2__namespace = /*#__PURE__*/_interopNamespace(Dialog2);
41
+ var QRCode__default = /*#__PURE__*/_interopDefault(QRCode);
42
+
43
+ // src/context/PaymentContext.tsx
44
+
45
+ // src/utils/collect.ts
46
+ var SCRIPT_SRC = "https://secure.networkmerchants.com/token/Collect.js";
47
+ var loadCollectJs = (tokenizationKey) => {
48
+ if (typeof document === "undefined") return;
49
+ const trimmed = tokenizationKey?.trim();
50
+ if (!trimmed || trimmed.length < 10) {
51
+ console.warn("payments-ui: invalid Collect.js key, skipping load");
52
+ return;
53
+ }
54
+ const existing = document.querySelector(`script[src="${SCRIPT_SRC}"]`);
55
+ if (existing) return;
56
+ const script = document.createElement("script");
57
+ script.src = SCRIPT_SRC;
58
+ script.setAttribute("data-tokenization-key", trimmed);
59
+ script.setAttribute("data-field-ccnumber-placeholder", "0000 0000 0000 0000");
60
+ script.setAttribute("data-field-ccexp-placeholder", "10 / 25");
61
+ script.setAttribute("data-field-cvv-placeholder", "123");
62
+ script.setAttribute("data-variant", "inline");
63
+ script.async = true;
64
+ document.head.appendChild(script);
65
+ };
66
+
67
+ // src/services/apiClient.ts
68
+ var buildUrl = (baseUrl, path, { params, query }) => {
69
+ let resolved = `${baseUrl}${path}`;
70
+ if (params) {
71
+ Object.entries(params).forEach(([key, value]) => {
72
+ resolved = resolved.replace(`:${key}`, encodeURIComponent(String(value)));
73
+ });
74
+ }
75
+ if (query) {
76
+ const queryString = new URLSearchParams();
77
+ Object.entries(query).forEach(([key, value]) => {
78
+ if (value === void 0 || value === null) return;
79
+ queryString.append(key, String(value));
80
+ });
81
+ if ([...queryString.keys()].length > 0) {
82
+ resolved = `${resolved}?${queryString.toString()}`;
83
+ }
84
+ }
85
+ return resolved;
86
+ };
87
+ var createApiClient = (paymentConfig, baseUrl, fetcher, resolveAuthToken) => {
88
+ const request = async (method, path, options) => {
89
+ const url = buildUrl(baseUrl, path, options ?? {});
90
+ const headers = {
91
+ "Content-Type": "application/json",
92
+ ...paymentConfig.defaultHeaders ?? {},
93
+ ...options?.headers ?? {}
94
+ };
95
+ const token = await resolveAuthToken();
96
+ if (token) {
97
+ headers.Authorization = `Bearer ${token}`;
98
+ }
99
+ const response = await fetcher(url, {
100
+ method,
101
+ headers,
102
+ body: options?.body ? JSON.stringify(options.body) : void 0
103
+ });
104
+ if (!response.ok) {
105
+ const message = await response.text();
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
+ };
114
+ return {
115
+ request,
116
+ get: (path, options) => request("GET", path, options),
117
+ post: (path, options) => request("POST", path, options),
118
+ put: (path, options) => request("PUT", path, options),
119
+ patch: (path, options) => request("PATCH", path, options),
120
+ delete: (path, options) => request("DELETE", path, options)
121
+ };
122
+ };
123
+
124
+ // src/services/CardPaymentService.ts
125
+ var CardPaymentService = class {
126
+ constructor(config) {
127
+ this.config = config;
128
+ this.collectLoaded = false;
129
+ }
130
+ async ensureCollectLoaded() {
131
+ if (this.collectLoaded) return;
132
+ if (!this.config.collectJsKey) {
133
+ throw new Error("payments-ui: collect.js key missing");
134
+ }
135
+ loadCollectJs(this.config.collectJsKey);
136
+ this.collectLoaded = true;
137
+ }
138
+ buildCreatePayload(result) {
139
+ return {
140
+ payment_token: result.token,
141
+ first_name: result.billing.firstName,
142
+ last_name: result.billing.lastName,
143
+ address1: result.billing.address1,
144
+ address2: result.billing.address2,
145
+ city: result.billing.city,
146
+ state: result.billing.stateRegion,
147
+ zip: result.billing.postalCode,
148
+ country: result.billing.country,
149
+ email: result.billing.email,
150
+ provider: result.billing.provider
151
+ };
152
+ }
153
+ };
154
+
155
+ // src/services/PaymentMethodService.ts
156
+ var PaymentMethodService = class {
157
+ constructor(api) {
158
+ this.api = api;
159
+ }
160
+ async list(params) {
161
+ return this.api.get("/payment-methods", {
162
+ query: {
163
+ page: params?.page ?? 1,
164
+ page_size: params?.pageSize ?? 50
165
+ }
166
+ });
167
+ }
168
+ async create(payload) {
169
+ return this.api.post("/payment-methods", {
170
+ body: { ...payload }
171
+ });
172
+ }
173
+ async remove(id) {
174
+ await this.api.delete(`/payment-methods/${id}`);
175
+ }
176
+ async activate(id) {
177
+ await this.api.put(`/payment-methods/${id}/activate`);
178
+ }
179
+ };
180
+
181
+ // src/services/SolanaPaymentService.ts
182
+ var SolanaPaymentService = class {
183
+ constructor(api) {
184
+ this.api = api;
185
+ }
186
+ async generatePayment(priceId, token, userWallet) {
187
+ return this.api.post("/solana/generate", {
188
+ body: { price_id: priceId, token, user_wallet: userWallet }
189
+ });
190
+ }
191
+ async submitPayment(signedTransaction, priceId, intentId, memo) {
192
+ return this.api.post("/solana/submit", {
193
+ body: {
194
+ signed_transaction: signedTransaction,
195
+ price_id: priceId,
196
+ intent_id: intentId,
197
+ ...memo ? { memo } : {}
198
+ }
199
+ });
200
+ }
201
+ async fetchSupportedTokens() {
202
+ const response = await this.api.get(
203
+ "/solana/tokens"
204
+ );
205
+ return response.tokens;
206
+ }
207
+ async getSupportedTokens() {
208
+ return this.fetchSupportedTokens();
209
+ }
210
+ async generateQrCode(priceId, token, userWallet) {
211
+ return this.api.post("/solana/qr", {
212
+ body: {
213
+ price_id: priceId,
214
+ token,
215
+ ...userWallet ? { user_wallet: userWallet } : {}
216
+ }
217
+ });
218
+ }
219
+ async generateQRCode(priceId, token, userWallet) {
220
+ return this.generateQrCode(priceId, token, userWallet);
221
+ }
222
+ async checkPaymentStatus(reference, memo) {
223
+ return this.api.get("/solana/check", {
224
+ query: {
225
+ reference,
226
+ ...memo ? { memo } : {}
227
+ }
228
+ });
229
+ }
230
+ };
231
+
232
+ // src/services/TokenCatalog.ts
233
+ var TokenCatalog = class {
234
+ constructor(solanaService, options = {}) {
235
+ this.solanaService = solanaService;
236
+ this.options = options;
237
+ this.cache = [];
238
+ this.lastFetched = 0;
239
+ }
240
+ get ttl() {
241
+ return this.options.ttlMs ?? 5 * 60 * 1e3;
242
+ }
243
+ async getTokens(force = false) {
244
+ const isStale = Date.now() - this.lastFetched > this.ttl;
245
+ if (!force && this.cache.length > 0 && !isStale) {
246
+ return this.cache;
247
+ }
248
+ const tokens = await this.solanaService.fetchSupportedTokens();
249
+ this.cache = tokens;
250
+ this.lastFetched = Date.now();
251
+ return tokens;
252
+ }
253
+ getCached() {
254
+ return this.cache;
255
+ }
256
+ };
257
+
258
+ // src/services/WalletGateway.ts
259
+ var WalletGateway = class {
260
+ constructor() {
261
+ this.adapter = null;
262
+ }
263
+ setAdapter(adapter) {
264
+ this.adapter = adapter;
265
+ }
266
+ getPublicKey() {
267
+ if (!this.adapter?.publicKey) return null;
268
+ try {
269
+ return this.adapter.publicKey.toBase58();
270
+ } catch {
271
+ return null;
272
+ }
273
+ }
274
+ async sign(transaction) {
275
+ if (!this.adapter) {
276
+ throw new Error("payments-ui: wallet adapter not set");
277
+ }
278
+ if (typeof this.adapter.signVersionedTransaction === "function") {
279
+ return this.adapter.signVersionedTransaction(transaction);
280
+ }
281
+ if (typeof this.adapter.signTransaction === "function") {
282
+ return this.adapter.signTransaction(transaction);
283
+ }
284
+ throw new Error("payments-ui: wallet adapter cannot sign transactions");
285
+ }
286
+ };
287
+
288
+ // src/services/SubscriptionService.ts
289
+ var SubscriptionService = class {
290
+ constructor(api) {
291
+ this.api = api;
292
+ }
293
+ async subscribe(platform, payload) {
294
+ const body = this.serializePayload(platform, payload);
295
+ return this.api.post(
296
+ `/subscriptions/process/${platform}`,
297
+ {
298
+ body
299
+ }
300
+ );
301
+ }
302
+ async generateFlexFormUrl(payload) {
303
+ return this.api.post("/subscriptions/flexform", {
304
+ body: { ...payload }
305
+ });
306
+ }
307
+ serializePayload(platform, payload) {
308
+ if (platform === "nmi") {
309
+ const data2 = payload;
310
+ if (!data2.priceId) {
311
+ throw new Error("payments-ui: priceId is required for NMI subscriptions");
312
+ }
313
+ if (!data2.paymentToken && !data2.paymentMethodId) {
314
+ throw new Error(
315
+ "payments-ui: paymentToken or paymentMethodId is required for NMI subscriptions"
316
+ );
317
+ }
318
+ const body = {
319
+ price_id: data2.priceId,
320
+ processor: data2.processor ?? "nmi",
321
+ provider: data2.provider
322
+ };
323
+ if (data2.email) body.email = data2.email;
324
+ if (data2.firstName) body.first_name = data2.firstName;
325
+ if (data2.lastName) body.last_name = data2.lastName;
326
+ if (data2.address1) body.address1 = data2.address1;
327
+ if (data2.city) body.city = data2.city;
328
+ if (data2.state) body.state = data2.state;
329
+ if (data2.zipCode) body.zip = data2.zipCode;
330
+ if (data2.country) body.country = data2.country;
331
+ if (data2.paymentToken) body.payment_token = data2.paymentToken;
332
+ if (data2.paymentMethodId) body.payment_method_id = data2.paymentMethodId;
333
+ return body;
334
+ }
335
+ const data = payload;
336
+ if (!data.priceId) {
337
+ throw new Error("payments-ui: priceId is required for CCBill subscriptions");
338
+ }
339
+ return {
340
+ price_id: data.priceId,
341
+ processor: data.processor ?? "ccbill",
342
+ email: data.email,
343
+ first_name: data.firstName,
344
+ last_name: data.lastName,
345
+ zip: data.zipCode,
346
+ country: data.country
347
+ };
348
+ }
349
+ };
350
+
351
+ // src/core/PaymentApp.ts
352
+ var PaymentApp = class {
353
+ constructor(options) {
354
+ this.resolveAuthToken = async () => {
355
+ if (!this.config.getAuthToken) {
356
+ return null;
357
+ }
358
+ try {
359
+ const result = this.config.getAuthToken();
360
+ if (result instanceof Promise) {
361
+ return await result ?? null;
362
+ }
363
+ return result ?? null;
364
+ } catch (error) {
365
+ console.warn("payments-ui: failed to resolve auth token", error);
366
+ return null;
367
+ }
368
+ };
369
+ this.config = options.config;
370
+ this.fetcher = options.fetcher ?? options.config.fetcher ?? globalThis.fetch?.bind(globalThis);
371
+ if (!this.fetcher) {
372
+ throw new Error("payments-ui: fetch implementation is required");
373
+ }
374
+ this.services = this.createServices();
375
+ }
376
+ getConfig() {
377
+ return this.config;
378
+ }
379
+ getFetcher() {
380
+ return this.fetcher;
381
+ }
382
+ getServices() {
383
+ return this.services;
384
+ }
385
+ createServices() {
386
+ const billingApi = createApiClient(
387
+ this.config,
388
+ this.config.endpoints.billingBaseUrl,
389
+ this.fetcher,
390
+ this.resolveAuthToken
391
+ );
392
+ const accountBaseUrl = this.config.endpoints.accountBaseUrl ?? this.config.endpoints.billingBaseUrl;
393
+ const accountApi = createApiClient(
394
+ this.config,
395
+ accountBaseUrl,
396
+ this.fetcher,
397
+ this.resolveAuthToken
398
+ );
399
+ const solanaPayments = new SolanaPaymentService(billingApi);
400
+ const paymentMethods = new PaymentMethodService(accountApi);
401
+ const cardPayments = new CardPaymentService(this.config);
402
+ const walletGateway = new WalletGateway();
403
+ const tokenCatalog = new TokenCatalog(solanaPayments);
404
+ const subscriptions = new SubscriptionService(billingApi);
405
+ return {
406
+ cardPayments,
407
+ paymentMethods,
408
+ solanaPayments,
409
+ tokenCatalog,
410
+ walletGateway,
411
+ subscriptions,
412
+ billingApi,
413
+ accountApi
414
+ };
415
+ }
416
+ };
417
+ var initialState = {
418
+ selectedMethodId: null,
419
+ solanaModalOpen: false,
420
+ savedPaymentStatus: "idle",
421
+ savedPaymentError: null,
422
+ newCardStatus: "idle",
423
+ newCardError: null,
424
+ solanaTab: "wallet",
425
+ solanaStatus: "selecting",
426
+ solanaError: null,
427
+ solanaTransactionId: null,
428
+ solanaSelectedToken: null,
429
+ solanaTokenAmount: 0
430
+ };
431
+ var createPaymentStore = (options) => vanilla.createStore((set, get) => {
432
+ const notifyStatus = (status, context) => {
433
+ options?.callbacks?.onStatusChange?.({ status, context });
434
+ };
435
+ const notifySuccess = (payload) => {
436
+ if (!options?.callbacks?.onSuccess) return;
437
+ options.callbacks.onSuccess(payload ?? {});
438
+ };
439
+ const notifyError = (error) => {
440
+ options?.callbacks?.onError?.(new Error(error));
441
+ };
442
+ return {
443
+ ...initialState,
444
+ setSelectedMethod: (methodId) => set({ selectedMethodId: methodId }),
445
+ setSolanaModalOpen: (open) => set({ solanaModalOpen: open }),
446
+ setSolanaTab: (tab) => set({ solanaTab: tab }),
447
+ setSolanaSelectedToken: (symbol) => set({ solanaSelectedToken: symbol }),
448
+ setSolanaTokenAmount: (amount) => set({ solanaTokenAmount: amount }),
449
+ setSolanaTransactionId: (txId) => set({ solanaTransactionId: txId }),
450
+ startSavedPayment: () => {
451
+ notifyStatus("processing", { source: "saved-payment" });
452
+ set({ savedPaymentStatus: "processing", savedPaymentError: null });
453
+ },
454
+ completeSavedPayment: () => {
455
+ notifyStatus("success", { source: "saved-payment" });
456
+ set({ savedPaymentStatus: "success", savedPaymentError: null });
457
+ },
458
+ failSavedPayment: (error) => {
459
+ notifyStatus("error", { source: "saved-payment" });
460
+ notifyError(error);
461
+ set({ savedPaymentStatus: "error", savedPaymentError: error });
462
+ },
463
+ resetSavedPayment: () => set({ savedPaymentStatus: "idle", savedPaymentError: null }),
464
+ startNewCardPayment: () => {
465
+ notifyStatus("processing", { source: "new-card" });
466
+ set({ newCardStatus: "processing", newCardError: null });
467
+ },
468
+ completeNewCardPayment: () => {
469
+ notifyStatus("success", { source: "new-card" });
470
+ set({ newCardStatus: "success", newCardError: null });
471
+ },
472
+ failNewCardPayment: (error) => {
473
+ notifyStatus("error", { source: "new-card" });
474
+ notifyError(error);
475
+ set({ newCardStatus: "error", newCardError: error });
476
+ },
477
+ resetNewCardPayment: () => set({ newCardStatus: "idle", newCardError: null }),
478
+ startSolanaPayment: () => {
479
+ notifyStatus("processing", { source: "solana" });
480
+ set({ solanaStatus: "processing", solanaError: null });
481
+ },
482
+ confirmSolanaPayment: () => set({ solanaStatus: "confirming" }),
483
+ completeSolanaPayment: (payload) => {
484
+ notifyStatus("success", { source: "solana" });
485
+ notifySuccess(payload);
486
+ set({ solanaStatus: "success", solanaError: null });
487
+ },
488
+ failSolanaPayment: (error) => {
489
+ notifyStatus("error", { source: "solana" });
490
+ notifyError(error);
491
+ set({ solanaStatus: "error", solanaError: error });
492
+ },
493
+ resetSolanaPayment: () => set({
494
+ solanaStatus: "selecting",
495
+ solanaError: null,
496
+ solanaTransactionId: null
497
+ }),
498
+ resetAll: () => set(initialState)
499
+ };
500
+ });
501
+ var PaymentContext = react.createContext(void 0);
502
+ var PaymentProvider = ({
503
+ config,
504
+ children
505
+ }) => {
506
+ const app = react.useMemo(() => new PaymentApp({ config }), [config]);
507
+ const store = react.useMemo(
508
+ () => createPaymentStore({ callbacks: config.callbacks }),
509
+ [config.callbacks]
510
+ );
511
+ const value = react.useMemo(() => {
512
+ return {
513
+ config: app.getConfig(),
514
+ fetcher: app.getFetcher(),
515
+ resolveAuthToken: app.resolveAuthToken,
516
+ app,
517
+ services: app.getServices(),
518
+ store
519
+ };
520
+ }, [app, store]);
521
+ react.useEffect(() => {
522
+ if (!value.config.collectJsKey) return;
523
+ loadCollectJs(value.config.collectJsKey);
524
+ }, [value.config.collectJsKey]);
525
+ return /* @__PURE__ */ jsxRuntime.jsx(PaymentContext.Provider, { value, children });
526
+ };
527
+ var usePaymentContext = () => {
528
+ const context = react.useContext(PaymentContext);
529
+ if (!context) {
530
+ throw new Error("usePaymentContext must be used within a PaymentProvider");
531
+ }
532
+ return context;
533
+ };
534
+ var customCountries = [
535
+ { code: "TW", name: "Taiwan, Province of China" },
536
+ { code: "KR", name: "Korea" },
537
+ { code: "KP", name: "North Korea" },
538
+ { code: "PS", name: "Palestine, State of" },
539
+ { code: "VA", name: "Holy See (Vatican City State)" },
540
+ { code: "BQ", name: "Bonaire, Sint Eustatius and Saba" },
541
+ { code: "SX", name: "Sint Maarten (Dutch part)" },
542
+ { code: "MK", name: "North Macedonia" },
543
+ { code: "SZ", name: "Eswatini" },
544
+ { code: "FM", name: "Micronesia, Federated States Of" },
545
+ { code: "TR", name: "T\xFCrkiye" }
546
+ ];
547
+ countryList__default.default.overwrite(customCountries);
548
+ var countries = countryList__default.default.getData().sort((a, b) => a.name.localeCompare(b.name));
549
+
550
+ // src/hooks/useCountryDropdown.ts
551
+ function useCountryDropdown() {
552
+ const [country, setCountry] = react.useState("");
553
+ const [countryOpen, setCountryOpen] = react.useState(false);
554
+ const [countrySearch, setCountrySearch] = react.useState("");
555
+ const countryDropdownRef = react.useRef(null);
556
+ const filteredCountries = countries.filter(
557
+ (country2) => country2.name.toLowerCase().includes(countrySearch.toLowerCase())
558
+ );
559
+ react.useEffect(() => {
560
+ function handleClickOutside(event) {
561
+ if (countryDropdownRef.current && !countryDropdownRef.current.contains(event.target)) {
562
+ setCountryOpen(false);
563
+ }
564
+ }
565
+ document.addEventListener("mousedown", handleClickOutside);
566
+ return () => {
567
+ document.removeEventListener("mousedown", handleClickOutside);
568
+ };
569
+ }, []);
570
+ return {
571
+ country,
572
+ setCountry,
573
+ countryOpen,
574
+ setCountryOpen,
575
+ countrySearch,
576
+ setCountrySearch,
577
+ countryDropdownRef,
578
+ filteredCountries
579
+ };
580
+ }
581
+ var defaultBilling = {
582
+ firstName: "",
583
+ lastName: "",
584
+ address1: "",
585
+ address2: "",
586
+ city: "",
587
+ stateRegion: "",
588
+ postalCode: "",
589
+ country: "US",
590
+ email: "",
591
+ provider: "mobius"
592
+ };
593
+ var buildSelector = (prefix, field) => `#${prefix}-${field}`;
594
+ var CardDetailsForm = ({
595
+ visible,
596
+ onTokenize,
597
+ submitLabel,
598
+ submitting = false,
599
+ defaultValues,
600
+ externalError,
601
+ collectPrefix = "card-form",
602
+ className,
603
+ onBillingChange,
604
+ submitDisabled = false
605
+ }) => {
606
+ const { config } = usePaymentContext();
607
+ const defaultValuesKey = react.useMemo(() => JSON.stringify(defaultValues ?? {}), [defaultValues]);
608
+ const mergedDefaults = react.useMemo(
609
+ () => ({
610
+ ...defaultBilling,
611
+ ...defaultValues,
612
+ email: defaultValues?.email ?? config.defaultUser?.email ?? defaultBilling.email
613
+ }),
614
+ [defaultValuesKey, config.defaultUser?.email]
615
+ );
616
+ const [firstName, setFirstName] = react.useState(mergedDefaults.firstName);
617
+ const [lastName, setLastName] = react.useState(mergedDefaults.lastName);
618
+ const [address1, setAddress1] = react.useState(mergedDefaults.address1);
619
+ const [address2, setAddress2] = react.useState(mergedDefaults.address2 ?? "");
620
+ const [city, setCity] = react.useState(mergedDefaults.city);
621
+ const [stateRegion, setStateRegion] = react.useState(mergedDefaults.stateRegion ?? "");
622
+ const [postalCode, setPostalCode] = react.useState(mergedDefaults.postalCode);
623
+ const [country, setCountry] = react.useState(mergedDefaults.country);
624
+ const [email, setEmail] = react.useState(mergedDefaults.email ?? "");
625
+ const [localError, setLocalError] = react.useState(null);
626
+ const [isTokenizing, setIsTokenizing] = react.useState(false);
627
+ const {
628
+ countryDropdownRef,
629
+ countryOpen,
630
+ setCountryOpen,
631
+ countrySearch,
632
+ setCountrySearch,
633
+ filteredCountries
634
+ } = useCountryDropdown();
635
+ react.useEffect(() => {
636
+ if (!visible) {
637
+ setLocalError(null);
638
+ setIsTokenizing(false);
639
+ }
640
+ }, [visible]);
641
+ react.useEffect(() => {
642
+ if (!visible) return;
643
+ setFirstName(mergedDefaults.firstName);
644
+ setLastName(mergedDefaults.lastName);
645
+ setAddress1(mergedDefaults.address1);
646
+ setAddress2(mergedDefaults.address2 ?? "");
647
+ setCity(mergedDefaults.city);
648
+ setStateRegion(mergedDefaults.stateRegion ?? "");
649
+ setPostalCode(mergedDefaults.postalCode);
650
+ setCountry(mergedDefaults.country);
651
+ setEmail(mergedDefaults.email ?? "");
652
+ }, [defaultValuesKey, mergedDefaults, visible]);
653
+ react.useEffect(() => {
654
+ if (!onBillingChange) return;
655
+ onBillingChange({
656
+ firstName,
657
+ lastName,
658
+ address1,
659
+ address2,
660
+ city,
661
+ stateRegion,
662
+ postalCode,
663
+ country,
664
+ email,
665
+ provider: mergedDefaults.provider ?? "mobius"
666
+ });
667
+ }, [
668
+ firstName,
669
+ lastName,
670
+ address1,
671
+ address2,
672
+ city,
673
+ stateRegion,
674
+ postalCode,
675
+ country,
676
+ email,
677
+ mergedDefaults.provider,
678
+ onBillingChange
679
+ ]);
680
+ react.useEffect(() => {
681
+ if (typeof window === "undefined" || !window.CollectJS || !visible) {
682
+ return;
683
+ }
684
+ const handlers = window.__doujinsCollectHandlers || (window.__doujinsCollectHandlers = {});
685
+ handlers[collectPrefix] = (response) => {
686
+ setIsTokenizing(false);
687
+ if (!response.token) {
688
+ setLocalError("Payment tokenization failed. Please try again.");
689
+ return;
690
+ }
691
+ const billing = {
692
+ firstName,
693
+ lastName,
694
+ address1,
695
+ address2,
696
+ city,
697
+ stateRegion,
698
+ postalCode,
699
+ country,
700
+ email,
701
+ provider: mergedDefaults.provider ?? "mobius"
702
+ };
703
+ onTokenize(response.token, billing);
704
+ };
705
+ const configured = window.__doujinsCollectConfigured || (window.__doujinsCollectConfigured = {});
706
+ if (!configured[collectPrefix]) {
707
+ window.CollectJS.configure({
708
+ variant: "inline",
709
+ fields: {
710
+ ccnumber: { selector: buildSelector(collectPrefix, "ccnumber") },
711
+ ccexp: { selector: buildSelector(collectPrefix, "ccexp") },
712
+ cvv: { selector: buildSelector(collectPrefix, "cvv") }
713
+ },
714
+ callback: (response) => {
715
+ const fn = window.__doujinsCollectHandlers?.[collectPrefix];
716
+ fn?.(response);
717
+ }
718
+ });
719
+ configured[collectPrefix] = true;
720
+ }
721
+ }, [
722
+ collectPrefix,
723
+ firstName,
724
+ lastName,
725
+ address1,
726
+ address2,
727
+ city,
728
+ stateRegion,
729
+ postalCode,
730
+ country,
731
+ email,
732
+ mergedDefaults.provider,
733
+ onTokenize,
734
+ visible
735
+ ]);
736
+ const validate = () => {
737
+ if (!firstName.trim() || !lastName.trim() || !address1.trim() || !city.trim() || !postalCode.trim() || !country.trim() || !email.trim()) {
738
+ setLocalError("Please complete all required billing fields.");
739
+ return false;
740
+ }
741
+ setLocalError(null);
742
+ return true;
743
+ };
744
+ const handleSubmit = (event) => {
745
+ event.preventDefault();
746
+ if (!validate()) return;
747
+ if (!window.CollectJS) {
748
+ setLocalError("Payment form is not ready. Please try again later.");
749
+ return;
750
+ }
751
+ setIsTokenizing(true);
752
+ window.CollectJS.startPaymentRequest();
753
+ };
754
+ const errorMessage = localError ?? externalError;
755
+ return /* @__PURE__ */ jsxRuntime.jsxs(
756
+ "form",
757
+ {
758
+ className: clsx__default.default("payments-ui-card-form", className),
759
+ onSubmit: handleSubmit,
760
+ noValidate: true,
761
+ children: [
762
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-grid", children: [
763
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "payments-ui-label", children: [
764
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
765
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, { className: "payments-ui-icon" }),
766
+ " First name"
767
+ ] }),
768
+ /* @__PURE__ */ jsxRuntime.jsx(
769
+ "input",
770
+ {
771
+ className: "payments-ui-input",
772
+ value: firstName,
773
+ onChange: (e) => setFirstName(e.target.value),
774
+ required: true
775
+ }
776
+ )
777
+ ] }),
778
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "payments-ui-label", children: [
779
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
780
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, { className: "payments-ui-icon" }),
781
+ " Last name"
782
+ ] }),
783
+ /* @__PURE__ */ jsxRuntime.jsx(
784
+ "input",
785
+ {
786
+ className: "payments-ui-input",
787
+ value: lastName,
788
+ onChange: (e) => setLastName(e.target.value),
789
+ required: true
790
+ }
791
+ )
792
+ ] })
793
+ ] }),
794
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "payments-ui-label", children: [
795
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Email" }),
796
+ /* @__PURE__ */ jsxRuntime.jsx(
797
+ "input",
798
+ {
799
+ type: "email",
800
+ className: "payments-ui-input",
801
+ value: email,
802
+ onChange: (e) => setEmail(e.target.value),
803
+ required: true
804
+ }
805
+ )
806
+ ] }),
807
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "payments-ui-label", children: [
808
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Address line 1" }),
809
+ /* @__PURE__ */ jsxRuntime.jsx(
810
+ "input",
811
+ {
812
+ className: "payments-ui-input",
813
+ value: address1,
814
+ onChange: (e) => setAddress1(e.target.value),
815
+ required: true
816
+ }
817
+ )
818
+ ] }),
819
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "payments-ui-label", children: [
820
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Address line 2 (optional)" }),
821
+ /* @__PURE__ */ jsxRuntime.jsx(
822
+ "input",
823
+ {
824
+ className: "payments-ui-input",
825
+ value: address2,
826
+ onChange: (e) => setAddress2(e.target.value)
827
+ }
828
+ )
829
+ ] }),
830
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-grid", children: [
831
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "payments-ui-label", children: [
832
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "City" }),
833
+ /* @__PURE__ */ jsxRuntime.jsx(
834
+ "input",
835
+ {
836
+ className: "payments-ui-input",
837
+ value: city,
838
+ onChange: (e) => setCity(e.target.value),
839
+ required: true
840
+ }
841
+ )
842
+ ] }),
843
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "payments-ui-label", children: [
844
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "State / Region" }),
845
+ /* @__PURE__ */ jsxRuntime.jsx(
846
+ "input",
847
+ {
848
+ className: "payments-ui-input",
849
+ value: stateRegion,
850
+ onChange: (e) => setStateRegion(e.target.value)
851
+ }
852
+ )
853
+ ] })
854
+ ] }),
855
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-grid", children: [
856
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "payments-ui-label", children: [
857
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
858
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MapPin, { className: "payments-ui-icon" }),
859
+ " Postal code"
860
+ ] }),
861
+ /* @__PURE__ */ jsxRuntime.jsx(
862
+ "input",
863
+ {
864
+ className: "payments-ui-input",
865
+ value: postalCode,
866
+ onChange: (e) => setPostalCode(e.target.value),
867
+ required: true
868
+ }
869
+ )
870
+ ] }),
871
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-label", children: [
872
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Country" }),
873
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-country", ref: countryDropdownRef, children: [
874
+ /* @__PURE__ */ jsxRuntime.jsxs(
875
+ "button",
876
+ {
877
+ type: "button",
878
+ className: "payments-ui-country-toggle",
879
+ onClick: () => setCountryOpen((prev) => !prev),
880
+ children: [
881
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: country }),
882
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "payments-ui-icon" })
883
+ ]
884
+ }
885
+ ),
886
+ countryOpen && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-country-menu", children: [
887
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-country-search", children: [
888
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "payments-ui-icon" }),
889
+ /* @__PURE__ */ jsxRuntime.jsx(
890
+ "input",
891
+ {
892
+ placeholder: "Search country",
893
+ value: countrySearch,
894
+ onChange: (e) => setCountrySearch(e.target.value)
895
+ }
896
+ )
897
+ ] }),
898
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { children: filteredCountries.map((option) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
899
+ "button",
900
+ {
901
+ type: "button",
902
+ onClick: () => {
903
+ setCountry(option.code);
904
+ setCountryOpen(false);
905
+ },
906
+ children: option.name
907
+ }
908
+ ) }, option.code)) })
909
+ ] })
910
+ ] })
911
+ ] })
912
+ ] }),
913
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-label", children: [
914
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Card number" }),
915
+ /* @__PURE__ */ jsxRuntime.jsx(
916
+ "div",
917
+ {
918
+ id: buildSelector(collectPrefix, "ccnumber").slice(1),
919
+ className: "payments-ui-collect-field"
920
+ }
921
+ )
922
+ ] }),
923
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-grid", children: [
924
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-label", children: [
925
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Expiry" }),
926
+ /* @__PURE__ */ jsxRuntime.jsx(
927
+ "div",
928
+ {
929
+ id: buildSelector(collectPrefix, "ccexp").slice(1),
930
+ className: "payments-ui-collect-field"
931
+ }
932
+ )
933
+ ] }),
934
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-label", children: [
935
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "CVV" }),
936
+ /* @__PURE__ */ jsxRuntime.jsx(
937
+ "div",
938
+ {
939
+ id: buildSelector(collectPrefix, "cvv").slice(1),
940
+ className: "payments-ui-collect-field"
941
+ }
942
+ )
943
+ ] })
944
+ ] }),
945
+ errorMessage && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payments-ui-error", children: errorMessage }),
946
+ /* @__PURE__ */ jsxRuntime.jsxs(
947
+ "button",
948
+ {
949
+ type: "submit",
950
+ className: "payments-ui-button",
951
+ disabled: submitting || isTokenizing || submitDisabled,
952
+ children: [
953
+ (submitting || isTokenizing) && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "payments-ui-spinner" }),
954
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CreditCard, { className: "payments-ui-icon" }),
955
+ submitting || isTokenizing ? "Processing..." : submitLabel
956
+ ]
957
+ }
958
+ )
959
+ ]
960
+ }
961
+ );
962
+ };
963
+ var usePaymentMethodService = () => {
964
+ const { services } = usePaymentContext();
965
+ return react.useMemo(() => services.paymentMethods, [services]);
966
+ };
967
+
968
+ // src/hooks/usePaymentMethods.ts
969
+ var PAYMENT_METHODS_KEY = ["payments-ui", "payment-methods"];
970
+ var usePaymentMethods = () => {
971
+ const service = usePaymentMethodService();
972
+ const queryClient = reactQuery.useQueryClient();
973
+ const listQuery = reactQuery.useQuery({
974
+ queryKey: PAYMENT_METHODS_KEY,
975
+ queryFn: () => service.list({ pageSize: 50 })
976
+ });
977
+ const createMutation = reactQuery.useMutation({
978
+ mutationFn: ({ token, billing }) => {
979
+ const payload = {
980
+ payment_token: token,
981
+ first_name: billing.firstName,
982
+ last_name: billing.lastName,
983
+ address1: billing.address1,
984
+ address2: billing.address2,
985
+ city: billing.city,
986
+ state: billing.stateRegion,
987
+ zip: billing.postalCode,
988
+ country: billing.country,
989
+ email: billing.email,
990
+ provider: billing.provider
991
+ };
992
+ return service.create(payload);
993
+ },
994
+ onSuccess: () => {
995
+ void queryClient.invalidateQueries({ queryKey: PAYMENT_METHODS_KEY });
996
+ }
997
+ });
998
+ const deleteMutation = reactQuery.useMutation({
999
+ mutationFn: ({ id }) => service.remove(id),
1000
+ onSuccess: () => {
1001
+ void queryClient.invalidateQueries({ queryKey: PAYMENT_METHODS_KEY });
1002
+ }
1003
+ });
1004
+ return {
1005
+ listQuery,
1006
+ createMutation,
1007
+ deleteMutation
1008
+ };
1009
+ };
1010
+ var formatCardLabel = (method) => {
1011
+ const brand = method.card_type ? method.card_type.toUpperCase() : "CARD";
1012
+ const lastFour = method.last_four ? `\u2022\u2022\u2022\u2022 ${method.last_four}` : "";
1013
+ return `${brand} ${lastFour}`.trim();
1014
+ };
1015
+ var StoredPaymentMethods = ({
1016
+ selectedMethodId,
1017
+ onMethodSelect,
1018
+ showAddButton = true,
1019
+ heading = "Payment Methods",
1020
+ description = "Manage your saved cards"
1021
+ }) => {
1022
+ const { listQuery, createMutation, deleteMutation } = usePaymentMethods();
1023
+ const [isModalOpen, setIsModalOpen] = react.useState(false);
1024
+ const [deletingId, setDeletingId] = react.useState(null);
1025
+ const payments = react.useMemo(() => listQuery.data?.data ?? [], [listQuery.data]);
1026
+ const handleCardTokenize = (token, billing) => {
1027
+ createMutation.mutate({ token, billing });
1028
+ };
1029
+ const handleDelete = (method) => {
1030
+ setDeletingId(method.id);
1031
+ deleteMutation.mutate(
1032
+ { id: method.id },
1033
+ {
1034
+ onSettled: () => setDeletingId(null)
1035
+ }
1036
+ );
1037
+ };
1038
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-panel", children: [
1039
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-panel-header", children: [
1040
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1041
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "payments-ui-panel-title", children: [
1042
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.WalletCards, { className: "payments-ui-icon" }),
1043
+ " ",
1044
+ heading
1045
+ ] }),
1046
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payments-ui-panel-description", children: description })
1047
+ ] }),
1048
+ showAddButton && /* @__PURE__ */ jsxRuntime.jsxs(
1049
+ "button",
1050
+ {
1051
+ className: "payments-ui-button",
1052
+ type: "button",
1053
+ onClick: () => setIsModalOpen(true),
1054
+ children: [
1055
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CreditCard, { className: "payments-ui-icon" }),
1056
+ " Add card"
1057
+ ]
1058
+ }
1059
+ )
1060
+ ] }),
1061
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payments-ui-panel-body", children: listQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-empty", children: [
1062
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "payments-ui-spinner" }),
1063
+ " Loading cards..."
1064
+ ] }) : payments.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payments-ui-empty", children: "No saved payment methods yet." }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payments-ui-method-list", children: payments.map((method) => {
1065
+ const isSelected = selectedMethodId === method.id;
1066
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1067
+ "div",
1068
+ {
1069
+ className: clsx__default.default("payments-ui-method-item", {
1070
+ "is-selected": isSelected
1071
+ }),
1072
+ children: [
1073
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1074
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payments-ui-method-label", children: formatCardLabel(method) }),
1075
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "payments-ui-method-meta", children: [
1076
+ "Added on",
1077
+ " ",
1078
+ method.created_at ? new Date(method.created_at).toLocaleDateString() : "unknown"
1079
+ ] })
1080
+ ] }),
1081
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-method-actions", children: [
1082
+ onMethodSelect && /* @__PURE__ */ jsxRuntime.jsx(
1083
+ "button",
1084
+ {
1085
+ type: "button",
1086
+ className: "payments-ui-text-button",
1087
+ onClick: () => onMethodSelect(method),
1088
+ children: isSelected ? "Selected" : "Use card"
1089
+ }
1090
+ ),
1091
+ /* @__PURE__ */ jsxRuntime.jsx(
1092
+ "button",
1093
+ {
1094
+ type: "button",
1095
+ className: "payments-ui-icon-button payments-ui-danger",
1096
+ onClick: () => handleDelete(method),
1097
+ disabled: deletingId === method.id && deleteMutation.isPending,
1098
+ children: deletingId === method.id && deleteMutation.isPending ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "payments-ui-spinner" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "payments-ui-icon" })
1099
+ }
1100
+ )
1101
+ ] })
1102
+ ]
1103
+ },
1104
+ method.id
1105
+ );
1106
+ }) }) }),
1107
+ /* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Root, { open: isModalOpen, onOpenChange: setIsModalOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Portal, { children: [
1108
+ /* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Overlay, { className: "payments-ui-modal-overlay" }),
1109
+ /* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Content, { className: "payments-ui-modal", children: [
1110
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-modal-header", children: [
1111
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1112
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { children: "Add a new card" }),
1113
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Your card details are tokenized securely via our payment provider." })
1114
+ ] }),
1115
+ /* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Close, { className: "payments-ui-icon-button", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "payments-ui-icon" }) })
1116
+ ] }),
1117
+ /* @__PURE__ */ jsxRuntime.jsx(
1118
+ CardDetailsForm,
1119
+ {
1120
+ visible: isModalOpen,
1121
+ collectPrefix: "payments-ui-card",
1122
+ submitting: createMutation.isPending,
1123
+ submitLabel: "Save card",
1124
+ externalError: createMutation.error?.message ?? null,
1125
+ onTokenize: handleCardTokenize
1126
+ }
1127
+ )
1128
+ ] })
1129
+ ] }) })
1130
+ ] });
1131
+ };
1132
+ var useSolanaService = () => {
1133
+ const { services } = usePaymentContext();
1134
+ return react.useMemo(() => services.solanaPayments, [services]);
1135
+ };
1136
+ var getSolBalance = async (connection, publicKey) => {
1137
+ try {
1138
+ const lamports = await connection.getBalance(publicKey);
1139
+ return lamports / web3_js.LAMPORTS_PER_SOL;
1140
+ } catch (error) {
1141
+ console.error("Failed to fetch SOL balance:", error);
1142
+ return 0;
1143
+ }
1144
+ };
1145
+ var fetchAllTokenBalances = async (connection, publicKey) => {
1146
+ const balances = /* @__PURE__ */ new Map();
1147
+ try {
1148
+ const tokenAccounts = await connection.getParsedProgramAccounts(
1149
+ splToken.TOKEN_PROGRAM_ID,
1150
+ {
1151
+ filters: [
1152
+ {
1153
+ dataSize: 165
1154
+ // Size of token account
1155
+ },
1156
+ {
1157
+ memcmp: {
1158
+ offset: 32,
1159
+ // Owner field offset
1160
+ bytes: publicKey.toString()
1161
+ }
1162
+ }
1163
+ ]
1164
+ }
1165
+ );
1166
+ for (const account of tokenAccounts) {
1167
+ const data = account.account.data;
1168
+ const parsed = data.parsed;
1169
+ const info = parsed?.info;
1170
+ if (info && info.tokenAmount) {
1171
+ const mintAddress = info.mint;
1172
+ const uiAmount = info.tokenAmount.uiAmount || 0;
1173
+ if (uiAmount > 0) {
1174
+ balances.set(mintAddress, uiAmount);
1175
+ }
1176
+ }
1177
+ }
1178
+ } catch (error) {
1179
+ console.error("Failed to fetch token balances:", error);
1180
+ }
1181
+ return balances;
1182
+ };
1183
+ var fetchSupportedTokenBalances = async (connection, publicKey, supportedTokens) => {
1184
+ const results = [];
1185
+ const solBalance = await getSolBalance(connection, publicKey);
1186
+ const solTokenMeta = supportedTokens.find((token) => token.is_native || token.symbol === "SOL") || {
1187
+ symbol: "SOL",
1188
+ name: "Solana",
1189
+ mint: "So11111111111111111111111111111111111111112",
1190
+ decimals: 9};
1191
+ results.push({
1192
+ mint: solTokenMeta.mint,
1193
+ symbol: solTokenMeta.symbol,
1194
+ name: solTokenMeta.name,
1195
+ balance: solBalance,
1196
+ uiBalance: solBalance.toFixed(solTokenMeta.decimals <= 4 ? solTokenMeta.decimals : 4),
1197
+ decimals: solTokenMeta.decimals,
1198
+ isNative: true
1199
+ });
1200
+ const tokenBalances = await fetchAllTokenBalances(connection, publicKey);
1201
+ const tokenMetaByMint = new Map(
1202
+ supportedTokens.filter((token) => !(token.is_native || token.symbol === "SOL")).map((token) => [token.mint, token])
1203
+ );
1204
+ for (const [mint, tokenMeta] of tokenMetaByMint.entries()) {
1205
+ const balance = tokenBalances.get(mint) || 0;
1206
+ results.push({
1207
+ mint,
1208
+ symbol: tokenMeta.symbol,
1209
+ name: tokenMeta.name,
1210
+ balance,
1211
+ uiBalance: balance.toFixed(tokenMeta.decimals <= 6 ? tokenMeta.decimals : 6),
1212
+ decimals: tokenMeta.decimals,
1213
+ isNative: false
1214
+ });
1215
+ }
1216
+ return results;
1217
+ };
1218
+ var hasSufficientBalance = (balance, requiredAmount, buffer = 0.05) => {
1219
+ const requiredWithBuffer = requiredAmount * (1 + buffer);
1220
+ return balance >= requiredWithBuffer;
1221
+ };
1222
+
1223
+ // src/hooks/useSolanaDirectPayment.ts
1224
+ var useSolanaDirectPayment = (options) => {
1225
+ const { priceId, tokenAmount, selectedToken, supportedTokens, onStart, onConfirming, onSuccess, onError } = options;
1226
+ const { connected, publicKey, wallet, signTransaction } = walletAdapterReact.useWallet();
1227
+ const { config } = usePaymentContext();
1228
+ const solanaService = useSolanaService();
1229
+ const [tokenBalance, setTokenBalance] = react.useState(null);
1230
+ const [isBalanceLoading, setIsBalanceLoading] = react.useState(false);
1231
+ const [isProcessing, setIsProcessing] = react.useState(false);
1232
+ const connection = react.useMemo(() => {
1233
+ const rpc = config.solanaRpcUrl ?? "https://api.mainnet-beta.solana.com";
1234
+ return new web3_js.Connection(rpc);
1235
+ }, [config.solanaRpcUrl]);
1236
+ const fetchTokenBalance = react.useCallback(async () => {
1237
+ if (!connected || !publicKey || !selectedToken) {
1238
+ setTokenBalance({ balance: 0, hasBalance: false });
1239
+ return;
1240
+ }
1241
+ try {
1242
+ setIsBalanceLoading(true);
1243
+ const balances = await fetchSupportedTokenBalances(
1244
+ connection,
1245
+ publicKey,
1246
+ supportedTokens
1247
+ );
1248
+ const balanceInfo = balances.find(
1249
+ (tb) => tb.symbol === selectedToken.symbol || tb.mint === selectedToken.mint
1250
+ );
1251
+ const balance = balanceInfo?.balance || 0;
1252
+ const hasBalanceFlag = hasSufficientBalance(balance, tokenAmount);
1253
+ setTokenBalance({ balance, hasBalance: hasBalanceFlag });
1254
+ } catch (error) {
1255
+ console.error("Failed to fetch token balance:", error);
1256
+ setTokenBalance({ balance: 0, hasBalance: false });
1257
+ } finally {
1258
+ setIsBalanceLoading(false);
1259
+ }
1260
+ }, [connected, publicKey, connection, selectedToken, tokenAmount, supportedTokens]);
1261
+ react.useEffect(() => {
1262
+ if (connected && publicKey && selectedToken) {
1263
+ void fetchTokenBalance();
1264
+ }
1265
+ }, [connected, publicKey, selectedToken, tokenAmount, fetchTokenBalance]);
1266
+ const decodeTransaction = react.useCallback((serialized) => {
1267
+ const buffer$1 = buffer.Buffer.from(serialized, "base64");
1268
+ try {
1269
+ return web3_js.VersionedTransaction.deserialize(buffer$1);
1270
+ } catch (err) {
1271
+ try {
1272
+ return web3_js.Transaction.from(buffer$1);
1273
+ } catch (legacyErr) {
1274
+ console.error("Failed to deserialize transaction", legacyErr);
1275
+ throw new Error("Invalid transaction payload received from server");
1276
+ }
1277
+ }
1278
+ }, []);
1279
+ const isVersionedTransaction = (tx) => {
1280
+ return !!tx && typeof tx === "object" && "version" in tx;
1281
+ };
1282
+ const signWithWallet = react.useCallback(
1283
+ async (tx) => {
1284
+ if (!wallet) {
1285
+ throw new Error("Wallet adapter is not available");
1286
+ }
1287
+ const adapter = wallet.adapter;
1288
+ if (isVersionedTransaction(tx)) {
1289
+ if (adapter.supportedTransactionVersions) {
1290
+ const supported = adapter.supportedTransactionVersions;
1291
+ if (!supported.has(tx.version)) {
1292
+ throw new Error("Connected wallet does not support this transaction version");
1293
+ }
1294
+ }
1295
+ if (adapter.signVersionedTransaction) {
1296
+ return adapter.signVersionedTransaction(tx);
1297
+ }
1298
+ }
1299
+ if (adapter.signTransaction) {
1300
+ return adapter.signTransaction(tx);
1301
+ }
1302
+ if (signTransaction) {
1303
+ return signTransaction(tx);
1304
+ }
1305
+ throw new Error("Connected wallet cannot sign transactions");
1306
+ },
1307
+ [wallet, signTransaction]
1308
+ );
1309
+ const pay = react.useCallback(async () => {
1310
+ if (!connected || !publicKey) {
1311
+ onError("Wallet not connected");
1312
+ return;
1313
+ }
1314
+ if (!selectedToken) {
1315
+ onError("No payment token selected");
1316
+ return;
1317
+ }
1318
+ if (!tokenBalance?.hasBalance) {
1319
+ onError("Insufficient balance for this token");
1320
+ return;
1321
+ }
1322
+ try {
1323
+ setIsProcessing(true);
1324
+ onStart();
1325
+ const paymentData = await solanaService.generatePayment(
1326
+ priceId,
1327
+ selectedToken.symbol,
1328
+ publicKey.toBase58()
1329
+ );
1330
+ const transactionToSign = decodeTransaction(paymentData.transaction);
1331
+ const signedTx = await signWithWallet(transactionToSign);
1332
+ const signedSerialized = buffer.Buffer.from(signedTx.serialize()).toString("base64");
1333
+ onConfirming();
1334
+ const result = await solanaService.submitPayment(
1335
+ signedSerialized,
1336
+ priceId,
1337
+ paymentData.intent_id,
1338
+ `Payment for subscription - ${selectedToken.symbol}`
1339
+ );
1340
+ onSuccess(result, result.transaction_id);
1341
+ } catch (err) {
1342
+ console.error("Payment failed:", err);
1343
+ let errorMessage = "Payment failed. Please try again.";
1344
+ const message = err instanceof Error ? err.message : typeof err === "string" ? err : "";
1345
+ if (message.includes("User rejected")) {
1346
+ errorMessage = "Payment cancelled by user";
1347
+ } else if (/insufficient\s+funds/i.test(message)) {
1348
+ errorMessage = "Insufficient balance for this token";
1349
+ } else if (message) {
1350
+ errorMessage = message;
1351
+ }
1352
+ onError(errorMessage);
1353
+ } finally {
1354
+ setIsProcessing(false);
1355
+ }
1356
+ }, [
1357
+ connected,
1358
+ publicKey,
1359
+ selectedToken,
1360
+ tokenBalance?.hasBalance,
1361
+ onError,
1362
+ onStart,
1363
+ solanaService,
1364
+ priceId,
1365
+ decodeTransaction,
1366
+ signWithWallet,
1367
+ onConfirming,
1368
+ onSuccess
1369
+ ]);
1370
+ const balanceLabel = tokenBalance ? `${tokenBalance.balance.toFixed(4)} ${selectedToken?.symbol ?? ""}` : "--";
1371
+ return {
1372
+ isBalanceLoading,
1373
+ isProcessing,
1374
+ balanceLabel,
1375
+ canPay: Boolean(
1376
+ connected && tokenBalance?.hasBalance && !isProcessing
1377
+ ),
1378
+ pay
1379
+ };
1380
+ };
1381
+ var DirectPayment = ({
1382
+ priceId,
1383
+ tokenAmount,
1384
+ selectedToken,
1385
+ supportedTokens,
1386
+ onPaymentStart,
1387
+ onPaymentConfirming,
1388
+ onPaymentSuccess,
1389
+ onPaymentError
1390
+ }) => {
1391
+ const { isBalanceLoading, balanceLabel, canPay, isProcessing, pay } = useSolanaDirectPayment({
1392
+ priceId,
1393
+ tokenAmount,
1394
+ selectedToken,
1395
+ supportedTokens,
1396
+ onStart: onPaymentStart,
1397
+ onConfirming: onPaymentConfirming,
1398
+ onSuccess: onPaymentSuccess,
1399
+ onError: onPaymentError
1400
+ });
1401
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-panel", children: [
1402
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payments-ui-panel-header", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1403
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "payments-ui-panel-title", children: [
1404
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Wallet, { className: "payments-ui-icon" }),
1405
+ " Pay with connected wallet"
1406
+ ] }),
1407
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payments-ui-panel-description", children: "Sign the transaction directly in your Solana wallet." })
1408
+ ] }) }),
1409
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-panel-body", children: [
1410
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-balance-row", children: [
1411
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Available balance" }),
1412
+ isBalanceLoading ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "payments-ui-spinner" }) : /* @__PURE__ */ jsxRuntime.jsx("strong", { children: balanceLabel })
1413
+ ] }),
1414
+ /* @__PURE__ */ jsxRuntime.jsxs(
1415
+ "button",
1416
+ {
1417
+ type: "button",
1418
+ className: "payments-ui-button",
1419
+ disabled: !canPay,
1420
+ onClick: pay,
1421
+ children: [
1422
+ isProcessing ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "payments-ui-spinner" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Wallet, { className: "payments-ui-icon" }),
1423
+ isProcessing ? "Processing..." : "Pay with wallet"
1424
+ ]
1425
+ }
1426
+ )
1427
+ ] })
1428
+ ] });
1429
+ };
1430
+ var useSolanaQrPayment = (options) => {
1431
+ const { priceId, selectedToken, onSuccess, onError } = options;
1432
+ const solanaService = useSolanaService();
1433
+ const [intent, setIntent] = react.useState(null);
1434
+ const [qrDataUri, setQrDataUri] = react.useState(null);
1435
+ const [isLoading, setIsLoading] = react.useState(false);
1436
+ const [error, setError] = react.useState(null);
1437
+ const [timeRemaining, setTimeRemaining] = react.useState(0);
1438
+ const pollRef = react.useRef(null);
1439
+ const countdownRef = react.useRef(null);
1440
+ const clearTimers = react.useCallback(() => {
1441
+ if (pollRef.current) {
1442
+ clearInterval(pollRef.current);
1443
+ pollRef.current = null;
1444
+ }
1445
+ if (countdownRef.current) {
1446
+ clearInterval(countdownRef.current);
1447
+ countdownRef.current = null;
1448
+ }
1449
+ }, []);
1450
+ react.useEffect(() => {
1451
+ return () => {
1452
+ clearTimers();
1453
+ };
1454
+ }, [clearTimers]);
1455
+ const handleError = react.useCallback(
1456
+ (message, notifyParent = false) => {
1457
+ clearTimers();
1458
+ setError(message);
1459
+ setIntent(null);
1460
+ setQrDataUri(null);
1461
+ setTimeRemaining(0);
1462
+ if (notifyParent) {
1463
+ onError(message);
1464
+ }
1465
+ },
1466
+ [clearTimers, onError]
1467
+ );
1468
+ const handleSuccess = react.useCallback(
1469
+ (status) => {
1470
+ clearTimers();
1471
+ setTimeRemaining(0);
1472
+ setIntent(null);
1473
+ onSuccess(status.payment_id, status.transaction || "");
1474
+ },
1475
+ [clearTimers, onSuccess]
1476
+ );
1477
+ const pollStatus = react.useCallback(
1478
+ async (reference) => {
1479
+ try {
1480
+ const status = await solanaService.checkPaymentStatus(reference);
1481
+ if (status.status === "confirmed") {
1482
+ handleSuccess(status);
1483
+ }
1484
+ if (status.status === "failed") {
1485
+ handleError(
1486
+ status.error_message || "Payment failed. Please try again.",
1487
+ true
1488
+ );
1489
+ }
1490
+ } catch (err) {
1491
+ console.error("Failed to poll Solana Pay status:", err);
1492
+ }
1493
+ },
1494
+ [handleError, handleSuccess, solanaService]
1495
+ );
1496
+ const startCountdown = react.useCallback(
1497
+ (expiresAt, reference) => {
1498
+ const updateTime = () => {
1499
+ const remaining = Math.max(0, Math.floor(expiresAt - Date.now() / 1e3));
1500
+ setTimeRemaining(remaining);
1501
+ if (remaining === 0) {
1502
+ handleError("Payment intent expired. Please generate a new QR code.");
1503
+ }
1504
+ };
1505
+ updateTime();
1506
+ countdownRef.current = setInterval(updateTime, 1e3);
1507
+ pollRef.current = setInterval(() => void pollStatus(reference), 4e3);
1508
+ },
1509
+ [handleError, pollStatus]
1510
+ );
1511
+ const renderQr = react.useCallback(async (qrIntent) => {
1512
+ try {
1513
+ const dataUri = await QRCode__default.default.toDataURL(qrIntent.url, {
1514
+ width: 320,
1515
+ margin: 1,
1516
+ color: {
1517
+ dark: "#0f1116",
1518
+ light: "#ffffff"
1519
+ }
1520
+ });
1521
+ setQrDataUri(dataUri);
1522
+ } catch (err) {
1523
+ console.error("Failed to render QR code:", err);
1524
+ handleError("Unable to render QR code");
1525
+ }
1526
+ }, [handleError]);
1527
+ const fetchIntent = react.useCallback(async () => {
1528
+ if (!selectedToken) {
1529
+ setIntent(null);
1530
+ setQrDataUri(null);
1531
+ setError(null);
1532
+ clearTimers();
1533
+ setTimeRemaining(0);
1534
+ return;
1535
+ }
1536
+ try {
1537
+ setIsLoading(true);
1538
+ setError(null);
1539
+ clearTimers();
1540
+ const nextIntent = await solanaService.generateQRCode(
1541
+ priceId,
1542
+ selectedToken.symbol
1543
+ );
1544
+ setIntent(nextIntent);
1545
+ setTimeRemaining(
1546
+ Math.max(0, Math.floor(nextIntent.expires_at - Date.now() / 1e3))
1547
+ );
1548
+ await renderQr(nextIntent);
1549
+ startCountdown(nextIntent.expires_at, nextIntent.reference);
1550
+ pollStatus(nextIntent.reference);
1551
+ } catch (err) {
1552
+ console.error("Failed to generate Solana Pay QR intent:", err);
1553
+ const message = err instanceof Error ? err.message : "Unable to create Solana Pay QR code";
1554
+ handleError(message);
1555
+ } finally {
1556
+ setIsLoading(false);
1557
+ }
1558
+ }, [
1559
+ clearTimers,
1560
+ handleError,
1561
+ pollStatus,
1562
+ priceId,
1563
+ selectedToken,
1564
+ solanaService,
1565
+ startCountdown,
1566
+ renderQr
1567
+ ]);
1568
+ react.useEffect(() => {
1569
+ void fetchIntent();
1570
+ }, [fetchIntent]);
1571
+ return {
1572
+ intent,
1573
+ qrDataUri,
1574
+ isLoading,
1575
+ error,
1576
+ timeRemaining,
1577
+ refresh: fetchIntent
1578
+ };
1579
+ };
1580
+ var QRCodePayment = ({
1581
+ priceId,
1582
+ selectedToken,
1583
+ onPaymentError,
1584
+ onPaymentSuccess
1585
+ }) => {
1586
+ const { intent, qrDataUri, isLoading, error, timeRemaining, refresh } = useSolanaQrPayment({
1587
+ priceId,
1588
+ selectedToken,
1589
+ onError: onPaymentError,
1590
+ onSuccess: onPaymentSuccess
1591
+ });
1592
+ if (!selectedToken) {
1593
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payments-ui-empty", children: "Select a token to continue." });
1594
+ }
1595
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-panel", children: [
1596
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-panel-header", children: [
1597
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1598
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payments-ui-panel-title", children: "Scan with Solana Pay" }),
1599
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payments-ui-panel-description", children: "Use any Solana Pay compatible wallet to scan and confirm." })
1600
+ ] }),
1601
+ /* @__PURE__ */ jsxRuntime.jsx(
1602
+ "button",
1603
+ {
1604
+ type: "button",
1605
+ className: "payments-ui-icon-button",
1606
+ onClick: () => refresh(),
1607
+ disabled: isLoading,
1608
+ children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "payments-ui-spinner" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { className: "payments-ui-icon" })
1609
+ }
1610
+ )
1611
+ ] }),
1612
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payments-ui-error", children: error }),
1613
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payments-ui-qr-wrapper", children: qrDataUri ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: qrDataUri, alt: "Solana Pay QR" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payments-ui-empty", children: isLoading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1614
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "payments-ui-spinner" }),
1615
+ "Generating QR code..."
1616
+ ] }) : "QR code unavailable" }) }),
1617
+ intent && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-countdown", children: [
1618
+ "Expires in ",
1619
+ timeRemaining,
1620
+ "s \xB7 ",
1621
+ intent.token_amount,
1622
+ " ",
1623
+ intent.token_symbol
1624
+ ] })
1625
+ ] });
1626
+ };
1627
+ var PaymentStatus = ({
1628
+ state,
1629
+ usdAmount,
1630
+ solAmount,
1631
+ errorMessage,
1632
+ transactionId,
1633
+ onRetry,
1634
+ onClose
1635
+ }) => {
1636
+ if (state === "success") {
1637
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-status success", children: [
1638
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "payments-ui-status-icon" }),
1639
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { children: "Payment confirmed" }),
1640
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
1641
+ usdAmount.toFixed(2),
1642
+ " USD (~",
1643
+ solAmount.toFixed(4),
1644
+ " SOL)."
1645
+ ] }),
1646
+ transactionId && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "payments-ui-status-meta", children: [
1647
+ "Txn: ",
1648
+ transactionId
1649
+ ] }),
1650
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "payments-ui-button", type: "button", onClick: onClose, children: "Close" })
1651
+ ] });
1652
+ }
1653
+ if (state === "error") {
1654
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-status error", children: [
1655
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "payments-ui-status-icon" }),
1656
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { children: "Payment failed" }),
1657
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: errorMessage ?? "Please try again." }),
1658
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-status-actions", children: [
1659
+ /* @__PURE__ */ jsxRuntime.jsxs("button", { className: "payments-ui-button", type: "button", onClick: onRetry, children: [
1660
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { className: "payments-ui-icon" }),
1661
+ " Retry"
1662
+ ] }),
1663
+ /* @__PURE__ */ jsxRuntime.jsx("button", { className: "payments-ui-text-button", type: "button", onClick: onClose, children: "Cancel" })
1664
+ ] })
1665
+ ] });
1666
+ }
1667
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-status pending", children: [
1668
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "payments-ui-spinner" }),
1669
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { children: state === "confirming" ? "Waiting for confirmations" : "Processing payment" }),
1670
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
1671
+ "Paying ",
1672
+ usdAmount.toFixed(2),
1673
+ " USD (~",
1674
+ solAmount.toFixed(4),
1675
+ " SOL)."
1676
+ ] }),
1677
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payments-ui-status-meta", children: "This can take up to 60 seconds on Solana mainnet." })
1678
+ ] });
1679
+ };
1680
+ var useSupportedTokens = () => {
1681
+ const solanaService = useSolanaService();
1682
+ const [tokens, setTokens] = react.useState([]);
1683
+ const [isLoading, setIsLoading] = react.useState(false);
1684
+ const [error, setError] = react.useState(null);
1685
+ const [lastFetched, setLastFetched] = react.useState(null);
1686
+ const CACHE_DURATION = 5 * 60 * 1e3;
1687
+ const tokensRef = react.useRef(tokens);
1688
+ const lastFetchedRef = react.useRef(lastFetched);
1689
+ tokensRef.current = tokens;
1690
+ lastFetchedRef.current = lastFetched;
1691
+ const fetchSupportedTokens = react.useCallback(async () => {
1692
+ if (tokensRef.current.length > 0 && lastFetchedRef.current && Date.now() - lastFetchedRef.current < CACHE_DURATION) {
1693
+ return tokensRef.current;
1694
+ }
1695
+ setIsLoading(true);
1696
+ setError(null);
1697
+ try {
1698
+ const tokens2 = await solanaService.getSupportedTokens();
1699
+ const sortedTokens = [...tokens2].sort(
1700
+ (a, b) => a.symbol.localeCompare(b.symbol)
1701
+ );
1702
+ setTokens(sortedTokens);
1703
+ setLastFetched(Date.now());
1704
+ return sortedTokens;
1705
+ } catch (error2) {
1706
+ const errorMessage = error2 instanceof Error ? error2.message : "Failed to fetch supported tokens";
1707
+ setError(errorMessage);
1708
+ console.error("Failed to fetch supported tokens:", error2);
1709
+ return [];
1710
+ } finally {
1711
+ setIsLoading(false);
1712
+ }
1713
+ }, [solanaService]);
1714
+ react.useEffect(() => {
1715
+ if (tokens.length === 0) {
1716
+ fetchSupportedTokens();
1717
+ }
1718
+ }, [tokens.length, fetchSupportedTokens]);
1719
+ const getTokenBySymbol = react.useCallback(
1720
+ (symbol) => {
1721
+ return tokens.find((token) => token.symbol === symbol);
1722
+ },
1723
+ [tokens]
1724
+ );
1725
+ const getTokenByMint = react.useCallback(
1726
+ (mintAddress) => {
1727
+ return tokens.find((token) => token.mint === mintAddress);
1728
+ },
1729
+ [tokens]
1730
+ );
1731
+ const isTokenSupported = react.useCallback(
1732
+ (symbol) => {
1733
+ return tokens.some((token) => token.symbol === symbol);
1734
+ },
1735
+ [tokens]
1736
+ );
1737
+ const getUSDCToken = react.useCallback(() => {
1738
+ return getTokenBySymbol("USDC");
1739
+ }, [getTokenBySymbol]);
1740
+ const getPYUSDToken = react.useCallback(() => {
1741
+ return getTokenBySymbol("PYUSD");
1742
+ }, [getTokenBySymbol]);
1743
+ const getSOLToken = react.useCallback(() => {
1744
+ return getTokenBySymbol("SOL");
1745
+ }, [getTokenBySymbol]);
1746
+ const getStablecoins = react.useCallback(() => {
1747
+ return tokens.filter((token) => ["USDC", "PYUSD"].includes(token.symbol));
1748
+ }, [tokens]);
1749
+ const refreshTokens = react.useCallback(async () => {
1750
+ setLastFetched(null);
1751
+ return await fetchSupportedTokens();
1752
+ }, [fetchSupportedTokens]);
1753
+ const isCacheStale = react.useCallback(() => {
1754
+ if (!lastFetched) return true;
1755
+ return Date.now() - lastFetched > CACHE_DURATION;
1756
+ }, [lastFetched]);
1757
+ const getTokenDisplayInfo = react.useCallback((token) => {
1758
+ return {
1759
+ ...token,
1760
+ displayName: `${token.name} (${token.symbol})`,
1761
+ shortAddress: `${token.mint.slice(0, 4)}...${token.mint.slice(-4)}`,
1762
+ decimalPlaces: token.decimals
1763
+ };
1764
+ }, []);
1765
+ const getTokenPrice = react.useCallback(
1766
+ (symbol) => {
1767
+ const token = getTokenBySymbol(symbol);
1768
+ if (!token) return 0;
1769
+ const price = token.price ?? 0;
1770
+ return typeof price === "number" ? price : Number(price);
1771
+ },
1772
+ [getTokenBySymbol]
1773
+ );
1774
+ const calculateTokenAmount = react.useCallback(
1775
+ (usdAmount, tokenSymbol) => {
1776
+ const token = getTokenBySymbol(tokenSymbol);
1777
+ const price = getTokenPrice(tokenSymbol);
1778
+ if (!token || price === 0) return "0";
1779
+ const tokenAmount = usdAmount / price;
1780
+ const tokenAmountInSmallestUnit = tokenAmount * Math.pow(10, token.decimals);
1781
+ return Math.floor(tokenAmountInSmallestUnit).toString();
1782
+ },
1783
+ [getTokenBySymbol, getTokenPrice]
1784
+ );
1785
+ const formatTokenAmount = react.useCallback(
1786
+ (amount, tokenSymbol) => {
1787
+ const token = getTokenBySymbol(tokenSymbol);
1788
+ if (!token) return "0";
1789
+ const numericAmount = parseFloat(amount);
1790
+ const displayAmount = numericAmount / Math.pow(10, token.decimals);
1791
+ return displayAmount.toFixed(token.decimals <= 6 ? token.decimals : 6);
1792
+ },
1793
+ [getTokenBySymbol]
1794
+ );
1795
+ return {
1796
+ tokens,
1797
+ isLoading,
1798
+ error,
1799
+ lastFetched,
1800
+ fetchSupportedTokens,
1801
+ refreshTokens,
1802
+ getTokenBySymbol,
1803
+ getTokenByMint,
1804
+ isTokenSupported,
1805
+ getUSDCToken,
1806
+ getPYUSDToken,
1807
+ getSOLToken,
1808
+ getStablecoins,
1809
+ getTokenDisplayInfo,
1810
+ getTokenPrice,
1811
+ calculateTokenAmount,
1812
+ formatTokenAmount,
1813
+ isCacheStale,
1814
+ hasTokens: tokens.length > 0,
1815
+ tokenCount: tokens.length
1816
+ };
1817
+ };
1818
+ var usePaymentStore = (selector) => {
1819
+ const { store } = usePaymentContext();
1820
+ return zustand.useStore(store, selector);
1821
+ };
1822
+
1823
+ // src/state/selectors.ts
1824
+ var selectCheckoutFlow = (state) => ({
1825
+ selectedMethodId: state.selectedMethodId,
1826
+ savedStatus: state.savedPaymentStatus,
1827
+ savedError: state.savedPaymentError,
1828
+ newCardStatus: state.newCardStatus,
1829
+ newCardError: state.newCardError,
1830
+ solanaModalOpen: state.solanaModalOpen,
1831
+ setSelectedMethod: state.setSelectedMethod,
1832
+ setSolanaModalOpen: state.setSolanaModalOpen,
1833
+ startSavedPayment: state.startSavedPayment,
1834
+ completeSavedPayment: state.completeSavedPayment,
1835
+ failSavedPayment: state.failSavedPayment,
1836
+ startNewCardPayment: state.startNewCardPayment,
1837
+ completeNewCardPayment: state.completeNewCardPayment,
1838
+ failNewCardPayment: state.failNewCardPayment,
1839
+ resetSavedPayment: state.resetSavedPayment
1840
+ });
1841
+ var selectSolanaFlow = (state) => ({
1842
+ tab: state.solanaTab,
1843
+ status: state.solanaStatus,
1844
+ error: state.solanaError,
1845
+ transactionId: state.solanaTransactionId,
1846
+ tokenAmount: state.solanaTokenAmount,
1847
+ selectedTokenSymbol: state.solanaSelectedToken,
1848
+ setTab: state.setSolanaTab,
1849
+ setTokenAmount: state.setSolanaTokenAmount,
1850
+ setTransactionId: state.setSolanaTransactionId,
1851
+ setSelectedTokenSymbol: state.setSolanaSelectedToken,
1852
+ startSolanaPayment: state.startSolanaPayment,
1853
+ confirmSolanaPayment: state.confirmSolanaPayment,
1854
+ completeSolanaPayment: state.completeSolanaPayment,
1855
+ failSolanaPayment: state.failSolanaPayment,
1856
+ resetSolanaPayment: state.resetSolanaPayment
1857
+ });
1858
+ var SolanaPaymentSelector = ({
1859
+ isOpen,
1860
+ onClose,
1861
+ priceId,
1862
+ usdAmount,
1863
+ onSuccess,
1864
+ onError
1865
+ }) => {
1866
+ const { connected } = walletAdapterReact.useWallet();
1867
+ const {
1868
+ tab: activeTab,
1869
+ status: paymentState,
1870
+ error: errorMessage,
1871
+ transactionId,
1872
+ tokenAmount,
1873
+ selectedTokenSymbol,
1874
+ setTab,
1875
+ setTokenAmount,
1876
+ setTransactionId,
1877
+ setSelectedTokenSymbol,
1878
+ startSolanaPayment,
1879
+ confirmSolanaPayment,
1880
+ completeSolanaPayment,
1881
+ failSolanaPayment,
1882
+ resetSolanaPayment
1883
+ } = usePaymentStore(selectSolanaFlow);
1884
+ const {
1885
+ tokens,
1886
+ isLoading: tokensLoading,
1887
+ error: tokensError
1888
+ } = useSupportedTokens();
1889
+ const selectedToken = react.useMemo(() => {
1890
+ if (!tokens.length) return null;
1891
+ const explicit = tokens.find((token) => token.symbol === selectedTokenSymbol);
1892
+ return explicit || tokens[0];
1893
+ }, [tokens, selectedTokenSymbol]);
1894
+ react.useEffect(() => {
1895
+ if (!selectedTokenSymbol && tokens.length) {
1896
+ const defaultToken = tokens.find((token) => token.symbol === "SOL") || tokens[0];
1897
+ setSelectedTokenSymbol(defaultToken.symbol);
1898
+ }
1899
+ }, [tokens, selectedTokenSymbol, setSelectedTokenSymbol]);
1900
+ const handlePaymentStart = react.useCallback(() => {
1901
+ startSolanaPayment();
1902
+ }, [startSolanaPayment]);
1903
+ const handlePaymentConfirming = react.useCallback(() => {
1904
+ confirmSolanaPayment();
1905
+ }, [confirmSolanaPayment]);
1906
+ const handlePaymentSuccess = react.useCallback(
1907
+ (result, txId) => {
1908
+ const resolvedTx = txId || (typeof result === "string" ? result : result.transaction_id);
1909
+ setTransactionId(resolvedTx);
1910
+ completeSolanaPayment(
1911
+ typeof result === "string" ? {
1912
+ transactionId: resolvedTx,
1913
+ processor: "solana",
1914
+ metadata: { source: "solana-pay" }
1915
+ } : {
1916
+ transactionId: result.transaction_id,
1917
+ intentId: result.intent_id,
1918
+ processor: "solana",
1919
+ metadata: {
1920
+ purchaseId: result.purchase_id,
1921
+ amount: result.amount,
1922
+ currency: result.currency
1923
+ }
1924
+ }
1925
+ );
1926
+ setTimeout(() => {
1927
+ onSuccess(result);
1928
+ }, 1500);
1929
+ },
1930
+ [completeSolanaPayment, onSuccess, setTransactionId]
1931
+ );
1932
+ const handlePaymentError = react.useCallback(
1933
+ (error) => {
1934
+ failSolanaPayment(error);
1935
+ onError?.(error);
1936
+ },
1937
+ [failSolanaPayment, onError]
1938
+ );
1939
+ const handleRetry = react.useCallback(() => {
1940
+ resetSolanaPayment();
1941
+ setTransactionId(null);
1942
+ }, [resetSolanaPayment, setTransactionId]);
1943
+ const handleClose = react.useCallback(() => {
1944
+ if (paymentState === "processing" || paymentState === "confirming") {
1945
+ return;
1946
+ }
1947
+ resetSolanaPayment();
1948
+ setTransactionId(null);
1949
+ onClose();
1950
+ }, [paymentState, resetSolanaPayment, setTransactionId, onClose]);
1951
+ react.useEffect(() => {
1952
+ if (!isOpen || !selectedToken || usdAmount === 0) {
1953
+ setTokenAmount(0);
1954
+ return;
1955
+ }
1956
+ const price = selectedToken.price ?? 0;
1957
+ if (!price || price <= 0) {
1958
+ setTokenAmount(0);
1959
+ return;
1960
+ }
1961
+ setTokenAmount(usdAmount / price);
1962
+ }, [isOpen, usdAmount, selectedToken, setTokenAmount]);
1963
+ const handleTokenChange = react.useCallback(
1964
+ (event) => {
1965
+ setSelectedTokenSymbol(event.target.value);
1966
+ },
1967
+ [setSelectedTokenSymbol]
1968
+ );
1969
+ const wasConnectedRef = react.useRef(connected);
1970
+ react.useEffect(() => {
1971
+ if (connected && !wasConnectedRef.current) {
1972
+ setTab("wallet");
1973
+ }
1974
+ if (!connected && wasConnectedRef.current) {
1975
+ setTab("qr");
1976
+ }
1977
+ wasConnectedRef.current = connected;
1978
+ }, [connected, setTab]);
1979
+ const renderSelector = () => {
1980
+ if (tokensLoading) {
1981
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-empty", children: [
1982
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "payments-ui-spinner" }),
1983
+ " Loading tokens\u2026"
1984
+ ] });
1985
+ }
1986
+ if (tokensError) {
1987
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payments-ui-error", children: tokensError });
1988
+ }
1989
+ if (!tokens.length) {
1990
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payments-ui-empty", children: "No payment tokens available." });
1991
+ }
1992
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-token-select", children: [
1993
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
1994
+ "Payment token",
1995
+ /* @__PURE__ */ jsxRuntime.jsx("select", { value: selectedTokenSymbol ?? "", onChange: handleTokenChange, children: tokens.map((token) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: token.symbol, children: [
1996
+ token.name,
1997
+ " (",
1998
+ token.symbol,
1999
+ ")"
2000
+ ] }, token.symbol)) })
2001
+ ] }),
2002
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "payments-ui-token-meta", children: [
2003
+ "\u2248 ",
2004
+ tokenAmount.toFixed(4),
2005
+ " ",
2006
+ selectedToken?.symbol
2007
+ ] })
2008
+ ] });
2009
+ };
2010
+ if (paymentState !== "selecting") {
2011
+ return /* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Portal, { children: [
2012
+ /* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Overlay, { className: "payments-ui-modal-overlay" }),
2013
+ /* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Content, { className: "payments-ui-modal", children: [
2014
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-modal-header", children: [
2015
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2016
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { children: "Complete your payment" }),
2017
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Follow the prompts below to finish." })
2018
+ ] }),
2019
+ /* @__PURE__ */ jsxRuntime.jsx(
2020
+ Dialog2__namespace.Close,
2021
+ {
2022
+ className: "payments-ui-icon-button",
2023
+ disabled: paymentState === "processing" || paymentState === "confirming",
2024
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "payments-ui-icon" })
2025
+ }
2026
+ )
2027
+ ] }),
2028
+ /* @__PURE__ */ jsxRuntime.jsx(
2029
+ PaymentStatus,
2030
+ {
2031
+ state: paymentState,
2032
+ usdAmount,
2033
+ solAmount: tokenAmount,
2034
+ onRetry: handleRetry,
2035
+ onClose: handleClose,
2036
+ errorMessage,
2037
+ transactionId
2038
+ }
2039
+ )
2040
+ ] })
2041
+ ] }) });
2042
+ }
2043
+ return /* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Root, { open: isOpen, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Portal, { children: [
2044
+ /* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Overlay, { className: "payments-ui-modal-overlay" }),
2045
+ /* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Content, { className: "payments-ui-modal", children: [
2046
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-modal-header", children: [
2047
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2048
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { children: "Complete your payment" }),
2049
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Select a token and preferred method." })
2050
+ ] }),
2051
+ /* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Close, { className: "payments-ui-icon-button", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "payments-ui-icon" }) })
2052
+ ] }),
2053
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-tab-header", children: [
2054
+ /* @__PURE__ */ jsxRuntime.jsx(
2055
+ "button",
2056
+ {
2057
+ type: "button",
2058
+ className: activeTab === "wallet" ? "active" : "",
2059
+ onClick: () => setTab("wallet"),
2060
+ disabled: !connected,
2061
+ children: "Pay with wallet"
2062
+ }
2063
+ ),
2064
+ /* @__PURE__ */ jsxRuntime.jsx(
2065
+ "button",
2066
+ {
2067
+ type: "button",
2068
+ className: activeTab === "qr" ? "active" : "",
2069
+ onClick: () => setTab("qr"),
2070
+ children: "Scan QR"
2071
+ }
2072
+ )
2073
+ ] }),
2074
+ renderSelector(),
2075
+ activeTab === "wallet" ? /* @__PURE__ */ jsxRuntime.jsx(
2076
+ DirectPayment,
2077
+ {
2078
+ priceId,
2079
+ tokenAmount,
2080
+ selectedToken,
2081
+ supportedTokens: tokens,
2082
+ onPaymentStart: handlePaymentStart,
2083
+ onPaymentConfirming: handlePaymentConfirming,
2084
+ onPaymentSuccess: handlePaymentSuccess,
2085
+ onPaymentError: handlePaymentError
2086
+ }
2087
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
2088
+ QRCodePayment,
2089
+ {
2090
+ priceId,
2091
+ selectedToken,
2092
+ onPaymentError: handlePaymentError,
2093
+ onPaymentSuccess: handlePaymentSuccess
2094
+ }
2095
+ )
2096
+ ] })
2097
+ ] }) });
2098
+ };
2099
+ var PaymentExperience = ({
2100
+ priceId,
2101
+ usdAmount,
2102
+ onNewCardPayment,
2103
+ onSavedMethodPayment,
2104
+ enableNewCard = true,
2105
+ enableStoredMethods = true,
2106
+ enableSolanaPay = true,
2107
+ checkoutSummary,
2108
+ onSolanaSuccess,
2109
+ onSolanaError
2110
+ }) => {
2111
+ const showNewCard = enableNewCard && Boolean(onNewCardPayment);
2112
+ const showStored = enableStoredMethods;
2113
+ const {
2114
+ selectedMethodId,
2115
+ savedStatus,
2116
+ savedError,
2117
+ newCardStatus,
2118
+ newCardError,
2119
+ solanaModalOpen,
2120
+ setSelectedMethod,
2121
+ setSolanaModalOpen,
2122
+ startSavedPayment,
2123
+ completeSavedPayment,
2124
+ failSavedPayment,
2125
+ startNewCardPayment,
2126
+ completeNewCardPayment,
2127
+ failNewCardPayment,
2128
+ resetSavedPayment
2129
+ } = usePaymentStore(selectCheckoutFlow);
2130
+ const handleMethodSelect = (method) => {
2131
+ setSelectedMethod(method.id);
2132
+ resetSavedPayment();
2133
+ };
2134
+ const handleNewCardTokenize = async (token, billing) => {
2135
+ if (!onNewCardPayment) return;
2136
+ try {
2137
+ startNewCardPayment();
2138
+ await onNewCardPayment({ token, billing });
2139
+ completeNewCardPayment();
2140
+ } catch (error) {
2141
+ const message = error instanceof Error ? error.message : "Unable to complete payment";
2142
+ failNewCardPayment(message);
2143
+ }
2144
+ };
2145
+ const handleSavedPayment = async () => {
2146
+ if (!onSavedMethodPayment || !selectedMethodId) return;
2147
+ try {
2148
+ startSavedPayment();
2149
+ await onSavedMethodPayment({
2150
+ paymentMethodId: selectedMethodId,
2151
+ amount: usdAmount
2152
+ });
2153
+ completeSavedPayment();
2154
+ } catch (error) {
2155
+ const message = error instanceof Error ? error.message : "Unable to complete payment with saved card";
2156
+ failSavedPayment(message);
2157
+ }
2158
+ };
2159
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-experience", children: [
2160
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "payments-ui-experience-header", children: [
2161
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2162
+ /* @__PURE__ */ jsxRuntime.jsxs("h2", { children: [
2163
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CreditCard, { className: "payments-ui-icon" }),
2164
+ " Secure checkout"
2165
+ ] }),
2166
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
2167
+ "Amount due: $",
2168
+ usdAmount.toFixed(2)
2169
+ ] })
2170
+ ] }),
2171
+ checkoutSummary && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payments-ui-summary", children: checkoutSummary })
2172
+ ] }),
2173
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-experience-grid", children: [
2174
+ showStored && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-column", children: [
2175
+ /* @__PURE__ */ jsxRuntime.jsx(
2176
+ StoredPaymentMethods,
2177
+ {
2178
+ selectedMethodId,
2179
+ onMethodSelect: handleMethodSelect,
2180
+ heading: "Saved cards",
2181
+ description: "Use or manage your saved payment methods."
2182
+ }
2183
+ ),
2184
+ onSavedMethodPayment && /* @__PURE__ */ jsxRuntime.jsx(
2185
+ "button",
2186
+ {
2187
+ type: "button",
2188
+ className: "payments-ui-button",
2189
+ disabled: !selectedMethodId || savedStatus === "processing",
2190
+ onClick: handleSavedPayment,
2191
+ children: savedStatus === "processing" ? "Processing\u2026" : "Pay with selected card"
2192
+ }
2193
+ ),
2194
+ savedError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payments-ui-error", children: savedError })
2195
+ ] }),
2196
+ showNewCard && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payments-ui-column", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-panel", children: [
2197
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "payments-ui-panel-header", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2198
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "payments-ui-panel-title", children: [
2199
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CreditCard, { className: "payments-ui-icon" }),
2200
+ " Pay with a new card"
2201
+ ] }),
2202
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "payments-ui-panel-description", children: "Card details are tokenized via Collect.js and never hit your server." })
2203
+ ] }) }),
2204
+ /* @__PURE__ */ jsxRuntime.jsx(
2205
+ CardDetailsForm,
2206
+ {
2207
+ visible: true,
2208
+ submitLabel: "Pay now",
2209
+ submitting: newCardStatus === "processing",
2210
+ externalError: newCardError,
2211
+ onTokenize: handleNewCardTokenize
2212
+ }
2213
+ )
2214
+ ] }) })
2215
+ ] }),
2216
+ enableSolanaPay && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "payments-ui-solana-banner", children: [
2217
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2218
+ /* @__PURE__ */ jsxRuntime.jsxs("h3", { children: [
2219
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "payments-ui-icon" }),
2220
+ " Prefer Solana Pay?"
2221
+ ] }),
2222
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Use a Solana wallet or QR code for instant settlement." })
2223
+ ] }),
2224
+ /* @__PURE__ */ jsxRuntime.jsx(
2225
+ "button",
2226
+ {
2227
+ type: "button",
2228
+ className: "payments-ui-button",
2229
+ onClick: () => setSolanaModalOpen(true),
2230
+ children: "Open Solana Pay"
2231
+ }
2232
+ )
2233
+ ] }),
2234
+ enableSolanaPay && /* @__PURE__ */ jsxRuntime.jsx(
2235
+ SolanaPaymentSelector,
2236
+ {
2237
+ isOpen: solanaModalOpen,
2238
+ onClose: () => setSolanaModalOpen(false),
2239
+ priceId,
2240
+ usdAmount,
2241
+ onSuccess: (result) => {
2242
+ setSolanaModalOpen(false);
2243
+ onSolanaSuccess?.(result);
2244
+ },
2245
+ onError: onSolanaError
2246
+ }
2247
+ )
2248
+ ] });
2249
+ };
2250
+ var useTokenBalance = (tokens) => {
2251
+ const { publicKey } = walletAdapterReact.useWallet();
2252
+ const { connection } = walletAdapterReact.useConnection();
2253
+ const [balances, setBalances] = react.useState([]);
2254
+ const [isLoading, setIsLoading] = react.useState(false);
2255
+ const [error, setError] = react.useState(null);
2256
+ const fetchTokenBalance = react.useCallback(
2257
+ async (token, walletAddress) => {
2258
+ try {
2259
+ const mintPublicKey = new web3_js.PublicKey(token.mint);
2260
+ const associatedTokenAddress = await splToken.getAssociatedTokenAddress(
2261
+ mintPublicKey,
2262
+ walletAddress
2263
+ );
2264
+ try {
2265
+ const tokenAccount = await splToken.getAccount(
2266
+ connection,
2267
+ associatedTokenAddress
2268
+ );
2269
+ const balance = Number(tokenAccount.amount);
2270
+ const uiAmount = balance / Math.pow(10, token.decimals);
2271
+ return {
2272
+ token,
2273
+ balance,
2274
+ uiAmount,
2275
+ hasBalance: balance > 0
2276
+ };
2277
+ } catch (accountError) {
2278
+ return {
2279
+ token,
2280
+ balance: 0,
2281
+ uiAmount: 0,
2282
+ hasBalance: false
2283
+ };
2284
+ }
2285
+ } catch (error2) {
2286
+ console.error(`Failed to fetch balance for ${token.symbol}:`, error2);
2287
+ return {
2288
+ token,
2289
+ balance: 0,
2290
+ uiAmount: 0,
2291
+ hasBalance: false
2292
+ };
2293
+ }
2294
+ },
2295
+ [connection]
2296
+ );
2297
+ const tokensKey = react.useMemo(() => tokens.map((t) => t.mint).join(","), [tokens]);
2298
+ react.useEffect(() => {
2299
+ if (!publicKey || tokens.length === 0) {
2300
+ setBalances([]);
2301
+ return;
2302
+ }
2303
+ setIsLoading(true);
2304
+ setError(null);
2305
+ const fetchAllBalances = async () => {
2306
+ try {
2307
+ const balancePromises = tokens.map(
2308
+ (token) => fetchTokenBalance(token, publicKey)
2309
+ );
2310
+ const tokenBalances = await Promise.all(balancePromises);
2311
+ setBalances(tokenBalances);
2312
+ } catch (error2) {
2313
+ const errorMessage = error2 instanceof Error ? error2.message : "Failed to fetch token balances";
2314
+ setError(errorMessage);
2315
+ console.error("Failed to fetch token balances:", error2);
2316
+ } finally {
2317
+ setIsLoading(false);
2318
+ }
2319
+ };
2320
+ fetchAllBalances();
2321
+ }, [publicKey, tokensKey, fetchTokenBalance]);
2322
+ const getTokenBalance = react.useCallback(
2323
+ (tokenSymbol) => {
2324
+ return balances.find((balance) => balance.token.symbol === tokenSymbol);
2325
+ },
2326
+ [balances]
2327
+ );
2328
+ const hasSufficientBalance2 = react.useCallback(
2329
+ (tokenSymbol, requiredAmount) => {
2330
+ const balance = getTokenBalance(tokenSymbol);
2331
+ return balance ? balance.uiAmount >= requiredAmount : false;
2332
+ },
2333
+ [getTokenBalance]
2334
+ );
2335
+ const getFormattedBalance = react.useCallback(
2336
+ (tokenSymbol) => {
2337
+ const balance = getTokenBalance(tokenSymbol);
2338
+ if (!balance) return "0.00";
2339
+ if (balance.uiAmount < 0.01) {
2340
+ return balance.uiAmount.toFixed(6);
2341
+ } else if (balance.uiAmount < 1) {
2342
+ return balance.uiAmount.toFixed(4);
2343
+ } else {
2344
+ return balance.uiAmount.toFixed(2);
2345
+ }
2346
+ },
2347
+ [getTokenBalance]
2348
+ );
2349
+ const refreshBalances = react.useCallback(async () => {
2350
+ if (!publicKey || tokens.length === 0) {
2351
+ setBalances([]);
2352
+ return;
2353
+ }
2354
+ setIsLoading(true);
2355
+ setError(null);
2356
+ try {
2357
+ const balancePromises = tokens.map(
2358
+ (token) => fetchTokenBalance(token, publicKey)
2359
+ );
2360
+ const tokenBalances = await Promise.all(balancePromises);
2361
+ setBalances(tokenBalances);
2362
+ } catch (error2) {
2363
+ const errorMessage = error2 instanceof Error ? error2.message : "Failed to fetch token balances";
2364
+ setError(errorMessage);
2365
+ console.error("Failed to fetch token balances:", error2);
2366
+ } finally {
2367
+ setIsLoading(false);
2368
+ }
2369
+ }, [publicKey, tokens, fetchTokenBalance]);
2370
+ const getTotalValue = react.useCallback(
2371
+ (priceData) => {
2372
+ if (!priceData) return 0;
2373
+ return balances.reduce((total, balance) => {
2374
+ const price = priceData[balance.token.symbol] || 0;
2375
+ return total + balance.uiAmount * price;
2376
+ }, 0);
2377
+ },
2378
+ [balances]
2379
+ );
2380
+ const sortedBalances = [...balances].sort((a, b) => b.uiAmount - a.uiAmount);
2381
+ const positiveBalances = balances.filter((balance) => balance.hasBalance);
2382
+ return {
2383
+ balances: sortedBalances,
2384
+ positiveBalances,
2385
+ isLoading,
2386
+ error,
2387
+ refreshBalances,
2388
+ getTokenBalance,
2389
+ hasSufficientBalance: hasSufficientBalance2,
2390
+ getFormattedBalance,
2391
+ getTotalValue,
2392
+ hasAnyBalance: positiveBalances.length > 0,
2393
+ isConnected: !!publicKey
2394
+ };
2395
+ };
2396
+ var useDirectWalletPayment = () => {
2397
+ const { publicKey, signTransaction, connected } = walletAdapterReact.useWallet();
2398
+ const solanaService = useSolanaService();
2399
+ const [paymentState, setPaymentState] = react.useState({
2400
+ loading: false,
2401
+ error: null,
2402
+ success: false,
2403
+ transactionId: null
2404
+ });
2405
+ const resetPayment = react.useCallback(() => {
2406
+ setPaymentState({
2407
+ loading: false,
2408
+ error: null,
2409
+ success: false,
2410
+ transactionId: null
2411
+ });
2412
+ }, []);
2413
+ const payWithWallet = react.useCallback(
2414
+ async (token, priceId) => {
2415
+ if (!connected || !publicKey || !signTransaction) {
2416
+ setPaymentState((prev) => ({
2417
+ ...prev,
2418
+ error: "Wallet not connected. Please connect your wallet first."
2419
+ }));
2420
+ return;
2421
+ }
2422
+ setPaymentState({
2423
+ loading: true,
2424
+ error: null,
2425
+ success: false,
2426
+ transactionId: null
2427
+ });
2428
+ try {
2429
+ console.log("Generating payment transaction...", {
2430
+ token: token.symbol,
2431
+ priceId
2432
+ });
2433
+ const paymentData = await solanaService.generatePayment(
2434
+ priceId,
2435
+ token.symbol,
2436
+ publicKey.toBase58()
2437
+ );
2438
+ const transactionBuffer = buffer.Buffer.from(paymentData.transaction, "base64");
2439
+ const transaction = web3_js.Transaction.from(transactionBuffer);
2440
+ console.log("Requesting wallet signature...");
2441
+ const signedTransaction = await signTransaction(transaction);
2442
+ const signedTransactionBase64 = signedTransaction.serialize().toString("base64");
2443
+ console.log("Submitting signed transaction...");
2444
+ const submitResult = await solanaService.submitPayment(
2445
+ signedTransactionBase64,
2446
+ priceId,
2447
+ paymentData.intent_id
2448
+ );
2449
+ setPaymentState({
2450
+ loading: false,
2451
+ error: null,
2452
+ success: true,
2453
+ transactionId: submitResult.transaction_id
2454
+ });
2455
+ console.log("Payment successful!", submitResult);
2456
+ } catch (err) {
2457
+ console.error("Payment failed:", err);
2458
+ let errorMessage = "Payment failed. Please try again.";
2459
+ const message = err instanceof Error ? err.message : typeof err === "string" ? err : "";
2460
+ if (message.includes("User rejected")) {
2461
+ errorMessage = "Payment cancelled by user.";
2462
+ } else if (/insufficient\s+funds/i.test(message)) {
2463
+ errorMessage = `Insufficient ${token.symbol} balance.`;
2464
+ } else if (/network/i.test(message)) {
2465
+ errorMessage = "Network error. Please check your connection.";
2466
+ } else if (message) {
2467
+ errorMessage = message;
2468
+ }
2469
+ setPaymentState({
2470
+ loading: false,
2471
+ error: errorMessage,
2472
+ success: false,
2473
+ transactionId: null
2474
+ });
2475
+ }
2476
+ },
2477
+ [connected, publicKey, signTransaction, solanaService]
2478
+ );
2479
+ return {
2480
+ paymentState,
2481
+ payWithWallet,
2482
+ resetPayment
2483
+ };
2484
+ };
2485
+ var usePaymentStatus = (options = {}) => {
2486
+ const { connection } = walletAdapterReact.useConnection();
2487
+ const { services } = usePaymentContext();
2488
+ const billingApi = services.billingApi;
2489
+ const {
2490
+ transactionId,
2491
+ purchaseId,
2492
+ onConfirmed,
2493
+ onFailed,
2494
+ maxRetries = 30,
2495
+ // 5 minutes with 10s intervals
2496
+ retryInterval = 1e4
2497
+ // 10 seconds
2498
+ } = options;
2499
+ const [status, setStatus] = react.useState(null);
2500
+ const [paymentStatus, setPaymentStatus] = react.useState(null);
2501
+ const [isLoading, setIsLoading] = react.useState(false);
2502
+ const [error, setError] = react.useState(null);
2503
+ const [retryCount, setRetryCount] = react.useState(0);
2504
+ const intervalRef = react.useRef(null);
2505
+ const isMonitoringRef = react.useRef(false);
2506
+ react.useEffect(() => {
2507
+ return () => {
2508
+ if (intervalRef.current) {
2509
+ clearInterval(intervalRef.current);
2510
+ }
2511
+ };
2512
+ }, []);
2513
+ const checkTransactionStatus = react.useCallback(
2514
+ async (signature) => {
2515
+ try {
2516
+ const statusResponse = await connection.getSignatureStatus(signature, {
2517
+ searchTransactionHistory: true
2518
+ });
2519
+ if (statusResponse.value === null) {
2520
+ return {
2521
+ signature,
2522
+ confirmationStatus: "processed",
2523
+ confirmations: 0,
2524
+ error: "Transaction not found"
2525
+ };
2526
+ }
2527
+ const { confirmationStatus, confirmations, slot, err } = statusResponse.value;
2528
+ let blockTime;
2529
+ if (slot) {
2530
+ try {
2531
+ blockTime = await connection.getBlockTime(slot) || void 0;
2532
+ } catch (error2) {
2533
+ }
2534
+ }
2535
+ return {
2536
+ signature,
2537
+ confirmationStatus: err ? "failed" : confirmationStatus || "processed",
2538
+ confirmations: confirmations || 0,
2539
+ slot,
2540
+ blockTime,
2541
+ error: err ? `Transaction failed: ${err}` : void 0
2542
+ };
2543
+ } catch (error2) {
2544
+ console.error("Failed to check transaction status:", error2);
2545
+ return {
2546
+ signature,
2547
+ confirmationStatus: "failed",
2548
+ confirmations: 0,
2549
+ error: error2 instanceof Error ? error2.message : "Failed to check transaction status"
2550
+ };
2551
+ }
2552
+ },
2553
+ [connection]
2554
+ );
2555
+ const checkPaymentStatus = react.useCallback(
2556
+ async (id) => {
2557
+ try {
2558
+ const data = await billingApi.get(
2559
+ `/payment/status/${id}`
2560
+ );
2561
+ return data;
2562
+ } catch (error2) {
2563
+ if (error2?.status === 404) {
2564
+ return null;
2565
+ }
2566
+ console.error("Failed to check payment status:", error2);
2567
+ return null;
2568
+ }
2569
+ },
2570
+ [billingApi]
2571
+ );
2572
+ const startMonitoring = react.useCallback(async () => {
2573
+ if (isMonitoringRef.current || !transactionId && !purchaseId) {
2574
+ return;
2575
+ }
2576
+ isMonitoringRef.current = true;
2577
+ setIsLoading(true);
2578
+ setError(null);
2579
+ setRetryCount(0);
2580
+ const monitor = async () => {
2581
+ try {
2582
+ let currentStatus = null;
2583
+ let currentPaymentStatus = null;
2584
+ if (transactionId) {
2585
+ currentStatus = await checkTransactionStatus(transactionId);
2586
+ setStatus(currentStatus);
2587
+ }
2588
+ if (purchaseId) {
2589
+ currentPaymentStatus = await checkPaymentStatus(purchaseId);
2590
+ setPaymentStatus(currentPaymentStatus);
2591
+ }
2592
+ const isBlockchainConfirmed = currentStatus?.confirmationStatus === "finalized" || currentStatus?.confirmationStatus === "confirmed";
2593
+ const isBackendConfirmed = currentPaymentStatus?.status === "confirmed";
2594
+ const isBlockchainFailed = currentStatus?.confirmationStatus === "failed" || currentStatus?.error;
2595
+ const isBackendFailed = currentPaymentStatus?.status === "failed";
2596
+ if (isBlockchainConfirmed || isBackendConfirmed) {
2597
+ if (intervalRef.current) {
2598
+ clearInterval(intervalRef.current);
2599
+ }
2600
+ isMonitoringRef.current = false;
2601
+ setIsLoading(false);
2602
+ onConfirmed?.();
2603
+ return;
2604
+ }
2605
+ if (isBlockchainFailed || isBackendFailed) {
2606
+ if (intervalRef.current) {
2607
+ clearInterval(intervalRef.current);
2608
+ }
2609
+ isMonitoringRef.current = false;
2610
+ setIsLoading(false);
2611
+ const errorMessage = currentStatus?.error || "Payment failed";
2612
+ setError(errorMessage);
2613
+ onFailed?.(errorMessage);
2614
+ return;
2615
+ }
2616
+ setRetryCount((prev) => prev + 1);
2617
+ if (retryCount >= maxRetries) {
2618
+ if (intervalRef.current) {
2619
+ clearInterval(intervalRef.current);
2620
+ }
2621
+ isMonitoringRef.current = false;
2622
+ setIsLoading(false);
2623
+ const timeoutError = "Payment confirmation timeout - please check your transaction manually";
2624
+ setError(timeoutError);
2625
+ onFailed?.(timeoutError);
2626
+ return;
2627
+ }
2628
+ } catch (error2) {
2629
+ console.error("Error monitoring payment:", error2);
2630
+ setRetryCount((prev) => prev + 1);
2631
+ if (retryCount >= maxRetries) {
2632
+ if (intervalRef.current) {
2633
+ clearInterval(intervalRef.current);
2634
+ }
2635
+ isMonitoringRef.current = false;
2636
+ setIsLoading(false);
2637
+ const monitorError = "Failed to monitor payment status";
2638
+ setError(monitorError);
2639
+ onFailed?.(monitorError);
2640
+ }
2641
+ }
2642
+ };
2643
+ await monitor();
2644
+ intervalRef.current = setInterval(monitor, retryInterval);
2645
+ }, [
2646
+ transactionId,
2647
+ purchaseId,
2648
+ checkTransactionStatus,
2649
+ checkPaymentStatus,
2650
+ onConfirmed,
2651
+ onFailed,
2652
+ maxRetries,
2653
+ retryInterval,
2654
+ retryCount
2655
+ ]);
2656
+ const stopMonitoring = react.useCallback(() => {
2657
+ if (intervalRef.current) {
2658
+ clearInterval(intervalRef.current);
2659
+ }
2660
+ isMonitoringRef.current = false;
2661
+ setIsLoading(false);
2662
+ }, []);
2663
+ const checkStatus = react.useCallback(async () => {
2664
+ if (!transactionId && !purchaseId) return;
2665
+ setIsLoading(true);
2666
+ setError(null);
2667
+ try {
2668
+ if (transactionId) {
2669
+ const txStatus = await checkTransactionStatus(transactionId);
2670
+ setStatus(txStatus);
2671
+ }
2672
+ if (purchaseId) {
2673
+ const payStatus = await checkPaymentStatus(purchaseId);
2674
+ setPaymentStatus(payStatus);
2675
+ }
2676
+ } catch (error2) {
2677
+ const errorMessage = error2 instanceof Error ? error2.message : "Failed to check status";
2678
+ setError(errorMessage);
2679
+ } finally {
2680
+ setIsLoading(false);
2681
+ }
2682
+ }, [transactionId, purchaseId, checkTransactionStatus, checkPaymentStatus]);
2683
+ react.useEffect(() => {
2684
+ if ((transactionId || purchaseId) && !isMonitoringRef.current) {
2685
+ startMonitoring();
2686
+ }
2687
+ return () => {
2688
+ stopMonitoring();
2689
+ };
2690
+ }, [transactionId, purchaseId, startMonitoring, stopMonitoring]);
2691
+ const getConfirmationStatus = react.useCallback(() => {
2692
+ if (paymentStatus?.status === "confirmed") return "confirmed";
2693
+ if (paymentStatus?.status === "failed") return "failed";
2694
+ if (status?.confirmationStatus === "finalized") return "confirmed";
2695
+ if (status?.confirmationStatus === "confirmed") return "confirmed";
2696
+ if (status?.confirmationStatus === "failed" || status?.error)
2697
+ return "failed";
2698
+ return "pending";
2699
+ }, [status, paymentStatus]);
2700
+ const getSolscanUrl = react.useCallback(
2701
+ (signature) => {
2702
+ const txId = signature || transactionId;
2703
+ if (!txId) return null;
2704
+ return `https://solscan.io/tx/${txId}`;
2705
+ },
2706
+ [transactionId]
2707
+ );
2708
+ return {
2709
+ status,
2710
+ paymentStatus,
2711
+ isLoading,
2712
+ error,
2713
+ retryCount,
2714
+ maxRetries,
2715
+ isMonitoring: isMonitoringRef.current,
2716
+ confirmationStatus: getConfirmationStatus(),
2717
+ startMonitoring,
2718
+ stopMonitoring,
2719
+ checkStatus,
2720
+ getSolscanUrl,
2721
+ isConfirmed: getConfirmationStatus() === "confirmed",
2722
+ isFailed: getConfirmationStatus() === "failed",
2723
+ isPending: getConfirmationStatus() === "pending"
2724
+ };
2725
+ };
2726
+ var useSubscriptionActions = () => {
2727
+ const { services } = usePaymentContext();
2728
+ const ensurePrice = (priceId) => {
2729
+ if (!priceId) {
2730
+ throw new Error("payments-ui: priceId is required for subscription actions");
2731
+ }
2732
+ return priceId;
2733
+ };
2734
+ const subscribeWithCard = react.useCallback(
2735
+ async ({
2736
+ priceId,
2737
+ processor = "nmi",
2738
+ provider,
2739
+ paymentToken,
2740
+ billing
2741
+ }) => {
2742
+ const payload = {
2743
+ priceId: ensurePrice(priceId),
2744
+ paymentToken,
2745
+ processor,
2746
+ provider,
2747
+ email: billing.email,
2748
+ firstName: billing.firstName,
2749
+ lastName: billing.lastName,
2750
+ address1: billing.address1,
2751
+ city: billing.city,
2752
+ state: billing.stateRegion,
2753
+ zipCode: billing.postalCode,
2754
+ country: billing.country
2755
+ };
2756
+ return services.subscriptions.subscribe("nmi", payload);
2757
+ },
2758
+ [services]
2759
+ );
2760
+ const subscribeWithSavedMethod = react.useCallback(
2761
+ async ({
2762
+ priceId,
2763
+ processor = "nmi",
2764
+ provider,
2765
+ paymentMethodId,
2766
+ email
2767
+ }) => {
2768
+ const payload = {
2769
+ priceId: ensurePrice(priceId),
2770
+ paymentMethodId,
2771
+ processor,
2772
+ provider,
2773
+ email
2774
+ };
2775
+ return services.subscriptions.subscribe("nmi", payload);
2776
+ },
2777
+ [services]
2778
+ );
2779
+ const subscribeWithCCBill = react.useCallback(
2780
+ async ({
2781
+ priceId,
2782
+ email,
2783
+ firstName,
2784
+ lastName,
2785
+ zipCode,
2786
+ country,
2787
+ processor = "ccbill"
2788
+ }) => {
2789
+ const payload = {
2790
+ priceId: ensurePrice(priceId),
2791
+ email,
2792
+ firstName,
2793
+ lastName,
2794
+ zipCode,
2795
+ country,
2796
+ processor
2797
+ };
2798
+ return services.subscriptions.subscribe("ccbill", payload);
2799
+ },
2800
+ [services]
2801
+ );
2802
+ const generateFlexFormUrl = react.useCallback(
2803
+ async ({
2804
+ priceId,
2805
+ firstName,
2806
+ lastName,
2807
+ address1,
2808
+ city,
2809
+ state,
2810
+ zipCode,
2811
+ country
2812
+ }) => {
2813
+ const payload = {
2814
+ price_id: ensurePrice(priceId),
2815
+ first_name: firstName,
2816
+ last_name: lastName,
2817
+ address1,
2818
+ city,
2819
+ state,
2820
+ zip_code: zipCode,
2821
+ country
2822
+ };
2823
+ return services.subscriptions.generateFlexFormUrl(payload);
2824
+ },
2825
+ [services]
2826
+ );
2827
+ return {
2828
+ subscribeWithCard,
2829
+ subscribeWithSavedMethod,
2830
+ subscribeWithCCBill,
2831
+ generateFlexFormUrl
2832
+ };
2833
+ };
2834
+
2835
+ exports.CardDetailsForm = CardDetailsForm;
2836
+ exports.CardPaymentService = CardPaymentService;
2837
+ exports.PaymentApp = PaymentApp;
2838
+ exports.PaymentExperience = PaymentExperience;
2839
+ exports.PaymentMethodService = PaymentMethodService;
2840
+ exports.PaymentProvider = PaymentProvider;
2841
+ exports.SolanaPaymentSelector = SolanaPaymentSelector;
2842
+ exports.SolanaPaymentService = SolanaPaymentService;
2843
+ exports.StoredPaymentMethods = StoredPaymentMethods;
2844
+ exports.SubscriptionService = SubscriptionService;
2845
+ exports.TokenCatalog = TokenCatalog;
2846
+ exports.WalletGateway = WalletGateway;
2847
+ exports.createPaymentStore = createPaymentStore;
2848
+ exports.useDirectWalletPayment = useDirectWalletPayment;
2849
+ exports.usePaymentContext = usePaymentContext;
2850
+ exports.usePaymentMethodService = usePaymentMethodService;
2851
+ exports.usePaymentMethods = usePaymentMethods;
2852
+ exports.usePaymentStatus = usePaymentStatus;
2853
+ exports.usePaymentStore = usePaymentStore;
2854
+ exports.useSolanaDirectPayment = useSolanaDirectPayment;
2855
+ exports.useSolanaQrPayment = useSolanaQrPayment;
2856
+ exports.useSolanaService = useSolanaService;
2857
+ exports.useSubscriptionActions = useSubscriptionActions;
2858
+ exports.useSupportedTokens = useSupportedTokens;
2859
+ exports.useTokenBalance = useTokenBalance;
2860
+ //# sourceMappingURL=index.cjs.map
2861
+ //# sourceMappingURL=index.cjs.map