@doujins/payments-ui 0.1.15 → 0.1.17-alpha

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.js CHANGED
@@ -1,5 +1,5 @@
1
- import * as React15 from 'react';
2
- import React15__default, { createContext, useMemo, useState, useEffect, useCallback, useRef, useContext } from 'react';
1
+ import * as React4 from 'react';
2
+ import React4__default, { createContext, useMemo, useState, useEffect, useCallback, useRef, useContext } from 'react';
3
3
  import { useQueryClient, useQuery, useMutation, QueryClient, QueryClientProvider, useInfiniteQuery } from '@tanstack/react-query';
4
4
  import { ConnectionProvider, WalletProvider, useWallet, useConnection } from '@solana/wallet-adapter-react';
5
5
  import { WalletModalProvider } from '@solana/wallet-adapter-react-ui';
@@ -11,7 +11,7 @@ import { SolflareWalletAdapter } from '@solana/wallet-adapter-solflare';
11
11
  import { TrustWalletAdapter } from '@solana/wallet-adapter-trust';
12
12
  import { CoinbaseWalletAdapter } from '@solana/wallet-adapter-coinbase';
13
13
  import * as DialogPrimitive from '@radix-ui/react-dialog';
14
- import { ChevronDown, ChevronUp, Check, Loader2, CheckCircle, AlertCircle, Wallet, Ban, TriangleAlert, CreditCard, Trash2, Shield, UserRound, Calendar, KeyRound, XIcon, XCircle, RotateCcw, RefreshCw } from 'lucide-react';
14
+ import { ChevronDown, ChevronUp, Check, Pencil, Loader2, CheckCircle, AlertCircle, Wallet, Ban, TriangleAlert, CreditCard, Trash2, WalletCards, RefreshCw, Shield, UserRound, Calendar, KeyRound, XIcon, XCircle, RotateCcw } from 'lucide-react';
15
15
  import clsx2, { clsx } from 'clsx';
16
16
  import { twMerge } from 'tailwind-merge';
17
17
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -248,6 +248,7 @@ var createClient = (config) => {
248
248
  return normalizeList(result);
249
249
  },
250
250
  createPaymentMethod(payload) {
251
+ console.log("Creating payment method with payload:", payload);
251
252
  return request("POST", "/me/payment-methods", {
252
253
  body: payload
253
254
  });
@@ -265,10 +266,10 @@ var createClient = (config) => {
265
266
  },
266
267
  checkout(payload, idempotencyKey) {
267
268
  const key = idempotencyKey ?? crypto.randomUUID();
268
- return request("POST", "/me/checkout", {
269
+ return request("POST", "/checkout", {
269
270
  body: payload,
270
271
  headers: {
271
- "Idempotency-Key": key
272
+ "X-Idempotency-Key": key
272
273
  }
273
274
  });
274
275
  },
@@ -277,6 +278,36 @@ var createClient = (config) => {
277
278
  body: feedback ? { feedback } : void 0
278
279
  });
279
280
  },
281
+ async listSubscriptions(params) {
282
+ const result = await request(
283
+ "GET",
284
+ "/me/subscriptions",
285
+ {
286
+ query: {
287
+ status: params?.status,
288
+ limit: params?.limit,
289
+ offset: params?.offset
290
+ }
291
+ }
292
+ );
293
+ return normalizeList(result);
294
+ },
295
+ updateSubscriptionPaymentMethod(payload) {
296
+ return request("PUT", "/me/subscriptions/payment-method", {
297
+ body: {
298
+ subscription_id: payload.subscription_id,
299
+ payment_method_id: payload.payment_method_id
300
+ }
301
+ });
302
+ },
303
+ resumeSubscription() {
304
+ return request("POST", "/me/subscriptions/resume");
305
+ },
306
+ changeSubscription(payload) {
307
+ return request("POST", "/me/subscriptions/change", {
308
+ body: payload
309
+ });
310
+ },
280
311
  async getPaymentHistory(params) {
281
312
  const result = await request("GET", "/me/payments", {
282
313
  query: {
@@ -422,6 +453,19 @@ function DialogHeader({ className, ...props }) {
422
453
  }
423
454
  );
424
455
  }
456
+ function DialogFooter({ className, ...props }) {
457
+ return /* @__PURE__ */ jsx(
458
+ "div",
459
+ {
460
+ "data-slot": "dialog-footer",
461
+ className: cn(
462
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
463
+ className
464
+ ),
465
+ ...props
466
+ }
467
+ );
468
+ }
425
469
  function DialogTitle({
426
470
  className,
427
471
  ...props
@@ -488,7 +532,7 @@ var buttonVariants = cva(
488
532
  }
489
533
  }
490
534
  );
491
- var Button = React15.forwardRef(
535
+ var Button = React4.forwardRef(
492
536
  ({ className, variant, size, asChild = false, ...props }, ref) => {
493
537
  const Comp = asChild ? Slot : "button";
494
538
  return /* @__PURE__ */ jsx(
@@ -502,7 +546,7 @@ var Button = React15.forwardRef(
502
546
  }
503
547
  );
504
548
  Button.displayName = "Button";
505
- var Input = React15.forwardRef(
549
+ var Input = React4.forwardRef(
506
550
  ({ className, type, ...props }, ref) => {
507
551
  return /* @__PURE__ */ jsx(
508
552
  "input",
@@ -522,7 +566,7 @@ Input.displayName = "Input";
522
566
  var labelVariants = cva(
523
567
  "text-sm font-medium leading-none text-foreground peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
524
568
  );
525
- var Label = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
569
+ var Label = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
526
570
  LabelPrimitive.Root,
527
571
  {
528
572
  ref,
@@ -531,9 +575,9 @@ var Label = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
531
575
  }
532
576
  ));
533
577
  Label.displayName = LabelPrimitive.Root.displayName;
534
- var Select = ({ children, ...props }) => /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsx(SelectPrimitive.Root, { ...props, children }) });
578
+ var Select = SelectPrimitive.Root;
535
579
  var SelectValue = SelectPrimitive.Value;
536
- var SelectTrigger = React15.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
580
+ var SelectTrigger = React4.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
537
581
  SelectPrimitive.Trigger,
538
582
  {
539
583
  ref,
@@ -549,7 +593,7 @@ var SelectTrigger = React15.forwardRef(({ className, children, ...props }, ref)
549
593
  }
550
594
  ));
551
595
  SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
552
- var SelectScrollUpButton = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
596
+ var SelectScrollUpButton = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
553
597
  SelectPrimitive.ScrollUpButton,
554
598
  {
555
599
  ref,
@@ -562,7 +606,7 @@ var SelectScrollUpButton = React15.forwardRef(({ className, ...props }, ref) =>
562
606
  }
563
607
  ));
564
608
  SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
565
- var SelectScrollDownButton = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
609
+ var SelectScrollDownButton = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
566
610
  SelectPrimitive.ScrollDownButton,
567
611
  {
568
612
  ref,
@@ -575,39 +619,61 @@ var SelectScrollDownButton = React15.forwardRef(({ className, ...props }, ref) =
575
619
  }
576
620
  ));
577
621
  SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
578
- var SelectContent = React15.forwardRef(
579
- ({
580
- className,
581
- children,
582
- position = "popper",
583
- side = "bottom",
584
- sideOffset = 4,
585
- align = "start",
586
- ...props
587
- }, ref) => {
588
- const popperProps = position === "popper" ? { side, sideOffset, align } : {};
589
- return /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
590
- SelectPrimitive.Content,
591
- {
592
- ref,
593
- className: cn(
594
- "z-[200] max-h-64 w-[var(--radix-select-trigger-width)] overflow-y-auto overflow-x-hidden rounded-md border border-white/20 bg-background-regular text-foreground shadow-lg backdrop-blur-xl data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
595
- className
596
- ),
597
- position,
598
- ...popperProps,
599
- ...props,
600
- children: [
601
- /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
602
- /* @__PURE__ */ jsx(SelectPrimitive.Viewport, { className: "p-1", children }),
603
- /* @__PURE__ */ jsx(SelectScrollDownButton, {})
604
- ]
622
+ var SelectContent = React4.forwardRef(({ className, children, position = "popper", ...props }, ref) => {
623
+ const viewportRef = React4.useRef(null);
624
+ const contentRef = React4.useCallback((node) => {
625
+ if (node) {
626
+ node.style.setProperty("max-height", "40vh", "important");
627
+ const anchorWidth = window.getComputedStyle(node).getPropertyValue("--radix-popper-anchor-width");
628
+ if (anchorWidth) {
629
+ node.style.setProperty("width", anchorWidth, "important");
605
630
  }
606
- ) });
607
- }
608
- );
631
+ }
632
+ }, []);
633
+ const combinedRef = React4.useCallback((node) => {
634
+ contentRef(node);
635
+ if (typeof ref === "function") {
636
+ ref(node);
637
+ } else if (ref) {
638
+ ref.current = node;
639
+ }
640
+ }, [contentRef, ref]);
641
+ React4.useEffect(() => {
642
+ if (viewportRef.current) {
643
+ viewportRef.current.style.setProperty("max-height", "40vh", "important");
644
+ }
645
+ });
646
+ return /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
647
+ SelectPrimitive.Content,
648
+ {
649
+ ref: combinedRef,
650
+ className: cn(
651
+ "relative z-50 min-w-[8rem] overflow-hidden rounded-md border border-white/20 bg-background-regular text-foreground shadow-lg backdrop-blur-xl data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
652
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
653
+ className
654
+ ),
655
+ position,
656
+ ...props,
657
+ children: [
658
+ /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
659
+ /* @__PURE__ */ jsx(
660
+ SelectPrimitive.Viewport,
661
+ {
662
+ ref: viewportRef,
663
+ className: cn(
664
+ "p-1 overflow-y-auto",
665
+ position === "popper" && "w-full min-w-[var(--radix-select-trigger-width)]"
666
+ ),
667
+ children
668
+ }
669
+ ),
670
+ /* @__PURE__ */ jsx(SelectScrollDownButton, {})
671
+ ]
672
+ }
673
+ ) });
674
+ });
609
675
  SelectContent.displayName = SelectPrimitive.Content.displayName;
610
- var SelectLabel = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
676
+ var SelectLabel = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
611
677
  SelectPrimitive.Label,
612
678
  {
613
679
  ref,
@@ -616,7 +682,7 @@ var SelectLabel = React15.forwardRef(({ className, ...props }, ref) => /* @__PUR
616
682
  }
617
683
  ));
618
684
  SelectLabel.displayName = SelectPrimitive.Label.displayName;
619
- var SelectItem = React15.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
685
+ var SelectItem = React4.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
620
686
  SelectPrimitive.Item,
621
687
  {
622
688
  ref,
@@ -632,7 +698,7 @@ var SelectItem = React15.forwardRef(({ className, children, ...props }, ref) =>
632
698
  }
633
699
  ));
634
700
  SelectItem.displayName = SelectPrimitive.Item.displayName;
635
- var SelectSeparator = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
701
+ var SelectSeparator = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
636
702
  SelectPrimitive.Separator,
637
703
  {
638
704
  ref,
@@ -654,6 +720,28 @@ var defaultBillingDetails = {
654
720
  email: "",
655
721
  provider: "mobius"
656
722
  };
723
+ var defaultCardDetailsFormTranslations = {
724
+ firstName: "First name",
725
+ lastName: "Last name",
726
+ email: "Email",
727
+ address: "Address",
728
+ city: "City",
729
+ state: "State / Region",
730
+ postalCode: "Postal code",
731
+ country: "Country",
732
+ cardNumber: "Card number",
733
+ expiry: "Expiry",
734
+ cvv: "CVV",
735
+ submit: "Submit",
736
+ processing: "Processing\u2026",
737
+ errorRequiredFields: "Please complete all required billing fields.",
738
+ errorTokenization: "Payment tokenization failed. Please try again.",
739
+ errorFormNotReady: "Payment form is not ready. Please try again later.",
740
+ infoSecure: "Your payment information is encrypted and processed securely.",
741
+ cancel: "Cancel",
742
+ editEmail: "Edit email",
743
+ selectCountry: "Select a country"
744
+ };
657
745
  var buildSelector = (prefix, field) => `#${prefix}-${field}`;
658
746
  var CardDetailsForm = ({
659
747
  visible,
@@ -665,8 +753,10 @@ var CardDetailsForm = ({
665
753
  collectPrefix = "card-form",
666
754
  className,
667
755
  onBillingChange,
668
- submitDisabled = false
756
+ submitDisabled = false,
757
+ translations
669
758
  }) => {
759
+ const t = { ...defaultCardDetailsFormTranslations, ...translations };
670
760
  const { config } = usePaymentContext();
671
761
  const defaultValuesKey = useMemo(() => JSON.stringify(defaultValues ?? {}), [defaultValues]);
672
762
  const mergedDefaults = useMemo(
@@ -685,6 +775,7 @@ var CardDetailsForm = ({
685
775
  const [postalCode, setPostalCode] = useState(mergedDefaults.postalCode);
686
776
  const [country, setCountry] = useState(mergedDefaults.country);
687
777
  const [email, setEmail] = useState(mergedDefaults.email ?? "");
778
+ const [isEditingEmail, setIsEditingEmail] = useState(false);
688
779
  const [localError, setLocalError] = useState(null);
689
780
  const [isTokenizing, setIsTokenizing] = useState(false);
690
781
  const [collectReady, setCollectReady] = useState(
@@ -706,8 +797,11 @@ var CardDetailsForm = ({
706
797
  if (!visible) {
707
798
  setLocalError(null);
708
799
  setIsTokenizing(false);
800
+ if (typeof window !== "undefined" && window.__doujinsCollectConfigured && collectPrefix) {
801
+ window.__doujinsCollectConfigured[collectPrefix] = false;
802
+ }
709
803
  }
710
- }, [visible]);
804
+ }, [visible, collectPrefix]);
711
805
  useEffect(() => {
712
806
  if (!visible) return;
713
807
  setFirstName(mergedDefaults.firstName);
@@ -769,6 +863,8 @@ var CardDetailsForm = ({
769
863
  setLocalError("Payment tokenization failed. Please try again.");
770
864
  return;
771
865
  }
866
+ let rawExp = response.card?.exp;
867
+ let formattedExp = rawExp && rawExp.length === 4 ? `${rawExp.slice(0, 2)}/${rawExp.slice(2)}` : rawExp;
772
868
  const billing = {
773
869
  firstName,
774
870
  lastName,
@@ -778,7 +874,10 @@ var CardDetailsForm = ({
778
874
  postalCode,
779
875
  country,
780
876
  email,
781
- provider: mergedDefaults.provider ?? "mobius"
877
+ provider: mergedDefaults.provider ?? "mobius",
878
+ last_four: response.card?.number,
879
+ card_type: response.card?.type,
880
+ expiry_date: formattedExp
782
881
  };
783
882
  onTokenize(response.token, billing);
784
883
  };
@@ -818,9 +917,8 @@ var CardDetailsForm = ({
818
917
  visible
819
918
  ]);
820
919
  const validate = () => {
821
- const emailRequired = !defaultValues?.email && !config.defaultUser?.email;
822
- if (!firstName.trim() || !lastName.trim() || !address1.trim() || !city.trim() || !postalCode.trim() || !country.trim() || emailRequired && !email.trim()) {
823
- setLocalError("Please complete all required billing fields.");
920
+ if (!firstName.trim() || !lastName.trim() || !address1.trim() || !city.trim() || !postalCode.trim() || !country.trim() || !email.trim()) {
921
+ setLocalError(t.errorRequiredFields);
824
922
  return false;
825
923
  }
826
924
  setLocalError(null);
@@ -830,7 +928,7 @@ var CardDetailsForm = ({
830
928
  event.preventDefault();
831
929
  if (!validate()) return;
832
930
  if (!window.CollectJS) {
833
- setLocalError("Payment form is not ready. Please try again later.");
931
+ setLocalError(t.errorFormNotReady);
834
932
  return;
835
933
  }
836
934
  setIsTokenizing(true);
@@ -838,7 +936,8 @@ var CardDetailsForm = ({
838
936
  };
839
937
  const errorMessage = localError ?? externalError;
840
938
  const collectFieldClass = "relative flex h-9 w-full items-center overflow-hidden rounded-md border border-white/30 bg-transparent px-3 text-sm text-foreground";
841
- const showEmailField = !defaultValues?.email && !config.defaultUser?.email;
939
+ const hasDefaultEmail = Boolean(defaultValues?.email || config.defaultUser?.email);
940
+ const showEmailInput = !hasDefaultEmail || isEditingEmail;
842
941
  return /* @__PURE__ */ jsxs(
843
942
  "form",
844
943
  {
@@ -849,7 +948,7 @@ var CardDetailsForm = ({
849
948
  errorMessage && /* @__PURE__ */ jsx("div", { className: "rounded-md border border-red-500/40 bg-red-500/10 px-4 py-2 text-sm text-red-400", children: errorMessage }),
850
949
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 md:flex-row", children: [
851
950
  /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-2", children: [
852
- /* @__PURE__ */ jsx(Label, { htmlFor: "firstName", children: "First name" }),
951
+ /* @__PURE__ */ jsx(Label, { htmlFor: "firstName", children: t.firstName }),
853
952
  /* @__PURE__ */ jsx(
854
953
  Input,
855
954
  {
@@ -861,7 +960,7 @@ var CardDetailsForm = ({
861
960
  )
862
961
  ] }),
863
962
  /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-2", children: [
864
- /* @__PURE__ */ jsx(Label, { htmlFor: "lastName", children: "Last name" }),
963
+ /* @__PURE__ */ jsx(Label, { htmlFor: "lastName", children: t.lastName }),
865
964
  /* @__PURE__ */ jsx(
866
965
  Input,
867
966
  {
@@ -873,21 +972,50 @@ var CardDetailsForm = ({
873
972
  )
874
973
  ] })
875
974
  ] }),
876
- showEmailField && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
877
- /* @__PURE__ */ jsx(Label, { htmlFor: "email", children: "Email" }),
878
- /* @__PURE__ */ jsx(
879
- Input,
880
- {
881
- id: "email",
882
- type: "email",
883
- value: email,
884
- onChange: (e) => setEmail(e.target.value),
885
- required: true
886
- }
887
- )
975
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
976
+ /* @__PURE__ */ jsx(Label, { htmlFor: "email", children: t.email }),
977
+ showEmailInput ? /* @__PURE__ */ jsxs("div", { className: "flex gap-2 items-center", children: [
978
+ /* @__PURE__ */ jsx(
979
+ Input,
980
+ {
981
+ id: "email",
982
+ type: "email",
983
+ value: email,
984
+ onChange: (e) => setEmail(e.target.value),
985
+ required: true,
986
+ className: "flex-1"
987
+ }
988
+ ),
989
+ hasDefaultEmail && /* @__PURE__ */ jsx(
990
+ Button,
991
+ {
992
+ type: "button",
993
+ variant: "ghost",
994
+ size: "sm",
995
+ onClick: () => {
996
+ setIsEditingEmail(false);
997
+ setEmail(mergedDefaults.email ?? "");
998
+ },
999
+ className: "px-3 text-xs text-muted-foreground hover:text-foreground",
1000
+ children: t.cancel
1001
+ }
1002
+ )
1003
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between h-9 w-full rounded-md border border-white/30 bg-transparent px-3 text-sm text-foreground", children: [
1004
+ /* @__PURE__ */ jsx("span", { children: email }),
1005
+ /* @__PURE__ */ jsx(
1006
+ "button",
1007
+ {
1008
+ type: "button",
1009
+ onClick: () => setIsEditingEmail(true),
1010
+ className: "text-muted-foreground hover:text-foreground transition-colors",
1011
+ "aria-label": t.editEmail,
1012
+ children: /* @__PURE__ */ jsx(Pencil, { className: "h-4 w-4" })
1013
+ }
1014
+ )
1015
+ ] })
888
1016
  ] }),
889
1017
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
890
- /* @__PURE__ */ jsx(Label, { htmlFor: "address1", children: "Address" }),
1018
+ /* @__PURE__ */ jsx(Label, { htmlFor: "address1", children: t.address }),
891
1019
  /* @__PURE__ */ jsx(
892
1020
  Input,
893
1021
  {
@@ -900,7 +1028,7 @@ var CardDetailsForm = ({
900
1028
  ] }),
901
1029
  /* @__PURE__ */ jsxs("div", { className: "grid gap-2 md:grid-cols-2", children: [
902
1030
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
903
- /* @__PURE__ */ jsx(Label, { htmlFor: "city", children: "City" }),
1031
+ /* @__PURE__ */ jsx(Label, { htmlFor: "city", children: t.city }),
904
1032
  /* @__PURE__ */ jsx(
905
1033
  Input,
906
1034
  {
@@ -912,7 +1040,7 @@ var CardDetailsForm = ({
912
1040
  )
913
1041
  ] }),
914
1042
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
915
- /* @__PURE__ */ jsx(Label, { htmlFor: "state", children: "State / Region" }),
1043
+ /* @__PURE__ */ jsx(Label, { htmlFor: "state", children: t.state }),
916
1044
  /* @__PURE__ */ jsx(
917
1045
  Input,
918
1046
  {
@@ -925,7 +1053,7 @@ var CardDetailsForm = ({
925
1053
  ] }),
926
1054
  /* @__PURE__ */ jsxs("div", { className: "grid gap-2 md:grid-cols-2", children: [
927
1055
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
928
- /* @__PURE__ */ jsx(Label, { htmlFor: "postal", children: "Postal code" }),
1056
+ /* @__PURE__ */ jsx(Label, { htmlFor: "postal", children: t.postalCode }),
929
1057
  /* @__PURE__ */ jsx(
930
1058
  Input,
931
1059
  {
@@ -937,24 +1065,24 @@ var CardDetailsForm = ({
937
1065
  )
938
1066
  ] }),
939
1067
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
940
- /* @__PURE__ */ jsx(Label, { children: "Country" }),
1068
+ /* @__PURE__ */ jsx(Label, { children: t.country }),
941
1069
  /* @__PURE__ */ jsxs(Select, { value: country, onValueChange: setCountry, children: [
942
- /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select a country" }) }),
943
- /* @__PURE__ */ jsx(SelectContent, { className: "max-h-64 w-full", children: countries.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.code, children: option.name }, option.code)) })
1070
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: t.selectCountry }) }),
1071
+ /* @__PURE__ */ jsx(SelectContent, { children: countries.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.code, children: option.name }, option.code)) })
944
1072
  ] })
945
1073
  ] })
946
1074
  ] }),
947
1075
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
948
- /* @__PURE__ */ jsx(Label, { children: "Card number" }),
1076
+ /* @__PURE__ */ jsx(Label, { children: t.cardNumber }),
949
1077
  /* @__PURE__ */ jsx("div", { id: buildSelector(collectPrefix, "ccnumber").slice(1), className: collectFieldClass })
950
1078
  ] }),
951
1079
  /* @__PURE__ */ jsxs("div", { className: "grid gap-2 md:grid-cols-2", children: [
952
1080
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
953
- /* @__PURE__ */ jsx(Label, { children: "Expiry" }),
1081
+ /* @__PURE__ */ jsx(Label, { children: t.expiry }),
954
1082
  /* @__PURE__ */ jsx("div", { id: buildSelector(collectPrefix, "ccexp").slice(1), className: collectFieldClass })
955
1083
  ] }),
956
1084
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
957
- /* @__PURE__ */ jsx(Label, { children: "CVV" }),
1085
+ /* @__PURE__ */ jsx(Label, { children: t.cvv }),
958
1086
  /* @__PURE__ */ jsx("div", { id: buildSelector(collectPrefix, "cvv").slice(1), className: collectFieldClass })
959
1087
  ] })
960
1088
  ] }),
@@ -966,11 +1094,12 @@ var CardDetailsForm = ({
966
1094
  disabled: submitting || submitDisabled || isTokenizing,
967
1095
  children: submitting || isTokenizing ? /* @__PURE__ */ jsxs(Fragment, { children: [
968
1096
  /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
969
- " Processing\u2026"
970
- ] }) : submitLabel
1097
+ " ",
1098
+ t.processing
1099
+ ] }) : submitLabel || t.submit
971
1100
  }
972
1101
  ),
973
- /* @__PURE__ */ jsx("p", { className: "text-xs text-white/60", children: "Your payment information is encrypted and processed securely." })
1102
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-white/60", children: t.infoSecure })
974
1103
  ]
975
1104
  }
976
1105
  );
@@ -1007,7 +1136,9 @@ var usePaymentMethods = () => {
1007
1136
  zip: billing.postalCode,
1008
1137
  country: billing.country,
1009
1138
  email: billing.email,
1010
- provider: billing.provider
1139
+ provider: billing.provider,
1140
+ last_four: billing.last_four,
1141
+ card_type: billing.card_type
1011
1142
  };
1012
1143
  return client.createPaymentMethod(payload);
1013
1144
  },
@@ -1046,7 +1177,7 @@ var badgeVariants = cva(
1046
1177
  function Badge({ className, variant, ...props }) {
1047
1178
  return /* @__PURE__ */ jsx("div", { className: cn(badgeVariants({ variant }), className), ...props });
1048
1179
  }
1049
- var ScrollArea = React15.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
1180
+ var ScrollArea = React4.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
1050
1181
  ScrollAreaPrimitive.Root,
1051
1182
  {
1052
1183
  ref,
@@ -1060,7 +1191,7 @@ var ScrollArea = React15.forwardRef(({ className, children, ...props }, ref) =>
1060
1191
  }
1061
1192
  ));
1062
1193
  ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
1063
- var ScrollBar = React15.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsx(
1194
+ var ScrollBar = React4.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsx(
1064
1195
  ScrollAreaPrimitive.ScrollAreaScrollbar,
1065
1196
  {
1066
1197
  ref,
@@ -1076,21 +1207,34 @@ var ScrollBar = React15.forwardRef(({ className, orientation = "vertical", ...pr
1076
1207
  }
1077
1208
  ));
1078
1209
  ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
1210
+ var defaultTranslations = {
1211
+ loadingCards: "Loading cards...",
1212
+ noSavedMethods: "No saved payment methods yet.",
1213
+ selectedLabel: "Selected",
1214
+ useCardLabel: "Use card"
1215
+ };
1079
1216
  var formatCardLabel = (method) => {
1080
- const brand = method.card_type ? method.card_type.toUpperCase() : "CARD";
1081
- const lastFour = method.last_four ? `\u2022\u2022\u2022\u2022 ${method.last_four}` : "";
1082
- return `${brand} ${lastFour}`.trim();
1217
+ if (method.card) {
1218
+ const brand = method.card.brand ? method.card.brand.toUpperCase() : "CARD";
1219
+ const lastFour = method.card.last4 ? `\u2022\u2022\u2022\u2022 ${method.card.last4}` : "";
1220
+ const exp = method.card.exp_month && method.card.exp_year ? ` \u2022 ${String(method.card.exp_month).padStart(2, "0")}/${String(method.card.exp_year).slice(-2)}` : "";
1221
+ return `${brand} ${lastFour}${exp}`.trim();
1222
+ }
1223
+ return "CARD";
1083
1224
  };
1084
1225
  var StoredPaymentMethods = ({
1085
1226
  selectedMethodId,
1086
- onMethodSelect
1227
+ onMethodSelect,
1228
+ translations
1087
1229
  }) => {
1088
1230
  const { listQuery } = usePaymentMethods();
1089
1231
  const payments = useMemo(() => listQuery.data?.data ?? [], [listQuery.data]);
1232
+ const t = { ...defaultTranslations, ...translations };
1090
1233
  return /* @__PURE__ */ jsx("div", { className: "space-y-4", children: listQuery.isLoading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center py-4 text-sm text-muted-foreground", children: [
1091
1234
  /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
1092
- " Loading cards..."
1093
- ] }) : payments.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-4 text-center 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) => {
1235
+ " ",
1236
+ t.loadingCards
1237
+ ] }) : payments.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-4 text-center text-sm text-muted-foreground", children: t.noSavedMethods }) : /* @__PURE__ */ jsx(ScrollArea, { className: "max-h-[320px] pr-2", children: /* @__PURE__ */ jsx("div", { className: "space-y-3", children: payments.map((method) => {
1094
1238
  const isSelected = selectedMethodId === method.id;
1095
1239
  return /* @__PURE__ */ jsxs(
1096
1240
  "div",
@@ -1113,7 +1257,7 @@ var StoredPaymentMethods = ({
1113
1257
  disabled: isSelected,
1114
1258
  onClick: () => onMethodSelect(method),
1115
1259
  className: clsx2("px-3", { "bg-muted/90": !isSelected, "bg-inherit": isSelected }),
1116
- children: isSelected ? "Selected" : "Use card"
1260
+ children: isSelected ? t.selectedLabel : t.useCardLabel
1117
1261
  }
1118
1262
  )
1119
1263
  ] })
@@ -1123,6 +1267,26 @@ var StoredPaymentMethods = ({
1123
1267
  );
1124
1268
  }) }) }) });
1125
1269
  };
1270
+
1271
+ // src/utils/errorMessages.ts
1272
+ var resolveErrorMessageByCode = (error, translationErrors, fallbackMessage) => {
1273
+ const errors = translationErrors ?? {};
1274
+ const defaultMessage = fallbackMessage ?? (error instanceof Error ? error.message : typeof error === "string" ? error : "An unexpected error occurred");
1275
+ if (error instanceof ClientApiError) {
1276
+ const payload = error.body;
1277
+ const code = payload?.code ?? payload?.error?.code;
1278
+ if (code && errors[code]) return errors[code];
1279
+ if (typeof payload?.error?.message === "string") return payload.error.message;
1280
+ return error.message;
1281
+ }
1282
+ if (typeof error === "object" && error !== null && "code" in error) {
1283
+ const code = error.code;
1284
+ if (typeof code === "string" && errors[code]) return errors[code];
1285
+ }
1286
+ if (error instanceof Error) return error.message;
1287
+ if (typeof error === "string") return error;
1288
+ return defaultMessage;
1289
+ };
1126
1290
  function Tabs({
1127
1291
  className,
1128
1292
  ...props
@@ -1145,7 +1309,7 @@ function TabsList({
1145
1309
  {
1146
1310
  "data-slot": "tabs-list",
1147
1311
  className: cn(
1148
- "bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
1312
+ "bg-transparent border border-white/30 text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-1",
1149
1313
  className
1150
1314
  ),
1151
1315
  ...props
@@ -1156,12 +1320,32 @@ function TabsTrigger({
1156
1320
  className,
1157
1321
  ...props
1158
1322
  }) {
1323
+ const triggerRef = React4.useRef(null);
1324
+ React4.useEffect(() => {
1325
+ const element = triggerRef.current;
1326
+ if (!element) return;
1327
+ const updateStyles = () => {
1328
+ const isActive = element.getAttribute("data-state") === "active";
1329
+ if (isActive) {
1330
+ element.style.setProperty("background-color", "rgba(255, 255, 255, 0.3)", "important");
1331
+ element.style.setProperty("border-color", "transparent", "important");
1332
+ } else {
1333
+ element.style.removeProperty("background-color");
1334
+ element.style.removeProperty("border-color");
1335
+ }
1336
+ };
1337
+ updateStyles();
1338
+ const observer = new MutationObserver(updateStyles);
1339
+ observer.observe(element, { attributes: true, attributeFilter: ["data-state"] });
1340
+ return () => observer.disconnect();
1341
+ }, []);
1159
1342
  return /* @__PURE__ */ jsx(
1160
1343
  TabsPrimitive.Trigger,
1161
1344
  {
1345
+ ref: triggerRef,
1162
1346
  "data-slot": "tabs-trigger",
1163
1347
  className: cn(
1164
- "data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1348
+ "data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring text-foreground/70 inline-flex h-[calc(100%-0.5rem)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow,background-color,border-color] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm hover:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1165
1349
  className
1166
1350
  ),
1167
1351
  ...props
@@ -1405,7 +1589,7 @@ var useSolanaQrPayment = (options) => {
1405
1589
  refresh
1406
1590
  };
1407
1591
  };
1408
- var Card = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1592
+ var Card = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1409
1593
  "div",
1410
1594
  {
1411
1595
  ref,
@@ -1417,7 +1601,7 @@ var Card = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
1417
1601
  }
1418
1602
  ));
1419
1603
  Card.displayName = "Card";
1420
- var CardHeader = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1604
+ var CardHeader = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1421
1605
  "div",
1422
1606
  {
1423
1607
  ref,
@@ -1426,7 +1610,7 @@ var CardHeader = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE
1426
1610
  }
1427
1611
  ));
1428
1612
  CardHeader.displayName = "CardHeader";
1429
- var CardTitle = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1613
+ var CardTitle = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1430
1614
  "div",
1431
1615
  {
1432
1616
  ref,
@@ -1438,7 +1622,7 @@ var CardTitle = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE_
1438
1622
  }
1439
1623
  ));
1440
1624
  CardTitle.displayName = "CardTitle";
1441
- var CardDescription = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1625
+ var CardDescription = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1442
1626
  "div",
1443
1627
  {
1444
1628
  ref,
@@ -1447,9 +1631,9 @@ var CardDescription = React15.forwardRef(({ className, ...props }, ref) => /* @_
1447
1631
  }
1448
1632
  ));
1449
1633
  CardDescription.displayName = "CardDescription";
1450
- var CardContent = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
1634
+ var CardContent = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
1451
1635
  CardContent.displayName = "CardContent";
1452
- var CardFooter = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1636
+ var CardFooter = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1453
1637
  "div",
1454
1638
  {
1455
1639
  ref,
@@ -1464,7 +1648,7 @@ var QRCodePayment = ({
1464
1648
  onPaymentError,
1465
1649
  onPaymentSuccess
1466
1650
  }) => {
1467
- const handleQrSuccess = React15__default.useCallback(
1651
+ const handleQrSuccess = React4__default.useCallback(
1468
1652
  (paymentId, txId) => {
1469
1653
  if (!paymentId && !txId) {
1470
1654
  onPaymentError("Missing payment confirmation details");
@@ -1898,20 +2082,45 @@ var SolanaPaymentView = ({
1898
2082
  renderBody()
1899
2083
  ] });
1900
2084
  };
2085
+ var defaultPaymentExperienceTranslations = {
2086
+ ...defaultCardDetailsFormTranslations,
2087
+ useSavedCardTab: "Use saved card",
2088
+ addNewCardTab: "Add new card",
2089
+ savedUnavailable: "Saved payment methods are unavailable right now. Add a new card to get started.",
2090
+ payWithSavedCard: "Pay with selected card",
2091
+ processingSavedCard: "Processing...",
2092
+ savedErrorFallback: "Unable to complete payment with saved card",
2093
+ newCardUnavailable: "Select a subscription plan to add a new card.",
2094
+ payNow: "Pay now",
2095
+ storedLoadingCards: "Loading cards...",
2096
+ storedNoSavedMethods: "No saved payment methods yet.",
2097
+ storedSelectedLabel: "Selected",
2098
+ storedUseCardLabel: "Use card",
2099
+ payWithCcbill: "Pay with CCBill",
2100
+ processingCcbill: "Redirecting...",
2101
+ errors: {}
2102
+ };
1901
2103
  var PaymentExperience = ({
1902
2104
  priceId,
1903
2105
  usdAmount,
1904
2106
  onNewCardPayment,
1905
2107
  onSavedMethodPayment,
2108
+ onCcbillPayment,
1906
2109
  enableNewCard = true,
1907
2110
  enableStoredMethods = true,
1908
2111
  enableSolanaPay = true,
1909
2112
  onSolanaSuccess,
1910
2113
  onSolanaError,
1911
- initialMode = "cards"
2114
+ initialMode = "cards",
2115
+ translations
1912
2116
  }) => {
2117
+ const t = {
2118
+ ...defaultPaymentExperienceTranslations,
2119
+ ...translations
2120
+ };
1913
2121
  const showNewCard = enableNewCard && Boolean(onNewCardPayment);
1914
2122
  const showStored = enableStoredMethods && Boolean(onSavedMethodPayment);
2123
+ const showCcbill = showNewCard && Boolean(onCcbillPayment);
1915
2124
  const defaultTab = showStored ? "saved" : "new";
1916
2125
  const [activeTab, setActiveTab] = useState(defaultTab);
1917
2126
  const [mode, setMode] = useState(
@@ -1922,6 +2131,9 @@ var PaymentExperience = ({
1922
2131
  const [savedError, setSavedError] = useState(null);
1923
2132
  const [newCardStatus, setNewCardStatus] = useState("idle");
1924
2133
  const [newCardError, setNewCardError] = useState(null);
2134
+ const [billingDetails, setBillingDetails] = useState(null);
2135
+ const [ccbillStatus, setCcbillStatus] = useState("idle");
2136
+ const [ccbillError, setCcbillError] = useState(null);
1925
2137
  const { notifyStatus, notifyError } = usePaymentNotifications();
1926
2138
  useEffect(() => {
1927
2139
  setActiveTab(showStored ? "saved" : "new");
@@ -1937,6 +2149,24 @@ var PaymentExperience = ({
1937
2149
  setMode("cards");
1938
2150
  }
1939
2151
  }, [enableSolanaPay, initialMode]);
2152
+ useEffect(() => {
2153
+ if (!showNewCard) {
2154
+ setBillingDetails(null);
2155
+ setCcbillStatus("idle");
2156
+ setCcbillError(null);
2157
+ }
2158
+ }, [showNewCard]);
2159
+ const handleBillingChange = useCallback((billing) => {
2160
+ setBillingDetails(billing);
2161
+ setCcbillError(null);
2162
+ setCcbillStatus("idle");
2163
+ }, []);
2164
+ const isBillingComplete = useCallback((billing) => {
2165
+ if (!billing) return false;
2166
+ return Boolean(
2167
+ billing.firstName.trim() && billing.lastName.trim() && billing.address1.trim() && billing.city.trim() && billing.postalCode.trim() && billing.country.trim() && billing.email.trim()
2168
+ );
2169
+ }, []);
1940
2170
  const handleMethodSelect = useCallback((method) => {
1941
2171
  setSelectedMethodId(method.id);
1942
2172
  setSavedStatus("idle");
@@ -1955,13 +2185,17 @@ var PaymentExperience = ({
1955
2185
  setSavedStatus("success");
1956
2186
  notifyStatus("success", { source: "saved-payment" });
1957
2187
  } catch (error) {
1958
- const message = error instanceof Error ? error.message : "Unable to complete payment with saved card";
2188
+ const message = resolveErrorMessageByCode(
2189
+ error,
2190
+ t.errors,
2191
+ t.savedErrorFallback
2192
+ );
1959
2193
  setSavedStatus("error");
1960
2194
  setSavedError(message);
1961
2195
  notifyStatus("error", { source: "saved-payment" });
1962
2196
  notifyError(message);
1963
2197
  }
1964
- }, [notifyError, notifyStatus, onSavedMethodPayment, selectedMethodId, usdAmount]);
2198
+ }, [notifyError, notifyStatus, onSavedMethodPayment, selectedMethodId, t, usdAmount]);
1965
2199
  const handleNewCardTokenize = useCallback(
1966
2200
  async (token, billing) => {
1967
2201
  if (!onNewCardPayment) return;
@@ -1973,15 +2207,56 @@ var PaymentExperience = ({
1973
2207
  setNewCardStatus("success");
1974
2208
  notifyStatus("success", { source: "new-card" });
1975
2209
  } catch (error) {
1976
- const message = error instanceof Error ? error.message : "Unable to complete payment";
2210
+ const message = resolveErrorMessageByCode(
2211
+ error,
2212
+ t.errors,
2213
+ "Unable to complete payment"
2214
+ );
1977
2215
  setNewCardStatus("error");
1978
2216
  setNewCardError(message);
1979
2217
  notifyStatus("error", { source: "new-card" });
1980
2218
  notifyError(message);
1981
2219
  }
1982
2220
  },
1983
- [notifyError, notifyStatus, onNewCardPayment]
2221
+ [notifyError, notifyStatus, onNewCardPayment, t]
1984
2222
  );
2223
+ const handleCcbillPayment = useCallback(async () => {
2224
+ if (!onCcbillPayment) return;
2225
+ if (!billingDetails || !isBillingComplete(billingDetails)) {
2226
+ const message = t.errorRequiredFields;
2227
+ setActiveTab("new");
2228
+ setCcbillStatus("error");
2229
+ setCcbillError(message);
2230
+ notifyStatus("error", { source: "ccbill" });
2231
+ notifyError(message);
2232
+ return;
2233
+ }
2234
+ try {
2235
+ setCcbillStatus("processing");
2236
+ setCcbillError(null);
2237
+ notifyStatus("processing", { source: "ccbill" });
2238
+ await onCcbillPayment({ billing: billingDetails });
2239
+ setCcbillStatus("success");
2240
+ notifyStatus("success", { source: "ccbill" });
2241
+ } catch (error) {
2242
+ const message = resolveErrorMessageByCode(
2243
+ error,
2244
+ t.errors,
2245
+ "Unable to start CCBill checkout"
2246
+ );
2247
+ setCcbillStatus("error");
2248
+ setCcbillError(message);
2249
+ notifyStatus("error", { source: "ccbill" });
2250
+ notifyError(message);
2251
+ }
2252
+ }, [
2253
+ billingDetails,
2254
+ isBillingComplete,
2255
+ notifyError,
2256
+ notifyStatus,
2257
+ onCcbillPayment,
2258
+ t
2259
+ ]);
1985
2260
  useCallback(() => {
1986
2261
  if (!enableSolanaPay) return;
1987
2262
  setMode("solana");
@@ -2004,14 +2279,20 @@ var PaymentExperience = ({
2004
2279
  );
2005
2280
  const renderSavedTab = () => {
2006
2281
  if (!showStored) {
2007
- 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." });
2282
+ return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t.savedUnavailable });
2008
2283
  }
2009
2284
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
2010
2285
  /* @__PURE__ */ jsx(
2011
2286
  StoredPaymentMethods,
2012
2287
  {
2013
2288
  selectedMethodId,
2014
- onMethodSelect: handleMethodSelect
2289
+ onMethodSelect: handleMethodSelect,
2290
+ translations: {
2291
+ loadingCards: t.storedLoadingCards,
2292
+ noSavedMethods: t.storedNoSavedMethods,
2293
+ selectedLabel: t.storedSelectedLabel,
2294
+ useCardLabel: t.storedUseCardLabel
2295
+ }
2015
2296
  }
2016
2297
  ),
2017
2298
  /* @__PURE__ */ jsx(
@@ -2020,7 +2301,7 @@ var PaymentExperience = ({
2020
2301
  className: "w-full",
2021
2302
  disabled: !selectedMethodId || savedStatus === "processing",
2022
2303
  onClick: handleSavedPayment,
2023
- children: savedStatus === "processing" ? "Processing..." : "Pay with selected card"
2304
+ children: savedStatus === "processing" ? t.processingSavedCard : t.payWithSavedCard
2024
2305
  }
2025
2306
  ),
2026
2307
  savedError && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: savedError })
@@ -2028,29 +2309,50 @@ var PaymentExperience = ({
2028
2309
  };
2029
2310
  const renderNewTab = () => {
2030
2311
  if (!showNewCard) {
2031
- return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Select a subscription plan to add a new card." });
2312
+ return /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: t.newCardUnavailable });
2032
2313
  }
2033
2314
  return /* @__PURE__ */ jsx(
2034
2315
  CardDetailsForm,
2035
2316
  {
2036
2317
  visible: true,
2037
- submitLabel: "Pay now",
2318
+ submitLabel: t.payNow,
2038
2319
  externalError: newCardError,
2039
2320
  onTokenize: handleNewCardTokenize,
2040
- submitting: newCardStatus === "processing"
2321
+ submitting: newCardStatus === "processing",
2322
+ onBillingChange: handleBillingChange,
2323
+ translations: t
2041
2324
  }
2042
2325
  );
2043
2326
  };
2044
2327
  const renderCardExperience = () => /* @__PURE__ */ jsxs(Tabs, { value: activeTab, onValueChange: setActiveTab, children: [
2045
2328
  /* @__PURE__ */ jsxs(TabsList, { className: "w-full rounded-md mb-4", children: [
2046
- /* @__PURE__ */ jsx(TabsTrigger, { className: clsx("cursor-pointer", { "bg-background": activeTab == "saved" }), value: "saved", children: "Use saved card" }),
2047
- /* @__PURE__ */ jsx(TabsTrigger, { className: clsx("cursor-pointer", { "bg-background": activeTab == "new" }), value: "new", children: "Add new card" })
2329
+ /* @__PURE__ */ jsx(TabsTrigger, { className: "cursor-pointer", value: "saved", children: t.useSavedCardTab }),
2330
+ /* @__PURE__ */ jsx(TabsTrigger, { className: "cursor-pointer", value: "new", children: t.addNewCardTab })
2048
2331
  ] }),
2049
2332
  /* @__PURE__ */ jsx(TabsContent, { value: "saved", children: renderSavedTab() }),
2050
2333
  /* @__PURE__ */ jsx(TabsContent, { value: "new", children: renderNewTab() })
2051
2334
  ] });
2335
+ const renderCcbillAction = () => {
2336
+ if (!showCcbill) return null;
2337
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2 border-t border-white/10 pt-4", children: [
2338
+ /* @__PURE__ */ jsx(
2339
+ Button,
2340
+ {
2341
+ className: "w-full",
2342
+ variant: "outline",
2343
+ disabled: ccbillStatus === "processing",
2344
+ onClick: handleCcbillPayment,
2345
+ children: ccbillStatus === "processing" ? t.processingCcbill : t.payWithCcbill
2346
+ }
2347
+ ),
2348
+ ccbillError && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: ccbillError })
2349
+ ] });
2350
+ };
2052
2351
  return /* @__PURE__ */ jsxs("div", { className: "space-y-6 pt-4", children: [
2053
- mode === "cards" && renderCardExperience(),
2352
+ mode === "cards" && /* @__PURE__ */ jsxs(Fragment, { children: [
2353
+ renderCardExperience(),
2354
+ renderCcbillAction()
2355
+ ] }),
2054
2356
  mode === "solana" && enableSolanaPay && /* @__PURE__ */ jsx(
2055
2357
  SolanaPaymentView,
2056
2358
  {
@@ -2102,17 +2404,17 @@ var useSubscriptionActions = () => {
2102
2404
  const subscribeWithCard = useCallback(
2103
2405
  async ({
2104
2406
  priceId,
2105
- processor = "nmi",
2407
+ processor = "mobius",
2106
2408
  provider,
2107
2409
  paymentToken,
2108
2410
  billing,
2109
2411
  idempotencyKey
2110
2412
  }) => {
2111
- const payload = {
2112
- price_id: ensurePrice(priceId),
2413
+ if (processor !== "ccbill" && !paymentToken) {
2414
+ throw new Error("payments-ui: payment token is required for card checkout");
2415
+ }
2416
+ const payment = {
2113
2417
  processor,
2114
- provider,
2115
- payment_token: paymentToken,
2116
2418
  email: billing.email,
2117
2419
  first_name: billing.firstName,
2118
2420
  last_name: billing.lastName,
@@ -2122,6 +2424,17 @@ var useSubscriptionActions = () => {
2122
2424
  zip: billing.postalCode,
2123
2425
  country: billing.country
2124
2426
  };
2427
+ if (paymentToken) {
2428
+ payment.payment_token = paymentToken;
2429
+ payment.last_four = billing.last_four;
2430
+ payment.card_type = billing.card_type;
2431
+ payment.expiry_date = billing.expiry_date;
2432
+ }
2433
+ const payload = {
2434
+ price_id: ensurePrice(priceId),
2435
+ provider,
2436
+ payment
2437
+ };
2125
2438
  return client.checkout(payload, idempotencyKey);
2126
2439
  },
2127
2440
  [client]
@@ -2129,7 +2442,7 @@ var useSubscriptionActions = () => {
2129
2442
  const subscribeWithSavedMethod = useCallback(
2130
2443
  async ({
2131
2444
  priceId,
2132
- processor = "nmi",
2445
+ processor = "mobius",
2133
2446
  provider,
2134
2447
  paymentMethodId,
2135
2448
  email,
@@ -2137,10 +2450,12 @@ var useSubscriptionActions = () => {
2137
2450
  }) => {
2138
2451
  const payload = {
2139
2452
  price_id: ensurePrice(priceId),
2140
- processor,
2141
2453
  provider,
2142
- payment_method_id: paymentMethodId,
2143
- email
2454
+ payment: {
2455
+ processor,
2456
+ payment_method_id: paymentMethodId,
2457
+ email
2458
+ }
2144
2459
  };
2145
2460
  return client.checkout(payload, idempotencyKey);
2146
2461
  },
@@ -2159,12 +2474,14 @@ var useSubscriptionActions = () => {
2159
2474
  }) => {
2160
2475
  const payload = {
2161
2476
  price_id: ensurePrice(priceId),
2162
- processor,
2163
- email,
2164
- first_name: firstName,
2165
- last_name: lastName,
2166
- zip: zipCode,
2167
- country
2477
+ payment: {
2478
+ processor,
2479
+ email,
2480
+ first_name: firstName,
2481
+ last_name: lastName,
2482
+ zip: zipCode,
2483
+ country
2484
+ }
2168
2485
  };
2169
2486
  return client.checkout(payload, idempotencyKey);
2170
2487
  },
@@ -2176,6 +2493,11 @@ var useSubscriptionActions = () => {
2176
2493
  subscribeWithCCBill
2177
2494
  };
2178
2495
  };
2496
+ var defaultTranslations2 = {
2497
+ ...defaultPaymentExperienceTranslations,
2498
+ title: "Checkout",
2499
+ selectPlanMessage: "Select a subscription plan to continue."
2500
+ };
2179
2501
  var SubscriptionCheckoutModal = ({
2180
2502
  open,
2181
2503
  onOpenChange,
@@ -2190,11 +2512,16 @@ var SubscriptionCheckoutModal = ({
2190
2512
  enableSolanaPay = true,
2191
2513
  onSolanaSuccess,
2192
2514
  onSolanaError,
2193
- initialMode = "cards"
2515
+ initialMode = "cards",
2516
+ translations
2194
2517
  }) => {
2195
2518
  const [showSuccess, setShowSuccess] = useState(false);
2196
2519
  const [idempotencyKey, setIdempotencyKey] = useState(() => crypto.randomUUID());
2197
2520
  const { subscribeWithCard, subscribeWithSavedMethod } = useSubscriptionActions();
2521
+ const t = {
2522
+ ...defaultTranslations2,
2523
+ ...translations
2524
+ };
2198
2525
  useEffect(() => {
2199
2526
  if (open) {
2200
2527
  setIdempotencyKey(crypto.randomUUID());
@@ -2218,13 +2545,31 @@ var SubscriptionCheckoutModal = ({
2218
2545
  console.debug("[payments-ui] subscription success", result);
2219
2546
  }
2220
2547
  };
2221
- const assertCheckoutSuccess = (status, message) => {
2222
- if (status === "blocked") {
2223
- throw new Error(message || "This subscription cannot be completed right now.");
2548
+ const handleCheckoutResponse = (response) => {
2549
+ if (response.status === "blocked") {
2550
+ throw new Error(response.message || "This subscription cannot be completed right now.");
2224
2551
  }
2225
- if (status === "redirect_required") {
2226
- throw new Error(message || "Additional action required in an alternate flow.");
2552
+ const nextAction = response.next_action;
2553
+ if (nextAction?.type === "redirect_to_url") {
2554
+ const redirectUrl = nextAction.redirect_to_url?.url || response.payment?.redirect_url;
2555
+ if (!redirectUrl) {
2556
+ throw new Error(response.message || "Checkout requires a redirect URL.");
2557
+ }
2558
+ if (typeof window !== "undefined") {
2559
+ window.location.assign(redirectUrl);
2560
+ }
2561
+ return;
2227
2562
  }
2563
+ if (response.payment?.redirect_url) {
2564
+ if (typeof window !== "undefined") {
2565
+ window.location.assign(response.payment.redirect_url);
2566
+ }
2567
+ return;
2568
+ }
2569
+ if (nextAction && nextAction.type !== "none") {
2570
+ throw new Error(response.message || "Unsupported checkout action.");
2571
+ }
2572
+ notifySuccess();
2228
2573
  };
2229
2574
  const handleNewCardPayment = async ({ token, billing }) => {
2230
2575
  const response = await subscribeWithCard({
@@ -2234,8 +2579,7 @@ var SubscriptionCheckoutModal = ({
2234
2579
  paymentToken: token,
2235
2580
  priceId: ensurePrice()
2236
2581
  });
2237
- assertCheckoutSuccess(response.status, response.message);
2238
- notifySuccess();
2582
+ handleCheckoutResponse(response);
2239
2583
  };
2240
2584
  const handleSavedMethodPayment = async ({ paymentMethodId }) => {
2241
2585
  const response = await subscribeWithSavedMethod({
@@ -2245,8 +2589,16 @@ var SubscriptionCheckoutModal = ({
2245
2589
  email: userEmail ?? "",
2246
2590
  idempotencyKey
2247
2591
  });
2248
- assertCheckoutSuccess(response.status, response.message);
2249
- notifySuccess();
2592
+ handleCheckoutResponse(response);
2593
+ };
2594
+ const handleCcbillPayment = async ({ billing }) => {
2595
+ const response = await subscribeWithCard({
2596
+ billing,
2597
+ idempotencyKey,
2598
+ processor: "ccbill",
2599
+ priceId: ensurePrice()
2600
+ });
2601
+ handleCheckoutResponse(response);
2250
2602
  };
2251
2603
  const solanaSuccess = (result) => {
2252
2604
  notifySuccess(result);
@@ -2262,11 +2614,11 @@ var SubscriptionCheckoutModal = ({
2262
2614
  {
2263
2615
  className: "z-[100] max-w-xl max-h-[90vh] overflow-y-auto border border-white/20 p-6 backdrop-blur-xl bg-background-regular rounded-md [&::-webkit-scrollbar]:hidden",
2264
2616
  children: [
2265
- /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { className: "flex items-center gap-2 text-foreground", children: "Checkout" }) }),
2617
+ /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { className: "flex items-center gap-2 text-foreground", children: t.title }) }),
2266
2618
  /* @__PURE__ */ jsx("div", { className: "space-y-4", children: !priceId ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-center px-3 py-2 text-sm text-destructive", children: [
2267
2619
  /* @__PURE__ */ jsx(AlertCircle, { className: "h-4 w-4" }),
2268
2620
  " ",
2269
- /* @__PURE__ */ jsx("span", { children: "Select a subscription plan to continue." })
2621
+ /* @__PURE__ */ jsx("span", { children: t.selectPlanMessage })
2270
2622
  ] }) : /* @__PURE__ */ jsx(
2271
2623
  PaymentExperience,
2272
2624
  {
@@ -2279,7 +2631,9 @@ var SubscriptionCheckoutModal = ({
2279
2631
  enableStoredMethods: Boolean(priceId),
2280
2632
  enableSolanaPay: enableSolanaPay && Boolean(priceId),
2281
2633
  onNewCardPayment: priceId ? handleNewCardPayment : void 0,
2282
- onSavedMethodPayment: priceId ? handleSavedMethodPayment : void 0
2634
+ onSavedMethodPayment: priceId ? handleSavedMethodPayment : void 0,
2635
+ onCcbillPayment: priceId ? handleCcbillPayment : void 0,
2636
+ translations: t
2283
2637
  }
2284
2638
  ) })
2285
2639
  ]
@@ -2289,7 +2643,9 @@ var SubscriptionCheckoutModal = ({
2289
2643
  SubscriptionSuccessDialog,
2290
2644
  {
2291
2645
  open: showSuccess,
2292
- onClose: () => setShowSuccess(false),
2646
+ onClose: () => {
2647
+ setShowSuccess(false);
2648
+ },
2293
2649
  planName,
2294
2650
  amountLabel: amountLabel ?? `$${usdAmount.toFixed(2)}`,
2295
2651
  billingPeriodLabel
@@ -2524,7 +2880,7 @@ var SolanaPaymentSelector = ({
2524
2880
  }) => {
2525
2881
  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 bg-background/95 p-0 shadow-2xl [&::-webkit-scrollbar]:hidden", children: /* @__PURE__ */ jsx(SolanaPaymentView, { ...props, onClose }) }) });
2526
2882
  };
2527
- var Table = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ jsx(
2883
+ var Table = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ jsx(
2528
2884
  "table",
2529
2885
  {
2530
2886
  ref,
@@ -2533,9 +2889,9 @@ var Table = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
2533
2889
  }
2534
2890
  ) }));
2535
2891
  Table.displayName = "Table";
2536
- var TableHeader = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("thead", { ref, className: cn("[&_tr]:border-b", className), ...props }));
2892
+ var TableHeader = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("thead", { ref, className: cn("[&_tr]:border-b", className), ...props }));
2537
2893
  TableHeader.displayName = "TableHeader";
2538
- var TableBody = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2894
+ var TableBody = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2539
2895
  "tbody",
2540
2896
  {
2541
2897
  ref,
@@ -2544,7 +2900,7 @@ var TableBody = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE_
2544
2900
  }
2545
2901
  ));
2546
2902
  TableBody.displayName = "TableBody";
2547
- var TableFooter = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2903
+ var TableFooter = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2548
2904
  "tfoot",
2549
2905
  {
2550
2906
  ref,
@@ -2556,7 +2912,7 @@ var TableFooter = React15.forwardRef(({ className, ...props }, ref) => /* @__PUR
2556
2912
  }
2557
2913
  ));
2558
2914
  TableFooter.displayName = "TableFooter";
2559
- var TableRow = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2915
+ var TableRow = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2560
2916
  "tr",
2561
2917
  {
2562
2918
  ref,
@@ -2568,7 +2924,7 @@ var TableRow = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__
2568
2924
  }
2569
2925
  ));
2570
2926
  TableRow.displayName = "TableRow";
2571
- var TableHead = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2927
+ var TableHead = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2572
2928
  "th",
2573
2929
  {
2574
2930
  ref,
@@ -2580,7 +2936,7 @@ var TableHead = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE_
2580
2936
  }
2581
2937
  ));
2582
2938
  TableHead.displayName = "TableHead";
2583
- var TableCell = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2939
+ var TableCell = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2584
2940
  "td",
2585
2941
  {
2586
2942
  ref,
@@ -2589,7 +2945,7 @@ var TableCell = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE_
2589
2945
  }
2590
2946
  ));
2591
2947
  TableCell.displayName = "TableCell";
2592
- var TableCaption = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2948
+ var TableCaption = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2593
2949
  "caption",
2594
2950
  {
2595
2951
  ref,
@@ -2601,7 +2957,7 @@ TableCaption.displayName = "TableCaption";
2601
2957
  var AlertDialog = AlertDialogPrimitive.Root;
2602
2958
  var AlertDialogTrigger = AlertDialogPrimitive.Trigger;
2603
2959
  var AlertDialogPortal = AlertDialogPrimitive.Portal;
2604
- var AlertDialogOverlay = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2960
+ var AlertDialogOverlay = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2605
2961
  AlertDialogPrimitive.Overlay,
2606
2962
  {
2607
2963
  className: cn(
@@ -2613,7 +2969,7 @@ var AlertDialogOverlay = React15.forwardRef(({ className, ...props }, ref) => /*
2613
2969
  }
2614
2970
  ));
2615
2971
  AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
2616
- var AlertDialogContent = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxs(AlertDialogPortal, { children: [
2972
+ var AlertDialogContent = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxs(AlertDialogPortal, { children: [
2617
2973
  /* @__PURE__ */ jsx(AlertDialogOverlay, {}),
2618
2974
  /* @__PURE__ */ jsx(
2619
2975
  AlertDialogPrimitive.Content,
@@ -2656,7 +3012,7 @@ var AlertDialogFooter = ({
2656
3012
  }
2657
3013
  );
2658
3014
  AlertDialogFooter.displayName = "AlertDialogFooter";
2659
- var AlertDialogTitle = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3015
+ var AlertDialogTitle = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2660
3016
  AlertDialogPrimitive.Title,
2661
3017
  {
2662
3018
  ref,
@@ -2665,7 +3021,7 @@ var AlertDialogTitle = React15.forwardRef(({ className, ...props }, ref) => /* @
2665
3021
  }
2666
3022
  ));
2667
3023
  AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
2668
- var AlertDialogDescription = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3024
+ var AlertDialogDescription = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2669
3025
  AlertDialogPrimitive.Description,
2670
3026
  {
2671
3027
  ref,
@@ -2674,7 +3030,7 @@ var AlertDialogDescription = React15.forwardRef(({ className, ...props }, ref) =
2674
3030
  }
2675
3031
  ));
2676
3032
  AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
2677
- var AlertDialogAction = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3033
+ var AlertDialogAction = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2678
3034
  AlertDialogPrimitive.Action,
2679
3035
  {
2680
3036
  ref,
@@ -2683,7 +3039,7 @@ var AlertDialogAction = React15.forwardRef(({ className, ...props }, ref) => /*
2683
3039
  }
2684
3040
  ));
2685
3041
  AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
2686
- var AlertDialogCancel = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3042
+ var AlertDialogCancel = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2687
3043
  AlertDialogPrimitive.Cancel,
2688
3044
  {
2689
3045
  ref,
@@ -2696,7 +3052,7 @@ var AlertDialogCancel = React15.forwardRef(({ className, ...props }, ref) => /*
2696
3052
  }
2697
3053
  ));
2698
3054
  AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
2699
- var Textarea = React15.forwardRef(({ className, ...props }, ref) => {
3055
+ var Textarea = React4.forwardRef(({ className, ...props }, ref) => {
2700
3056
  return /* @__PURE__ */ jsx(
2701
3057
  "textarea",
2702
3058
  {
@@ -2714,7 +3070,7 @@ var notifyDefault = (payload) => {
2714
3070
  const level = payload.status === "destructive" ? "error" : "info";
2715
3071
  console[level === "error" ? "error" : "log"]("[payments-ui] cancellation", payload);
2716
3072
  };
2717
- var defaultTranslations = {
3073
+ var defaultTranslations3 = {
2718
3074
  buttonLabel: "Cancel Membership",
2719
3075
  title: "Confirm Membership Cancellation",
2720
3076
  description: "You are about to cancel your membership. Please review the consequences:",
@@ -2736,23 +3092,33 @@ var CancelMembershipDialog = ({
2736
3092
  minReasonLength = 15,
2737
3093
  onCancelled,
2738
3094
  onNotify,
2739
- translations: customTranslations
3095
+ translations: customTranslations,
3096
+ open,
3097
+ onOpenChange
2740
3098
  }) => {
2741
3099
  const { client } = usePaymentContext();
2742
3100
  const notify = onNotify ?? notifyDefault;
2743
- const t = { ...defaultTranslations, ...customTranslations };
3101
+ const t = { ...defaultTranslations3, ...customTranslations };
2744
3102
  const [cancelReason, setCancelReason] = useState("");
2745
- const [isOpen, setIsOpen] = useState(false);
3103
+ const [internalOpen, setInternalOpen] = useState(false);
2746
3104
  const [isReasonValid, setIsReasonValid] = useState(false);
2747
3105
  const [hasInteracted, setHasInteracted] = useState(false);
2748
3106
  const [isSubmitting, setIsSubmitting] = useState(false);
3107
+ const isControlled = typeof open === "boolean";
3108
+ const isOpen = isControlled ? open : internalOpen;
3109
+ const setOpen = (next) => {
3110
+ if (!isControlled) {
3111
+ setInternalOpen(next);
3112
+ }
3113
+ onOpenChange?.(next);
3114
+ };
2749
3115
  useEffect(() => {
2750
3116
  const trimmed = cancelReason.trim();
2751
3117
  setIsReasonValid(trimmed.length >= minReasonLength);
2752
3118
  }, [cancelReason, minReasonLength]);
2753
- const handleOpenChange = (open) => {
2754
- setIsOpen(open);
2755
- if (!open) {
3119
+ const handleOpenChange = (open2) => {
3120
+ setOpen(open2);
3121
+ if (!open2) {
2756
3122
  setCancelReason("");
2757
3123
  setIsReasonValid(false);
2758
3124
  setHasInteracted(false);
@@ -2789,7 +3155,7 @@ var CancelMembershipDialog = ({
2789
3155
  };
2790
3156
  const showError = hasInteracted && !isReasonValid;
2791
3157
  return /* @__PURE__ */ jsxs(AlertDialog, { open: isOpen, onOpenChange: handleOpenChange, children: [
2792
- /* @__PURE__ */ jsx(AlertDialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", className: "border-destructive/50 text-destructive", children: [
3158
+ /* @__PURE__ */ jsx(AlertDialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { className: "bg-destructive text-destructive-foreground border-destructive/50 hover:bg-destructive/90", children: [
2793
3159
  /* @__PURE__ */ jsx(Ban, { className: "mr-2 h-4 w-4" }),
2794
3160
  " ",
2795
3161
  t.buttonLabel
@@ -2856,7 +3222,7 @@ var notifyDefault2 = (payload) => {
2856
3222
  const level = payload.status === "destructive" ? "error" : "info";
2857
3223
  console[level === "error" ? "error" : "log"]("[payments-ui] billing", payload);
2858
3224
  };
2859
- var defaultTranslations2 = {
3225
+ var defaultTranslations4 = {
2860
3226
  title: "Transaction History",
2861
3227
  description: "Record of billing history",
2862
3228
  reviewActivity: "Review your account activity below",
@@ -2878,7 +3244,7 @@ var BillingHistory = ({
2878
3244
  }) => {
2879
3245
  const { client } = usePaymentContext();
2880
3246
  const notify = onNotify ?? notifyDefault2;
2881
- const t = { ...defaultTranslations2, ...customTranslations };
3247
+ const t = { ...defaultTranslations4, ...customTranslations };
2882
3248
  const [isExpanded, setIsExpanded] = useState(false);
2883
3249
  const observerRef = useRef(null);
2884
3250
  const loadMoreRef = useRef(null);
@@ -2987,15 +3353,21 @@ var BillingHistory = ({
2987
3353
  ] }) });
2988
3354
  };
2989
3355
  var formatCardLabel2 = (method) => {
2990
- const brand = method.card_type ? method.card_type.toUpperCase() : "CARD";
2991
- const lastFour = method.last_four ? `\u2022\u2022\u2022\u2022 ${method.last_four}` : "";
3356
+ if (method.card) {
3357
+ const brand2 = method.card.brand ? method.card.brand.toUpperCase() : "CARD";
3358
+ const lastFour2 = method.card.last4 ? `\u2022\u2022\u2022\u2022 ${method.card.last4}` : "";
3359
+ const exp = method.card.exp_month && method.card.exp_year ? ` \u2022 ${String(method.card.exp_month).padStart(2, "0")}/${String(method.card.exp_year).slice(-2)}` : "";
3360
+ return `${brand2} ${lastFour2}${exp}`.trim();
3361
+ }
3362
+ const brand = "CARD";
3363
+ const lastFour = "";
2992
3364
  return `${brand} ${lastFour}`.trim();
2993
3365
  };
2994
3366
  var notifyDefault3 = (payload) => {
2995
3367
  const level = payload.status === "destructive" ? "error" : "info";
2996
3368
  console[level === "error" ? "error" : "log"]("[payments-ui] notification", payload);
2997
3369
  };
2998
- var defaultTranslations3 = {
3370
+ var defaultTranslations5 = {
2999
3371
  title: "Payment Methods",
3000
3372
  description: "Manage your saved billing cards",
3001
3373
  addCard: "Add card",
@@ -3016,7 +3388,11 @@ var defaultTranslations3 = {
3016
3388
  cardUpdated: "Card updated",
3017
3389
  unableToRemoveCard: "Unable to remove card",
3018
3390
  defaultPaymentMethodUpdated: "Default payment method updated",
3019
- unableToSetDefault: "Unable to set default payment method"
3391
+ unableToSetDefault: "Unable to set default payment method",
3392
+ errors: {},
3393
+ activeSubscriptions: "Active subscriptions",
3394
+ showSubscriptions: "Show",
3395
+ hideSubscriptions: "Hide"
3020
3396
  };
3021
3397
  var PaymentMethodsSection = ({
3022
3398
  isAuthenticated = true,
@@ -3027,8 +3403,7 @@ var PaymentMethodsSection = ({
3027
3403
  onNotify,
3028
3404
  translations: customTranslations
3029
3405
  }) => {
3030
- const { client } = usePaymentContext();
3031
- const queryClient = useQueryClient();
3406
+ const { client, queryClient } = usePaymentContext();
3032
3407
  const paymentMethods = {
3033
3408
  list: (params) => client.listPaymentMethods({ limit: params.pageSize }),
3034
3409
  create: (payload) => client.createPaymentMethod(payload),
@@ -3038,13 +3413,15 @@ var PaymentMethodsSection = ({
3038
3413
  };
3039
3414
  const [isModalOpen, setIsModalOpen] = useState(false);
3040
3415
  const [deletingId, setDeletingId] = useState(null);
3416
+ const [createErrorMessage, setCreateErrorMessage] = useState(null);
3417
+ const [expandedSubscriptions, setExpandedSubscriptions] = useState({});
3041
3418
  const notify = onNotify ?? notifyDefault3;
3042
- const t = { ...defaultTranslations3, ...customTranslations };
3419
+ const t = { ...defaultTranslations5, ...customTranslations };
3043
3420
  const queryKey = ["payments-ui", "payment-methods"];
3044
3421
  const paymentQuery = useQuery({
3045
3422
  queryKey,
3046
3423
  queryFn: () => paymentMethods.list({ pageSize: 50 }),
3047
- enabled: isAuthenticated,
3424
+ enabled: isAuthenticated && !!client,
3048
3425
  staleTime: 3e4
3049
3426
  });
3050
3427
  const createMutation = useMutation({
@@ -3053,11 +3430,14 @@ var PaymentMethodsSection = ({
3053
3430
  notify({ title: t.cardAddedSuccess, status: "success" });
3054
3431
  setIsModalOpen(false);
3055
3432
  void queryClient.invalidateQueries({ queryKey });
3433
+ setCreateErrorMessage(null);
3056
3434
  },
3057
3435
  onError: (error) => {
3436
+ const message = resolveErrorMessageByCode(error, t.errors, error.message);
3437
+ setCreateErrorMessage(message);
3058
3438
  notify({
3059
3439
  title: t.unableToAddCard,
3060
- description: error.message,
3440
+ description: message,
3061
3441
  status: "destructive"
3062
3442
  });
3063
3443
  }
@@ -3068,6 +3448,9 @@ var PaymentMethodsSection = ({
3068
3448
  onSuccess: () => {
3069
3449
  notify({ title: t.cardRemoved, status: "success" });
3070
3450
  void queryClient.invalidateQueries({ queryKey });
3451
+ if (paymentQuery.refetch) {
3452
+ paymentQuery.refetch();
3453
+ }
3071
3454
  },
3072
3455
  onError: (error) => {
3073
3456
  notify({
@@ -3078,7 +3461,7 @@ var PaymentMethodsSection = ({
3078
3461
  },
3079
3462
  onSettled: () => setDeletingId(null)
3080
3463
  });
3081
- const activateMutation = useMutation({
3464
+ useMutation({
3082
3465
  mutationFn: (id) => paymentMethods.activate(id),
3083
3466
  onSuccess: () => {
3084
3467
  notify({ title: t.defaultPaymentMethodUpdated, status: "success" });
@@ -3095,8 +3478,14 @@ var PaymentMethodsSection = ({
3095
3478
  useEffect(() => {
3096
3479
  if (!isModalOpen) {
3097
3480
  createMutation.reset();
3481
+ setCreateErrorMessage(null);
3482
+ }
3483
+ }, [isModalOpen]);
3484
+ useEffect(() => {
3485
+ if (!isModalOpen && paymentQuery.refetch) {
3486
+ paymentQuery.refetch();
3098
3487
  }
3099
- }, [createMutation, isModalOpen]);
3488
+ }, [isModalOpen]);
3100
3489
  const payments = useMemo(() => paymentQuery.data?.data ?? [], [paymentQuery.data]);
3101
3490
  const loading = paymentQuery.isLoading || paymentQuery.isFetching;
3102
3491
  const buildPayload = (token, billing) => ({
@@ -3110,9 +3499,13 @@ var PaymentMethodsSection = ({
3110
3499
  zip: billing.postalCode,
3111
3500
  country: billing.country,
3112
3501
  email: billing.email,
3113
- provider: billing.provider
3502
+ provider: billing.provider,
3503
+ last_four: billing.last_four,
3504
+ card_type: billing.card_type,
3505
+ expiry_date: billing.expiry_date
3114
3506
  });
3115
3507
  const handleCardTokenize = (token, billing) => {
3508
+ setCreateErrorMessage(null);
3116
3509
  createMutation.mutate(buildPayload(token, billing));
3117
3510
  };
3118
3511
  return /* @__PURE__ */ jsxs(Card, { className: "border-0 bg-black/30 shadow-2xl backdrop-blur-xl", children: [
@@ -3131,32 +3524,19 @@ var PaymentMethodsSection = ({
3131
3524
  /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" }),
3132
3525
  " ",
3133
3526
  t.loadingCards
3134
- ] }) : payments.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-6 text-sm text-center", children: t.noPaymentMethods }) : /* @__PURE__ */ jsx("div", { className: "space-y-3", children: payments.map((method) => /* @__PURE__ */ jsxs(
3135
- "div",
3136
- {
3137
- className: "rounded-lg border bg-white/5 p-4 shadow-sm",
3138
- children: [
3139
- /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
3140
- /* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
3141
- /* @__PURE__ */ jsx("div", { className: "text-base font-medium text-white", children: formatCardLabel2(method) }),
3142
- /* @__PURE__ */ jsx(Badge, { variant: method.is_active ? "default" : "secondary", children: method.is_active ? t.active : t.inactive })
3527
+ ] }) : payments.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-6 text-sm text-center", children: t.noPaymentMethods }) : /* @__PURE__ */ jsx("div", { className: "space-y-3", children: payments.map((method) => {
3528
+ (method.subscriptions?.length ?? 0) > 0;
3529
+ expandedSubscriptions[method.id] ?? false;
3530
+ return /* @__PURE__ */ jsxs(
3531
+ "div",
3532
+ {
3533
+ className: "rounded-lg border bg-white/5 p-4 shadow-sm",
3534
+ children: [
3535
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
3536
+ /* @__PURE__ */ jsx("div", { className: "flex justify-between", children: /* @__PURE__ */ jsx("div", { className: "text-base font-medium text-white", children: formatCardLabel2(method) }) }),
3537
+ /* @__PURE__ */ jsx("div", { children: method.failure_reason && /* @__PURE__ */ jsx(Badge, { variant: "destructive", children: method.failure_reason }) })
3143
3538
  ] }),
3144
- /* @__PURE__ */ jsx("div", { children: method.failure_reason && /* @__PURE__ */ jsx(Badge, { variant: "destructive", children: method.failure_reason }) })
3145
- ] }),
3146
- /* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap gap-2", children: [
3147
- /* @__PURE__ */ jsxs(
3148
- Button,
3149
- {
3150
- variant: "outline",
3151
- disabled: method.is_active || activateMutation.isPending,
3152
- onClick: () => activateMutation.mutate(method.id),
3153
- children: [
3154
- activateMutation.isPending ? /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null,
3155
- method.is_active ? t.defaultMethod : t.makeDefault
3156
- ]
3157
- }
3158
- ),
3159
- /* @__PURE__ */ jsxs(
3539
+ /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-wrap gap-2", children: /* @__PURE__ */ jsxs(
3160
3540
  Button,
3161
3541
  {
3162
3542
  variant: "ghost",
@@ -3168,12 +3548,12 @@ var PaymentMethodsSection = ({
3168
3548
  t.remove
3169
3549
  ]
3170
3550
  }
3171
- )
3172
- ] })
3173
- ]
3174
- },
3175
- method.id
3176
- )) }) }),
3551
+ ) })
3552
+ ]
3553
+ },
3554
+ method.id
3555
+ );
3556
+ }) }) }),
3177
3557
  /* @__PURE__ */ jsx(Dialog, { open: isModalOpen, onOpenChange: setIsModalOpen, children: /* @__PURE__ */ jsxs(
3178
3558
  DialogContent,
3179
3559
  {
@@ -3191,12 +3571,13 @@ var PaymentMethodsSection = ({
3191
3571
  collectPrefix,
3192
3572
  onTokenize: handleCardTokenize,
3193
3573
  submitting: createMutation.isPending,
3574
+ translations: t,
3194
3575
  defaultValues: {
3195
3576
  provider,
3196
3577
  email: userEmail ?? "",
3197
3578
  country: defaultCountry
3198
3579
  },
3199
- externalError: createMutation.error?.message ?? null
3580
+ externalError: createErrorMessage
3200
3581
  }
3201
3582
  )
3202
3583
  ]
@@ -3204,7 +3585,388 @@ var PaymentMethodsSection = ({
3204
3585
  ) })
3205
3586
  ] });
3206
3587
  };
3207
- var Checkbox = React15.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3588
+ var notifyDefault4 = (payload) => {
3589
+ const level = payload.status === "destructive" ? "error" : "info";
3590
+ console[level === "error" ? "error" : "log"]("[payments-ui] notification", payload);
3591
+ };
3592
+ var defaultTranslations6 = {
3593
+ title: "Subscriptions",
3594
+ description: "Manage your active and recent subscriptions.",
3595
+ loading: "Loading subscriptions...",
3596
+ noSubscriptions: "No subscriptions found.",
3597
+ status: "Status",
3598
+ active: "Active",
3599
+ cancelled: "Cancelled",
3600
+ pastDue: "Past due",
3601
+ pending: "Pending",
3602
+ paymentMethodLabel: "Payment method",
3603
+ changePaymentMethod: "Change payment method",
3604
+ paymentMethodUpdated: "Payment method updated",
3605
+ paymentMethodUpdateFailed: "Unable to update payment method",
3606
+ resume: "Resume subscription",
3607
+ changePlan: "Change plan",
3608
+ changePlanPlaceholder: "Enter a new price ID",
3609
+ planChanged: "Subscription updated",
3610
+ planChangeFailed: "Unable to update subscription",
3611
+ planChangeUnavailable: "Plan changes are unavailable for this subscription.",
3612
+ resumeSuccess: "Resume requested",
3613
+ resumeFailed: "Unable to resume subscription",
3614
+ update: "Update",
3615
+ product: "Product",
3616
+ price: "Price",
3617
+ currentPeriod: "Current period",
3618
+ refresh: "Refresh",
3619
+ manage: "Manage",
3620
+ close: "Close",
3621
+ paymentMethodTab: "Payment method",
3622
+ changePlanTab: "Change plan",
3623
+ statusTab: "Details",
3624
+ cancelTab: "Cancel/Resume"
3625
+ };
3626
+ var formatCardLabel3 = (method) => {
3627
+ if (method.card) {
3628
+ const brand2 = method.card.brand ? method.card.brand.toUpperCase() : "CARD";
3629
+ const lastFour2 = method.card.last4 ? `\u2022\u2022\u2022\u2022 ${method.card.last4}` : "";
3630
+ const exp = method.card.exp_month && method.card.exp_year ? ` \u2022 ${String(method.card.exp_month).padStart(2, "0")}/${String(method.card.exp_year).slice(-2)}` : "";
3631
+ return `${brand2} ${lastFour2}${exp}`.trim();
3632
+ }
3633
+ const brand = "CARD";
3634
+ const lastFour = "";
3635
+ return `${brand} ${lastFour}`.trim();
3636
+ };
3637
+ var SubscriptionsSection = ({
3638
+ isAuthenticated = true,
3639
+ translations: customTranslations,
3640
+ onNotify,
3641
+ statusFilter,
3642
+ cancelDialogTranslations,
3643
+ onCancelled
3644
+ }) => {
3645
+ const { client, queryClient } = usePaymentContext();
3646
+ const notify = onNotify ?? notifyDefault4;
3647
+ const t = { ...defaultTranslations6, ...customTranslations };
3648
+ const cancelTranslations = cancelDialogTranslations ?? defaultTranslations3;
3649
+ const [paymentSelections, setPaymentSelections] = useState({});
3650
+ const [priceInputs, setPriceInputs] = useState({});
3651
+ const [activeSubId, setActiveSubId] = useState(null);
3652
+ const [cancelDialogOpen, setCancelDialogOpen] = useState(false);
3653
+ const [sectionsOpen, setSectionsOpen] = useState({
3654
+ status: true,
3655
+ payment: false,
3656
+ plan: false,
3657
+ cancel: false
3658
+ });
3659
+ const normalizedStatusFilter = statusFilter ?? "all";
3660
+ const subscriptionsQueryKey = ["payments-ui", "subscriptions", normalizedStatusFilter];
3661
+ const paymentMethodsQueryKey = ["payments-ui", "payment-methods"];
3662
+ const subscriptionsQuery = useQuery({
3663
+ queryKey: subscriptionsQueryKey,
3664
+ queryFn: () => client.listSubscriptions({
3665
+ status: normalizedStatusFilter,
3666
+ limit: 50
3667
+ }),
3668
+ enabled: isAuthenticated && !!client,
3669
+ staleTime: 3e4
3670
+ });
3671
+ const paymentMethodsQuery = useQuery({
3672
+ queryKey: paymentMethodsQueryKey,
3673
+ queryFn: () => client.listPaymentMethods({ limit: 50 }),
3674
+ enabled: isAuthenticated && !!client,
3675
+ staleTime: 3e4
3676
+ });
3677
+ const subscriptions = useMemo(() => subscriptionsQuery.data?.data ?? [], [subscriptionsQuery.data]);
3678
+ const paymentMethods = useMemo(() => paymentMethodsQuery.data?.data ?? [], [paymentMethodsQuery.data]);
3679
+ const activeSubscription = useMemo(
3680
+ () => subscriptions.find((sub) => sub.id === activeSubId),
3681
+ [subscriptions, activeSubId]
3682
+ );
3683
+ useEffect(() => {
3684
+ setPaymentSelections((prev) => {
3685
+ const next = {};
3686
+ subscriptions.forEach((sub) => {
3687
+ next[sub.id] = prev[sub.id] ?? "";
3688
+ });
3689
+ return next;
3690
+ });
3691
+ }, [subscriptions]);
3692
+ useEffect(() => {
3693
+ setCancelDialogOpen(false);
3694
+ }, [activeSubId]);
3695
+ const updatePaymentMethodMutation = useMutation({
3696
+ mutationFn: (payload) => client.updateSubscriptionPaymentMethod({
3697
+ subscription_id: payload.subscriptionId,
3698
+ payment_method_id: payload.paymentMethodId
3699
+ }),
3700
+ onSuccess: () => {
3701
+ notify({ title: t.paymentMethodUpdated, status: "success" });
3702
+ void queryClient.invalidateQueries({ queryKey: subscriptionsQueryKey });
3703
+ },
3704
+ onError: (error) => {
3705
+ notify({
3706
+ title: t.paymentMethodUpdateFailed,
3707
+ description: resolveErrorMessageByCode(error, {}, error.message),
3708
+ status: "destructive"
3709
+ });
3710
+ }
3711
+ });
3712
+ const resumeSubscriptionMutation = useMutation({
3713
+ mutationFn: () => client.resumeSubscription(),
3714
+ onSuccess: () => {
3715
+ notify({ title: t.resumeSuccess, status: "success" });
3716
+ void queryClient.invalidateQueries({ queryKey: subscriptionsQueryKey });
3717
+ },
3718
+ onError: (error) => {
3719
+ notify({
3720
+ title: t.resumeFailed,
3721
+ description: resolveErrorMessageByCode(error, {}, error.message),
3722
+ status: "destructive"
3723
+ });
3724
+ }
3725
+ });
3726
+ useMutation({
3727
+ mutationFn: (payload) => client.changeSubscription({ price_id: payload.priceId }),
3728
+ onSuccess: () => {
3729
+ notify({ title: t.planChanged, status: "success" });
3730
+ void queryClient.invalidateQueries({ queryKey: subscriptionsQueryKey });
3731
+ },
3732
+ onError: (error) => {
3733
+ notify({
3734
+ title: t.planChangeFailed,
3735
+ description: resolveErrorMessageByCode(error, {}, error.message),
3736
+ status: "destructive"
3737
+ });
3738
+ }
3739
+ });
3740
+ const isLoading = subscriptionsQuery.isLoading || subscriptionsQuery.isFetching;
3741
+ const isError = subscriptionsQuery.isError;
3742
+ const errorMessage = subscriptionsQuery.error instanceof Error ? subscriptionsQuery.error.message : void 0;
3743
+ useEffect(() => {
3744
+ if (subscriptionsQuery.refetch) {
3745
+ void subscriptionsQuery.refetch();
3746
+ }
3747
+ }, []);
3748
+ const formatPrice = (sub) => {
3749
+ const price = sub.price;
3750
+ if (!price) return t.price;
3751
+ const amount = (price.amount / 100).toFixed(2);
3752
+ const currency = price.currency ? price.currency?.toUpperCase() : "";
3753
+ return `${price.display_name ?? t.price} \u2014 ${currency} ${amount}`;
3754
+ };
3755
+ const renderStatusBadge = (status) => {
3756
+ const normalized = status.toLowerCase();
3757
+ const label = normalized === "active" ? t.active : normalized === "past_due" ? t.pastDue : normalized === "pending" ? t.pending : t.cancelled;
3758
+ const variant = normalized === "active" ? "default" : normalized === "past_due" ? "destructive" : normalized === "pending" ? "outline" : "secondary";
3759
+ return /* @__PURE__ */ jsx(Badge, { variant, children: label });
3760
+ };
3761
+ const handleUpdatePaymentMethod = (subscriptionId) => {
3762
+ const paymentMethodId = paymentSelections[subscriptionId];
3763
+ const currentPaymentMethodId = "pm_" + subscriptions.find((s) => s.id === subscriptionId)?.payment_method_id;
3764
+ if (!paymentMethodId) return;
3765
+ if (currentPaymentMethodId && paymentMethodId === currentPaymentMethodId) {
3766
+ notify({
3767
+ title: t.paymentMethodUpdateFailed,
3768
+ description: t.paymentMethodUpdateFailed,
3769
+ status: "destructive"
3770
+ });
3771
+ return;
3772
+ }
3773
+ updatePaymentMethodMutation.mutate({ subscriptionId, paymentMethodId });
3774
+ };
3775
+ const toggleSection = (key) => {
3776
+ setSectionsOpen((prev) => ({
3777
+ ...prev,
3778
+ [key]: !prev[key]
3779
+ }));
3780
+ };
3781
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3782
+ /* @__PURE__ */ jsxs(Card, { className: "border-0 bg-black/30 shadow-2xl backdrop-blur-xl", children: [
3783
+ /* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-col gap-4 md:flex-row md:items-center md:justify-between", children: [
3784
+ /* @__PURE__ */ jsxs("div", { children: [
3785
+ /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2", children: [
3786
+ /* @__PURE__ */ jsx(WalletCards, { className: "h-5 w-5" }),
3787
+ t.title
3788
+ ] }),
3789
+ /* @__PURE__ */ jsx(CardDescription, { children: t.description })
3790
+ ] }),
3791
+ /* @__PURE__ */ jsxs(
3792
+ Button,
3793
+ {
3794
+ variant: "ghost",
3795
+ size: "sm",
3796
+ onClick: () => subscriptionsQuery.refetch?.(),
3797
+ disabled: subscriptionsQuery.isFetching,
3798
+ children: [
3799
+ /* @__PURE__ */ jsx(RefreshCw, { className: "mr-2 h-4 w-4" }),
3800
+ " ",
3801
+ subscriptionsQuery.isFetching ? t.loading : t.refresh
3802
+ ]
3803
+ }
3804
+ )
3805
+ ] }),
3806
+ /* @__PURE__ */ jsx(CardContent, { className: "space-y-4", children: isLoading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center py-10 text-white/60", children: [
3807
+ /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" }),
3808
+ " ",
3809
+ t.loading
3810
+ ] }) : isError ? /* @__PURE__ */ jsx("div", { className: "rounded-md border border-red-500/30 bg-red-500/10 p-4 text-sm text-red-100", children: errorMessage ?? "Unable to load subscriptions." }) : subscriptions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-6 text-sm text-center", children: t.noSubscriptions }) : /* @__PURE__ */ jsx("div", { className: "space-y-3", children: subscriptions.map((subscription) => {
3811
+ return /* @__PURE__ */ jsx(
3812
+ "div",
3813
+ {
3814
+ className: "rounded-lg border bg-white/5 p-4 shadow-sm",
3815
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
3816
+ /* @__PURE__ */ jsxs("div", { children: [
3817
+ /* @__PURE__ */ jsx("div", { className: "text-base font-semibold text-white", children: subscription.product?.display_name ?? t.product }),
3818
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-white/80", children: formatPrice(subscription) })
3819
+ ] }),
3820
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3821
+ renderStatusBadge(subscription.status),
3822
+ /* @__PURE__ */ jsx(
3823
+ Button,
3824
+ {
3825
+ variant: "default",
3826
+ size: "sm",
3827
+ className: "rounded-full bg-foreground/10 text-muted-foreground hover:bg-foreground/20 hover:text-foreground",
3828
+ onClick: () => setActiveSubId(subscription.id),
3829
+ children: t.update
3830
+ }
3831
+ )
3832
+ ] })
3833
+ ] })
3834
+ },
3835
+ subscription.id
3836
+ );
3837
+ }) }) })
3838
+ ] }),
3839
+ /* @__PURE__ */ jsx(Dialog, { open: !!activeSubscription, onOpenChange: (open) => setActiveSubId(open ? activeSubId : null), children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-2xl min-h-[300px] border border-white/20 bg-background-regular p-6 text-foreground", children: [
3840
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
3841
+ /* @__PURE__ */ jsx(DialogTitle, { className: "flex items-center gap-2", children: activeSubscription?.product?.display_name ?? t.product }),
3842
+ /* @__PURE__ */ jsx(DialogDescription, { className: "text-white/70", children: activeSubscription ? formatPrice(activeSubscription) : null })
3843
+ ] }),
3844
+ activeSubscription && /* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-3", children: [
3845
+ /* @__PURE__ */ jsxs("div", { className: "overflow-hidden rounded-lg border border-white/15 bg-white/5", children: [
3846
+ /* @__PURE__ */ jsxs(
3847
+ "button",
3848
+ {
3849
+ type: "button",
3850
+ className: "flex w-full items-center justify-between px-4 py-3 text-left text-white hover:bg-white/10",
3851
+ onClick: () => toggleSection("status"),
3852
+ children: [
3853
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: t.statusTab }),
3854
+ /* @__PURE__ */ jsx(
3855
+ ChevronDown,
3856
+ {
3857
+ className: `h-4 w-4 transition-transform ${sectionsOpen.status ? "rotate-180" : ""}`
3858
+ }
3859
+ )
3860
+ ]
3861
+ }
3862
+ ),
3863
+ sectionsOpen.status ? /* @__PURE__ */ jsxs("div", { className: "space-y-3 border-t border-white/10 px-4 py-3", children: [
3864
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between rounded-lg border border-white/10 bg-white/5 p-3", children: [
3865
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-white/80", children: t.status }),
3866
+ renderStatusBadge(activeSubscription.status)
3867
+ ] }),
3868
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-white/60", children: [
3869
+ t.currentPeriod,
3870
+ ": ",
3871
+ activeSubscription.started_at ? new Date(activeSubscription.started_at).toLocaleDateString() : "\u2014",
3872
+ " \u2192",
3873
+ " ",
3874
+ activeSubscription.current_period_ends_at ?? "\u2014"
3875
+ ] }),
3876
+ activeSubscription.status.toLowerCase() === "cancelled" ? /* @__PURE__ */ jsxs(
3877
+ Button,
3878
+ {
3879
+ variant: "secondary",
3880
+ onClick: () => resumeSubscriptionMutation.mutate(),
3881
+ disabled: resumeSubscriptionMutation.isPending,
3882
+ className: "rounded-full px-4",
3883
+ children: [
3884
+ resumeSubscriptionMutation.isPending ? /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null,
3885
+ t.resume
3886
+ ]
3887
+ }
3888
+ ) : null
3889
+ ] }) : null
3890
+ ] }),
3891
+ /* @__PURE__ */ jsxs("div", { className: "overflow-hidden rounded-lg border border-white/15 bg-white/5", children: [
3892
+ /* @__PURE__ */ jsxs(
3893
+ "button",
3894
+ {
3895
+ type: "button",
3896
+ className: "flex w-full items-center justify-between px-4 py-3 text-left text-white hover:bg-white/10",
3897
+ onClick: () => toggleSection("payment"),
3898
+ children: [
3899
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: t.paymentMethodTab }),
3900
+ /* @__PURE__ */ jsx(
3901
+ ChevronDown,
3902
+ {
3903
+ className: `h-4 w-4 transition-transform ${sectionsOpen.payment ? "rotate-180" : ""}`
3904
+ }
3905
+ )
3906
+ ]
3907
+ }
3908
+ ),
3909
+ sectionsOpen.payment ? /* @__PURE__ */ jsxs("div", { className: "space-y-2 border-t border-white/10 px-4 py-3", children: [
3910
+ /* @__PURE__ */ jsx("div", { className: "text-xs uppercase tracking-wide text-white/60", children: t.paymentMethodLabel }),
3911
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 md:flex-row md:items-center md:gap-2", children: [
3912
+ /* @__PURE__ */ jsxs(
3913
+ Select,
3914
+ {
3915
+ value: paymentSelections[activeSubscription.id] || "pm_" + activeSubscription.payment_method_id || "",
3916
+ onValueChange: (value) => setPaymentSelections((prev) => ({ ...prev, [activeSubscription.id]: value })),
3917
+ disabled: paymentMethodsQuery.isLoading || paymentMethods.length === 0,
3918
+ children: [
3919
+ /* @__PURE__ */ jsx(SelectTrigger, { className: "w-full md:w-64 text-white", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: paymentMethodsQuery.isLoading ? t.loading : "" }) }),
3920
+ /* @__PURE__ */ jsx(SelectContent, { children: paymentMethods.filter((method) => method.id !== activeSubscription.payment_method_id).map((method) => /* @__PURE__ */ jsx(SelectItem, { value: method.id, children: formatCardLabel3(method) }, method.id)) })
3921
+ ]
3922
+ }
3923
+ ),
3924
+ /* @__PURE__ */ jsxs(
3925
+ Button,
3926
+ {
3927
+ size: "sm",
3928
+ onClick: () => handleUpdatePaymentMethod(activeSubscription.id),
3929
+ disabled: updatePaymentMethodMutation.isPending || !paymentSelections[activeSubscription.id] || paymentSelections[activeSubscription.id] === activeSubscription.payment_method_id,
3930
+ className: "border-0 bg-green-bg text-white hover:bg-green-bg/80 disabled:opacity-50",
3931
+ children: [
3932
+ updatePaymentMethodMutation.isPending ? /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null,
3933
+ t.update
3934
+ ]
3935
+ }
3936
+ )
3937
+ ] })
3938
+ ] }) : null
3939
+ ] })
3940
+ ] }),
3941
+ /* @__PURE__ */ jsxs(DialogFooter, { className: "flex flex-wrap gap-2", children: [
3942
+ /* @__PURE__ */ jsx(
3943
+ CancelMembershipDialog,
3944
+ {
3945
+ translations: cancelTranslations,
3946
+ onNotify,
3947
+ open: cancelDialogOpen,
3948
+ onOpenChange: (openState) => setCancelDialogOpen(openState),
3949
+ onCancelled: () => {
3950
+ void queryClient.invalidateQueries({ queryKey: subscriptionsQueryKey });
3951
+ onCancelled?.();
3952
+ setActiveSubId(null);
3953
+ }
3954
+ }
3955
+ ),
3956
+ /* @__PURE__ */ jsx(
3957
+ Button,
3958
+ {
3959
+ variant: "secondary",
3960
+ onClick: () => setActiveSubId(null),
3961
+ className: "border-white/20 bg-transparent text-foreground hover:bg-foreground/10 hover:text-foreground",
3962
+ children: t.close
3963
+ }
3964
+ )
3965
+ ] })
3966
+ ] }) })
3967
+ ] });
3968
+ };
3969
+ var Checkbox = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3208
3970
  CheckboxPrimitive.Root,
3209
3971
  {
3210
3972
  ref,
@@ -3719,6 +4481,6 @@ var usePaymentStatus = (options = {}) => {
3719
4481
  };
3720
4482
  };
3721
4483
 
3722
- export { BillingHistory, CancelMembershipDialog, CardDetailsForm, ClientApiError, PaymentContext, PaymentExperience, PaymentMethodsSection, PaymentProvider, PaymentsDialogProvider, SolanaPaymentSelector, SolanaPaymentView, StoredPaymentMethods, SubscriptionCheckoutModal, SubscriptionSuccessDialog, WalletDialog, WalletModal, createClient, usePaymentContext, usePaymentDialogs, usePaymentMethods, usePaymentNotifications, usePaymentStatus, useSolanaQrPayment, useSubscriptionActions, useSupportedTokens, useTokenBalance };
4484
+ export { BillingHistory, CancelMembershipDialog, CardDetailsForm, ClientApiError, PaymentContext, PaymentExperience, PaymentMethodsSection, PaymentProvider, PaymentsDialogProvider, SolanaPaymentSelector, SolanaPaymentView, StoredPaymentMethods, SubscriptionCheckoutModal, SubscriptionSuccessDialog, SubscriptionsSection, WalletDialog, WalletModal, createClient, defaultCardDetailsFormTranslations, defaultPaymentExperienceTranslations, defaultTranslations3 as defaultTranslations, usePaymentContext, usePaymentDialogs, usePaymentMethods, usePaymentNotifications, usePaymentStatus, useSolanaQrPayment, useSubscriptionActions, useSupportedTokens, useTokenBalance };
3723
4485
  //# sourceMappingURL=index.js.map
3724
4486
  //# sourceMappingURL=index.js.map