@doujins/payments-ui 0.0.11 → 0.0.13
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 +2107 -620
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +279 -73
- package/dist/index.d.ts +279 -73
- package/dist/index.js +2088 -621
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +14 -1
package/dist/index.js
CHANGED
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
import * as React3 from 'react';
|
|
2
|
-
import { createContext, useMemo,
|
|
3
|
-
import {
|
|
2
|
+
import { createContext, useMemo, useState, useEffect, useCallback, useRef, useContext } from 'react';
|
|
3
|
+
import { QueryClient, useQueryClient, useQuery, useMutation, QueryClientProvider, useInfiniteQuery } from '@tanstack/react-query';
|
|
4
|
+
import { useWallet, ConnectionProvider, WalletProvider, useConnection } from '@solana/wallet-adapter-react';
|
|
5
|
+
import { WalletModalProvider, WalletMultiButton } from '@solana/wallet-adapter-react-ui';
|
|
6
|
+
import '@solana/wallet-adapter-react-ui/styles.css';
|
|
7
|
+
import { Connection, VersionedTransaction, Transaction, clusterApiUrl, PublicKey, LAMPORTS_PER_SOL } from '@solana/web3.js';
|
|
8
|
+
import { PhantomWalletAdapter } from '@solana/wallet-adapter-phantom';
|
|
9
|
+
import { SolflareWalletAdapter } from '@solana/wallet-adapter-solflare';
|
|
10
|
+
import { TrustWalletAdapter } from '@solana/wallet-adapter-trust';
|
|
11
|
+
import { CoinbaseWalletAdapter } from '@solana/wallet-adapter-coinbase';
|
|
12
|
+
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
13
|
+
import { X, ChevronDown, ChevronUp, Check, User, MapPin, Loader2, CreditCard, WalletCards, Trash2, ArrowLeft, CheckCircle, AlertCircle, Wallet, Ban, TriangleAlert, Star, Copy, ExternalLink, Shield, UserRound, Calendar, KeyRound, Sparkles, XCircle, RotateCcw, RefreshCw } from 'lucide-react';
|
|
14
|
+
import { clsx } from 'clsx';
|
|
15
|
+
import { twMerge } from 'tailwind-merge';
|
|
4
16
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
|
-
import { ChevronDown, ChevronUp, Check, X, User, MapPin, Loader2, CreditCard, WalletCards, Trash2, Sparkles, CheckCircle, AlertCircle, Wallet, XCircle, RotateCcw, RefreshCw } from 'lucide-react';
|
|
6
17
|
import countryList from 'country-list';
|
|
7
18
|
import { cva } from 'class-variance-authority';
|
|
8
|
-
import { clsx } from 'clsx';
|
|
9
|
-
import { twMerge } from 'tailwind-merge';
|
|
10
19
|
import * as LabelPrimitive from '@radix-ui/react-label';
|
|
11
20
|
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
12
|
-
import { useQueryClient, useQuery, useMutation } from '@tanstack/react-query';
|
|
13
|
-
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
14
21
|
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
|
|
15
|
-
import
|
|
22
|
+
import * as TabsPrimitive from '@radix-ui/react-tabs';
|
|
16
23
|
import { Buffer } from 'buffer';
|
|
17
|
-
import { Connection, VersionedTransaction, Transaction, PublicKey, LAMPORTS_PER_SOL } from '@solana/web3.js';
|
|
18
24
|
import { getAssociatedTokenAddress, getAccount, TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
|
19
25
|
import QRCode from 'qrcode';
|
|
20
|
-
import
|
|
21
|
-
import
|
|
26
|
+
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
|
|
27
|
+
import bs58 from 'bs58';
|
|
28
|
+
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
|
|
22
29
|
|
|
23
30
|
// src/context/PaymentContext.tsx
|
|
24
31
|
|
|
@@ -301,6 +308,38 @@ var SubscriptionService = class {
|
|
|
301
308
|
body: { ...payload }
|
|
302
309
|
});
|
|
303
310
|
}
|
|
311
|
+
async getPaymentHistory(params) {
|
|
312
|
+
const limit = params?.limit ?? 10;
|
|
313
|
+
const offset = params?.offset ?? 0;
|
|
314
|
+
const query = {
|
|
315
|
+
limit: String(limit),
|
|
316
|
+
offset: String(offset)
|
|
317
|
+
};
|
|
318
|
+
if (params?.type) {
|
|
319
|
+
query.type = params.type;
|
|
320
|
+
}
|
|
321
|
+
const response = await this.api.get("/subscriptions/purchases", {
|
|
322
|
+
query
|
|
323
|
+
});
|
|
324
|
+
const totalItems = response?.total_items ?? 0;
|
|
325
|
+
const pageSize = limit;
|
|
326
|
+
const pageNumber = response?.page ?? (pageSize > 0 ? Math.floor(offset / pageSize) + 1 : 1);
|
|
327
|
+
const totalPages = response?.total_pages ?? (pageSize > 0 ? Math.ceil(totalItems / pageSize) : void 0);
|
|
328
|
+
return {
|
|
329
|
+
data: response?.data ?? [],
|
|
330
|
+
total_items: totalItems,
|
|
331
|
+
limit,
|
|
332
|
+
offset,
|
|
333
|
+
page: pageNumber,
|
|
334
|
+
page_size: pageSize,
|
|
335
|
+
total_pages: totalPages
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
async cancelSubscription(feedback) {
|
|
339
|
+
return this.api.post("/subscriptions/cancel", {
|
|
340
|
+
body: feedback ? { feedback } : void 0
|
|
341
|
+
});
|
|
342
|
+
}
|
|
304
343
|
serializePayload(platform, payload) {
|
|
305
344
|
if (platform === "nmi") {
|
|
306
345
|
const data2 = payload;
|
|
@@ -345,6 +384,48 @@ var SubscriptionService = class {
|
|
|
345
384
|
}
|
|
346
385
|
};
|
|
347
386
|
|
|
387
|
+
// src/services/SolanaWalletService.ts
|
|
388
|
+
var SolanaWalletService = class {
|
|
389
|
+
constructor(api) {
|
|
390
|
+
this.api = api;
|
|
391
|
+
}
|
|
392
|
+
async list() {
|
|
393
|
+
const response = await this.api.get(
|
|
394
|
+
"/wallet/solana"
|
|
395
|
+
);
|
|
396
|
+
if (Array.isArray(response)) {
|
|
397
|
+
return response;
|
|
398
|
+
}
|
|
399
|
+
if ("wallets" in response && Array.isArray(response.wallets)) {
|
|
400
|
+
return response.wallets;
|
|
401
|
+
}
|
|
402
|
+
if ("wallet" in response) {
|
|
403
|
+
return response.wallet ? [response.wallet] : [];
|
|
404
|
+
}
|
|
405
|
+
if ("address" in response) {
|
|
406
|
+
return [response];
|
|
407
|
+
}
|
|
408
|
+
return [];
|
|
409
|
+
}
|
|
410
|
+
async requestChallenge(wallet) {
|
|
411
|
+
return this.api.post("/wallet/solana/challenge", {
|
|
412
|
+
body: { wallet }
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
async verify(wallet, signature, nonce) {
|
|
416
|
+
const body = { wallet, signature };
|
|
417
|
+
if (nonce) {
|
|
418
|
+
body.nonce = nonce;
|
|
419
|
+
}
|
|
420
|
+
return this.api.post("/wallet/solana/verify", { body });
|
|
421
|
+
}
|
|
422
|
+
async remove(wallet) {
|
|
423
|
+
await this.api.delete("/wallet/solana", {
|
|
424
|
+
query: { wallet }
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
|
|
348
429
|
// src/core/PaymentApp.ts
|
|
349
430
|
var PaymentApp = class {
|
|
350
431
|
constructor(options) {
|
|
@@ -394,6 +475,7 @@ var PaymentApp = class {
|
|
|
394
475
|
this.resolveAuthToken
|
|
395
476
|
);
|
|
396
477
|
const solanaPayments = new SolanaPaymentService(billingApi);
|
|
478
|
+
const solanaWallets = new SolanaWalletService(billingApi);
|
|
397
479
|
const paymentMethods = new PaymentMethodService(accountApi);
|
|
398
480
|
const cardPayments = new CardPaymentService(this.config);
|
|
399
481
|
const walletGateway = new WalletGateway();
|
|
@@ -403,6 +485,7 @@ var PaymentApp = class {
|
|
|
403
485
|
cardPayments,
|
|
404
486
|
paymentMethods,
|
|
405
487
|
solanaPayments,
|
|
488
|
+
solanaWallets,
|
|
406
489
|
tokenCatalog,
|
|
407
490
|
walletGateway,
|
|
408
491
|
subscriptions,
|
|
@@ -411,141 +494,98 @@ var PaymentApp = class {
|
|
|
411
494
|
};
|
|
412
495
|
}
|
|
413
496
|
};
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
solanaError: null,
|
|
424
|
-
solanaTransactionId: null,
|
|
425
|
-
solanaSelectedToken: null,
|
|
426
|
-
solanaTokenAmount: 0
|
|
427
|
-
};
|
|
428
|
-
var createPaymentStore = (options) => createStore((set, get) => {
|
|
429
|
-
const notifyStatus = (status, context) => {
|
|
430
|
-
options?.callbacks?.onStatusChange?.({ status, context });
|
|
431
|
-
};
|
|
432
|
-
const notifySuccess = (payload) => {
|
|
433
|
-
if (!options?.callbacks?.onSuccess) return;
|
|
434
|
-
options.callbacks.onSuccess(payload ?? {});
|
|
435
|
-
};
|
|
436
|
-
const notifyError = (error) => {
|
|
437
|
-
options?.callbacks?.onError?.(new Error(error));
|
|
438
|
-
};
|
|
439
|
-
return {
|
|
440
|
-
...initialState,
|
|
441
|
-
setSelectedMethod: (methodId) => {
|
|
442
|
-
if (get().selectedMethodId === methodId) return;
|
|
443
|
-
set({ selectedMethodId: methodId });
|
|
444
|
-
},
|
|
445
|
-
setSolanaModalOpen: (open) => {
|
|
446
|
-
if (get().solanaModalOpen === open) return;
|
|
447
|
-
set({ solanaModalOpen: open });
|
|
448
|
-
},
|
|
449
|
-
setSolanaTab: (tab) => {
|
|
450
|
-
if (get().solanaTab === tab) return;
|
|
451
|
-
set({ solanaTab: tab });
|
|
452
|
-
},
|
|
453
|
-
setSolanaSelectedToken: (symbol) => {
|
|
454
|
-
if (get().solanaSelectedToken === symbol) return;
|
|
455
|
-
set({ solanaSelectedToken: symbol });
|
|
456
|
-
},
|
|
457
|
-
setSolanaTokenAmount: (amount) => {
|
|
458
|
-
if (get().solanaTokenAmount === amount) return;
|
|
459
|
-
set({ solanaTokenAmount: amount });
|
|
460
|
-
},
|
|
461
|
-
setSolanaTransactionId: (txId) => {
|
|
462
|
-
if (get().solanaTransactionId === txId) return;
|
|
463
|
-
set({ solanaTransactionId: txId });
|
|
464
|
-
},
|
|
465
|
-
startSavedPayment: () => {
|
|
466
|
-
notifyStatus("processing", { source: "saved-payment" });
|
|
467
|
-
set({ savedPaymentStatus: "processing", savedPaymentError: null });
|
|
468
|
-
},
|
|
469
|
-
completeSavedPayment: () => {
|
|
470
|
-
notifyStatus("success", { source: "saved-payment" });
|
|
471
|
-
set({ savedPaymentStatus: "success", savedPaymentError: null });
|
|
472
|
-
},
|
|
473
|
-
failSavedPayment: (error) => {
|
|
474
|
-
notifyStatus("error", { source: "saved-payment" });
|
|
475
|
-
notifyError(error);
|
|
476
|
-
set({ savedPaymentStatus: "error", savedPaymentError: error });
|
|
477
|
-
},
|
|
478
|
-
resetSavedPayment: () => set({ savedPaymentStatus: "idle", savedPaymentError: null }),
|
|
479
|
-
startNewCardPayment: () => {
|
|
480
|
-
notifyStatus("processing", { source: "new-card" });
|
|
481
|
-
set({ newCardStatus: "processing", newCardError: null });
|
|
482
|
-
},
|
|
483
|
-
completeNewCardPayment: () => {
|
|
484
|
-
notifyStatus("success", { source: "new-card" });
|
|
485
|
-
set({ newCardStatus: "success", newCardError: null });
|
|
486
|
-
},
|
|
487
|
-
failNewCardPayment: (error) => {
|
|
488
|
-
notifyStatus("error", { source: "new-card" });
|
|
489
|
-
notifyError(error);
|
|
490
|
-
set({ newCardStatus: "error", newCardError: error });
|
|
491
|
-
},
|
|
492
|
-
resetNewCardPayment: () => set({ newCardStatus: "idle", newCardError: null }),
|
|
493
|
-
startSolanaPayment: () => {
|
|
494
|
-
notifyStatus("processing", { source: "solana" });
|
|
495
|
-
set({ solanaStatus: "processing", solanaError: null });
|
|
496
|
-
},
|
|
497
|
-
confirmSolanaPayment: () => set({ solanaStatus: "confirming" }),
|
|
498
|
-
completeSolanaPayment: (payload) => {
|
|
499
|
-
notifyStatus("success", { source: "solana" });
|
|
500
|
-
notifySuccess(payload);
|
|
501
|
-
set({ solanaStatus: "success", solanaError: null });
|
|
502
|
-
},
|
|
503
|
-
failSolanaPayment: (error) => {
|
|
504
|
-
notifyStatus("error", { source: "solana" });
|
|
505
|
-
notifyError(error);
|
|
506
|
-
set({ solanaStatus: "error", solanaError: error });
|
|
497
|
+
|
|
498
|
+
// src/runtime/PaymentsRuntime.ts
|
|
499
|
+
var createQueryClient = () => new QueryClient({
|
|
500
|
+
defaultOptions: {
|
|
501
|
+
queries: {
|
|
502
|
+
staleTime: 3e4,
|
|
503
|
+
gcTime: 5 * 6e4,
|
|
504
|
+
refetchOnWindowFocus: false,
|
|
505
|
+
retry: 1
|
|
507
506
|
},
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
}),
|
|
513
|
-
resetAll: () => set(initialState)
|
|
514
|
-
};
|
|
507
|
+
mutations: {
|
|
508
|
+
retry: 1
|
|
509
|
+
}
|
|
510
|
+
}
|
|
515
511
|
});
|
|
516
|
-
var
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
const store = useMemo(
|
|
523
|
-
() => createPaymentStore({ callbacks: config.callbacks }),
|
|
524
|
-
[config.callbacks]
|
|
525
|
-
);
|
|
526
|
-
const value = useMemo(() => {
|
|
527
|
-
return {
|
|
528
|
-
config: app.getConfig(),
|
|
529
|
-
fetcher: app.getFetcher(),
|
|
530
|
-
resolveAuthToken: app.resolveAuthToken,
|
|
531
|
-
app,
|
|
532
|
-
services: app.getServices(),
|
|
533
|
-
store
|
|
534
|
-
};
|
|
535
|
-
}, [app, store]);
|
|
536
|
-
useEffect(() => {
|
|
537
|
-
if (!value.config.collectJsKey) return;
|
|
538
|
-
loadCollectJs(value.config.collectJsKey);
|
|
539
|
-
}, [value.config.collectJsKey]);
|
|
540
|
-
return /* @__PURE__ */ jsx(PaymentContext.Provider, { value, children });
|
|
541
|
-
};
|
|
542
|
-
var usePaymentContext = () => {
|
|
543
|
-
const context = useContext(PaymentContext);
|
|
544
|
-
if (!context) {
|
|
545
|
-
throw new Error("usePaymentContext must be used within a PaymentProvider");
|
|
512
|
+
var PaymentsRuntime = class {
|
|
513
|
+
constructor(config) {
|
|
514
|
+
this.config = config;
|
|
515
|
+
this.app = new PaymentApp({ config });
|
|
516
|
+
this.services = this.app.getServices();
|
|
517
|
+
this.queryClient = createQueryClient();
|
|
546
518
|
}
|
|
547
|
-
return context;
|
|
548
519
|
};
|
|
520
|
+
var createPaymentsRuntime = (config) => new PaymentsRuntime(config);
|
|
521
|
+
|
|
522
|
+
// node_modules/@solana/wallet-adapter-base/lib/esm/types.js
|
|
523
|
+
var WalletAdapterNetwork;
|
|
524
|
+
(function(WalletAdapterNetwork2) {
|
|
525
|
+
WalletAdapterNetwork2["Mainnet"] = "mainnet-beta";
|
|
526
|
+
WalletAdapterNetwork2["Testnet"] = "testnet";
|
|
527
|
+
WalletAdapterNetwork2["Devnet"] = "devnet";
|
|
528
|
+
})(WalletAdapterNetwork || (WalletAdapterNetwork = {}));
|
|
529
|
+
function cn(...inputs) {
|
|
530
|
+
return twMerge(clsx(inputs));
|
|
531
|
+
}
|
|
532
|
+
var Dialog = DialogPrimitive.Root;
|
|
533
|
+
var DialogPortal = ({ className, ...props }) => /* @__PURE__ */ jsx(DialogPrimitive.Portal, { className: cn(className), ...props });
|
|
534
|
+
DialogPortal.displayName = DialogPrimitive.Portal.displayName;
|
|
535
|
+
var DialogOverlay = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
536
|
+
DialogPrimitive.Overlay,
|
|
537
|
+
{
|
|
538
|
+
ref,
|
|
539
|
+
className: cn(
|
|
540
|
+
"fixed inset-0 z-50 bg-black/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in",
|
|
541
|
+
className
|
|
542
|
+
),
|
|
543
|
+
...props
|
|
544
|
+
}
|
|
545
|
+
));
|
|
546
|
+
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
547
|
+
var DialogContent = React3.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
|
|
548
|
+
/* @__PURE__ */ jsx(DialogOverlay, {}),
|
|
549
|
+
/* @__PURE__ */ jsxs(
|
|
550
|
+
DialogPrimitive.Content,
|
|
551
|
+
{
|
|
552
|
+
ref,
|
|
553
|
+
className: cn(
|
|
554
|
+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]",
|
|
555
|
+
className
|
|
556
|
+
),
|
|
557
|
+
...props,
|
|
558
|
+
children: [
|
|
559
|
+
children,
|
|
560
|
+
/* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none", children: [
|
|
561
|
+
/* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
|
|
562
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
|
|
563
|
+
] })
|
|
564
|
+
]
|
|
565
|
+
}
|
|
566
|
+
)
|
|
567
|
+
] }));
|
|
568
|
+
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|
569
|
+
var DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props });
|
|
570
|
+
DialogHeader.displayName = "DialogHeader";
|
|
571
|
+
var DialogTitle = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
572
|
+
DialogPrimitive.Title,
|
|
573
|
+
{
|
|
574
|
+
ref,
|
|
575
|
+
className: cn("text-lg font-semibold leading-none tracking-tight", className),
|
|
576
|
+
...props
|
|
577
|
+
}
|
|
578
|
+
));
|
|
579
|
+
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
580
|
+
var DialogDescription = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
581
|
+
DialogPrimitive.Description,
|
|
582
|
+
{
|
|
583
|
+
ref,
|
|
584
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
585
|
+
...props
|
|
586
|
+
}
|
|
587
|
+
));
|
|
588
|
+
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|
549
589
|
var customCountries = [
|
|
550
590
|
{ code: "TW", name: "Taiwan, Province of China" },
|
|
551
591
|
{ code: "KR", name: "Korea" },
|
|
@@ -675,9 +715,6 @@ function getElementRef(element) {
|
|
|
675
715
|
}
|
|
676
716
|
return element.props.ref || element.ref;
|
|
677
717
|
}
|
|
678
|
-
function cn(...inputs) {
|
|
679
|
-
return twMerge(clsx(inputs));
|
|
680
|
-
}
|
|
681
718
|
var buttonVariants = cva(
|
|
682
719
|
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
|
|
683
720
|
{
|
|
@@ -820,7 +857,6 @@ var defaultBilling = {
|
|
|
820
857
|
firstName: "",
|
|
821
858
|
lastName: "",
|
|
822
859
|
address1: "",
|
|
823
|
-
address2: "",
|
|
824
860
|
city: "",
|
|
825
861
|
stateRegion: "",
|
|
826
862
|
postalCode: "",
|
|
@@ -854,7 +890,6 @@ var CardDetailsForm = ({
|
|
|
854
890
|
const [firstName, setFirstName] = useState(mergedDefaults.firstName);
|
|
855
891
|
const [lastName, setLastName] = useState(mergedDefaults.lastName);
|
|
856
892
|
const [address1, setAddress1] = useState(mergedDefaults.address1);
|
|
857
|
-
const [address2, setAddress2] = useState(mergedDefaults.address2 ?? "");
|
|
858
893
|
const [city, setCity] = useState(mergedDefaults.city);
|
|
859
894
|
const [stateRegion, setStateRegion] = useState(mergedDefaults.stateRegion ?? "");
|
|
860
895
|
const [postalCode, setPostalCode] = useState(mergedDefaults.postalCode);
|
|
@@ -873,7 +908,6 @@ var CardDetailsForm = ({
|
|
|
873
908
|
setFirstName(mergedDefaults.firstName);
|
|
874
909
|
setLastName(mergedDefaults.lastName);
|
|
875
910
|
setAddress1(mergedDefaults.address1);
|
|
876
|
-
setAddress2(mergedDefaults.address2 ?? "");
|
|
877
911
|
setCity(mergedDefaults.city);
|
|
878
912
|
setStateRegion(mergedDefaults.stateRegion ?? "");
|
|
879
913
|
setPostalCode(mergedDefaults.postalCode);
|
|
@@ -886,7 +920,6 @@ var CardDetailsForm = ({
|
|
|
886
920
|
firstName,
|
|
887
921
|
lastName,
|
|
888
922
|
address1,
|
|
889
|
-
address2,
|
|
890
923
|
city,
|
|
891
924
|
stateRegion,
|
|
892
925
|
postalCode,
|
|
@@ -898,7 +931,6 @@ var CardDetailsForm = ({
|
|
|
898
931
|
firstName,
|
|
899
932
|
lastName,
|
|
900
933
|
address1,
|
|
901
|
-
address2,
|
|
902
934
|
city,
|
|
903
935
|
stateRegion,
|
|
904
936
|
postalCode,
|
|
@@ -922,7 +954,6 @@ var CardDetailsForm = ({
|
|
|
922
954
|
firstName,
|
|
923
955
|
lastName,
|
|
924
956
|
address1,
|
|
925
|
-
address2,
|
|
926
957
|
city,
|
|
927
958
|
stateRegion,
|
|
928
959
|
postalCode,
|
|
@@ -953,7 +984,6 @@ var CardDetailsForm = ({
|
|
|
953
984
|
firstName,
|
|
954
985
|
lastName,
|
|
955
986
|
address1,
|
|
956
|
-
address2,
|
|
957
987
|
city,
|
|
958
988
|
stateRegion,
|
|
959
989
|
postalCode,
|
|
@@ -982,19 +1012,16 @@ var CardDetailsForm = ({
|
|
|
982
1012
|
window.CollectJS.startPaymentRequest();
|
|
983
1013
|
};
|
|
984
1014
|
const errorMessage = localError ?? externalError;
|
|
985
|
-
const collectFieldClass = "flex h-11 w-full items-center rounded-md border border-
|
|
1015
|
+
const collectFieldClass = "flex h-11 w-full items-center rounded-md border border-border/60 bg-background px-3 text-sm text-muted-foreground";
|
|
986
1016
|
return /* @__PURE__ */ jsxs(
|
|
987
1017
|
"form",
|
|
988
1018
|
{
|
|
989
|
-
className: cn(
|
|
990
|
-
"space-y-6 rounded-2xl border border-border/60 bg-card/90 p-6 shadow-lg",
|
|
991
|
-
className
|
|
992
|
-
),
|
|
1019
|
+
className: cn("space-y-5", className),
|
|
993
1020
|
onSubmit: handleSubmit,
|
|
994
1021
|
noValidate: true,
|
|
995
1022
|
children: [
|
|
996
1023
|
errorMessage && /* @__PURE__ */ jsx("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 px-4 py-2 text-sm text-destructive", children: errorMessage }),
|
|
997
|
-
/* @__PURE__ */ jsxs("div", { className: "grid gap-
|
|
1024
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-5 md:grid-cols-2", children: [
|
|
998
1025
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
999
1026
|
/* @__PURE__ */ jsxs(Label, { htmlFor: "payments-first", className: "flex items-center gap-2 text-muted-foreground", children: [
|
|
1000
1027
|
/* @__PURE__ */ jsx(User, { className: "h-4 w-4" }),
|
|
@@ -1040,7 +1067,7 @@ var CardDetailsForm = ({
|
|
|
1040
1067
|
)
|
|
1041
1068
|
] }),
|
|
1042
1069
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1043
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "payments-address1", children: "Address
|
|
1070
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "payments-address1", children: "Address" }),
|
|
1044
1071
|
/* @__PURE__ */ jsx(
|
|
1045
1072
|
Input,
|
|
1046
1073
|
{
|
|
@@ -1051,18 +1078,7 @@ var CardDetailsForm = ({
|
|
|
1051
1078
|
}
|
|
1052
1079
|
)
|
|
1053
1080
|
] }),
|
|
1054
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
1055
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "payments-address2", children: "Address line 2 (optional)" }),
|
|
1056
|
-
/* @__PURE__ */ jsx(
|
|
1057
|
-
Input,
|
|
1058
|
-
{
|
|
1059
|
-
id: "payments-address2",
|
|
1060
|
-
value: address2,
|
|
1061
|
-
onChange: (e) => setAddress2(e.target.value)
|
|
1062
|
-
}
|
|
1063
|
-
)
|
|
1064
|
-
] }),
|
|
1065
|
-
/* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [
|
|
1081
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-5 md:grid-cols-2", children: [
|
|
1066
1082
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1067
1083
|
/* @__PURE__ */ jsx(Label, { htmlFor: "payments-city", children: "City" }),
|
|
1068
1084
|
/* @__PURE__ */ jsx(
|
|
@@ -1087,7 +1103,7 @@ var CardDetailsForm = ({
|
|
|
1087
1103
|
)
|
|
1088
1104
|
] })
|
|
1089
1105
|
] }),
|
|
1090
|
-
/* @__PURE__ */ jsxs("div", { className: "grid gap-
|
|
1106
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-5 md:grid-cols-2", children: [
|
|
1091
1107
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1092
1108
|
/* @__PURE__ */ jsxs(Label, { htmlFor: "payments-postal", className: "flex items-center gap-2 text-muted-foreground", children: [
|
|
1093
1109
|
/* @__PURE__ */ jsx(MapPin, { className: "h-4 w-4" }),
|
|
@@ -1107,7 +1123,7 @@ var CardDetailsForm = ({
|
|
|
1107
1123
|
/* @__PURE__ */ jsx(Label, { children: "Country" }),
|
|
1108
1124
|
/* @__PURE__ */ jsxs(Select, { value: country, onValueChange: setCountry, children: [
|
|
1109
1125
|
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select a country" }) }),
|
|
1110
|
-
/* @__PURE__ */ jsx(SelectContent, { className: "max-h-64", children: countries.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.code, children: option.name }, option.code)) })
|
|
1126
|
+
/* @__PURE__ */ jsx(SelectContent, { className: "max-h-64 w-full", children: countries.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.code, children: option.name }, option.code)) })
|
|
1111
1127
|
] })
|
|
1112
1128
|
] })
|
|
1113
1129
|
] }),
|
|
@@ -1196,63 +1212,6 @@ var usePaymentMethods = () => {
|
|
|
1196
1212
|
deleteMutation
|
|
1197
1213
|
};
|
|
1198
1214
|
};
|
|
1199
|
-
var Dialog = DialogPrimitive.Root;
|
|
1200
|
-
var DialogPortal = ({ className, ...props }) => /* @__PURE__ */ jsx(DialogPrimitive.Portal, { className: cn(className), ...props });
|
|
1201
|
-
DialogPortal.displayName = DialogPrimitive.Portal.displayName;
|
|
1202
|
-
var DialogOverlay = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1203
|
-
DialogPrimitive.Overlay,
|
|
1204
|
-
{
|
|
1205
|
-
ref,
|
|
1206
|
-
className: cn(
|
|
1207
|
-
"fixed inset-0 z-50 bg-black/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in",
|
|
1208
|
-
className
|
|
1209
|
-
),
|
|
1210
|
-
...props
|
|
1211
|
-
}
|
|
1212
|
-
));
|
|
1213
|
-
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
1214
|
-
var DialogContent = React3.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
|
|
1215
|
-
/* @__PURE__ */ jsx(DialogOverlay, {}),
|
|
1216
|
-
/* @__PURE__ */ jsxs(
|
|
1217
|
-
DialogPrimitive.Content,
|
|
1218
|
-
{
|
|
1219
|
-
ref,
|
|
1220
|
-
className: cn(
|
|
1221
|
-
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]",
|
|
1222
|
-
className
|
|
1223
|
-
),
|
|
1224
|
-
...props,
|
|
1225
|
-
children: [
|
|
1226
|
-
children,
|
|
1227
|
-
/* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none", children: [
|
|
1228
|
-
/* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
|
|
1229
|
-
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
|
|
1230
|
-
] })
|
|
1231
|
-
]
|
|
1232
|
-
}
|
|
1233
|
-
)
|
|
1234
|
-
] }));
|
|
1235
|
-
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|
1236
|
-
var DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props });
|
|
1237
|
-
DialogHeader.displayName = "DialogHeader";
|
|
1238
|
-
var DialogTitle = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1239
|
-
DialogPrimitive.Title,
|
|
1240
|
-
{
|
|
1241
|
-
ref,
|
|
1242
|
-
className: cn("text-lg font-semibold leading-none tracking-tight", className),
|
|
1243
|
-
...props
|
|
1244
|
-
}
|
|
1245
|
-
));
|
|
1246
|
-
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
1247
|
-
var DialogDescription = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1248
|
-
DialogPrimitive.Description,
|
|
1249
|
-
{
|
|
1250
|
-
ref,
|
|
1251
|
-
className: cn("text-sm text-muted-foreground", className),
|
|
1252
|
-
...props
|
|
1253
|
-
}
|
|
1254
|
-
));
|
|
1255
|
-
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|
1256
1215
|
var badgeVariants = cva(
|
|
1257
1216
|
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none",
|
|
1258
1217
|
{
|
|
@@ -1302,43 +1261,6 @@ var ScrollBar = React3.forwardRef(({ className, orientation = "vertical", ...pro
|
|
|
1302
1261
|
}
|
|
1303
1262
|
));
|
|
1304
1263
|
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
|
|
1305
|
-
var Card = React3.forwardRef(
|
|
1306
|
-
({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1307
|
-
"div",
|
|
1308
|
-
{
|
|
1309
|
-
ref,
|
|
1310
|
-
className: cn("rounded-xl border bg-card text-card-foreground shadow", className),
|
|
1311
|
-
...props
|
|
1312
|
-
}
|
|
1313
|
-
)
|
|
1314
|
-
);
|
|
1315
|
-
Card.displayName = "Card";
|
|
1316
|
-
var CardHeader = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1317
|
-
"div",
|
|
1318
|
-
{
|
|
1319
|
-
ref,
|
|
1320
|
-
className: cn("flex flex-col space-y-1.5 p-6", className),
|
|
1321
|
-
...props
|
|
1322
|
-
}
|
|
1323
|
-
));
|
|
1324
|
-
CardHeader.displayName = "CardHeader";
|
|
1325
|
-
var CardTitle = React3.forwardRef(
|
|
1326
|
-
({ className, ...props }, ref) => /* @__PURE__ */ jsx("h3", { ref, className: cn("text-2xl font-semibold leading-none tracking-tight", className), ...props })
|
|
1327
|
-
);
|
|
1328
|
-
CardTitle.displayName = "CardTitle";
|
|
1329
|
-
var CardDescription = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("p", { ref, className: cn("text-sm text-muted-foreground", className), ...props }));
|
|
1330
|
-
CardDescription.displayName = "CardDescription";
|
|
1331
|
-
var CardContent = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
|
|
1332
|
-
CardContent.displayName = "CardContent";
|
|
1333
|
-
var CardFooter = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1334
|
-
"div",
|
|
1335
|
-
{
|
|
1336
|
-
ref,
|
|
1337
|
-
className: cn("flex items-center p-6 pt-0", className),
|
|
1338
|
-
...props
|
|
1339
|
-
}
|
|
1340
|
-
));
|
|
1341
|
-
CardFooter.displayName = "CardFooter";
|
|
1342
1264
|
var formatCardLabel = (method) => {
|
|
1343
1265
|
const brand = method.card_type ? method.card_type.toUpperCase() : "CARD";
|
|
1344
1266
|
const lastFour = method.last_four ? `\u2022\u2022\u2022\u2022 ${method.last_four}` : "";
|
|
@@ -1367,22 +1289,22 @@ var StoredPaymentMethods = ({
|
|
|
1367
1289
|
}
|
|
1368
1290
|
);
|
|
1369
1291
|
};
|
|
1370
|
-
return /* @__PURE__ */ jsxs(
|
|
1371
|
-
/* @__PURE__ */ jsxs(
|
|
1372
|
-
/* @__PURE__ */ jsxs("div", {
|
|
1373
|
-
/* @__PURE__ */
|
|
1292
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
1293
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
|
|
1294
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1295
|
+
/* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-sm font-medium text-muted-foreground", children: [
|
|
1374
1296
|
/* @__PURE__ */ jsx(WalletCards, { className: "h-4 w-4" }),
|
|
1375
1297
|
" ",
|
|
1376
1298
|
heading
|
|
1377
|
-
] })
|
|
1378
|
-
/* @__PURE__ */ jsx(
|
|
1299
|
+
] }),
|
|
1300
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: description })
|
|
1379
1301
|
] }),
|
|
1380
|
-
showAddButton && /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: () => setIsModalOpen(true), children: [
|
|
1302
|
+
showAddButton && /* @__PURE__ */ jsxs(Button, { size: "sm", variant: "outline", onClick: () => setIsModalOpen(true), children: [
|
|
1381
1303
|
/* @__PURE__ */ jsx(CreditCard, { className: "mr-2 h-4 w-4" }),
|
|
1382
1304
|
" Add card"
|
|
1383
1305
|
] })
|
|
1384
1306
|
] }),
|
|
1385
|
-
|
|
1307
|
+
listQuery.isLoading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center rounded-lg border border-dashed border-border/60 bg-muted/10 py-8 text-sm text-muted-foreground", children: [
|
|
1386
1308
|
/* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
|
|
1387
1309
|
" Loading cards\u2026"
|
|
1388
1310
|
] }) : payments.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-dashed border-border/60 bg-muted/10 px-4 py-6 text-sm text-muted-foreground", children: "No saved payment methods yet." }) : /* @__PURE__ */ jsx(ScrollArea, { className: "max-h-[320px] pr-2", children: /* @__PURE__ */ jsx("div", { className: "space-y-3", children: payments.map((method) => {
|
|
@@ -1440,8 +1362,8 @@ var StoredPaymentMethods = ({
|
|
|
1440
1362
|
},
|
|
1441
1363
|
method.id
|
|
1442
1364
|
);
|
|
1443
|
-
}) }) })
|
|
1444
|
-
/* @__PURE__ */ jsx(Dialog, { open: isModalOpen, onOpenChange: setIsModalOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-h-[
|
|
1365
|
+
}) }) }),
|
|
1366
|
+
/* @__PURE__ */ jsx(Dialog, { open: isModalOpen, onOpenChange: setIsModalOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-h-[85vh] overflow-y-auto", children: [
|
|
1445
1367
|
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
1446
1368
|
/* @__PURE__ */ jsx(DialogTitle, { children: "Add a new card" }),
|
|
1447
1369
|
/* @__PURE__ */ jsx(DialogDescription, { children: "Your card details are tokenized securely via our payment provider." })
|
|
@@ -1460,6 +1382,71 @@ var StoredPaymentMethods = ({
|
|
|
1460
1382
|
] }) })
|
|
1461
1383
|
] });
|
|
1462
1384
|
};
|
|
1385
|
+
var Tabs = TabsPrimitive.Root;
|
|
1386
|
+
var TabsList = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1387
|
+
TabsPrimitive.List,
|
|
1388
|
+
{
|
|
1389
|
+
ref,
|
|
1390
|
+
className: cn(
|
|
1391
|
+
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
|
|
1392
|
+
className
|
|
1393
|
+
),
|
|
1394
|
+
...props
|
|
1395
|
+
}
|
|
1396
|
+
));
|
|
1397
|
+
TabsList.displayName = TabsPrimitive.List.displayName;
|
|
1398
|
+
var TabsTrigger = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1399
|
+
TabsPrimitive.Trigger,
|
|
1400
|
+
{
|
|
1401
|
+
ref,
|
|
1402
|
+
className: cn(
|
|
1403
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
|
|
1404
|
+
className
|
|
1405
|
+
),
|
|
1406
|
+
...props
|
|
1407
|
+
}
|
|
1408
|
+
));
|
|
1409
|
+
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
|
1410
|
+
var TabsContent = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1411
|
+
TabsPrimitive.Content,
|
|
1412
|
+
{
|
|
1413
|
+
ref,
|
|
1414
|
+
className: cn(
|
|
1415
|
+
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
1416
|
+
className
|
|
1417
|
+
),
|
|
1418
|
+
...props
|
|
1419
|
+
}
|
|
1420
|
+
));
|
|
1421
|
+
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
|
1422
|
+
var usePaymentNotifications = () => {
|
|
1423
|
+
const { config } = usePaymentContext();
|
|
1424
|
+
const notifyStatus = useCallback(
|
|
1425
|
+
(status, context) => {
|
|
1426
|
+
config.callbacks?.onStatusChange?.({ status, context });
|
|
1427
|
+
},
|
|
1428
|
+
[config.callbacks]
|
|
1429
|
+
);
|
|
1430
|
+
const notifySuccess = useCallback(
|
|
1431
|
+
(payload) => {
|
|
1432
|
+
config.callbacks?.onSuccess?.(payload ?? {});
|
|
1433
|
+
},
|
|
1434
|
+
[config.callbacks]
|
|
1435
|
+
);
|
|
1436
|
+
const notifyError = useCallback(
|
|
1437
|
+
(error) => {
|
|
1438
|
+
config.callbacks?.onError?.(
|
|
1439
|
+
typeof error === "string" ? new Error(error) : error
|
|
1440
|
+
);
|
|
1441
|
+
},
|
|
1442
|
+
[config.callbacks]
|
|
1443
|
+
);
|
|
1444
|
+
return {
|
|
1445
|
+
notifyStatus,
|
|
1446
|
+
notifySuccess,
|
|
1447
|
+
notifyError
|
|
1448
|
+
};
|
|
1449
|
+
};
|
|
1463
1450
|
var useSolanaService = () => {
|
|
1464
1451
|
const { services } = usePaymentContext();
|
|
1465
1452
|
return useMemo(() => services.solanaPayments, [services]);
|
|
@@ -1736,6 +1723,43 @@ var useSolanaDirectPayment = (options) => {
|
|
|
1736
1723
|
pay
|
|
1737
1724
|
};
|
|
1738
1725
|
};
|
|
1726
|
+
var Card = React3.forwardRef(
|
|
1727
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1728
|
+
"div",
|
|
1729
|
+
{
|
|
1730
|
+
ref,
|
|
1731
|
+
className: cn("rounded-xl border bg-card text-card-foreground shadow", className),
|
|
1732
|
+
...props
|
|
1733
|
+
}
|
|
1734
|
+
)
|
|
1735
|
+
);
|
|
1736
|
+
Card.displayName = "Card";
|
|
1737
|
+
var CardHeader = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1738
|
+
"div",
|
|
1739
|
+
{
|
|
1740
|
+
ref,
|
|
1741
|
+
className: cn("flex flex-col space-y-1.5 p-6", className),
|
|
1742
|
+
...props
|
|
1743
|
+
}
|
|
1744
|
+
));
|
|
1745
|
+
CardHeader.displayName = "CardHeader";
|
|
1746
|
+
var CardTitle = React3.forwardRef(
|
|
1747
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx("h3", { ref, className: cn("text-2xl font-semibold leading-none tracking-tight", className), ...props })
|
|
1748
|
+
);
|
|
1749
|
+
CardTitle.displayName = "CardTitle";
|
|
1750
|
+
var CardDescription = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("p", { ref, className: cn("text-sm text-muted-foreground", className), ...props }));
|
|
1751
|
+
CardDescription.displayName = "CardDescription";
|
|
1752
|
+
var CardContent = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
|
|
1753
|
+
CardContent.displayName = "CardContent";
|
|
1754
|
+
var CardFooter = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
1755
|
+
"div",
|
|
1756
|
+
{
|
|
1757
|
+
ref,
|
|
1758
|
+
className: cn("flex items-center p-6 pt-0", className),
|
|
1759
|
+
...props
|
|
1760
|
+
}
|
|
1761
|
+
));
|
|
1762
|
+
CardFooter.displayName = "CardFooter";
|
|
1739
1763
|
var DirectPayment = ({
|
|
1740
1764
|
priceId,
|
|
1741
1765
|
tokenAmount,
|
|
@@ -1756,7 +1780,7 @@ var DirectPayment = ({
|
|
|
1756
1780
|
onSuccess: onPaymentSuccess,
|
|
1757
1781
|
onError: onPaymentError
|
|
1758
1782
|
});
|
|
1759
|
-
return /* @__PURE__ */ jsxs(Card, { className: "space-y-4 border border-border/60 bg-background/80 p-6", children: [
|
|
1783
|
+
return /* @__PURE__ */ jsxs(Card, { className: "space-y-4 rounded-md border border-border/60 bg-background/80 shadow-none p-6", children: [
|
|
1760
1784
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1761
1785
|
/* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: [
|
|
1762
1786
|
/* @__PURE__ */ jsx(Wallet, { className: "h-4 w-4" }),
|
|
@@ -1988,9 +2012,9 @@ var QRCodePayment = ({
|
|
|
1988
2012
|
onSuccess: onPaymentSuccess
|
|
1989
2013
|
});
|
|
1990
2014
|
if (!selectedToken) {
|
|
1991
|
-
return /* @__PURE__ */ jsx("div", { className: "rounded-
|
|
2015
|
+
return /* @__PURE__ */ jsx("div", { className: "rounded-md border border-dashed border-border/60 bg-muted/10 px-4 py-6 text-center text-sm text-muted-foreground", children: "Select a token to continue." });
|
|
1992
2016
|
}
|
|
1993
|
-
return /* @__PURE__ */ jsxs(Card, { className: "space-y-4 border border-border/60 bg-background/80 p-6", children: [
|
|
2017
|
+
return /* @__PURE__ */ jsxs(Card, { className: "space-y-4 border border-border/60 bg-background/80 p-6 shadow-none rounded-md", children: [
|
|
1994
2018
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
|
|
1995
2019
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1996
2020
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: "Scan with Solana Pay" }),
|
|
@@ -2219,109 +2243,21 @@ var useSupportedTokens = () => {
|
|
|
2219
2243
|
tokenCount: tokens.length
|
|
2220
2244
|
};
|
|
2221
2245
|
};
|
|
2222
|
-
var
|
|
2223
|
-
const { store } = usePaymentContext();
|
|
2224
|
-
return useStore(store, selector);
|
|
2225
|
-
};
|
|
2226
|
-
|
|
2227
|
-
// src/state/selectors.ts
|
|
2228
|
-
var selectCheckoutFlow = (state) => ({
|
|
2229
|
-
selectedMethodId: state.selectedMethodId,
|
|
2230
|
-
savedStatus: state.savedPaymentStatus,
|
|
2231
|
-
savedError: state.savedPaymentError,
|
|
2232
|
-
newCardStatus: state.newCardStatus,
|
|
2233
|
-
newCardError: state.newCardError,
|
|
2234
|
-
solanaModalOpen: state.solanaModalOpen,
|
|
2235
|
-
setSelectedMethod: state.setSelectedMethod,
|
|
2236
|
-
setSolanaModalOpen: state.setSolanaModalOpen,
|
|
2237
|
-
startSavedPayment: state.startSavedPayment,
|
|
2238
|
-
completeSavedPayment: state.completeSavedPayment,
|
|
2239
|
-
failSavedPayment: state.failSavedPayment,
|
|
2240
|
-
startNewCardPayment: state.startNewCardPayment,
|
|
2241
|
-
completeNewCardPayment: state.completeNewCardPayment,
|
|
2242
|
-
failNewCardPayment: state.failNewCardPayment,
|
|
2243
|
-
resetSavedPayment: state.resetSavedPayment
|
|
2244
|
-
});
|
|
2245
|
-
var selectSolanaFlow = (state) => ({
|
|
2246
|
-
tab: state.solanaTab,
|
|
2247
|
-
status: state.solanaStatus,
|
|
2248
|
-
error: state.solanaError,
|
|
2249
|
-
transactionId: state.solanaTransactionId,
|
|
2250
|
-
tokenAmount: state.solanaTokenAmount,
|
|
2251
|
-
selectedTokenSymbol: state.solanaSelectedToken,
|
|
2252
|
-
setTab: state.setSolanaTab,
|
|
2253
|
-
setTokenAmount: state.setSolanaTokenAmount,
|
|
2254
|
-
setTransactionId: state.setSolanaTransactionId,
|
|
2255
|
-
setSelectedTokenSymbol: state.setSolanaSelectedToken,
|
|
2256
|
-
startSolanaPayment: state.startSolanaPayment,
|
|
2257
|
-
confirmSolanaPayment: state.confirmSolanaPayment,
|
|
2258
|
-
completeSolanaPayment: state.completeSolanaPayment,
|
|
2259
|
-
failSolanaPayment: state.failSolanaPayment,
|
|
2260
|
-
resetSolanaPayment: state.resetSolanaPayment
|
|
2261
|
-
});
|
|
2262
|
-
var Tabs = TabsPrimitive.Root;
|
|
2263
|
-
var TabsList = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2264
|
-
TabsPrimitive.List,
|
|
2265
|
-
{
|
|
2266
|
-
ref,
|
|
2267
|
-
className: cn(
|
|
2268
|
-
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
|
|
2269
|
-
className
|
|
2270
|
-
),
|
|
2271
|
-
...props
|
|
2272
|
-
}
|
|
2273
|
-
));
|
|
2274
|
-
TabsList.displayName = TabsPrimitive.List.displayName;
|
|
2275
|
-
var TabsTrigger = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2276
|
-
TabsPrimitive.Trigger,
|
|
2277
|
-
{
|
|
2278
|
-
ref,
|
|
2279
|
-
className: cn(
|
|
2280
|
-
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
|
|
2281
|
-
className
|
|
2282
|
-
),
|
|
2283
|
-
...props
|
|
2284
|
-
}
|
|
2285
|
-
));
|
|
2286
|
-
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
|
2287
|
-
var TabsContent = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2288
|
-
TabsPrimitive.Content,
|
|
2289
|
-
{
|
|
2290
|
-
ref,
|
|
2291
|
-
className: cn(
|
|
2292
|
-
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
2293
|
-
className
|
|
2294
|
-
),
|
|
2295
|
-
...props
|
|
2296
|
-
}
|
|
2297
|
-
));
|
|
2298
|
-
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
|
2299
|
-
var SolanaPaymentSelector = ({
|
|
2300
|
-
isOpen,
|
|
2301
|
-
onClose,
|
|
2246
|
+
var SolanaPaymentView = ({
|
|
2302
2247
|
priceId,
|
|
2303
2248
|
usdAmount,
|
|
2304
2249
|
onSuccess,
|
|
2305
|
-
onError
|
|
2250
|
+
onError,
|
|
2251
|
+
onClose
|
|
2306
2252
|
}) => {
|
|
2307
2253
|
const { connected } = useWallet();
|
|
2308
|
-
const {
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
setTab,
|
|
2316
|
-
setTokenAmount,
|
|
2317
|
-
setTransactionId,
|
|
2318
|
-
setSelectedTokenSymbol,
|
|
2319
|
-
startSolanaPayment,
|
|
2320
|
-
confirmSolanaPayment,
|
|
2321
|
-
completeSolanaPayment,
|
|
2322
|
-
failSolanaPayment,
|
|
2323
|
-
resetSolanaPayment
|
|
2324
|
-
} = usePaymentStore(selectSolanaFlow);
|
|
2254
|
+
const { notifyStatus, notifyError, notifySuccess } = usePaymentNotifications();
|
|
2255
|
+
const [activeTab, setActiveTab] = useState("wallet");
|
|
2256
|
+
const [paymentState, setPaymentState] = useState("selecting");
|
|
2257
|
+
const [errorMessage, setErrorMessage] = useState(null);
|
|
2258
|
+
const [transactionId, setTransactionId] = useState(null);
|
|
2259
|
+
const [tokenAmount, setTokenAmount] = useState(0);
|
|
2260
|
+
const [selectedTokenSymbol, setSelectedTokenSymbol] = useState(null);
|
|
2325
2261
|
const {
|
|
2326
2262
|
tokens,
|
|
2327
2263
|
isLoading: tokensLoading,
|
|
@@ -2337,60 +2273,70 @@ var SolanaPaymentSelector = ({
|
|
|
2337
2273
|
const defaultToken = tokens.find((token) => token.symbol === "SOL") || tokens[0];
|
|
2338
2274
|
setSelectedTokenSymbol(defaultToken.symbol);
|
|
2339
2275
|
}
|
|
2340
|
-
}, [tokens, selectedTokenSymbol
|
|
2276
|
+
}, [tokens, selectedTokenSymbol]);
|
|
2341
2277
|
const handlePaymentStart = useCallback(() => {
|
|
2342
|
-
|
|
2343
|
-
|
|
2278
|
+
setPaymentState("processing");
|
|
2279
|
+
setErrorMessage(null);
|
|
2280
|
+
notifyStatus("processing", { source: "solana" });
|
|
2281
|
+
}, [notifyStatus]);
|
|
2344
2282
|
const handlePaymentConfirming = useCallback(() => {
|
|
2345
|
-
|
|
2346
|
-
}, [
|
|
2283
|
+
setPaymentState("confirming");
|
|
2284
|
+
}, []);
|
|
2347
2285
|
const handlePaymentSuccess = useCallback(
|
|
2348
2286
|
(result, txId) => {
|
|
2349
2287
|
const resolvedTx = txId || (typeof result === "string" ? result : result.transaction_id);
|
|
2350
2288
|
setTransactionId(resolvedTx);
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2289
|
+
setPaymentState("success");
|
|
2290
|
+
setErrorMessage(null);
|
|
2291
|
+
const payload = typeof result === "string" ? {
|
|
2292
|
+
transactionId: resolvedTx,
|
|
2293
|
+
processor: "solana",
|
|
2294
|
+
metadata: { source: "solana-pay" }
|
|
2295
|
+
} : {
|
|
2296
|
+
transactionId: result.transaction_id,
|
|
2297
|
+
intentId: result.intent_id,
|
|
2298
|
+
processor: "solana",
|
|
2299
|
+
metadata: {
|
|
2300
|
+
purchaseId: result.purchase_id,
|
|
2301
|
+
amount: result.amount,
|
|
2302
|
+
currency: result.currency
|
|
2365
2303
|
}
|
|
2366
|
-
|
|
2304
|
+
};
|
|
2305
|
+
notifyStatus("success", { source: "solana" });
|
|
2306
|
+
notifySuccess(payload);
|
|
2367
2307
|
setTimeout(() => {
|
|
2368
2308
|
onSuccess(result);
|
|
2369
2309
|
}, 1500);
|
|
2370
2310
|
},
|
|
2371
|
-
[
|
|
2311
|
+
[notifyStatus, notifySuccess, onSuccess]
|
|
2372
2312
|
);
|
|
2373
2313
|
const handlePaymentError = useCallback(
|
|
2374
2314
|
(error) => {
|
|
2375
|
-
|
|
2315
|
+
setPaymentState("error");
|
|
2316
|
+
setErrorMessage(error);
|
|
2317
|
+
notifyStatus("error", { source: "solana" });
|
|
2318
|
+
notifyError(error);
|
|
2376
2319
|
onError?.(error);
|
|
2377
2320
|
},
|
|
2378
|
-
[
|
|
2321
|
+
[notifyError, notifyStatus, onError]
|
|
2379
2322
|
);
|
|
2380
|
-
const
|
|
2381
|
-
|
|
2323
|
+
const resetState = useCallback(() => {
|
|
2324
|
+
setPaymentState("selecting");
|
|
2325
|
+
setErrorMessage(null);
|
|
2382
2326
|
setTransactionId(null);
|
|
2383
|
-
}, [
|
|
2327
|
+
}, []);
|
|
2328
|
+
const handleRetry = useCallback(() => {
|
|
2329
|
+
resetState();
|
|
2330
|
+
}, [resetState]);
|
|
2384
2331
|
const handleClose = useCallback(() => {
|
|
2385
2332
|
if (paymentState === "processing" || paymentState === "confirming") {
|
|
2386
2333
|
return;
|
|
2387
2334
|
}
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
}, [paymentState, resetSolanaPayment, setTransactionId, onClose]);
|
|
2335
|
+
resetState();
|
|
2336
|
+
onClose?.();
|
|
2337
|
+
}, [paymentState, onClose, resetState]);
|
|
2392
2338
|
useEffect(() => {
|
|
2393
|
-
if (!
|
|
2339
|
+
if (!selectedToken || usdAmount === 0) {
|
|
2394
2340
|
setTokenAmount(0);
|
|
2395
2341
|
return;
|
|
2396
2342
|
}
|
|
@@ -2400,23 +2346,20 @@ var SolanaPaymentSelector = ({
|
|
|
2400
2346
|
return;
|
|
2401
2347
|
}
|
|
2402
2348
|
setTokenAmount(usdAmount / price);
|
|
2403
|
-
}, [
|
|
2404
|
-
const handleTokenChange = useCallback(
|
|
2405
|
-
(value)
|
|
2406
|
-
|
|
2407
|
-
},
|
|
2408
|
-
[setSelectedTokenSymbol]
|
|
2409
|
-
);
|
|
2349
|
+
}, [usdAmount, selectedToken]);
|
|
2350
|
+
const handleTokenChange = useCallback((value) => {
|
|
2351
|
+
setSelectedTokenSymbol(value);
|
|
2352
|
+
}, []);
|
|
2410
2353
|
const wasConnectedRef = useRef(connected);
|
|
2411
2354
|
useEffect(() => {
|
|
2412
2355
|
if (connected && !wasConnectedRef.current) {
|
|
2413
|
-
|
|
2356
|
+
setActiveTab("wallet");
|
|
2414
2357
|
}
|
|
2415
2358
|
if (!connected && wasConnectedRef.current) {
|
|
2416
|
-
|
|
2359
|
+
setActiveTab("qr");
|
|
2417
2360
|
}
|
|
2418
2361
|
wasConnectedRef.current = connected;
|
|
2419
|
-
}, [connected
|
|
2362
|
+
}, [connected]);
|
|
2420
2363
|
const renderBody = () => {
|
|
2421
2364
|
if (paymentState !== "selecting") {
|
|
2422
2365
|
return /* @__PURE__ */ jsx(
|
|
@@ -2432,63 +2375,79 @@ var SolanaPaymentSelector = ({
|
|
|
2432
2375
|
}
|
|
2433
2376
|
);
|
|
2434
2377
|
}
|
|
2435
|
-
|
|
2436
|
-
/* @__PURE__ */
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2378
|
+
if (tokensLoading) {
|
|
2379
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center py-10 text-sm text-muted-foreground", children: [
|
|
2380
|
+
/* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
|
|
2381
|
+
" Loading supported tokens\u2026"
|
|
2382
|
+
] });
|
|
2383
|
+
}
|
|
2384
|
+
if (tokensError) {
|
|
2385
|
+
return /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: tokensError });
|
|
2386
|
+
}
|
|
2387
|
+
if (!tokens.length) {
|
|
2388
|
+
return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "No payment tokens available." });
|
|
2389
|
+
}
|
|
2390
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
2391
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1 text-center", children: [
|
|
2392
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Amount due" }),
|
|
2393
|
+
/* @__PURE__ */ jsxs("p", { className: "text-3xl font-semibold text-foreground", children: [
|
|
2441
2394
|
"$",
|
|
2442
2395
|
usdAmount.toFixed(2),
|
|
2443
2396
|
" USD"
|
|
2444
2397
|
] }),
|
|
2445
|
-
selectedToken && tokenAmount > 0 && /* @__PURE__ */ jsxs("
|
|
2398
|
+
selectedToken && tokenAmount > 0 && /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
|
|
2446
2399
|
"\u2248 ",
|
|
2447
2400
|
tokenAmount.toFixed(selectedToken.symbol === "SOL" ? 4 : 2),
|
|
2448
2401
|
" ",
|
|
2449
2402
|
selectedToken.symbol
|
|
2450
2403
|
] })
|
|
2451
2404
|
] }),
|
|
2452
|
-
/* @__PURE__ */ jsxs(
|
|
2453
|
-
/* @__PURE__ */ jsx(
|
|
2454
|
-
/* @__PURE__ */
|
|
2455
|
-
token
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2405
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2406
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: "Select token" }),
|
|
2407
|
+
/* @__PURE__ */ jsxs(Select, { value: selectedToken?.symbol ?? "", onValueChange: handleTokenChange, children: [
|
|
2408
|
+
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select token" }) }),
|
|
2409
|
+
/* @__PURE__ */ jsx(SelectContent, { className: "max-h-64", children: tokens.map((token) => /* @__PURE__ */ jsxs(SelectItem, { value: token.symbol, children: [
|
|
2410
|
+
token.name,
|
|
2411
|
+
" (",
|
|
2412
|
+
token.symbol,
|
|
2413
|
+
")"
|
|
2414
|
+
] }, token.symbol)) })
|
|
2415
|
+
] })
|
|
2460
2416
|
] }),
|
|
2461
|
-
/* @__PURE__ */ jsxs(
|
|
2417
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-3", children: /* @__PURE__ */ jsxs(
|
|
2462
2418
|
Tabs,
|
|
2463
2419
|
{
|
|
2464
2420
|
value: activeTab,
|
|
2465
|
-
onValueChange: (value) =>
|
|
2466
|
-
className: "w-full",
|
|
2421
|
+
onValueChange: (value) => setActiveTab(value),
|
|
2422
|
+
className: "w-full space-y-3",
|
|
2467
2423
|
children: [
|
|
2468
|
-
/* @__PURE__ */ jsxs(TabsList, { className: "grid w-full grid-cols-2 bg-muted/
|
|
2424
|
+
/* @__PURE__ */ jsxs(TabsList, { className: "grid w-full grid-cols-2 bg-muted/10", children: [
|
|
2469
2425
|
/* @__PURE__ */ jsxs(TabsTrigger, { value: "wallet", disabled: !connected, children: [
|
|
2470
2426
|
/* @__PURE__ */ jsx(Wallet, { className: "mr-2 h-4 w-4" }),
|
|
2471
|
-
"
|
|
2427
|
+
" Wallet"
|
|
2472
2428
|
] }),
|
|
2473
2429
|
/* @__PURE__ */ jsxs(TabsTrigger, { value: "qr", children: [
|
|
2474
2430
|
/* @__PURE__ */ jsx(CreditCard, { className: "mr-2 h-4 w-4" }),
|
|
2475
|
-
"
|
|
2431
|
+
" QR Code"
|
|
2476
2432
|
] })
|
|
2477
2433
|
] }),
|
|
2478
|
-
/* @__PURE__ */
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2434
|
+
/* @__PURE__ */ jsxs(TabsContent, { value: "wallet", className: "space-y-4", children: [
|
|
2435
|
+
activeTab === "wallet" && /* @__PURE__ */ jsx(
|
|
2436
|
+
DirectPayment,
|
|
2437
|
+
{
|
|
2438
|
+
priceId,
|
|
2439
|
+
tokenAmount,
|
|
2440
|
+
selectedToken,
|
|
2441
|
+
supportedTokens: tokens,
|
|
2442
|
+
onPaymentStart: handlePaymentStart,
|
|
2443
|
+
onPaymentConfirming: handlePaymentConfirming,
|
|
2444
|
+
onPaymentSuccess: handlePaymentSuccess,
|
|
2445
|
+
onPaymentError: handlePaymentError
|
|
2446
|
+
}
|
|
2447
|
+
),
|
|
2448
|
+
!connected && /* @__PURE__ */ jsx("div", { className: "text-sm text-amber-100", children: "Connect your Solana wallet to continue or switch to QR mode." })
|
|
2449
|
+
] }),
|
|
2450
|
+
/* @__PURE__ */ jsx(TabsContent, { value: "qr", children: activeTab === "qr" && /* @__PURE__ */ jsx(
|
|
2492
2451
|
QRCodePayment,
|
|
2493
2452
|
{
|
|
2494
2453
|
priceId,
|
|
@@ -2499,17 +2458,34 @@ var SolanaPaymentSelector = ({
|
|
|
2499
2458
|
) })
|
|
2500
2459
|
]
|
|
2501
2460
|
}
|
|
2502
|
-
)
|
|
2503
|
-
|
|
2504
|
-
] }) });
|
|
2461
|
+
) })
|
|
2462
|
+
] });
|
|
2505
2463
|
};
|
|
2506
|
-
return /* @__PURE__ */
|
|
2507
|
-
/* @__PURE__ */ jsxs(
|
|
2508
|
-
/* @__PURE__ */
|
|
2509
|
-
|
|
2464
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
2465
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
|
|
2466
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
2467
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs uppercase tracking-wide text-muted-foreground", children: "Solana Pay checkout" }),
|
|
2468
|
+
/* @__PURE__ */ jsx("p", { className: "text-2xl font-semibold text-foreground", children: "Pay with Solana" }),
|
|
2469
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Choose a supported token and send the payment with your wallet or a QR code." })
|
|
2470
|
+
] }),
|
|
2471
|
+
onClose && /* @__PURE__ */ jsxs(
|
|
2472
|
+
Button,
|
|
2473
|
+
{
|
|
2474
|
+
type: "button",
|
|
2475
|
+
size: "sm",
|
|
2476
|
+
variant: "ghost",
|
|
2477
|
+
onClick: handleClose,
|
|
2478
|
+
disabled: paymentState === "processing" || paymentState === "confirming",
|
|
2479
|
+
className: "h-8 px-2 text-sm",
|
|
2480
|
+
children: [
|
|
2481
|
+
/* @__PURE__ */ jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }),
|
|
2482
|
+
" Back"
|
|
2483
|
+
]
|
|
2484
|
+
}
|
|
2485
|
+
)
|
|
2510
2486
|
] }),
|
|
2511
2487
|
renderBody()
|
|
2512
|
-
] })
|
|
2488
|
+
] });
|
|
2513
2489
|
};
|
|
2514
2490
|
var PaymentExperience = ({
|
|
2515
2491
|
priceId,
|
|
@@ -2519,138 +2495,176 @@ var PaymentExperience = ({
|
|
|
2519
2495
|
enableNewCard = true,
|
|
2520
2496
|
enableStoredMethods = true,
|
|
2521
2497
|
enableSolanaPay = true,
|
|
2522
|
-
checkoutSummary,
|
|
2523
2498
|
onSolanaSuccess,
|
|
2524
|
-
onSolanaError
|
|
2499
|
+
onSolanaError,
|
|
2500
|
+
initialMode = "cards"
|
|
2525
2501
|
}) => {
|
|
2526
2502
|
const showNewCard = enableNewCard && Boolean(onNewCardPayment);
|
|
2527
|
-
const showStored = enableStoredMethods;
|
|
2528
|
-
const
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
setSelectedMethod(method.id);
|
|
2547
|
-
resetSavedPayment();
|
|
2548
|
-
};
|
|
2549
|
-
const handleNewCardTokenize = async (token, billing) => {
|
|
2550
|
-
if (!onNewCardPayment) return;
|
|
2551
|
-
try {
|
|
2552
|
-
startNewCardPayment();
|
|
2553
|
-
await onNewCardPayment({ token, billing });
|
|
2554
|
-
completeNewCardPayment();
|
|
2555
|
-
} catch (error) {
|
|
2556
|
-
const message = error instanceof Error ? error.message : "Unable to complete payment";
|
|
2557
|
-
failNewCardPayment(message);
|
|
2503
|
+
const showStored = enableStoredMethods && Boolean(onSavedMethodPayment);
|
|
2504
|
+
const defaultTab = showStored ? "saved" : "new";
|
|
2505
|
+
const [activeTab, setActiveTab] = useState(defaultTab);
|
|
2506
|
+
const [mode, setMode] = useState(
|
|
2507
|
+
() => initialMode === "solana" && enableSolanaPay ? "solana" : "cards"
|
|
2508
|
+
);
|
|
2509
|
+
const [selectedMethodId, setSelectedMethodId] = useState(null);
|
|
2510
|
+
const [savedStatus, setSavedStatus] = useState("idle");
|
|
2511
|
+
const [savedError, setSavedError] = useState(null);
|
|
2512
|
+
const [newCardStatus, setNewCardStatus] = useState("idle");
|
|
2513
|
+
const [newCardError, setNewCardError] = useState(null);
|
|
2514
|
+
const { notifyStatus, notifySuccess, notifyError } = usePaymentNotifications();
|
|
2515
|
+
useEffect(() => {
|
|
2516
|
+
setActiveTab(showStored ? "saved" : "new");
|
|
2517
|
+
}, [showStored]);
|
|
2518
|
+
useEffect(() => {
|
|
2519
|
+
if (!enableSolanaPay) {
|
|
2520
|
+
setMode("cards");
|
|
2521
|
+
return;
|
|
2558
2522
|
}
|
|
2559
|
-
|
|
2560
|
-
|
|
2523
|
+
if (initialMode === "solana") {
|
|
2524
|
+
setMode("solana");
|
|
2525
|
+
} else {
|
|
2526
|
+
setMode("cards");
|
|
2527
|
+
}
|
|
2528
|
+
}, [enableSolanaPay, initialMode]);
|
|
2529
|
+
const handleMethodSelect = useCallback((method) => {
|
|
2530
|
+
setSelectedMethodId(method.id);
|
|
2531
|
+
setSavedStatus("idle");
|
|
2532
|
+
setSavedError(null);
|
|
2533
|
+
}, []);
|
|
2534
|
+
const handleSavedPayment = useCallback(async () => {
|
|
2561
2535
|
if (!onSavedMethodPayment || !selectedMethodId) return;
|
|
2562
2536
|
try {
|
|
2563
|
-
|
|
2537
|
+
setSavedStatus("processing");
|
|
2538
|
+
setSavedError(null);
|
|
2539
|
+
notifyStatus("processing", { source: "saved-payment" });
|
|
2564
2540
|
await onSavedMethodPayment({
|
|
2565
2541
|
paymentMethodId: selectedMethodId,
|
|
2566
2542
|
amount: usdAmount
|
|
2567
2543
|
});
|
|
2568
|
-
|
|
2544
|
+
setSavedStatus("success");
|
|
2545
|
+
notifyStatus("success", { source: "saved-payment" });
|
|
2569
2546
|
} catch (error) {
|
|
2570
2547
|
const message = error instanceof Error ? error.message : "Unable to complete payment with saved card";
|
|
2571
|
-
|
|
2548
|
+
setSavedStatus("error");
|
|
2549
|
+
setSavedError(message);
|
|
2550
|
+
notifyStatus("error", { source: "saved-payment" });
|
|
2551
|
+
notifyError(message);
|
|
2552
|
+
}
|
|
2553
|
+
}, [notifyError, notifyStatus, onSavedMethodPayment, selectedMethodId, usdAmount]);
|
|
2554
|
+
const handleNewCardTokenize = useCallback(
|
|
2555
|
+
async (token, billing) => {
|
|
2556
|
+
if (!onNewCardPayment) return;
|
|
2557
|
+
try {
|
|
2558
|
+
setNewCardStatus("processing");
|
|
2559
|
+
setNewCardError(null);
|
|
2560
|
+
notifyStatus("processing", { source: "new-card" });
|
|
2561
|
+
await onNewCardPayment({ token, billing });
|
|
2562
|
+
setNewCardStatus("success");
|
|
2563
|
+
notifyStatus("success", { source: "new-card" });
|
|
2564
|
+
} catch (error) {
|
|
2565
|
+
const message = error instanceof Error ? error.message : "Unable to complete payment";
|
|
2566
|
+
setNewCardStatus("error");
|
|
2567
|
+
setNewCardError(message);
|
|
2568
|
+
notifyStatus("error", { source: "new-card" });
|
|
2569
|
+
notifyError(message);
|
|
2570
|
+
}
|
|
2571
|
+
},
|
|
2572
|
+
[notifyError, notifyStatus, onNewCardPayment]
|
|
2573
|
+
);
|
|
2574
|
+
const showSolanaView = useCallback(() => {
|
|
2575
|
+
if (!enableSolanaPay) return;
|
|
2576
|
+
setMode("solana");
|
|
2577
|
+
}, [enableSolanaPay]);
|
|
2578
|
+
const exitSolanaView = useCallback(() => {
|
|
2579
|
+
setMode("cards");
|
|
2580
|
+
}, []);
|
|
2581
|
+
const handleSolanaSuccess = useCallback(
|
|
2582
|
+
(result) => {
|
|
2583
|
+
onSolanaSuccess?.(result);
|
|
2584
|
+
exitSolanaView();
|
|
2585
|
+
},
|
|
2586
|
+
[exitSolanaView, onSolanaSuccess]
|
|
2587
|
+
);
|
|
2588
|
+
const handleSolanaError = useCallback(
|
|
2589
|
+
(error) => {
|
|
2590
|
+
onSolanaError?.(error);
|
|
2591
|
+
},
|
|
2592
|
+
[onSolanaError]
|
|
2593
|
+
);
|
|
2594
|
+
const renderSavedTab = () => {
|
|
2595
|
+
if (!showStored) {
|
|
2596
|
+
return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Saved payment methods are unavailable right now. Add a new card to get started." });
|
|
2572
2597
|
}
|
|
2598
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
2599
|
+
/* @__PURE__ */ jsx(
|
|
2600
|
+
StoredPaymentMethods,
|
|
2601
|
+
{
|
|
2602
|
+
heading: "Saved cards",
|
|
2603
|
+
selectedMethodId,
|
|
2604
|
+
onMethodSelect: handleMethodSelect,
|
|
2605
|
+
description: "Select one of your stored payment methods.",
|
|
2606
|
+
showAddButton: false
|
|
2607
|
+
}
|
|
2608
|
+
),
|
|
2609
|
+
/* @__PURE__ */ jsx(
|
|
2610
|
+
Button,
|
|
2611
|
+
{
|
|
2612
|
+
className: "w-full",
|
|
2613
|
+
disabled: !selectedMethodId || savedStatus === "processing",
|
|
2614
|
+
onClick: handleSavedPayment,
|
|
2615
|
+
children: savedStatus === "processing" ? "Processing\u2026" : "Pay with selected card"
|
|
2616
|
+
}
|
|
2617
|
+
),
|
|
2618
|
+
savedError && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: savedError })
|
|
2619
|
+
] });
|
|
2573
2620
|
};
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
/* @__PURE__ */
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
" Secure checkout"
|
|
2581
|
-
] }),
|
|
2582
|
-
/* @__PURE__ */ jsxs(CardDescription, { children: [
|
|
2583
|
-
"Amount due: $",
|
|
2584
|
-
usdAmount.toFixed(2)
|
|
2585
|
-
] })
|
|
2586
|
-
] }),
|
|
2587
|
-
checkoutSummary && /* @__PURE__ */ jsx("div", { children: checkoutSummary })
|
|
2588
|
-
] }),
|
|
2589
|
-
/* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs("div", { className: "grid gap-8 lg:grid-cols-2", children: [
|
|
2590
|
-
showStored && /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
2591
|
-
/* @__PURE__ */ jsx(
|
|
2592
|
-
StoredPaymentMethods,
|
|
2593
|
-
{
|
|
2594
|
-
selectedMethodId,
|
|
2595
|
-
onMethodSelect: handleMethodSelect,
|
|
2596
|
-
heading: "Saved cards",
|
|
2597
|
-
description: "Use or manage your saved payment methods."
|
|
2598
|
-
}
|
|
2599
|
-
),
|
|
2600
|
-
onSavedMethodPayment && /* @__PURE__ */ jsx(
|
|
2601
|
-
Button,
|
|
2602
|
-
{
|
|
2603
|
-
className: "w-full",
|
|
2604
|
-
disabled: !selectedMethodId || savedStatus === "processing",
|
|
2605
|
-
onClick: handleSavedPayment,
|
|
2606
|
-
children: savedStatus === "processing" ? "Processing\u2026" : "Pay with selected card"
|
|
2607
|
-
}
|
|
2608
|
-
),
|
|
2609
|
-
savedError && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: savedError })
|
|
2610
|
-
] }),
|
|
2611
|
-
showNewCard && /* @__PURE__ */ jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsxs("div", { className: "rounded-2xl border border-border/60 bg-background/80 p-6", children: [
|
|
2612
|
-
/* @__PURE__ */ jsxs("div", { className: "mb-4 space-y-1", children: [
|
|
2613
|
-
/* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: [
|
|
2614
|
-
/* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4" }),
|
|
2615
|
-
" Pay with a new card"
|
|
2616
|
-
] }),
|
|
2617
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Card details are tokenized via Collect.js and never hit your server." })
|
|
2618
|
-
] }),
|
|
2619
|
-
/* @__PURE__ */ jsx(
|
|
2620
|
-
CardDetailsForm,
|
|
2621
|
-
{
|
|
2622
|
-
visible: true,
|
|
2623
|
-
submitLabel: "Pay now",
|
|
2624
|
-
submitting: newCardStatus === "processing",
|
|
2625
|
-
externalError: newCardError,
|
|
2626
|
-
onTokenize: handleNewCardTokenize
|
|
2627
|
-
}
|
|
2628
|
-
)
|
|
2629
|
-
] }) })
|
|
2630
|
-
] }) })
|
|
2631
|
-
] }),
|
|
2632
|
-
enableSolanaPay && /* @__PURE__ */ jsx(Card, { className: "border border-primary/40 bg-primary/5", children: /* @__PURE__ */ jsxs(CardContent, { className: "flex flex-col gap-4 text-sm text-primary md:flex-row md:items-center md:justify-between", children: [
|
|
2633
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
2634
|
-
/* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-base font-semibold text-primary", children: [
|
|
2635
|
-
/* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4" }),
|
|
2636
|
-
" Prefer Solana Pay?"
|
|
2637
|
-
] }),
|
|
2638
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-primary/80", children: "Use a Solana wallet or QR code for instant settlement." })
|
|
2639
|
-
] }),
|
|
2640
|
-
/* @__PURE__ */ jsx(Button, { onClick: () => setSolanaModalOpen(true), children: "Open Solana Pay" })
|
|
2641
|
-
] }) }),
|
|
2642
|
-
enableSolanaPay && /* @__PURE__ */ jsx(
|
|
2643
|
-
SolanaPaymentSelector,
|
|
2621
|
+
const renderNewTab = () => {
|
|
2622
|
+
if (!showNewCard) {
|
|
2623
|
+
return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Select a subscription plan to add a new card." });
|
|
2624
|
+
}
|
|
2625
|
+
return /* @__PURE__ */ jsx(
|
|
2626
|
+
CardDetailsForm,
|
|
2644
2627
|
{
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2628
|
+
visible: true,
|
|
2629
|
+
submitLabel: "Pay now",
|
|
2630
|
+
externalError: newCardError,
|
|
2631
|
+
onTokenize: handleNewCardTokenize,
|
|
2632
|
+
submitting: newCardStatus === "processing"
|
|
2633
|
+
}
|
|
2634
|
+
);
|
|
2635
|
+
};
|
|
2636
|
+
const renderCardExperience = () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2637
|
+
/* @__PURE__ */ jsxs(
|
|
2638
|
+
Tabs,
|
|
2639
|
+
{
|
|
2640
|
+
value: activeTab,
|
|
2641
|
+
onValueChange: (value) => setActiveTab(value),
|
|
2642
|
+
className: "space-y-3",
|
|
2643
|
+
children: [
|
|
2644
|
+
/* @__PURE__ */ jsxs(TabsList, { className: "grid w-full grid-cols-2 border border-border/60", children: [
|
|
2645
|
+
/* @__PURE__ */ jsx(TabsTrigger, { value: "saved", disabled: !showStored, children: "Use saved card" }),
|
|
2646
|
+
/* @__PURE__ */ jsx(TabsTrigger, { value: "new", disabled: !showNewCard, children: "Add new card" })
|
|
2647
|
+
] }),
|
|
2648
|
+
/* @__PURE__ */ jsx(TabsContent, { value: "saved", className: "space-y-4", children: renderSavedTab() }),
|
|
2649
|
+
/* @__PURE__ */ jsx(TabsContent, { value: "new", className: "space-y-4", children: renderNewTab() })
|
|
2650
|
+
]
|
|
2651
|
+
}
|
|
2652
|
+
),
|
|
2653
|
+
enableSolanaPay && /* @__PURE__ */ jsxs(Button, { className: "w-full", variant: "secondary", onClick: showSolanaView, children: [
|
|
2654
|
+
/* @__PURE__ */ jsx(Sparkles, { className: "mr-2 h-4 w-4" }),
|
|
2655
|
+
" Pay with Solana"
|
|
2656
|
+
] })
|
|
2657
|
+
] });
|
|
2658
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-6 pt-4", children: [
|
|
2659
|
+
mode === "cards" && renderCardExperience(),
|
|
2660
|
+
mode === "solana" && enableSolanaPay && /* @__PURE__ */ jsx(
|
|
2661
|
+
SolanaPaymentView,
|
|
2662
|
+
{
|
|
2663
|
+
priceId,
|
|
2664
|
+
usdAmount,
|
|
2665
|
+
onSuccess: handleSolanaSuccess,
|
|
2666
|
+
onError: handleSolanaError,
|
|
2667
|
+
onClose: exitSolanaView
|
|
2654
2668
|
}
|
|
2655
2669
|
)
|
|
2656
2670
|
] });
|
|
@@ -2664,23 +2678,23 @@ var SubscriptionSuccessDialog = ({
|
|
|
2664
2678
|
}) => {
|
|
2665
2679
|
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (value) => {
|
|
2666
2680
|
if (!value) onClose();
|
|
2667
|
-
}, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-
|
|
2668
|
-
/* @__PURE__ */ jsxs(
|
|
2669
|
-
/* @__PURE__ */
|
|
2670
|
-
|
|
2671
|
-
"Subscription activated"
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
+
}, children: /* @__PURE__ */ jsxs(DialogContent, { className: "w-full max-w-md overflow-hidden border border-border/70 bg-background/95 p-0 shadow-2xl", children: [
|
|
2682
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-gradient-to-b from-primary/25 via-primary/10 to-background px-6 py-8 text-center", children: [
|
|
2683
|
+
/* @__PURE__ */ jsx("div", { className: "mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-background/60", children: /* @__PURE__ */ jsx(CheckCircle, { className: "h-10 w-10 text-primary" }) }),
|
|
2684
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
2685
|
+
/* @__PURE__ */ jsx(DialogTitle, { className: "text-2xl font-semibold text-foreground", children: "Subscription activated" }),
|
|
2686
|
+
/* @__PURE__ */ jsxs(DialogDescription, { className: "text-base text-muted-foreground", children: [
|
|
2687
|
+
"You now have access to ",
|
|
2688
|
+
planName,
|
|
2689
|
+
". Billing: ",
|
|
2690
|
+
amountLabel,
|
|
2691
|
+
" / ",
|
|
2692
|
+
billingPeriodLabel,
|
|
2693
|
+
"."
|
|
2694
|
+
] })
|
|
2681
2695
|
] })
|
|
2682
2696
|
] }),
|
|
2683
|
-
/* @__PURE__ */ jsx(
|
|
2697
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 py-6", children: /* @__PURE__ */ jsx(Button, { className: "w-full", onClick: onClose, children: "Continue exploring" }) })
|
|
2684
2698
|
] }) });
|
|
2685
2699
|
};
|
|
2686
2700
|
var useSubscriptionActions = () => {
|
|
@@ -2802,23 +2816,22 @@ var SubscriptionCheckoutModal = ({
|
|
|
2802
2816
|
userEmail,
|
|
2803
2817
|
provider = "mobius",
|
|
2804
2818
|
onSuccess,
|
|
2805
|
-
enableSolanaPay = true
|
|
2819
|
+
enableSolanaPay = true,
|
|
2820
|
+
onSolanaSuccess,
|
|
2821
|
+
onSolanaError,
|
|
2822
|
+
initialMode = "cards"
|
|
2806
2823
|
}) => {
|
|
2807
2824
|
const [showSuccess, setShowSuccess] = useState(false);
|
|
2808
2825
|
const { subscribeWithCard, subscribeWithSavedMethod } = useSubscriptionActions();
|
|
2809
2826
|
const handleClose = useCallback(
|
|
2810
2827
|
(nextOpen) => {
|
|
2811
2828
|
onOpenChange(nextOpen);
|
|
2812
|
-
if (!nextOpen)
|
|
2813
|
-
setShowSuccess(false);
|
|
2814
|
-
}
|
|
2829
|
+
if (!nextOpen) setShowSuccess(false);
|
|
2815
2830
|
},
|
|
2816
2831
|
[onOpenChange]
|
|
2817
2832
|
);
|
|
2818
2833
|
const ensurePrice = () => {
|
|
2819
|
-
if (!priceId)
|
|
2820
|
-
throw new Error("Select a plan before subscribing.");
|
|
2821
|
-
}
|
|
2834
|
+
if (!priceId) throw new Error("Select a plan before subscribing.");
|
|
2822
2835
|
return priceId;
|
|
2823
2836
|
};
|
|
2824
2837
|
const notifySuccess = (result) => {
|
|
@@ -2848,40 +2861,34 @@ var SubscriptionCheckoutModal = ({
|
|
|
2848
2861
|
};
|
|
2849
2862
|
const solanaSuccess = (result) => {
|
|
2850
2863
|
notifySuccess(result);
|
|
2864
|
+
onSolanaSuccess?.(result);
|
|
2851
2865
|
onOpenChange(false);
|
|
2852
2866
|
};
|
|
2853
|
-
const
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
/* @__PURE__ */ jsx("p", { className: "font-medium text-foreground", children: planName ?? "Selected plan" }),
|
|
2857
|
-
/* @__PURE__ */ jsxs("p", { children: [
|
|
2858
|
-
amountLabel ?? `$${usdAmount.toFixed(2)}`,
|
|
2859
|
-
" ",
|
|
2860
|
-
billingPeriodLabel ? `/ ${billingPeriodLabel}` : ""
|
|
2861
|
-
] })
|
|
2862
|
-
] });
|
|
2863
|
-
}, [planName, amountLabel, billingPeriodLabel, usdAmount]);
|
|
2867
|
+
const solanaError = (error) => {
|
|
2868
|
+
onSolanaError?.(error);
|
|
2869
|
+
};
|
|
2864
2870
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2865
|
-
/* @__PURE__ */ jsx(Dialog, { open, onOpenChange: handleClose, children: /* @__PURE__ */
|
|
2866
|
-
!priceId && /* @__PURE__ */ jsxs("div", { className: "
|
|
2871
|
+
/* @__PURE__ */ jsx(Dialog, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsx(DialogContent, { className: "w-full max-w-3xl max-h-[90vh] overflow-y-auto rounded-md border border-border/60 bg-background p-0 [&::-webkit-scrollbar]:hidden", children: /* @__PURE__ */ jsxs("div", { className: "p-6 space-y-6", children: [
|
|
2872
|
+
!priceId && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: [
|
|
2867
2873
|
/* @__PURE__ */ jsx(AlertCircle, { className: "h-4 w-4" }),
|
|
2868
2874
|
" Select a subscription plan to continue."
|
|
2869
2875
|
] }),
|
|
2870
2876
|
/* @__PURE__ */ jsx(
|
|
2871
2877
|
PaymentExperience,
|
|
2872
2878
|
{
|
|
2873
|
-
priceId: priceId ?? "",
|
|
2874
2879
|
usdAmount,
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2880
|
+
priceId: priceId ?? "",
|
|
2881
|
+
onSolanaSuccess: solanaSuccess,
|
|
2882
|
+
onSolanaError: solanaError,
|
|
2878
2883
|
enableNewCard: Boolean(priceId),
|
|
2879
2884
|
enableStoredMethods: Boolean(priceId),
|
|
2880
2885
|
enableSolanaPay: enableSolanaPay && Boolean(priceId),
|
|
2881
|
-
|
|
2886
|
+
onNewCardPayment: priceId ? handleNewCardPayment : void 0,
|
|
2887
|
+
onSavedMethodPayment: priceId ? handleSavedMethodPayment : void 0,
|
|
2888
|
+
initialMode
|
|
2882
2889
|
}
|
|
2883
2890
|
)
|
|
2884
|
-
] }) }),
|
|
2891
|
+
] }) }) }),
|
|
2885
2892
|
/* @__PURE__ */ jsx(
|
|
2886
2893
|
SubscriptionSuccessDialog,
|
|
2887
2894
|
{
|
|
@@ -2894,6 +2901,1466 @@ var SubscriptionCheckoutModal = ({
|
|
|
2894
2901
|
)
|
|
2895
2902
|
] });
|
|
2896
2903
|
};
|
|
2904
|
+
var wallets = [
|
|
2905
|
+
{
|
|
2906
|
+
id: "phantom",
|
|
2907
|
+
name: "Phantom",
|
|
2908
|
+
icon: "https://phantom.app/img/logo.png"
|
|
2909
|
+
},
|
|
2910
|
+
{
|
|
2911
|
+
id: "solflare",
|
|
2912
|
+
name: "Solflare",
|
|
2913
|
+
icon: "https://solflare.com/favicon.ico"
|
|
2914
|
+
}
|
|
2915
|
+
];
|
|
2916
|
+
var WalletModal = ({ open, onOpenChange }) => {
|
|
2917
|
+
const [expandedWallet, setExpandedWallet] = useState(null);
|
|
2918
|
+
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "w-full max-w-lg max-h-[90vh] overflow-y-auto rounded-md border border-border/70 bg-background/95 p-0 shadow-2xl [&::-webkit-scrollbar]:hidden", children: [
|
|
2919
|
+
/* @__PURE__ */ jsxs(DialogHeader, { className: "border-b border-border/40 bg-gradient-to-r from-primary/10 via-background to-background px-6 py-5 text-left", children: [
|
|
2920
|
+
/* @__PURE__ */ jsxs(DialogTitle, { className: "flex items-center gap-2 text-foreground", children: [
|
|
2921
|
+
/* @__PURE__ */ jsx(Wallet, { className: "h-5 w-5 text-primary" }),
|
|
2922
|
+
" Connect a Solana wallet"
|
|
2923
|
+
] }),
|
|
2924
|
+
/* @__PURE__ */ jsx(DialogDescription, { className: "text-sm text-muted-foreground", children: "Pick a supported wallet to link with Doujins. Verified wallets unlock Solana payments and withdrawals." })
|
|
2925
|
+
] }),
|
|
2926
|
+
/* @__PURE__ */ jsxs("div", { className: "p-6 space-y-4", children: [
|
|
2927
|
+
wallets.map((wallet) => /* @__PURE__ */ jsxs(
|
|
2928
|
+
"div",
|
|
2929
|
+
{
|
|
2930
|
+
className: "rounded-2xl border border-border/60 bg-background/80 p-4 shadow-sm",
|
|
2931
|
+
children: [
|
|
2932
|
+
/* @__PURE__ */ jsxs(
|
|
2933
|
+
"button",
|
|
2934
|
+
{
|
|
2935
|
+
className: "flex w-full items-center justify-between",
|
|
2936
|
+
onClick: () => setExpandedWallet((prev) => prev === wallet.id ? null : wallet.id),
|
|
2937
|
+
children: [
|
|
2938
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-left", children: [
|
|
2939
|
+
/* @__PURE__ */ jsx("img", { src: wallet.icon, alt: wallet.name, className: "h-10 w-10 rounded-full" }),
|
|
2940
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2941
|
+
/* @__PURE__ */ jsx("p", { className: "text-base font-semibold text-foreground", children: wallet.name }),
|
|
2942
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Browser extension or mobile app" })
|
|
2943
|
+
] })
|
|
2944
|
+
] }),
|
|
2945
|
+
expandedWallet === wallet.id ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4 text-muted-foreground" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground" })
|
|
2946
|
+
]
|
|
2947
|
+
}
|
|
2948
|
+
),
|
|
2949
|
+
expandedWallet === wallet.id && /* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-3 text-sm text-muted-foreground", children: [
|
|
2950
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
2951
|
+
"Open the ",
|
|
2952
|
+
wallet.name,
|
|
2953
|
+
" wallet, approve the connection request, and confirm the signature prompt to finish linking."
|
|
2954
|
+
] }),
|
|
2955
|
+
/* @__PURE__ */ jsxs(Button, { className: "w-full", variant: "outline", disabled: true, children: [
|
|
2956
|
+
"Connect with ",
|
|
2957
|
+
wallet.name,
|
|
2958
|
+
" (coming soon)"
|
|
2959
|
+
] })
|
|
2960
|
+
] })
|
|
2961
|
+
]
|
|
2962
|
+
},
|
|
2963
|
+
wallet.id
|
|
2964
|
+
)),
|
|
2965
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-2xl border border-border/60 bg-muted/10 p-4 text-xs text-muted-foreground", children: "Don\u2019t see your wallet? Additional providers will be added soon. Contact support if you need manual verification." })
|
|
2966
|
+
] })
|
|
2967
|
+
] }) });
|
|
2968
|
+
};
|
|
2969
|
+
var createDialogState = () => ({
|
|
2970
|
+
isOpen: false,
|
|
2971
|
+
props: null
|
|
2972
|
+
});
|
|
2973
|
+
var PaymentsDialogContext = createContext(void 0);
|
|
2974
|
+
var PaymentsDialogProvider = ({
|
|
2975
|
+
children
|
|
2976
|
+
}) => {
|
|
2977
|
+
const [checkoutState, setCheckoutState] = useState(
|
|
2978
|
+
() => createDialogState()
|
|
2979
|
+
);
|
|
2980
|
+
const [walletState, setWalletState] = useState(
|
|
2981
|
+
() => createDialogState()
|
|
2982
|
+
);
|
|
2983
|
+
const contextValue = useMemo(() => {
|
|
2984
|
+
const openCheckout = (options) => setCheckoutState({
|
|
2985
|
+
isOpen: true,
|
|
2986
|
+
props: options
|
|
2987
|
+
});
|
|
2988
|
+
return {
|
|
2989
|
+
checkout: {
|
|
2990
|
+
isOpen: checkoutState.isOpen,
|
|
2991
|
+
open: openCheckout,
|
|
2992
|
+
close: () => setCheckoutState(createDialogState())
|
|
2993
|
+
},
|
|
2994
|
+
solana: {
|
|
2995
|
+
isOpen: checkoutState.isOpen && checkoutState.props?.initialMode === "solana",
|
|
2996
|
+
open: (options) => openCheckout({
|
|
2997
|
+
priceId: options.priceId,
|
|
2998
|
+
usdAmount: options.usdAmount,
|
|
2999
|
+
enableSolanaPay: true,
|
|
3000
|
+
initialMode: "solana",
|
|
3001
|
+
onSolanaSuccess: options.onSuccess,
|
|
3002
|
+
onSolanaError: options.onError
|
|
3003
|
+
}),
|
|
3004
|
+
close: () => setCheckoutState(createDialogState())
|
|
3005
|
+
},
|
|
3006
|
+
wallet: {
|
|
3007
|
+
isOpen: walletState.isOpen,
|
|
3008
|
+
open: (options) => setWalletState({
|
|
3009
|
+
isOpen: true,
|
|
3010
|
+
props: options ?? null
|
|
3011
|
+
}),
|
|
3012
|
+
close: () => setWalletState(createDialogState())
|
|
3013
|
+
}
|
|
3014
|
+
};
|
|
3015
|
+
}, [checkoutState, walletState.isOpen]);
|
|
3016
|
+
return /* @__PURE__ */ jsxs(PaymentsDialogContext.Provider, { value: contextValue, children: [
|
|
3017
|
+
children,
|
|
3018
|
+
checkoutState.props && /* @__PURE__ */ jsx(
|
|
3019
|
+
SubscriptionCheckoutModal,
|
|
3020
|
+
{
|
|
3021
|
+
open: checkoutState.isOpen,
|
|
3022
|
+
onOpenChange: (open) => open ? setCheckoutState((prev) => ({ ...prev, isOpen: true })) : setCheckoutState(createDialogState()),
|
|
3023
|
+
...checkoutState.props
|
|
3024
|
+
}
|
|
3025
|
+
),
|
|
3026
|
+
/* @__PURE__ */ jsx(
|
|
3027
|
+
WalletModal,
|
|
3028
|
+
{
|
|
3029
|
+
open: walletState.isOpen,
|
|
3030
|
+
onOpenChange: (open) => open ? setWalletState((prev) => ({ ...prev, isOpen: true })) : setWalletState(createDialogState()),
|
|
3031
|
+
...walletState.props ?? {}
|
|
3032
|
+
}
|
|
3033
|
+
)
|
|
3034
|
+
] });
|
|
3035
|
+
};
|
|
3036
|
+
var usePaymentDialogs = () => {
|
|
3037
|
+
const context = useContext(PaymentsDialogContext);
|
|
3038
|
+
if (!context) {
|
|
3039
|
+
throw new Error("usePaymentDialogs must be used within PaymentProvider");
|
|
3040
|
+
}
|
|
3041
|
+
return context;
|
|
3042
|
+
};
|
|
3043
|
+
var PaymentContext = createContext(void 0);
|
|
3044
|
+
var PaymentProvider = ({
|
|
3045
|
+
config,
|
|
3046
|
+
runtime: runtimeProp,
|
|
3047
|
+
children
|
|
3048
|
+
}) => {
|
|
3049
|
+
const runtime = useMemo(
|
|
3050
|
+
() => runtimeProp ?? createPaymentsRuntime(config),
|
|
3051
|
+
[runtimeProp, config]
|
|
3052
|
+
);
|
|
3053
|
+
const solanaEndpoint = useMemo(() => {
|
|
3054
|
+
if (config.solana?.endpoint) return config.solana.endpoint;
|
|
3055
|
+
const network = config.solana?.network ?? WalletAdapterNetwork.Mainnet;
|
|
3056
|
+
return clusterApiUrl(network);
|
|
3057
|
+
}, [config.solana?.endpoint, config.solana?.network]);
|
|
3058
|
+
const walletAdapters = useMemo(() => {
|
|
3059
|
+
if (config.solana?.wallets?.length) {
|
|
3060
|
+
return config.solana.wallets;
|
|
3061
|
+
}
|
|
3062
|
+
return [
|
|
3063
|
+
new PhantomWalletAdapter(),
|
|
3064
|
+
new SolflareWalletAdapter(),
|
|
3065
|
+
new TrustWalletAdapter(),
|
|
3066
|
+
new CoinbaseWalletAdapter()
|
|
3067
|
+
];
|
|
3068
|
+
}, [config.solana?.wallets]);
|
|
3069
|
+
const autoConnect = config.solana?.autoConnect ?? true;
|
|
3070
|
+
const value = useMemo(() => {
|
|
3071
|
+
return {
|
|
3072
|
+
config: runtime.config,
|
|
3073
|
+
fetcher: runtime.app.getFetcher(),
|
|
3074
|
+
resolveAuthToken: runtime.app.resolveAuthToken,
|
|
3075
|
+
app: runtime.app,
|
|
3076
|
+
services: runtime.services,
|
|
3077
|
+
queryClient: runtime.queryClient
|
|
3078
|
+
};
|
|
3079
|
+
}, [runtime]);
|
|
3080
|
+
useEffect(() => {
|
|
3081
|
+
if (!config.collectJsKey) return;
|
|
3082
|
+
loadCollectJs(config.collectJsKey);
|
|
3083
|
+
}, [config.collectJsKey]);
|
|
3084
|
+
return /* @__PURE__ */ jsx(PaymentContext.Provider, { value, children: /* @__PURE__ */ jsx(QueryClientProvider, { client: runtime.queryClient, children: /* @__PURE__ */ jsx(ConnectionProvider, { endpoint: solanaEndpoint, config: { commitment: "confirmed" }, children: /* @__PURE__ */ jsx(WalletProvider, { wallets: walletAdapters, autoConnect, children: /* @__PURE__ */ jsx(WalletModalProvider, { children: /* @__PURE__ */ jsx(PaymentsDialogProvider, { children }) }) }) }) }) });
|
|
3085
|
+
};
|
|
3086
|
+
var usePaymentContext = () => {
|
|
3087
|
+
const context = useContext(PaymentContext);
|
|
3088
|
+
if (!context) {
|
|
3089
|
+
throw new Error("usePaymentContext must be used within a PaymentProvider");
|
|
3090
|
+
}
|
|
3091
|
+
return context;
|
|
3092
|
+
};
|
|
3093
|
+
var SolanaPaymentSelector = ({
|
|
3094
|
+
isOpen,
|
|
3095
|
+
onClose,
|
|
3096
|
+
...props
|
|
3097
|
+
}) => {
|
|
3098
|
+
return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: (value) => value ? void 0 : onClose(), children: /* @__PURE__ */ jsx(DialogContent, { className: "w-full max-w-2xl max-h-[90vh] overflow-y-auto rounded-md border border-border/70 bg-background/95 p-0 shadow-2xl [&::-webkit-scrollbar]:hidden", children: /* @__PURE__ */ jsx(SolanaPaymentView, { ...props, onClose }) }) });
|
|
3099
|
+
};
|
|
3100
|
+
var Table = React3.forwardRef(
|
|
3101
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3102
|
+
"table",
|
|
3103
|
+
{
|
|
3104
|
+
ref,
|
|
3105
|
+
className: cn("w-full caption-bottom text-sm", className),
|
|
3106
|
+
...props
|
|
3107
|
+
}
|
|
3108
|
+
)
|
|
3109
|
+
);
|
|
3110
|
+
Table.displayName = "Table";
|
|
3111
|
+
var TableHeader = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("thead", { ref, className: cn("[&_tr]:border-b", className), ...props }));
|
|
3112
|
+
TableHeader.displayName = "TableHeader";
|
|
3113
|
+
var TableBody = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("tbody", { ref, className: cn("[&_tr:last-child]:border-0", className), ...props }));
|
|
3114
|
+
TableBody.displayName = "TableBody";
|
|
3115
|
+
var TableFooter = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("tfoot", { ref, className: cn("bg-muted/50 font-medium text-muted-foreground", className), ...props }));
|
|
3116
|
+
TableFooter.displayName = "TableFooter";
|
|
3117
|
+
var TableRow = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("tr", { ref, className: cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className), ...props }));
|
|
3118
|
+
TableRow.displayName = "TableRow";
|
|
3119
|
+
var TableHead = React3.forwardRef(
|
|
3120
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3121
|
+
"th",
|
|
3122
|
+
{
|
|
3123
|
+
ref,
|
|
3124
|
+
className: cn(
|
|
3125
|
+
"h-10 px-2 text-left align-middle text-xs font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
|
3126
|
+
className
|
|
3127
|
+
),
|
|
3128
|
+
...props
|
|
3129
|
+
}
|
|
3130
|
+
)
|
|
3131
|
+
);
|
|
3132
|
+
TableHead.displayName = "TableHead";
|
|
3133
|
+
var TableCell = React3.forwardRef(
|
|
3134
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3135
|
+
"td",
|
|
3136
|
+
{
|
|
3137
|
+
ref,
|
|
3138
|
+
className: cn("p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", className),
|
|
3139
|
+
...props
|
|
3140
|
+
}
|
|
3141
|
+
)
|
|
3142
|
+
);
|
|
3143
|
+
TableCell.displayName = "TableCell";
|
|
3144
|
+
var TableCaption = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("caption", { ref, className: cn("mt-4 text-sm text-muted-foreground", className), ...props }));
|
|
3145
|
+
TableCaption.displayName = "TableCaption";
|
|
3146
|
+
var AlertDialog = AlertDialogPrimitive.Root;
|
|
3147
|
+
var AlertDialogTrigger = AlertDialogPrimitive.Trigger;
|
|
3148
|
+
var AlertDialogPortal = AlertDialogPrimitive.Portal;
|
|
3149
|
+
var AlertDialogOverlay = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3150
|
+
AlertDialogPrimitive.Overlay,
|
|
3151
|
+
{
|
|
3152
|
+
ref,
|
|
3153
|
+
className: cn(
|
|
3154
|
+
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0",
|
|
3155
|
+
className
|
|
3156
|
+
),
|
|
3157
|
+
...props
|
|
3158
|
+
}
|
|
3159
|
+
));
|
|
3160
|
+
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
|
|
3161
|
+
var AlertDialogContent = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxs(AlertDialogPortal, { children: [
|
|
3162
|
+
/* @__PURE__ */ jsx(AlertDialogOverlay, {}),
|
|
3163
|
+
/* @__PURE__ */ jsx(
|
|
3164
|
+
AlertDialogPrimitive.Content,
|
|
3165
|
+
{
|
|
3166
|
+
ref,
|
|
3167
|
+
className: cn(
|
|
3168
|
+
"fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-popover p-6 text-popover-foreground shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
3169
|
+
className
|
|
3170
|
+
),
|
|
3171
|
+
...props
|
|
3172
|
+
}
|
|
3173
|
+
)
|
|
3174
|
+
] }));
|
|
3175
|
+
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
|
|
3176
|
+
var AlertDialogHeader = ({
|
|
3177
|
+
className,
|
|
3178
|
+
...props
|
|
3179
|
+
}) => /* @__PURE__ */ jsx("div", { className: cn("flex flex-col space-y-2 text-center sm:text-left", className), ...props });
|
|
3180
|
+
AlertDialogHeader.displayName = "AlertDialogHeader";
|
|
3181
|
+
var AlertDialogFooter = ({
|
|
3182
|
+
className,
|
|
3183
|
+
...props
|
|
3184
|
+
}) => /* @__PURE__ */ jsx(
|
|
3185
|
+
"div",
|
|
3186
|
+
{
|
|
3187
|
+
className: cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end sm:gap-2", className),
|
|
3188
|
+
...props
|
|
3189
|
+
}
|
|
3190
|
+
);
|
|
3191
|
+
AlertDialogFooter.displayName = "AlertDialogFooter";
|
|
3192
|
+
var AlertDialogTitle = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(AlertDialogPrimitive.Title, { ref, className: cn("text-lg font-semibold", className), ...props }));
|
|
3193
|
+
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
|
|
3194
|
+
var AlertDialogDescription = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(AlertDialogPrimitive.Description, { ref, className: cn("text-sm text-muted-foreground", className), ...props }));
|
|
3195
|
+
AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
|
|
3196
|
+
var AlertDialogAction = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(AlertDialogPrimitive.Action, { ref, className: cn(buttonVariants(), className), ...props }));
|
|
3197
|
+
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
|
|
3198
|
+
var AlertDialogCancel = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3199
|
+
AlertDialogPrimitive.Cancel,
|
|
3200
|
+
{
|
|
3201
|
+
ref,
|
|
3202
|
+
className: cn(buttonVariants({ variant: "outline" }), "mt-2 sm:mt-0", className),
|
|
3203
|
+
...props
|
|
3204
|
+
}
|
|
3205
|
+
));
|
|
3206
|
+
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
|
|
3207
|
+
var Textarea = React3.forwardRef(
|
|
3208
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
3209
|
+
"textarea",
|
|
3210
|
+
{
|
|
3211
|
+
ref,
|
|
3212
|
+
className: cn(
|
|
3213
|
+
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
3214
|
+
className
|
|
3215
|
+
),
|
|
3216
|
+
...props
|
|
3217
|
+
}
|
|
3218
|
+
)
|
|
3219
|
+
);
|
|
3220
|
+
Textarea.displayName = "Textarea";
|
|
3221
|
+
var notifyDefault = (payload) => {
|
|
3222
|
+
const level = payload.status === "destructive" ? "error" : "info";
|
|
3223
|
+
console[level === "error" ? "error" : "log"]("[payments-ui] cancellation", payload);
|
|
3224
|
+
};
|
|
3225
|
+
var CancelMembershipDialog = ({
|
|
3226
|
+
minReasonLength = 15,
|
|
3227
|
+
onCancelled,
|
|
3228
|
+
onNotify
|
|
3229
|
+
}) => {
|
|
3230
|
+
const { services } = usePaymentContext();
|
|
3231
|
+
const notify = onNotify ?? notifyDefault;
|
|
3232
|
+
const [cancelReason, setCancelReason] = useState("");
|
|
3233
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
3234
|
+
const [isReasonValid, setIsReasonValid] = useState(false);
|
|
3235
|
+
const [hasInteracted, setHasInteracted] = useState(false);
|
|
3236
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
3237
|
+
useEffect(() => {
|
|
3238
|
+
const trimmed = cancelReason.trim();
|
|
3239
|
+
setIsReasonValid(trimmed.length >= minReasonLength);
|
|
3240
|
+
}, [cancelReason, minReasonLength]);
|
|
3241
|
+
const handleOpenChange = (open) => {
|
|
3242
|
+
setIsOpen(open);
|
|
3243
|
+
if (!open) {
|
|
3244
|
+
setCancelReason("");
|
|
3245
|
+
setIsReasonValid(false);
|
|
3246
|
+
setHasInteracted(false);
|
|
3247
|
+
setIsSubmitting(false);
|
|
3248
|
+
}
|
|
3249
|
+
};
|
|
3250
|
+
const handleReasonChange = (e) => {
|
|
3251
|
+
setCancelReason(e.target.value);
|
|
3252
|
+
if (!hasInteracted) {
|
|
3253
|
+
setHasInteracted(true);
|
|
3254
|
+
}
|
|
3255
|
+
};
|
|
3256
|
+
const handleConfirm = async () => {
|
|
3257
|
+
if (!isReasonValid) {
|
|
3258
|
+
setHasInteracted(true);
|
|
3259
|
+
return;
|
|
3260
|
+
}
|
|
3261
|
+
setIsSubmitting(true);
|
|
3262
|
+
try {
|
|
3263
|
+
await services.subscriptions.cancelSubscription(cancelReason.trim());
|
|
3264
|
+
notify({
|
|
3265
|
+
title: "Membership cancelled",
|
|
3266
|
+
description: "Your subscription has been cancelled successfully.",
|
|
3267
|
+
status: "success"
|
|
3268
|
+
});
|
|
3269
|
+
onCancelled?.();
|
|
3270
|
+
handleOpenChange(false);
|
|
3271
|
+
} catch (error) {
|
|
3272
|
+
const message = error instanceof Error ? error.message : "Unable to cancel membership";
|
|
3273
|
+
notify({ title: "Cancellation failed", description: message, status: "destructive" });
|
|
3274
|
+
} finally {
|
|
3275
|
+
setIsSubmitting(false);
|
|
3276
|
+
}
|
|
3277
|
+
};
|
|
3278
|
+
const showError = hasInteracted && !isReasonValid;
|
|
3279
|
+
return /* @__PURE__ */ jsxs(AlertDialog, { open: isOpen, onOpenChange: handleOpenChange, children: [
|
|
3280
|
+
/* @__PURE__ */ jsx(AlertDialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", className: "border-destructive/50 text-destructive", children: [
|
|
3281
|
+
/* @__PURE__ */ jsx(Ban, { className: "mr-2 h-4 w-4" }),
|
|
3282
|
+
" Cancel Membership"
|
|
3283
|
+
] }) }),
|
|
3284
|
+
/* @__PURE__ */ jsxs(AlertDialogContent, { className: "max-h-[90vh] overflow-y-auto rounded-md border border-border bg-background", children: [
|
|
3285
|
+
/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
|
|
3286
|
+
/* @__PURE__ */ jsxs(AlertDialogTitle, { className: "flex items-center gap-2 text-lg font-semibold", children: [
|
|
3287
|
+
/* @__PURE__ */ jsx(TriangleAlert, { className: "h-5 w-5 text-destructive" }),
|
|
3288
|
+
" Confirm Membership Cancellation"
|
|
3289
|
+
] }),
|
|
3290
|
+
/* @__PURE__ */ jsxs(AlertDialogDescription, { className: "mt-3 space-y-3 text-muted-foreground", children: [
|
|
3291
|
+
/* @__PURE__ */ jsx("p", { children: "You are about to cancel your membership. Please review the consequences:" }),
|
|
3292
|
+
/* @__PURE__ */ jsxs("ul", { className: "list-disc space-y-1 pl-5 text-sm", children: [
|
|
3293
|
+
/* @__PURE__ */ jsx("li", { children: "You will immediately lose access to premium features upon confirmation." }),
|
|
3294
|
+
/* @__PURE__ */ jsx("li", { children: "Your benefits remain active until the end of the billing cycle." }),
|
|
3295
|
+
/* @__PURE__ */ jsx("li", { children: "Your account will revert to the free plan afterwards." })
|
|
3296
|
+
] })
|
|
3297
|
+
] })
|
|
3298
|
+
] }),
|
|
3299
|
+
/* @__PURE__ */ jsxs("div", { className: "my-4 space-y-2 py-2", children: [
|
|
3300
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "cancelReason", className: "text-sm font-medium", children: "Please provide a reason for cancellation (required):" }),
|
|
3301
|
+
/* @__PURE__ */ jsx(
|
|
3302
|
+
Textarea,
|
|
3303
|
+
{
|
|
3304
|
+
id: "cancelReason",
|
|
3305
|
+
value: cancelReason,
|
|
3306
|
+
onChange: handleReasonChange,
|
|
3307
|
+
placeholder: "Your feedback helps us improve...",
|
|
3308
|
+
className: cn(
|
|
3309
|
+
"w-full resize-none border-border bg-background",
|
|
3310
|
+
showError && "border-destructive"
|
|
3311
|
+
),
|
|
3312
|
+
rows: 4,
|
|
3313
|
+
"aria-describedby": "reason-hint",
|
|
3314
|
+
"aria-invalid": showError
|
|
3315
|
+
}
|
|
3316
|
+
),
|
|
3317
|
+
/* @__PURE__ */ jsx(
|
|
3318
|
+
"p",
|
|
3319
|
+
{
|
|
3320
|
+
id: "reason-hint",
|
|
3321
|
+
className: `text-xs ${showError ? "text-destructive" : "text-muted-foreground"}`,
|
|
3322
|
+
children: showError ? `Reason must be at least ${minReasonLength} characters long.` : `Minimum ${minReasonLength} characters required.`
|
|
3323
|
+
}
|
|
3324
|
+
)
|
|
3325
|
+
] }),
|
|
3326
|
+
/* @__PURE__ */ jsxs(AlertDialogFooter, { className: "mt-6 gap-2", children: [
|
|
3327
|
+
/* @__PURE__ */ jsx(AlertDialogCancel, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", className: "border-border text-muted-foreground", children: "Keep Membership" }) }),
|
|
3328
|
+
/* @__PURE__ */ jsx(AlertDialogAction, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
3329
|
+
Button,
|
|
3330
|
+
{
|
|
3331
|
+
className: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
3332
|
+
onClick: handleConfirm,
|
|
3333
|
+
disabled: !isReasonValid || isSubmitting,
|
|
3334
|
+
children: isSubmitting ? "Cancelling..." : "Confirm Cancellation"
|
|
3335
|
+
}
|
|
3336
|
+
) })
|
|
3337
|
+
] })
|
|
3338
|
+
] })
|
|
3339
|
+
] });
|
|
3340
|
+
};
|
|
3341
|
+
var notifyDefault2 = (payload) => {
|
|
3342
|
+
const level = payload.status === "destructive" ? "error" : "info";
|
|
3343
|
+
console[level === "error" ? "error" : "log"]("[payments-ui] billing", payload);
|
|
3344
|
+
};
|
|
3345
|
+
var BillingHistory = ({
|
|
3346
|
+
pageSize = 10,
|
|
3347
|
+
initialPage = 1,
|
|
3348
|
+
enableCancel = true,
|
|
3349
|
+
onNotify
|
|
3350
|
+
}) => {
|
|
3351
|
+
const { services } = usePaymentContext();
|
|
3352
|
+
const notify = onNotify ?? notifyDefault2;
|
|
3353
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
3354
|
+
const observerRef = useRef(null);
|
|
3355
|
+
const loadMoreRef = useRef(null);
|
|
3356
|
+
const historyQuery = useInfiniteQuery({
|
|
3357
|
+
queryKey: ["payments-ui", "billing-history", pageSize],
|
|
3358
|
+
queryFn: async ({ pageParam = initialPage }) => {
|
|
3359
|
+
const offset = (pageParam - 1) * pageSize;
|
|
3360
|
+
return services.subscriptions.getPaymentHistory({ limit: pageSize, offset });
|
|
3361
|
+
},
|
|
3362
|
+
initialPageParam: initialPage,
|
|
3363
|
+
getNextPageParam: (lastPage) => {
|
|
3364
|
+
const nextOffset = (lastPage.offset ?? 0) + lastPage.data.length;
|
|
3365
|
+
if (lastPage.total_items <= nextOffset) {
|
|
3366
|
+
return void 0;
|
|
3367
|
+
}
|
|
3368
|
+
return lastPage.page ? lastPage.page + 1 : initialPage + 1;
|
|
3369
|
+
},
|
|
3370
|
+
staleTime: 5 * 60 * 1e3
|
|
3371
|
+
});
|
|
3372
|
+
useEffect(() => {
|
|
3373
|
+
if (!loadMoreRef.current || !isExpanded) return;
|
|
3374
|
+
observerRef.current = new IntersectionObserver((entries) => {
|
|
3375
|
+
const [entry] = entries;
|
|
3376
|
+
if (entry?.isIntersecting && historyQuery.hasNextPage && !historyQuery.isFetchingNextPage) {
|
|
3377
|
+
historyQuery.fetchNextPage().catch(() => {
|
|
3378
|
+
notify({ title: "Failed to load more history", status: "destructive" });
|
|
3379
|
+
});
|
|
3380
|
+
}
|
|
3381
|
+
});
|
|
3382
|
+
observerRef.current.observe(loadMoreRef.current);
|
|
3383
|
+
return () => {
|
|
3384
|
+
observerRef.current?.disconnect();
|
|
3385
|
+
};
|
|
3386
|
+
}, [historyQuery, isExpanded, notify]);
|
|
3387
|
+
const payments = useMemo(() => {
|
|
3388
|
+
const data = historyQuery.data;
|
|
3389
|
+
return data?.pages ?? [];
|
|
3390
|
+
}, [historyQuery.data]);
|
|
3391
|
+
const formatDate = (value) => new Date(value).toLocaleDateString("en-US", {
|
|
3392
|
+
year: "numeric",
|
|
3393
|
+
month: "short",
|
|
3394
|
+
day: "numeric"
|
|
3395
|
+
});
|
|
3396
|
+
const formatAmount = (amount, currency) => {
|
|
3397
|
+
try {
|
|
3398
|
+
return new Intl.NumberFormat("en-US", {
|
|
3399
|
+
style: "currency",
|
|
3400
|
+
currency
|
|
3401
|
+
}).format(amount);
|
|
3402
|
+
} catch {
|
|
3403
|
+
return `${amount.toFixed(2)} ${currency}`;
|
|
3404
|
+
}
|
|
3405
|
+
};
|
|
3406
|
+
return /* @__PURE__ */ jsx(Card, { className: "border-0 bg-background/5 shadow-lg", children: /* @__PURE__ */ jsxs("div", { className: "p-4 sm:p-6", children: [
|
|
3407
|
+
/* @__PURE__ */ jsxs("div", { className: "flex cursor-pointer items-center justify-between", onClick: () => setIsExpanded((prev) => !prev), children: [
|
|
3408
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
3409
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl font-semibold", children: "Transaction History" }),
|
|
3410
|
+
/* @__PURE__ */ jsx(CardDescription, { children: "Record of billing history" })
|
|
3411
|
+
] }),
|
|
3412
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: cn("h-5 w-5 text-muted-foreground transition-transform", isExpanded && "rotate-180") })
|
|
3413
|
+
] }),
|
|
3414
|
+
/* @__PURE__ */ jsx(
|
|
3415
|
+
"div",
|
|
3416
|
+
{
|
|
3417
|
+
className: cn(
|
|
3418
|
+
"overflow-hidden transition-all duration-300",
|
|
3419
|
+
isExpanded ? "mt-4 max-h-[1000px] opacity-100" : "max-h-0 opacity-0"
|
|
3420
|
+
),
|
|
3421
|
+
children: /* @__PURE__ */ jsx(CardContent, { className: "p-0 pt-4", children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
3422
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between", children: [
|
|
3423
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Review your account activity below" }),
|
|
3424
|
+
enableCancel && /* @__PURE__ */ jsx(CancelMembershipDialog, { onNotify: notify })
|
|
3425
|
+
] }),
|
|
3426
|
+
/* @__PURE__ */ jsx("div", { className: "max-h-[300px] overflow-y-auto rounded-lg border border-border/70", children: /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: historyQuery.isLoading ? /* @__PURE__ */ jsx("p", { className: "p-4 text-center text-sm text-muted-foreground", children: "Loading..." }) : historyQuery.isError ? /* @__PURE__ */ jsx("p", { className: "p-4 text-center text-sm text-destructive", children: "Error loading billing history." }) : /* @__PURE__ */ jsxs(Table, { children: [
|
|
3427
|
+
/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { className: "border-border/60", children: [
|
|
3428
|
+
/* @__PURE__ */ jsx(TableHead, { children: "Reference" }),
|
|
3429
|
+
/* @__PURE__ */ jsx(TableHead, { children: "Date" }),
|
|
3430
|
+
/* @__PURE__ */ jsx(TableHead, { children: "Amount" }),
|
|
3431
|
+
/* @__PURE__ */ jsx(TableHead, { children: "Processor" }),
|
|
3432
|
+
/* @__PURE__ */ jsx(TableHead, { children: "Status" })
|
|
3433
|
+
] }) }),
|
|
3434
|
+
/* @__PURE__ */ jsx(TableBody, { children: payments.map(
|
|
3435
|
+
(page) => page.data.map((payment) => /* @__PURE__ */ jsxs(TableRow, { className: "border-border/40", children: [
|
|
3436
|
+
/* @__PURE__ */ jsx(TableCell, { className: "font-mono text-sm", children: payment.id.slice(0, 7).toUpperCase() }),
|
|
3437
|
+
/* @__PURE__ */ jsx(TableCell, { children: formatDate(payment.purchased_at) }),
|
|
3438
|
+
/* @__PURE__ */ jsx(TableCell, { children: formatAmount(payment.amount, payment.currency) }),
|
|
3439
|
+
/* @__PURE__ */ jsx(TableCell, { className: "capitalize", children: payment.processor }),
|
|
3440
|
+
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Badge, { className: "bg-emerald-500/10 text-emerald-400", children: (payment.status || "completed").toLowerCase() }) })
|
|
3441
|
+
] }, payment.id))
|
|
3442
|
+
) })
|
|
3443
|
+
] }) }) }),
|
|
3444
|
+
/* @__PURE__ */ jsx("div", { ref: loadMoreRef, className: "h-10 w-full", children: historyQuery.isFetchingNextPage && /* @__PURE__ */ jsx("p", { className: "text-center text-sm text-muted-foreground", children: "Loading more..." }) })
|
|
3445
|
+
] }) })
|
|
3446
|
+
}
|
|
3447
|
+
)
|
|
3448
|
+
] }) });
|
|
3449
|
+
};
|
|
3450
|
+
var formatCardLabel2 = (method) => {
|
|
3451
|
+
const brand = method.card_type ? method.card_type.toUpperCase() : "CARD";
|
|
3452
|
+
const lastFour = method.last_four ? `\u2022\u2022\u2022\u2022 ${method.last_four}` : "";
|
|
3453
|
+
return `${brand} ${lastFour}`.trim();
|
|
3454
|
+
};
|
|
3455
|
+
var notifyDefault3 = (payload) => {
|
|
3456
|
+
const level = payload.status === "destructive" ? "error" : "info";
|
|
3457
|
+
console[level === "error" ? "error" : "log"]("[payments-ui] notification", payload);
|
|
3458
|
+
};
|
|
3459
|
+
var PaymentMethodsSection = ({
|
|
3460
|
+
isAuthenticated = true,
|
|
3461
|
+
userEmail,
|
|
3462
|
+
provider = "mobius",
|
|
3463
|
+
defaultCountry = "US",
|
|
3464
|
+
collectPrefix = "account-card",
|
|
3465
|
+
onNotify
|
|
3466
|
+
}) => {
|
|
3467
|
+
const paymentMethods = usePaymentMethodService();
|
|
3468
|
+
const queryClient = useQueryClient();
|
|
3469
|
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
3470
|
+
const [deletingId, setDeletingId] = useState(null);
|
|
3471
|
+
const notify = onNotify ?? notifyDefault3;
|
|
3472
|
+
const queryKey = ["payments-ui", "payment-methods"];
|
|
3473
|
+
const paymentQuery = useQuery({
|
|
3474
|
+
queryKey,
|
|
3475
|
+
queryFn: () => paymentMethods.list({ pageSize: 50 }),
|
|
3476
|
+
enabled: isAuthenticated,
|
|
3477
|
+
staleTime: 3e4
|
|
3478
|
+
});
|
|
3479
|
+
const createMutation = useMutation({
|
|
3480
|
+
mutationFn: (payload) => paymentMethods.create(payload),
|
|
3481
|
+
onSuccess: () => {
|
|
3482
|
+
notify({ title: "Card added successfully", status: "success" });
|
|
3483
|
+
setIsModalOpen(false);
|
|
3484
|
+
void queryClient.invalidateQueries({ queryKey });
|
|
3485
|
+
},
|
|
3486
|
+
onError: (error) => {
|
|
3487
|
+
notify({
|
|
3488
|
+
title: "Unable to add card",
|
|
3489
|
+
description: error.message,
|
|
3490
|
+
status: "destructive"
|
|
3491
|
+
});
|
|
3492
|
+
}
|
|
3493
|
+
});
|
|
3494
|
+
const deleteMutation = useMutation({
|
|
3495
|
+
mutationFn: (id) => paymentMethods.remove(id),
|
|
3496
|
+
onMutate: (id) => setDeletingId(id),
|
|
3497
|
+
onSuccess: () => {
|
|
3498
|
+
notify({ title: "Card removed", status: "success" });
|
|
3499
|
+
void queryClient.invalidateQueries({ queryKey });
|
|
3500
|
+
},
|
|
3501
|
+
onError: (error) => {
|
|
3502
|
+
notify({
|
|
3503
|
+
title: "Unable to remove card",
|
|
3504
|
+
description: error.message,
|
|
3505
|
+
status: "destructive"
|
|
3506
|
+
});
|
|
3507
|
+
},
|
|
3508
|
+
onSettled: () => setDeletingId(null)
|
|
3509
|
+
});
|
|
3510
|
+
useEffect(() => {
|
|
3511
|
+
if (!isModalOpen) {
|
|
3512
|
+
createMutation.reset();
|
|
3513
|
+
}
|
|
3514
|
+
}, [createMutation, isModalOpen]);
|
|
3515
|
+
const payments = useMemo(() => paymentQuery.data?.data ?? [], [paymentQuery.data]);
|
|
3516
|
+
const loading = paymentQuery.isLoading || paymentQuery.isFetching;
|
|
3517
|
+
const handleCardTokenize = (token, billing) => {
|
|
3518
|
+
const payload = {
|
|
3519
|
+
payment_token: token,
|
|
3520
|
+
first_name: billing.firstName,
|
|
3521
|
+
last_name: billing.lastName,
|
|
3522
|
+
address1: billing.address1,
|
|
3523
|
+
address2: billing.address2,
|
|
3524
|
+
city: billing.city,
|
|
3525
|
+
state: billing.stateRegion,
|
|
3526
|
+
zip: billing.postalCode,
|
|
3527
|
+
country: billing.country,
|
|
3528
|
+
email: billing.email,
|
|
3529
|
+
provider: billing.provider
|
|
3530
|
+
};
|
|
3531
|
+
createMutation.mutate(payload);
|
|
3532
|
+
};
|
|
3533
|
+
return /* @__PURE__ */ jsxs(Card, { className: "border-0 bg-background/5 shadow-lg", children: [
|
|
3534
|
+
/* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-col gap-4 md:flex-row md:items-center md:justify-between", children: [
|
|
3535
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
3536
|
+
/* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2 text-xl", children: [
|
|
3537
|
+
/* @__PURE__ */ jsx(WalletCards, { className: "h-5 w-5 text-primary" }),
|
|
3538
|
+
" Payment Methods"
|
|
3539
|
+
] }),
|
|
3540
|
+
/* @__PURE__ */ jsx(CardDescription, { children: "Manage your saved billing cards" })
|
|
3541
|
+
] }),
|
|
3542
|
+
/* @__PURE__ */ jsxs(Button, { onClick: () => setIsModalOpen(true), children: [
|
|
3543
|
+
/* @__PURE__ */ jsx(CreditCard, { className: "mr-2 h-4 w-4" }),
|
|
3544
|
+
" Add card"
|
|
3545
|
+
] })
|
|
3546
|
+
] }),
|
|
3547
|
+
/* @__PURE__ */ jsx(CardContent, { className: "space-y-4", children: loading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center py-10 text-muted-foreground", children: [
|
|
3548
|
+
/* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" }),
|
|
3549
|
+
" Loading cards..."
|
|
3550
|
+
] }) : payments.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-dashed border-border/60 bg-muted/10 p-6 text-sm text-muted-foreground", children: "No saved payment methods yet." }) : /* @__PURE__ */ jsx("div", { className: "space-y-3", children: payments.map((method) => /* @__PURE__ */ jsxs(
|
|
3551
|
+
"div",
|
|
3552
|
+
{
|
|
3553
|
+
className: "rounded-lg border border-border/80 bg-background/40 p-4 shadow-sm",
|
|
3554
|
+
children: [
|
|
3555
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 md:flex-row md:items-center md:justify-between", children: [
|
|
3556
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
3557
|
+
/* @__PURE__ */ jsx("h4", { className: "text-base font-medium text-foreground", children: formatCardLabel2(method) }),
|
|
3558
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
|
|
3559
|
+
"Added on",
|
|
3560
|
+
" ",
|
|
3561
|
+
method.created_at ? new Date(method.created_at).toLocaleDateString() : "unknown date"
|
|
3562
|
+
] })
|
|
3563
|
+
] }),
|
|
3564
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
3565
|
+
/* @__PURE__ */ jsx(
|
|
3566
|
+
Badge,
|
|
3567
|
+
{
|
|
3568
|
+
variant: method.is_active ? "default" : "secondary",
|
|
3569
|
+
className: method.is_active ? "bg-emerald-500/20 text-emerald-400" : "",
|
|
3570
|
+
children: method.is_active ? "Active" : "Inactive"
|
|
3571
|
+
}
|
|
3572
|
+
),
|
|
3573
|
+
method.failure_reason && /* @__PURE__ */ jsx(Badge, { variant: "destructive", children: method.failure_reason })
|
|
3574
|
+
] })
|
|
3575
|
+
] }),
|
|
3576
|
+
/* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-wrap gap-2", children: /* @__PURE__ */ jsxs(
|
|
3577
|
+
Button,
|
|
3578
|
+
{
|
|
3579
|
+
variant: "ghost",
|
|
3580
|
+
className: "text-destructive hover:text-destructive",
|
|
3581
|
+
disabled: deletingId === method.id && deleteMutation.isPending,
|
|
3582
|
+
onClick: () => deleteMutation.mutate(method.id),
|
|
3583
|
+
children: [
|
|
3584
|
+
deletingId === method.id && deleteMutation.isPending ? /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Trash2, { className: "mr-2 h-4 w-4" }),
|
|
3585
|
+
"Remove"
|
|
3586
|
+
]
|
|
3587
|
+
}
|
|
3588
|
+
) })
|
|
3589
|
+
]
|
|
3590
|
+
},
|
|
3591
|
+
method.id
|
|
3592
|
+
)) }) }),
|
|
3593
|
+
/* @__PURE__ */ jsx(Dialog, { open: isModalOpen, onOpenChange: setIsModalOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-h-[95vh] overflow-y-auto border border-border bg-background", children: [
|
|
3594
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
3595
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: "Add a new card" }),
|
|
3596
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: "Your card details are tokenized securely via our payment provider." })
|
|
3597
|
+
] }),
|
|
3598
|
+
/* @__PURE__ */ jsx(
|
|
3599
|
+
CardDetailsForm,
|
|
3600
|
+
{
|
|
3601
|
+
visible: isModalOpen,
|
|
3602
|
+
collectPrefix,
|
|
3603
|
+
submitting: createMutation.isPending,
|
|
3604
|
+
submitLabel: "Save card",
|
|
3605
|
+
defaultValues: {
|
|
3606
|
+
email: userEmail ?? "",
|
|
3607
|
+
country: defaultCountry,
|
|
3608
|
+
provider
|
|
3609
|
+
},
|
|
3610
|
+
externalError: createMutation.error?.message ?? null,
|
|
3611
|
+
onTokenize: handleCardTokenize,
|
|
3612
|
+
className: "rounded-2xl border border-border bg-muted/20 p-6"
|
|
3613
|
+
}
|
|
3614
|
+
)
|
|
3615
|
+
] }) })
|
|
3616
|
+
] });
|
|
3617
|
+
};
|
|
3618
|
+
var useWalletList = (options = {}) => {
|
|
3619
|
+
const { services } = usePaymentContext();
|
|
3620
|
+
const [state, setState] = useState({
|
|
3621
|
+
wallets: [],
|
|
3622
|
+
isLoading: false,
|
|
3623
|
+
error: null
|
|
3624
|
+
});
|
|
3625
|
+
const fetchWallets = useCallback(async () => {
|
|
3626
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
3627
|
+
try {
|
|
3628
|
+
const wallets2 = await services.solanaWallets.list();
|
|
3629
|
+
setState({ wallets: wallets2, isLoading: false, error: null });
|
|
3630
|
+
return wallets2;
|
|
3631
|
+
} catch (error) {
|
|
3632
|
+
const message = error instanceof Error ? error.message : "Failed to load wallets";
|
|
3633
|
+
console.error("payments-ui: wallet list fetch failed", error);
|
|
3634
|
+
setState((prev) => ({ ...prev, isLoading: false, error: message }));
|
|
3635
|
+
throw error;
|
|
3636
|
+
}
|
|
3637
|
+
}, [services.solanaWallets]);
|
|
3638
|
+
const deleteWallet = useCallback(
|
|
3639
|
+
async (walletIdOrAddress) => {
|
|
3640
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
3641
|
+
try {
|
|
3642
|
+
await services.solanaWallets.remove(walletIdOrAddress);
|
|
3643
|
+
setState((prev) => ({
|
|
3644
|
+
...prev,
|
|
3645
|
+
wallets: prev.wallets.filter(
|
|
3646
|
+
(wallet) => wallet.id !== walletIdOrAddress && wallet.address !== walletIdOrAddress
|
|
3647
|
+
),
|
|
3648
|
+
isLoading: false
|
|
3649
|
+
}));
|
|
3650
|
+
} catch (error) {
|
|
3651
|
+
const message = error instanceof Error ? error.message : "Failed to remove wallet";
|
|
3652
|
+
console.error("payments-ui: wallet removal failed", error);
|
|
3653
|
+
setState((prev) => ({ ...prev, isLoading: false, error: message }));
|
|
3654
|
+
throw error;
|
|
3655
|
+
}
|
|
3656
|
+
},
|
|
3657
|
+
[services.solanaWallets]
|
|
3658
|
+
);
|
|
3659
|
+
const addWallet = useCallback((wallet) => {
|
|
3660
|
+
setState((prev) => ({ ...prev, wallets: [...prev.wallets, wallet] }));
|
|
3661
|
+
}, []);
|
|
3662
|
+
const updateWallet = useCallback((walletId, updates) => {
|
|
3663
|
+
setState((prev) => ({
|
|
3664
|
+
...prev,
|
|
3665
|
+
wallets: prev.wallets.map(
|
|
3666
|
+
(wallet) => wallet.id === walletId ? { ...wallet, ...updates } : wallet
|
|
3667
|
+
)
|
|
3668
|
+
}));
|
|
3669
|
+
}, []);
|
|
3670
|
+
const clearError = useCallback(() => {
|
|
3671
|
+
setState((prev) => ({ ...prev, error: null }));
|
|
3672
|
+
}, []);
|
|
3673
|
+
const findWalletByAddress = useCallback(
|
|
3674
|
+
(address) => state.wallets.find((wallet) => wallet.address === address),
|
|
3675
|
+
[state.wallets]
|
|
3676
|
+
);
|
|
3677
|
+
const getVerifiedWallets = useCallback(
|
|
3678
|
+
() => state.wallets.filter((wallet) => wallet.is_verified),
|
|
3679
|
+
[state.wallets]
|
|
3680
|
+
);
|
|
3681
|
+
useEffect(() => {
|
|
3682
|
+
if (options.autoFetch !== false) {
|
|
3683
|
+
fetchWallets().catch(() => {
|
|
3684
|
+
});
|
|
3685
|
+
}
|
|
3686
|
+
}, [fetchWallets, options.autoFetch]);
|
|
3687
|
+
return {
|
|
3688
|
+
wallets: state.wallets,
|
|
3689
|
+
isLoading: state.isLoading,
|
|
3690
|
+
error: state.error,
|
|
3691
|
+
fetchWallets,
|
|
3692
|
+
deleteWallet,
|
|
3693
|
+
addWallet,
|
|
3694
|
+
updateWallet,
|
|
3695
|
+
clearError,
|
|
3696
|
+
findWalletByAddress,
|
|
3697
|
+
getVerifiedWallets,
|
|
3698
|
+
hasVerifiedWallets: state.wallets.some((w) => w.is_verified),
|
|
3699
|
+
totalWallets: state.wallets.length
|
|
3700
|
+
};
|
|
3701
|
+
};
|
|
3702
|
+
var useWalletVerification = () => {
|
|
3703
|
+
const { services } = usePaymentContext();
|
|
3704
|
+
const { publicKey, signMessage } = useWallet();
|
|
3705
|
+
const [state, setState] = useState({
|
|
3706
|
+
isVerifying: false,
|
|
3707
|
+
isVerified: false,
|
|
3708
|
+
error: null
|
|
3709
|
+
});
|
|
3710
|
+
const signAndVerifyWallet = useCallback(
|
|
3711
|
+
async (walletAddress, message, nonce) => {
|
|
3712
|
+
if (!publicKey || !signMessage) {
|
|
3713
|
+
throw new Error("Wallet not connected or signing unavailable");
|
|
3714
|
+
}
|
|
3715
|
+
if (publicKey.toBase58() !== walletAddress) {
|
|
3716
|
+
throw new Error("Connected wallet does not match target wallet");
|
|
3717
|
+
}
|
|
3718
|
+
setState((prev) => ({ ...prev, isVerifying: true, error: null }));
|
|
3719
|
+
try {
|
|
3720
|
+
const encodedMessage = new TextEncoder().encode(message);
|
|
3721
|
+
const signature = await signMessage(encodedMessage);
|
|
3722
|
+
const signatureBase58 = bs58.encode(signature);
|
|
3723
|
+
const response = await services.solanaWallets.verify(
|
|
3724
|
+
walletAddress,
|
|
3725
|
+
signatureBase58,
|
|
3726
|
+
nonce
|
|
3727
|
+
);
|
|
3728
|
+
setState({ isVerifying: false, isVerified: response.verified, error: null });
|
|
3729
|
+
return response;
|
|
3730
|
+
} catch (error) {
|
|
3731
|
+
const message2 = error instanceof Error ? error.message : "Wallet verification failed";
|
|
3732
|
+
console.error("payments-ui: wallet verification failed", error);
|
|
3733
|
+
setState({ isVerifying: false, isVerified: false, error: message2 });
|
|
3734
|
+
throw error;
|
|
3735
|
+
}
|
|
3736
|
+
},
|
|
3737
|
+
[publicKey, signMessage, services.solanaWallets]
|
|
3738
|
+
);
|
|
3739
|
+
const clearError = useCallback(() => {
|
|
3740
|
+
setState((prev) => ({ ...prev, error: null }));
|
|
3741
|
+
}, []);
|
|
3742
|
+
const resetVerification = useCallback(() => {
|
|
3743
|
+
setState({ isVerifying: false, isVerified: false, error: null });
|
|
3744
|
+
}, []);
|
|
3745
|
+
const autoVerifyWallet = useCallback(
|
|
3746
|
+
async (walletAddress, message, nonce) => {
|
|
3747
|
+
try {
|
|
3748
|
+
return await signAndVerifyWallet(walletAddress, message, nonce);
|
|
3749
|
+
} catch (error) {
|
|
3750
|
+
console.warn("payments-ui: auto-verification skipped", error);
|
|
3751
|
+
return null;
|
|
3752
|
+
}
|
|
3753
|
+
},
|
|
3754
|
+
[signAndVerifyWallet]
|
|
3755
|
+
);
|
|
3756
|
+
return {
|
|
3757
|
+
...state,
|
|
3758
|
+
signAndVerifyWallet,
|
|
3759
|
+
autoVerifyWallet,
|
|
3760
|
+
clearError,
|
|
3761
|
+
resetVerification
|
|
3762
|
+
};
|
|
3763
|
+
};
|
|
3764
|
+
var useWalletConnection = () => {
|
|
3765
|
+
const { services } = usePaymentContext();
|
|
3766
|
+
const { publicKey, connected, connecting, disconnect, wallet } = useWallet();
|
|
3767
|
+
const [state, setState] = useState({
|
|
3768
|
+
isConnected: false,
|
|
3769
|
+
isConnecting: false,
|
|
3770
|
+
publicKey: null,
|
|
3771
|
+
wallets: [],
|
|
3772
|
+
isLoading: false,
|
|
3773
|
+
error: null
|
|
3774
|
+
});
|
|
3775
|
+
useEffect(() => {
|
|
3776
|
+
setState((prev) => ({
|
|
3777
|
+
...prev,
|
|
3778
|
+
isConnected: connected,
|
|
3779
|
+
isConnecting: connecting,
|
|
3780
|
+
publicKey: publicKey?.toBase58() ?? null
|
|
3781
|
+
}));
|
|
3782
|
+
}, [connected, connecting, publicKey]);
|
|
3783
|
+
const connectWalletToBackend = useCallback(
|
|
3784
|
+
async (address) => {
|
|
3785
|
+
if (!address) {
|
|
3786
|
+
throw new Error("Wallet address is required");
|
|
3787
|
+
}
|
|
3788
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
3789
|
+
try {
|
|
3790
|
+
const challenge = await services.solanaWallets.requestChallenge(address);
|
|
3791
|
+
setState((prev) => ({ ...prev, isLoading: false }));
|
|
3792
|
+
return challenge;
|
|
3793
|
+
} catch (error) {
|
|
3794
|
+
const message = error instanceof Error ? error.message : "Wallet challenge failed";
|
|
3795
|
+
console.error("payments-ui: wallet challenge failed", error);
|
|
3796
|
+
setState((prev) => ({ ...prev, isLoading: false, error: message }));
|
|
3797
|
+
throw error;
|
|
3798
|
+
}
|
|
3799
|
+
},
|
|
3800
|
+
[services.solanaWallets]
|
|
3801
|
+
);
|
|
3802
|
+
const connectWallet = useCallback(async () => {
|
|
3803
|
+
if (!wallet) {
|
|
3804
|
+
throw new Error("No wallet adapter selected");
|
|
3805
|
+
}
|
|
3806
|
+
setState((prev) => ({ ...prev, isConnecting: true, error: null }));
|
|
3807
|
+
try {
|
|
3808
|
+
await wallet.adapter.connect();
|
|
3809
|
+
if (publicKey) {
|
|
3810
|
+
await connectWalletToBackend(publicKey.toBase58());
|
|
3811
|
+
}
|
|
3812
|
+
} catch (error) {
|
|
3813
|
+
const message = error instanceof Error ? error.message : "Failed to connect wallet";
|
|
3814
|
+
console.error("payments-ui: wallet connection failed", error);
|
|
3815
|
+
setState((prev) => ({ ...prev, isConnecting: false, error: message }));
|
|
3816
|
+
throw error;
|
|
3817
|
+
} finally {
|
|
3818
|
+
setState((prev) => ({ ...prev, isConnecting: false }));
|
|
3819
|
+
}
|
|
3820
|
+
}, [wallet, publicKey, connectWalletToBackend]);
|
|
3821
|
+
const disconnectWallet = useCallback(async () => {
|
|
3822
|
+
try {
|
|
3823
|
+
await disconnect();
|
|
3824
|
+
setState((prev) => ({
|
|
3825
|
+
...prev,
|
|
3826
|
+
isConnected: false,
|
|
3827
|
+
publicKey: null,
|
|
3828
|
+
wallets: [],
|
|
3829
|
+
error: null
|
|
3830
|
+
}));
|
|
3831
|
+
} catch (error) {
|
|
3832
|
+
const message = error instanceof Error ? error.message : "Failed to disconnect wallet";
|
|
3833
|
+
console.error("payments-ui: wallet disconnect failed", error);
|
|
3834
|
+
setState((prev) => ({ ...prev, error: message }));
|
|
3835
|
+
throw error;
|
|
3836
|
+
}
|
|
3837
|
+
}, [disconnect]);
|
|
3838
|
+
const clearError = useCallback(() => {
|
|
3839
|
+
setState((prev) => ({ ...prev, error: null }));
|
|
3840
|
+
}, []);
|
|
3841
|
+
return {
|
|
3842
|
+
...state,
|
|
3843
|
+
walletName: wallet?.adapter.name,
|
|
3844
|
+
walletIcon: wallet?.adapter.icon,
|
|
3845
|
+
connectWallet,
|
|
3846
|
+
disconnectWallet,
|
|
3847
|
+
connectWalletToBackend,
|
|
3848
|
+
clearError
|
|
3849
|
+
};
|
|
3850
|
+
};
|
|
3851
|
+
var notifyDefault4 = (payload) => {
|
|
3852
|
+
console.log("[payments-ui] wallet-card", payload);
|
|
3853
|
+
};
|
|
3854
|
+
var WalletCard = ({
|
|
3855
|
+
wallet,
|
|
3856
|
+
isPrimary = false,
|
|
3857
|
+
isConnected = false,
|
|
3858
|
+
balance,
|
|
3859
|
+
onSetPrimary,
|
|
3860
|
+
onVerify,
|
|
3861
|
+
onDelete,
|
|
3862
|
+
isVerifying = false,
|
|
3863
|
+
isDeleting = false,
|
|
3864
|
+
notify = notifyDefault4
|
|
3865
|
+
}) => {
|
|
3866
|
+
const [isCopying, setIsCopying] = useState(false);
|
|
3867
|
+
const formatAddress = (address) => address.length <= 12 ? address : `${address.slice(0, 4)}...${address.slice(-4)}`;
|
|
3868
|
+
const copyToClipboard = async (text) => {
|
|
3869
|
+
setIsCopying(true);
|
|
3870
|
+
try {
|
|
3871
|
+
await navigator.clipboard.writeText(text);
|
|
3872
|
+
notify({ title: "Copied", description: "Wallet address copied to clipboard" });
|
|
3873
|
+
} catch (error) {
|
|
3874
|
+
notify({ title: "Failed to copy", description: error.message, status: "destructive" });
|
|
3875
|
+
} finally {
|
|
3876
|
+
setTimeout(() => setIsCopying(false), 500);
|
|
3877
|
+
}
|
|
3878
|
+
};
|
|
3879
|
+
const openExplorer = () => {
|
|
3880
|
+
window.open(`https://solscan.io/account/${wallet.address}`, "_blank", "noopener,noreferrer");
|
|
3881
|
+
};
|
|
3882
|
+
return /* @__PURE__ */ jsxs(
|
|
3883
|
+
"div",
|
|
3884
|
+
{
|
|
3885
|
+
className: cn(
|
|
3886
|
+
"rounded-lg border bg-background/40 p-4 transition-shadow",
|
|
3887
|
+
isPrimary && "border-primary/50 shadow-lg",
|
|
3888
|
+
isConnected && "ring-1 ring-primary"
|
|
3889
|
+
),
|
|
3890
|
+
children: [
|
|
3891
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
|
|
3892
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
3893
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-full bg-primary/20", children: /* @__PURE__ */ jsx("div", { className: "h-5 w-5 rounded-full bg-gradient-to-br from-primary to-primary/60" }) }),
|
|
3894
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
3895
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
3896
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-sm font-medium", children: formatAddress(wallet.address) }),
|
|
3897
|
+
isPrimary && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 rounded-full bg-yellow-500/20 px-2 py-0.5 text-xs text-yellow-500", children: [
|
|
3898
|
+
/* @__PURE__ */ jsx(Star, { className: "h-3 w-3 fill-yellow-500 text-yellow-500" }),
|
|
3899
|
+
" Primary"
|
|
3900
|
+
] }),
|
|
3901
|
+
wallet.is_verified ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs text-emerald-400", children: [
|
|
3902
|
+
/* @__PURE__ */ jsx(CheckCircle, { className: "h-3.5 w-3.5" }),
|
|
3903
|
+
" Verified"
|
|
3904
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs text-amber-400", children: [
|
|
3905
|
+
/* @__PURE__ */ jsx(AlertCircle, { className: "h-3.5 w-3.5" }),
|
|
3906
|
+
" Unverified"
|
|
3907
|
+
] }),
|
|
3908
|
+
isConnected && /* @__PURE__ */ jsx("span", { className: "rounded bg-blue-500/20 px-2 py-0.5 text-xs text-blue-400", children: "Connected" })
|
|
3909
|
+
] }),
|
|
3910
|
+
balance !== null && typeof balance !== "undefined" && /* @__PURE__ */ jsxs("p", { className: "mt-1 text-sm text-muted-foreground", children: [
|
|
3911
|
+
"Balance: ",
|
|
3912
|
+
balance.toFixed(4),
|
|
3913
|
+
" SOL"
|
|
3914
|
+
] }),
|
|
3915
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [
|
|
3916
|
+
"Added ",
|
|
3917
|
+
new Date(wallet.created_at).toLocaleDateString()
|
|
3918
|
+
] })
|
|
3919
|
+
] })
|
|
3920
|
+
] }) }),
|
|
3921
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
3922
|
+
/* @__PURE__ */ jsx(
|
|
3923
|
+
Button,
|
|
3924
|
+
{
|
|
3925
|
+
variant: "ghost",
|
|
3926
|
+
size: "icon",
|
|
3927
|
+
className: "h-8 w-8",
|
|
3928
|
+
onClick: () => copyToClipboard(wallet.address),
|
|
3929
|
+
disabled: isCopying,
|
|
3930
|
+
children: isCopying ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Copy, { className: "h-4 w-4" })
|
|
3931
|
+
}
|
|
3932
|
+
),
|
|
3933
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", className: "h-8 w-8", onClick: openExplorer, children: /* @__PURE__ */ jsx(ExternalLink, { className: "h-4 w-4" }) })
|
|
3934
|
+
] })
|
|
3935
|
+
] }),
|
|
3936
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-2", children: [
|
|
3937
|
+
!isPrimary && onSetPrimary && wallet.is_verified && /* @__PURE__ */ jsxs(
|
|
3938
|
+
Button,
|
|
3939
|
+
{
|
|
3940
|
+
variant: "outline",
|
|
3941
|
+
size: "sm",
|
|
3942
|
+
className: "border-primary/40 text-primary",
|
|
3943
|
+
onClick: () => onSetPrimary(wallet.id),
|
|
3944
|
+
children: [
|
|
3945
|
+
/* @__PURE__ */ jsx(Star, { className: "mr-1 h-3 w-3" }),
|
|
3946
|
+
" Set Primary"
|
|
3947
|
+
]
|
|
3948
|
+
}
|
|
3949
|
+
),
|
|
3950
|
+
!wallet.is_verified && onVerify && isConnected && /* @__PURE__ */ jsx(
|
|
3951
|
+
Button,
|
|
3952
|
+
{
|
|
3953
|
+
variant: "outline",
|
|
3954
|
+
size: "sm",
|
|
3955
|
+
className: "border-emerald-500/40 text-emerald-400",
|
|
3956
|
+
onClick: () => onVerify(wallet),
|
|
3957
|
+
disabled: isVerifying,
|
|
3958
|
+
children: isVerifying ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3959
|
+
/* @__PURE__ */ jsx(Loader2, { className: "mr-1 h-3 w-3 animate-spin" }),
|
|
3960
|
+
" Verifying..."
|
|
3961
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3962
|
+
/* @__PURE__ */ jsx(Shield, { className: "mr-1 h-3 w-3" }),
|
|
3963
|
+
" Verify"
|
|
3964
|
+
] })
|
|
3965
|
+
}
|
|
3966
|
+
),
|
|
3967
|
+
onDelete && /* @__PURE__ */ jsx(
|
|
3968
|
+
Button,
|
|
3969
|
+
{
|
|
3970
|
+
variant: "outline",
|
|
3971
|
+
size: "sm",
|
|
3972
|
+
className: "ml-auto border-destructive/40 text-destructive",
|
|
3973
|
+
onClick: () => onDelete(wallet.id),
|
|
3974
|
+
disabled: isDeleting,
|
|
3975
|
+
children: isDeleting ? /* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 animate-spin" }) : /* @__PURE__ */ jsx(Trash2, { className: "h-3 w-3" })
|
|
3976
|
+
}
|
|
3977
|
+
)
|
|
3978
|
+
] })
|
|
3979
|
+
]
|
|
3980
|
+
}
|
|
3981
|
+
);
|
|
3982
|
+
};
|
|
3983
|
+
var EmptyWalletState = () => /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center", children: [
|
|
3984
|
+
/* @__PURE__ */ jsx(Wallet, { className: "mb-4 h-12 w-12 text-muted-foreground" }),
|
|
3985
|
+
/* @__PURE__ */ jsx("h3", { className: "mb-2 text-lg font-medium", children: "No wallets connected" }),
|
|
3986
|
+
/* @__PURE__ */ jsx("p", { className: "mb-6 text-sm text-muted-foreground", children: "Connect your Solana wallet to get started" }),
|
|
3987
|
+
/* @__PURE__ */ jsx(WalletMultiButton, { className: "!bg-primary text-primary-foreground hover:!bg-primary/90" })
|
|
3988
|
+
] });
|
|
3989
|
+
var notifyDefault5 = (payload) => {
|
|
3990
|
+
console.log("[payments-ui] solana-wallets", payload);
|
|
3991
|
+
};
|
|
3992
|
+
var SolanaWalletSection = ({
|
|
3993
|
+
onNotify,
|
|
3994
|
+
rpcUrl
|
|
3995
|
+
}) => {
|
|
3996
|
+
const notify = onNotify ?? notifyDefault5;
|
|
3997
|
+
const { config } = usePaymentContext();
|
|
3998
|
+
const { connected, publicKey, disconnect } = useWallet();
|
|
3999
|
+
const { wallets: wallets2, isLoading, deleteWallet, fetchWallets } = useWalletList();
|
|
4000
|
+
const { signAndVerifyWallet } = useWalletVerification();
|
|
4001
|
+
const walletConnection = useWalletConnection();
|
|
4002
|
+
const [primaryWalletId, setPrimaryWalletId] = useState(null);
|
|
4003
|
+
const [showOtherWallets, setShowOtherWallets] = useState(false);
|
|
4004
|
+
const [balances, setBalances] = useState({});
|
|
4005
|
+
const [verifyingWalletId, setVerifyingWalletId] = useState(null);
|
|
4006
|
+
const [deletingWalletId, setDeletingWalletId] = useState(null);
|
|
4007
|
+
const [walletToDelete, setWalletToDelete] = useState(null);
|
|
4008
|
+
const [isRegistering, setIsRegistering] = useState(false);
|
|
4009
|
+
const rpcEndpoint = rpcUrl || config.solanaRpcUrl || "https://api.mainnet-beta.solana.com";
|
|
4010
|
+
const connection = useMemo(() => new Connection(rpcEndpoint), [rpcEndpoint]);
|
|
4011
|
+
const primaryWallet = wallets2.find((w) => w.id === primaryWalletId) ?? wallets2.find((w) => w.is_verified) ?? null;
|
|
4012
|
+
const otherWallets = wallets2.filter((w) => w.id !== primaryWallet?.id);
|
|
4013
|
+
const fetchBalance = useCallback(
|
|
4014
|
+
async (address) => {
|
|
4015
|
+
try {
|
|
4016
|
+
const pubkey = new PublicKey(address);
|
|
4017
|
+
const balance = await connection.getBalance(pubkey);
|
|
4018
|
+
return balance / LAMPORTS_PER_SOL;
|
|
4019
|
+
} catch (error) {
|
|
4020
|
+
console.error("payments-ui: failed to fetch balance", error);
|
|
4021
|
+
return null;
|
|
4022
|
+
}
|
|
4023
|
+
},
|
|
4024
|
+
[connection]
|
|
4025
|
+
);
|
|
4026
|
+
const fetchAllBalances = useCallback(async () => {
|
|
4027
|
+
const results = await Promise.all(
|
|
4028
|
+
wallets2.map(async (wallet) => ({ address: wallet.address, balance: await fetchBalance(wallet.address) }))
|
|
4029
|
+
);
|
|
4030
|
+
const next = {};
|
|
4031
|
+
results.forEach(({ address, balance }) => {
|
|
4032
|
+
if (balance !== null && typeof balance !== "undefined") {
|
|
4033
|
+
next[address] = balance;
|
|
4034
|
+
}
|
|
4035
|
+
});
|
|
4036
|
+
setBalances(next);
|
|
4037
|
+
}, [wallets2, fetchBalance]);
|
|
4038
|
+
useEffect(() => {
|
|
4039
|
+
if (wallets2.length > 0) {
|
|
4040
|
+
fetchAllBalances().catch(() => void 0);
|
|
4041
|
+
}
|
|
4042
|
+
}, [wallets2, fetchAllBalances]);
|
|
4043
|
+
useEffect(() => {
|
|
4044
|
+
const verifiedWallet = wallets2.find((w) => w.is_verified);
|
|
4045
|
+
if (verifiedWallet && !primaryWalletId) {
|
|
4046
|
+
setPrimaryWalletId(verifiedWallet.id);
|
|
4047
|
+
}
|
|
4048
|
+
}, [wallets2, primaryWalletId]);
|
|
4049
|
+
const registerWallet = useCallback(async () => {
|
|
4050
|
+
if (!connected || !publicKey || isRegistering) return;
|
|
4051
|
+
setIsRegistering(true);
|
|
4052
|
+
try {
|
|
4053
|
+
const challenge = await walletConnection.connectWalletToBackend(publicKey.toBase58());
|
|
4054
|
+
if (!challenge?.message) {
|
|
4055
|
+
throw new Error("Failed to retrieve wallet verification challenge");
|
|
4056
|
+
}
|
|
4057
|
+
await signAndVerifyWallet(challenge.wallet, challenge.message, challenge.nonce);
|
|
4058
|
+
await fetchWallets();
|
|
4059
|
+
notify({
|
|
4060
|
+
title: "Wallet verified",
|
|
4061
|
+
description: "Your wallet has been linked successfully.",
|
|
4062
|
+
status: "success"
|
|
4063
|
+
});
|
|
4064
|
+
} catch (error) {
|
|
4065
|
+
notify({
|
|
4066
|
+
title: "Wallet verification failed",
|
|
4067
|
+
description: error instanceof Error ? error.message : "Failed to verify wallet",
|
|
4068
|
+
status: "destructive"
|
|
4069
|
+
});
|
|
4070
|
+
} finally {
|
|
4071
|
+
setIsRegistering(false);
|
|
4072
|
+
}
|
|
4073
|
+
}, [connected, publicKey, isRegistering, walletConnection, signAndVerifyWallet, fetchWallets, notify]);
|
|
4074
|
+
useEffect(() => {
|
|
4075
|
+
if (connected && publicKey && !walletConnection.isConnecting && wallets2.length === 0) {
|
|
4076
|
+
registerWallet().catch(() => void 0);
|
|
4077
|
+
}
|
|
4078
|
+
}, [connected, publicKey, walletConnection.isConnecting, wallets2.length, registerWallet]);
|
|
4079
|
+
const handleVerifyWallet = useCallback(
|
|
4080
|
+
async (wallet) => {
|
|
4081
|
+
if (!connected || publicKey?.toBase58() !== wallet.address) {
|
|
4082
|
+
notify({
|
|
4083
|
+
title: "Connect wallet first",
|
|
4084
|
+
description: "Please connect the wallet you want to verify.",
|
|
4085
|
+
status: "destructive"
|
|
4086
|
+
});
|
|
4087
|
+
return;
|
|
4088
|
+
}
|
|
4089
|
+
setVerifyingWalletId(wallet.id);
|
|
4090
|
+
try {
|
|
4091
|
+
const challenge = await walletConnection.connectWalletToBackend(wallet.address);
|
|
4092
|
+
if (!challenge?.message) {
|
|
4093
|
+
throw new Error("Failed to retrieve verification challenge");
|
|
4094
|
+
}
|
|
4095
|
+
await signAndVerifyWallet(challenge.wallet, challenge.message, challenge.nonce);
|
|
4096
|
+
await fetchWallets();
|
|
4097
|
+
notify({ title: "Wallet verified", status: "success" });
|
|
4098
|
+
} catch (error) {
|
|
4099
|
+
notify({
|
|
4100
|
+
title: "Verification failed",
|
|
4101
|
+
description: error instanceof Error ? error.message : "Failed to verify wallet",
|
|
4102
|
+
status: "destructive"
|
|
4103
|
+
});
|
|
4104
|
+
} finally {
|
|
4105
|
+
setVerifyingWalletId(null);
|
|
4106
|
+
}
|
|
4107
|
+
},
|
|
4108
|
+
[connected, publicKey, walletConnection, signAndVerifyWallet, fetchWallets, notify]
|
|
4109
|
+
);
|
|
4110
|
+
const handleDeleteWallet = useCallback(
|
|
4111
|
+
async (walletId) => {
|
|
4112
|
+
setDeletingWalletId(walletId);
|
|
4113
|
+
try {
|
|
4114
|
+
await deleteWallet(walletId);
|
|
4115
|
+
if (primaryWalletId === walletId) {
|
|
4116
|
+
setPrimaryWalletId(null);
|
|
4117
|
+
}
|
|
4118
|
+
if (connected && publicKey) {
|
|
4119
|
+
const deletedWallet = wallets2.find((w) => w.id === walletId);
|
|
4120
|
+
if (deletedWallet?.address === publicKey.toBase58()) {
|
|
4121
|
+
await disconnect();
|
|
4122
|
+
}
|
|
4123
|
+
}
|
|
4124
|
+
notify({ title: "Wallet removed", status: "success" });
|
|
4125
|
+
} catch (error) {
|
|
4126
|
+
notify({
|
|
4127
|
+
title: "Failed to remove wallet",
|
|
4128
|
+
description: error instanceof Error ? error.message : "Please try again",
|
|
4129
|
+
status: "destructive"
|
|
4130
|
+
});
|
|
4131
|
+
} finally {
|
|
4132
|
+
setDeletingWalletId(null);
|
|
4133
|
+
setWalletToDelete(null);
|
|
4134
|
+
}
|
|
4135
|
+
},
|
|
4136
|
+
[deleteWallet, primaryWalletId, connected, publicKey, wallets2, disconnect, notify]
|
|
4137
|
+
);
|
|
4138
|
+
const handleSetPrimary = (walletId) => {
|
|
4139
|
+
setPrimaryWalletId(walletId);
|
|
4140
|
+
notify({ title: "Primary wallet updated", status: "success" });
|
|
4141
|
+
};
|
|
4142
|
+
const isWalletRegistered = connected && publicKey && wallets2.some((w) => w.address === publicKey.toBase58());
|
|
4143
|
+
if (isLoading && wallets2.length === 0) {
|
|
4144
|
+
return /* @__PURE__ */ jsxs(Card, { className: "border-0 bg-background/5", children: [
|
|
4145
|
+
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsx(CardTitle, { className: "text-xl", children: "Solana Wallets" }) }),
|
|
4146
|
+
/* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center py-8", children: [
|
|
4147
|
+
/* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-6 w-6 animate-spin text-muted-foreground" }),
|
|
4148
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Loading wallets..." })
|
|
4149
|
+
] }) })
|
|
4150
|
+
] });
|
|
4151
|
+
}
|
|
4152
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4153
|
+
/* @__PURE__ */ jsxs(Card, { className: "border-0 bg-background/5", children: [
|
|
4154
|
+
/* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
4155
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl", children: "Solana Wallets" }),
|
|
4156
|
+
/* @__PURE__ */ jsx(CardDescription, { children: "Connect and manage your Solana wallets for payments" })
|
|
4157
|
+
] }),
|
|
4158
|
+
/* @__PURE__ */ jsx(CardContent, { className: "space-y-4", children: wallets2.length === 0 && !connected ? /* @__PURE__ */ jsx(EmptyWalletState, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4159
|
+
primaryWallet && /* @__PURE__ */ jsxs("div", { children: [
|
|
4160
|
+
/* @__PURE__ */ jsx("h3", { className: "mb-3 text-sm font-medium uppercase tracking-wide text-muted-foreground", children: "Primary Wallet" }),
|
|
4161
|
+
/* @__PURE__ */ jsx(
|
|
4162
|
+
WalletCard,
|
|
4163
|
+
{
|
|
4164
|
+
wallet: primaryWallet,
|
|
4165
|
+
isPrimary: true,
|
|
4166
|
+
isConnected: connected && publicKey?.toBase58() === primaryWallet.address,
|
|
4167
|
+
balance: balances[primaryWallet.address],
|
|
4168
|
+
onVerify: handleVerifyWallet,
|
|
4169
|
+
onDelete: (id) => {
|
|
4170
|
+
const target = wallets2.find((w) => w.id === id);
|
|
4171
|
+
if (target) setWalletToDelete(target);
|
|
4172
|
+
},
|
|
4173
|
+
isVerifying: verifyingWalletId === primaryWallet.id,
|
|
4174
|
+
isDeleting: deletingWalletId === primaryWallet.id,
|
|
4175
|
+
notify
|
|
4176
|
+
}
|
|
4177
|
+
)
|
|
4178
|
+
] }),
|
|
4179
|
+
otherWallets.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
4180
|
+
/* @__PURE__ */ jsxs(
|
|
4181
|
+
"button",
|
|
4182
|
+
{
|
|
4183
|
+
className: "mb-3 flex w-full items-center justify-between text-left",
|
|
4184
|
+
onClick: () => setShowOtherWallets((prev) => !prev),
|
|
4185
|
+
children: [
|
|
4186
|
+
/* @__PURE__ */ jsxs("h3", { className: "text-sm font-medium uppercase tracking-wide text-muted-foreground", children: [
|
|
4187
|
+
"Other Wallets (",
|
|
4188
|
+
otherWallets.length,
|
|
4189
|
+
")"
|
|
4190
|
+
] }),
|
|
4191
|
+
showOtherWallets ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4 text-muted-foreground" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 text-muted-foreground" })
|
|
4192
|
+
]
|
|
4193
|
+
}
|
|
4194
|
+
),
|
|
4195
|
+
showOtherWallets && /* @__PURE__ */ jsx("div", { className: "space-y-2", children: otherWallets.map((wallet) => /* @__PURE__ */ jsx(
|
|
4196
|
+
WalletCard,
|
|
4197
|
+
{
|
|
4198
|
+
wallet,
|
|
4199
|
+
isConnected: connected && publicKey?.toBase58() === wallet.address,
|
|
4200
|
+
balance: balances[wallet.address],
|
|
4201
|
+
onSetPrimary: handleSetPrimary,
|
|
4202
|
+
onVerify: handleVerifyWallet,
|
|
4203
|
+
onDelete: (id) => {
|
|
4204
|
+
const target = wallets2.find((w) => w.id === id);
|
|
4205
|
+
if (target) setWalletToDelete(target);
|
|
4206
|
+
},
|
|
4207
|
+
isVerifying: verifyingWalletId === wallet.id,
|
|
4208
|
+
isDeleting: deletingWalletId === wallet.id,
|
|
4209
|
+
notify
|
|
4210
|
+
},
|
|
4211
|
+
wallet.id
|
|
4212
|
+
)) })
|
|
4213
|
+
] }),
|
|
4214
|
+
/* @__PURE__ */ jsx("div", { className: "pt-4", children: connected && publicKey ? isWalletRegistered ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
4215
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-emerald-500/40 bg-emerald-500/10 p-3 text-sm text-emerald-200", children: [
|
|
4216
|
+
/* @__PURE__ */ jsx(CheckCircle, { className: "h-4 w-4" }),
|
|
4217
|
+
" Connected wallet is verified and linked to your account."
|
|
4218
|
+
] }),
|
|
4219
|
+
/* @__PURE__ */ jsx(WalletMultiButton, { className: "w-full !bg-primary text-primary-foreground" })
|
|
4220
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
4221
|
+
/* @__PURE__ */ jsx("div", { className: "rounded-lg border border-amber-500/40 bg-amber-500/10 p-3 text-sm text-amber-200", children: "Your connected wallet is not registered with your account." }),
|
|
4222
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
4223
|
+
/* @__PURE__ */ jsx(Button, { onClick: registerWallet, disabled: isRegistering || walletConnection.isConnecting, className: "flex-1 bg-primary text-primary-foreground", children: isRegistering ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4224
|
+
/* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
|
|
4225
|
+
" Registering..."
|
|
4226
|
+
] }) : "Register This Wallet" }),
|
|
4227
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => disconnect(), variant: "outline", className: "border-border", children: "Disconnect" })
|
|
4228
|
+
] })
|
|
4229
|
+
] }) : /* @__PURE__ */ jsx(WalletMultiButton, { className: "w-full !bg-primary text-primary-foreground" }) })
|
|
4230
|
+
] }) })
|
|
4231
|
+
] }),
|
|
4232
|
+
/* @__PURE__ */ jsx(AlertDialog, { open: !!walletToDelete, onOpenChange: () => setWalletToDelete(null), children: /* @__PURE__ */ jsxs(AlertDialogContent, { children: [
|
|
4233
|
+
/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
|
|
4234
|
+
/* @__PURE__ */ jsx(AlertDialogTitle, { children: "Remove Wallet" }),
|
|
4235
|
+
/* @__PURE__ */ jsx(AlertDialogDescription, { children: "Are you sure you want to remove this wallet from your account? This action cannot be undone." })
|
|
4236
|
+
] }),
|
|
4237
|
+
/* @__PURE__ */ jsxs(AlertDialogFooter, { children: [
|
|
4238
|
+
/* @__PURE__ */ jsx(AlertDialogCancel, { children: "Cancel" }),
|
|
4239
|
+
/* @__PURE__ */ jsx(AlertDialogAction, { onClick: () => walletToDelete && handleDeleteWallet(walletToDelete.id), className: "bg-destructive text-destructive-foreground", children: "Remove" })
|
|
4240
|
+
] })
|
|
4241
|
+
] }) })
|
|
4242
|
+
] });
|
|
4243
|
+
};
|
|
4244
|
+
var WalletManagement = (props) => /* @__PURE__ */ jsx(SolanaWalletSection, { ...props });
|
|
4245
|
+
var Checkbox = React3.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
4246
|
+
CheckboxPrimitive.Root,
|
|
4247
|
+
{
|
|
4248
|
+
ref,
|
|
4249
|
+
className: cn(
|
|
4250
|
+
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
|
4251
|
+
className
|
|
4252
|
+
),
|
|
4253
|
+
...props,
|
|
4254
|
+
children: /* @__PURE__ */ jsx(
|
|
4255
|
+
CheckboxPrimitive.Indicator,
|
|
4256
|
+
{
|
|
4257
|
+
className: cn("flex items-center justify-center text-current"),
|
|
4258
|
+
children: /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" })
|
|
4259
|
+
}
|
|
4260
|
+
)
|
|
4261
|
+
}
|
|
4262
|
+
));
|
|
4263
|
+
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
|
4264
|
+
var initialState = {
|
|
4265
|
+
nameOnCard: "",
|
|
4266
|
+
cardNumber: "",
|
|
4267
|
+
expiration: "",
|
|
4268
|
+
cvv: "",
|
|
4269
|
+
termsAccepted: false
|
|
4270
|
+
};
|
|
4271
|
+
var WalletDialog = ({ open, onOpenChange }) => {
|
|
4272
|
+
const [form, setForm] = useState(initialState);
|
|
4273
|
+
const [errors, setErrors] = useState({});
|
|
4274
|
+
const validators = useMemo(
|
|
4275
|
+
() => ({
|
|
4276
|
+
nameOnCard: (value) => !value ? "Name is required" : void 0,
|
|
4277
|
+
cardNumber: (value) => /^\d{16}$/.test(value) ? void 0 : "Card number must be 16 digits",
|
|
4278
|
+
expiration: (value) => /^(0[1-9]|1[0-2])\/([2-9]\d)$/.test(value) ? void 0 : "Must be in MM/YY format",
|
|
4279
|
+
cvv: (value) => /^\d{3,4}$/.test(value) ? void 0 : "CVV must be 3 or 4 digits",
|
|
4280
|
+
termsAccepted: (value) => value ? void 0 : "You must accept the terms"
|
|
4281
|
+
}),
|
|
4282
|
+
[]
|
|
4283
|
+
);
|
|
4284
|
+
const updateField = (field, value) => {
|
|
4285
|
+
setForm((prev) => ({ ...prev, [field]: value }));
|
|
4286
|
+
setErrors((prev) => ({ ...prev, [field]: void 0 }));
|
|
4287
|
+
};
|
|
4288
|
+
const validate = () => {
|
|
4289
|
+
const next = {};
|
|
4290
|
+
Object.keys(validators).forEach((key) => {
|
|
4291
|
+
const validator = validators[key];
|
|
4292
|
+
const message = validator?.(form[key]);
|
|
4293
|
+
if (message) {
|
|
4294
|
+
next[key] = message;
|
|
4295
|
+
}
|
|
4296
|
+
});
|
|
4297
|
+
setErrors(next);
|
|
4298
|
+
return Object.keys(next).length === 0;
|
|
4299
|
+
};
|
|
4300
|
+
const handleSubmit = (event) => {
|
|
4301
|
+
event.preventDefault();
|
|
4302
|
+
if (!validate()) return;
|
|
4303
|
+
console.log("[payments-ui] wallet dialog submit", form);
|
|
4304
|
+
onOpenChange(false);
|
|
4305
|
+
setForm(initialState);
|
|
4306
|
+
};
|
|
4307
|
+
return /* @__PURE__ */ jsx(AlertDialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(AlertDialogContent, { className: "max-h-[95vh] max-w-lg overflow-y-auto rounded-2xl border border-border bg-background", children: [
|
|
4308
|
+
/* @__PURE__ */ jsxs(AlertDialogHeader, { className: "border-b border-border/60 pb-4", children: [
|
|
4309
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2", children: [
|
|
4310
|
+
/* @__PURE__ */ jsx(Shield, { className: "h-5 w-5 text-primary" }),
|
|
4311
|
+
/* @__PURE__ */ jsx(AlertDialogTitle, { className: "text-center text-base font-semibold uppercase tracking-wide", children: "Secure Payment via Mobius Pay" })
|
|
4312
|
+
] }),
|
|
4313
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-center text-sm text-muted-foreground", children: "$23 USD per month, cancel at any time." })
|
|
4314
|
+
] }),
|
|
4315
|
+
/* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "space-y-5 px-2 py-4 sm:px-4", children: [
|
|
4316
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
4317
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
4318
|
+
/* @__PURE__ */ jsx(Label, { className: "mb-1 block text-sm text-muted-foreground", children: "Name on Card" }),
|
|
4319
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
4320
|
+
/* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: /* @__PURE__ */ jsx(UserRound, { className: "h-4 w-4" }) }),
|
|
4321
|
+
/* @__PURE__ */ jsx(Input, { value: form.nameOnCard, onChange: (e) => updateField("nameOnCard", e.target.value), className: "pl-10" })
|
|
4322
|
+
] }),
|
|
4323
|
+
errors.nameOnCard && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-destructive", children: errors.nameOnCard })
|
|
4324
|
+
] }),
|
|
4325
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
4326
|
+
/* @__PURE__ */ jsx(Label, { className: "mb-1 block text-sm text-muted-foreground", children: "Credit Card Number" }),
|
|
4327
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
4328
|
+
/* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: /* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4" }) }),
|
|
4329
|
+
/* @__PURE__ */ jsx(Input, { value: form.cardNumber, onChange: (e) => updateField("cardNumber", e.target.value), className: "pl-10" })
|
|
4330
|
+
] }),
|
|
4331
|
+
errors.cardNumber && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-destructive", children: errors.cardNumber })
|
|
4332
|
+
] }),
|
|
4333
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
|
|
4334
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
4335
|
+
/* @__PURE__ */ jsx(Label, { className: "mb-1 block text-sm text-muted-foreground", children: "Expiration" }),
|
|
4336
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
4337
|
+
/* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: /* @__PURE__ */ jsx(Calendar, { className: "h-4 w-4" }) }),
|
|
4338
|
+
/* @__PURE__ */ jsx(Input, { value: form.expiration, onChange: (e) => updateField("expiration", e.target.value), className: "pl-10", placeholder: "MM/YY" })
|
|
4339
|
+
] }),
|
|
4340
|
+
errors.expiration && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-destructive", children: errors.expiration })
|
|
4341
|
+
] }),
|
|
4342
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
4343
|
+
/* @__PURE__ */ jsx(Label, { className: "mb-1 block text-sm text-muted-foreground", children: "CVV" }),
|
|
4344
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
4345
|
+
/* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground", children: /* @__PURE__ */ jsx(KeyRound, { className: "h-4 w-4" }) }),
|
|
4346
|
+
/* @__PURE__ */ jsx(Input, { value: form.cvv, onChange: (e) => updateField("cvv", e.target.value), className: "pl-10" })
|
|
4347
|
+
] }),
|
|
4348
|
+
errors.cvv && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-destructive", children: errors.cvv })
|
|
4349
|
+
] })
|
|
4350
|
+
] })
|
|
4351
|
+
] }),
|
|
4352
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 rounded-md border border-border/70 bg-muted/10 p-4", children: [
|
|
4353
|
+
/* @__PURE__ */ jsx(Checkbox, { id: "terms-agree", checked: form.termsAccepted, onCheckedChange: (checked) => updateField("termsAccepted", Boolean(checked)) }),
|
|
4354
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "terms-agree", className: "text-sm text-muted-foreground", children: "By completing this order, I confirm that I am 18 years or older and agree to your privacy policy and terms." })
|
|
4355
|
+
] }),
|
|
4356
|
+
errors.termsAccepted && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: errors.termsAccepted }),
|
|
4357
|
+
/* @__PURE__ */ jsxs(AlertDialogFooter, { className: "flex gap-2", children: [
|
|
4358
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", className: "flex-1", children: "Subscribe" }),
|
|
4359
|
+
/* @__PURE__ */ jsx(AlertDialogCancel, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", className: "flex-1", children: "Close" }) })
|
|
4360
|
+
] })
|
|
4361
|
+
] })
|
|
4362
|
+
] }) });
|
|
4363
|
+
};
|
|
2897
4364
|
var useTokenBalance = (tokens) => {
|
|
2898
4365
|
const { publicKey } = useWallet();
|
|
2899
4366
|
const { connection } = useConnection();
|
|
@@ -3413,6 +4880,6 @@ var useAlternativePaymentProvider = () => {
|
|
|
3413
4880
|
return { openFlexForm, isLoading, error };
|
|
3414
4881
|
};
|
|
3415
4882
|
|
|
3416
|
-
export { CardDetailsForm, CardPaymentService, PaymentApp, PaymentExperience, PaymentMethodService, PaymentProvider, SolanaPaymentSelector, SolanaPaymentService, StoredPaymentMethods, SubscriptionCheckoutModal, SubscriptionService, SubscriptionSuccessDialog, TokenCatalog, WalletGateway,
|
|
4883
|
+
export { BillingHistory, CancelMembershipDialog, CardDetailsForm, CardPaymentService, EmptyWalletState, PaymentApp, PaymentExperience, PaymentMethodService, PaymentMethodsSection, PaymentProvider, PaymentsDialogProvider, PaymentsRuntime, SolanaPaymentSelector, SolanaPaymentService, SolanaPaymentView, SolanaWalletSection, SolanaWalletService, StoredPaymentMethods, SubscriptionCheckoutModal, SubscriptionService, SubscriptionSuccessDialog, TokenCatalog, WalletCard, WalletDialog, WalletGateway, WalletManagement, WalletModal, createPaymentsRuntime, useAlternativePaymentProvider, useDirectWalletPayment, usePaymentContext, usePaymentDialogs, usePaymentMethodService, usePaymentMethods, usePaymentNotifications, usePaymentStatus, useSolanaDirectPayment, useSolanaQrPayment, useSolanaService, useSubscriptionActions, useSupportedTokens, useTokenBalance, useWalletConnection, useWalletList, useWalletVerification };
|
|
3417
4884
|
//# sourceMappingURL=index.js.map
|
|
3418
4885
|
//# sourceMappingURL=index.js.map
|