@doujins/payments-ui 0.1.16 → 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
@@ -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, Pencil, 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
@@ -676,6 +720,28 @@ var defaultBillingDetails = {
676
720
  email: "",
677
721
  provider: "mobius"
678
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
+ };
679
745
  var buildSelector = (prefix, field) => `#${prefix}-${field}`;
680
746
  var CardDetailsForm = ({
681
747
  visible,
@@ -687,8 +753,10 @@ var CardDetailsForm = ({
687
753
  collectPrefix = "card-form",
688
754
  className,
689
755
  onBillingChange,
690
- submitDisabled = false
756
+ submitDisabled = false,
757
+ translations
691
758
  }) => {
759
+ const t = { ...defaultCardDetailsFormTranslations, ...translations };
692
760
  const { config } = usePaymentContext();
693
761
  const defaultValuesKey = useMemo(() => JSON.stringify(defaultValues ?? {}), [defaultValues]);
694
762
  const mergedDefaults = useMemo(
@@ -729,8 +797,11 @@ var CardDetailsForm = ({
729
797
  if (!visible) {
730
798
  setLocalError(null);
731
799
  setIsTokenizing(false);
800
+ if (typeof window !== "undefined" && window.__doujinsCollectConfigured && collectPrefix) {
801
+ window.__doujinsCollectConfigured[collectPrefix] = false;
802
+ }
732
803
  }
733
- }, [visible]);
804
+ }, [visible, collectPrefix]);
734
805
  useEffect(() => {
735
806
  if (!visible) return;
736
807
  setFirstName(mergedDefaults.firstName);
@@ -792,6 +863,8 @@ var CardDetailsForm = ({
792
863
  setLocalError("Payment tokenization failed. Please try again.");
793
864
  return;
794
865
  }
866
+ let rawExp = response.card?.exp;
867
+ let formattedExp = rawExp && rawExp.length === 4 ? `${rawExp.slice(0, 2)}/${rawExp.slice(2)}` : rawExp;
795
868
  const billing = {
796
869
  firstName,
797
870
  lastName,
@@ -801,7 +874,10 @@ var CardDetailsForm = ({
801
874
  postalCode,
802
875
  country,
803
876
  email,
804
- 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
805
881
  };
806
882
  onTokenize(response.token, billing);
807
883
  };
@@ -842,7 +918,7 @@ var CardDetailsForm = ({
842
918
  ]);
843
919
  const validate = () => {
844
920
  if (!firstName.trim() || !lastName.trim() || !address1.trim() || !city.trim() || !postalCode.trim() || !country.trim() || !email.trim()) {
845
- setLocalError("Please complete all required billing fields.");
921
+ setLocalError(t.errorRequiredFields);
846
922
  return false;
847
923
  }
848
924
  setLocalError(null);
@@ -852,7 +928,7 @@ var CardDetailsForm = ({
852
928
  event.preventDefault();
853
929
  if (!validate()) return;
854
930
  if (!window.CollectJS) {
855
- setLocalError("Payment form is not ready. Please try again later.");
931
+ setLocalError(t.errorFormNotReady);
856
932
  return;
857
933
  }
858
934
  setIsTokenizing(true);
@@ -872,7 +948,7 @@ var CardDetailsForm = ({
872
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 }),
873
949
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 md:flex-row", children: [
874
950
  /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-2", children: [
875
- /* @__PURE__ */ jsx(Label, { htmlFor: "firstName", children: "First name" }),
951
+ /* @__PURE__ */ jsx(Label, { htmlFor: "firstName", children: t.firstName }),
876
952
  /* @__PURE__ */ jsx(
877
953
  Input,
878
954
  {
@@ -884,7 +960,7 @@ var CardDetailsForm = ({
884
960
  )
885
961
  ] }),
886
962
  /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-2", children: [
887
- /* @__PURE__ */ jsx(Label, { htmlFor: "lastName", children: "Last name" }),
963
+ /* @__PURE__ */ jsx(Label, { htmlFor: "lastName", children: t.lastName }),
888
964
  /* @__PURE__ */ jsx(
889
965
  Input,
890
966
  {
@@ -897,7 +973,7 @@ var CardDetailsForm = ({
897
973
  ] })
898
974
  ] }),
899
975
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
900
- /* @__PURE__ */ jsx(Label, { htmlFor: "email", children: "Email" }),
976
+ /* @__PURE__ */ jsx(Label, { htmlFor: "email", children: t.email }),
901
977
  showEmailInput ? /* @__PURE__ */ jsxs("div", { className: "flex gap-2 items-center", children: [
902
978
  /* @__PURE__ */ jsx(
903
979
  Input,
@@ -921,7 +997,7 @@ var CardDetailsForm = ({
921
997
  setEmail(mergedDefaults.email ?? "");
922
998
  },
923
999
  className: "px-3 text-xs text-muted-foreground hover:text-foreground",
924
- children: "Cancel"
1000
+ children: t.cancel
925
1001
  }
926
1002
  )
927
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: [
@@ -932,14 +1008,14 @@ var CardDetailsForm = ({
932
1008
  type: "button",
933
1009
  onClick: () => setIsEditingEmail(true),
934
1010
  className: "text-muted-foreground hover:text-foreground transition-colors",
935
- "aria-label": "Edit email",
1011
+ "aria-label": t.editEmail,
936
1012
  children: /* @__PURE__ */ jsx(Pencil, { className: "h-4 w-4" })
937
1013
  }
938
1014
  )
939
1015
  ] })
940
1016
  ] }),
941
1017
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
942
- /* @__PURE__ */ jsx(Label, { htmlFor: "address1", children: "Address" }),
1018
+ /* @__PURE__ */ jsx(Label, { htmlFor: "address1", children: t.address }),
943
1019
  /* @__PURE__ */ jsx(
944
1020
  Input,
945
1021
  {
@@ -952,7 +1028,7 @@ var CardDetailsForm = ({
952
1028
  ] }),
953
1029
  /* @__PURE__ */ jsxs("div", { className: "grid gap-2 md:grid-cols-2", children: [
954
1030
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
955
- /* @__PURE__ */ jsx(Label, { htmlFor: "city", children: "City" }),
1031
+ /* @__PURE__ */ jsx(Label, { htmlFor: "city", children: t.city }),
956
1032
  /* @__PURE__ */ jsx(
957
1033
  Input,
958
1034
  {
@@ -964,7 +1040,7 @@ var CardDetailsForm = ({
964
1040
  )
965
1041
  ] }),
966
1042
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
967
- /* @__PURE__ */ jsx(Label, { htmlFor: "state", children: "State / Region" }),
1043
+ /* @__PURE__ */ jsx(Label, { htmlFor: "state", children: t.state }),
968
1044
  /* @__PURE__ */ jsx(
969
1045
  Input,
970
1046
  {
@@ -977,7 +1053,7 @@ var CardDetailsForm = ({
977
1053
  ] }),
978
1054
  /* @__PURE__ */ jsxs("div", { className: "grid gap-2 md:grid-cols-2", children: [
979
1055
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
980
- /* @__PURE__ */ jsx(Label, { htmlFor: "postal", children: "Postal code" }),
1056
+ /* @__PURE__ */ jsx(Label, { htmlFor: "postal", children: t.postalCode }),
981
1057
  /* @__PURE__ */ jsx(
982
1058
  Input,
983
1059
  {
@@ -989,24 +1065,24 @@ var CardDetailsForm = ({
989
1065
  )
990
1066
  ] }),
991
1067
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
992
- /* @__PURE__ */ jsx(Label, { children: "Country" }),
1068
+ /* @__PURE__ */ jsx(Label, { children: t.country }),
993
1069
  /* @__PURE__ */ jsxs(Select, { value: country, onValueChange: setCountry, children: [
994
- /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select a country" }) }),
1070
+ /* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, { placeholder: t.selectCountry }) }),
995
1071
  /* @__PURE__ */ jsx(SelectContent, { children: countries.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.code, children: option.name }, option.code)) })
996
1072
  ] })
997
1073
  ] })
998
1074
  ] }),
999
1075
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1000
- /* @__PURE__ */ jsx(Label, { children: "Card number" }),
1076
+ /* @__PURE__ */ jsx(Label, { children: t.cardNumber }),
1001
1077
  /* @__PURE__ */ jsx("div", { id: buildSelector(collectPrefix, "ccnumber").slice(1), className: collectFieldClass })
1002
1078
  ] }),
1003
1079
  /* @__PURE__ */ jsxs("div", { className: "grid gap-2 md:grid-cols-2", children: [
1004
1080
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1005
- /* @__PURE__ */ jsx(Label, { children: "Expiry" }),
1081
+ /* @__PURE__ */ jsx(Label, { children: t.expiry }),
1006
1082
  /* @__PURE__ */ jsx("div", { id: buildSelector(collectPrefix, "ccexp").slice(1), className: collectFieldClass })
1007
1083
  ] }),
1008
1084
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1009
- /* @__PURE__ */ jsx(Label, { children: "CVV" }),
1085
+ /* @__PURE__ */ jsx(Label, { children: t.cvv }),
1010
1086
  /* @__PURE__ */ jsx("div", { id: buildSelector(collectPrefix, "cvv").slice(1), className: collectFieldClass })
1011
1087
  ] })
1012
1088
  ] }),
@@ -1018,11 +1094,12 @@ var CardDetailsForm = ({
1018
1094
  disabled: submitting || submitDisabled || isTokenizing,
1019
1095
  children: submitting || isTokenizing ? /* @__PURE__ */ jsxs(Fragment, { children: [
1020
1096
  /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
1021
- " Processing\u2026"
1022
- ] }) : submitLabel
1097
+ " ",
1098
+ t.processing
1099
+ ] }) : submitLabel || t.submit
1023
1100
  }
1024
1101
  ),
1025
- /* @__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 })
1026
1103
  ]
1027
1104
  }
1028
1105
  );
@@ -1059,7 +1136,9 @@ var usePaymentMethods = () => {
1059
1136
  zip: billing.postalCode,
1060
1137
  country: billing.country,
1061
1138
  email: billing.email,
1062
- provider: billing.provider
1139
+ provider: billing.provider,
1140
+ last_four: billing.last_four,
1141
+ card_type: billing.card_type
1063
1142
  };
1064
1143
  return client.createPaymentMethod(payload);
1065
1144
  },
@@ -1128,21 +1207,34 @@ var ScrollBar = React4.forwardRef(({ className, orientation = "vertical", ...pro
1128
1207
  }
1129
1208
  ));
1130
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
+ };
1131
1216
  var formatCardLabel = (method) => {
1132
- const brand = method.card_type ? method.card_type.toUpperCase() : "CARD";
1133
- const lastFour = method.last_four ? `\u2022\u2022\u2022\u2022 ${method.last_four}` : "";
1134
- 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";
1135
1224
  };
1136
1225
  var StoredPaymentMethods = ({
1137
1226
  selectedMethodId,
1138
- onMethodSelect
1227
+ onMethodSelect,
1228
+ translations
1139
1229
  }) => {
1140
1230
  const { listQuery } = usePaymentMethods();
1141
1231
  const payments = useMemo(() => listQuery.data?.data ?? [], [listQuery.data]);
1232
+ const t = { ...defaultTranslations, ...translations };
1142
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: [
1143
1234
  /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
1144
- " Loading cards..."
1145
- ] }) : 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) => {
1146
1238
  const isSelected = selectedMethodId === method.id;
1147
1239
  return /* @__PURE__ */ jsxs(
1148
1240
  "div",
@@ -1165,7 +1257,7 @@ var StoredPaymentMethods = ({
1165
1257
  disabled: isSelected,
1166
1258
  onClick: () => onMethodSelect(method),
1167
1259
  className: clsx2("px-3", { "bg-muted/90": !isSelected, "bg-inherit": isSelected }),
1168
- children: isSelected ? "Selected" : "Use card"
1260
+ children: isSelected ? t.selectedLabel : t.useCardLabel
1169
1261
  }
1170
1262
  )
1171
1263
  ] })
@@ -1175,6 +1267,26 @@ var StoredPaymentMethods = ({
1175
1267
  );
1176
1268
  }) }) }) });
1177
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
+ };
1178
1290
  function Tabs({
1179
1291
  className,
1180
1292
  ...props
@@ -1970,20 +2082,45 @@ var SolanaPaymentView = ({
1970
2082
  renderBody()
1971
2083
  ] });
1972
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
+ };
1973
2103
  var PaymentExperience = ({
1974
2104
  priceId,
1975
2105
  usdAmount,
1976
2106
  onNewCardPayment,
1977
2107
  onSavedMethodPayment,
2108
+ onCcbillPayment,
1978
2109
  enableNewCard = true,
1979
2110
  enableStoredMethods = true,
1980
2111
  enableSolanaPay = true,
1981
2112
  onSolanaSuccess,
1982
2113
  onSolanaError,
1983
- initialMode = "cards"
2114
+ initialMode = "cards",
2115
+ translations
1984
2116
  }) => {
2117
+ const t = {
2118
+ ...defaultPaymentExperienceTranslations,
2119
+ ...translations
2120
+ };
1985
2121
  const showNewCard = enableNewCard && Boolean(onNewCardPayment);
1986
2122
  const showStored = enableStoredMethods && Boolean(onSavedMethodPayment);
2123
+ const showCcbill = showNewCard && Boolean(onCcbillPayment);
1987
2124
  const defaultTab = showStored ? "saved" : "new";
1988
2125
  const [activeTab, setActiveTab] = useState(defaultTab);
1989
2126
  const [mode, setMode] = useState(
@@ -1994,6 +2131,9 @@ var PaymentExperience = ({
1994
2131
  const [savedError, setSavedError] = useState(null);
1995
2132
  const [newCardStatus, setNewCardStatus] = useState("idle");
1996
2133
  const [newCardError, setNewCardError] = useState(null);
2134
+ const [billingDetails, setBillingDetails] = useState(null);
2135
+ const [ccbillStatus, setCcbillStatus] = useState("idle");
2136
+ const [ccbillError, setCcbillError] = useState(null);
1997
2137
  const { notifyStatus, notifyError } = usePaymentNotifications();
1998
2138
  useEffect(() => {
1999
2139
  setActiveTab(showStored ? "saved" : "new");
@@ -2009,6 +2149,24 @@ var PaymentExperience = ({
2009
2149
  setMode("cards");
2010
2150
  }
2011
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
+ }, []);
2012
2170
  const handleMethodSelect = useCallback((method) => {
2013
2171
  setSelectedMethodId(method.id);
2014
2172
  setSavedStatus("idle");
@@ -2027,13 +2185,17 @@ var PaymentExperience = ({
2027
2185
  setSavedStatus("success");
2028
2186
  notifyStatus("success", { source: "saved-payment" });
2029
2187
  } catch (error) {
2030
- 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
+ );
2031
2193
  setSavedStatus("error");
2032
2194
  setSavedError(message);
2033
2195
  notifyStatus("error", { source: "saved-payment" });
2034
2196
  notifyError(message);
2035
2197
  }
2036
- }, [notifyError, notifyStatus, onSavedMethodPayment, selectedMethodId, usdAmount]);
2198
+ }, [notifyError, notifyStatus, onSavedMethodPayment, selectedMethodId, t, usdAmount]);
2037
2199
  const handleNewCardTokenize = useCallback(
2038
2200
  async (token, billing) => {
2039
2201
  if (!onNewCardPayment) return;
@@ -2045,15 +2207,56 @@ var PaymentExperience = ({
2045
2207
  setNewCardStatus("success");
2046
2208
  notifyStatus("success", { source: "new-card" });
2047
2209
  } catch (error) {
2048
- 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
+ );
2049
2215
  setNewCardStatus("error");
2050
2216
  setNewCardError(message);
2051
2217
  notifyStatus("error", { source: "new-card" });
2052
2218
  notifyError(message);
2053
2219
  }
2054
2220
  },
2055
- [notifyError, notifyStatus, onNewCardPayment]
2221
+ [notifyError, notifyStatus, onNewCardPayment, t]
2056
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
+ ]);
2057
2260
  useCallback(() => {
2058
2261
  if (!enableSolanaPay) return;
2059
2262
  setMode("solana");
@@ -2076,14 +2279,20 @@ var PaymentExperience = ({
2076
2279
  );
2077
2280
  const renderSavedTab = () => {
2078
2281
  if (!showStored) {
2079
- 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 });
2080
2283
  }
2081
2284
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
2082
2285
  /* @__PURE__ */ jsx(
2083
2286
  StoredPaymentMethods,
2084
2287
  {
2085
2288
  selectedMethodId,
2086
- onMethodSelect: handleMethodSelect
2289
+ onMethodSelect: handleMethodSelect,
2290
+ translations: {
2291
+ loadingCards: t.storedLoadingCards,
2292
+ noSavedMethods: t.storedNoSavedMethods,
2293
+ selectedLabel: t.storedSelectedLabel,
2294
+ useCardLabel: t.storedUseCardLabel
2295
+ }
2087
2296
  }
2088
2297
  ),
2089
2298
  /* @__PURE__ */ jsx(
@@ -2092,7 +2301,7 @@ var PaymentExperience = ({
2092
2301
  className: "w-full",
2093
2302
  disabled: !selectedMethodId || savedStatus === "processing",
2094
2303
  onClick: handleSavedPayment,
2095
- children: savedStatus === "processing" ? "Processing..." : "Pay with selected card"
2304
+ children: savedStatus === "processing" ? t.processingSavedCard : t.payWithSavedCard
2096
2305
  }
2097
2306
  ),
2098
2307
  savedError && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: savedError })
@@ -2100,29 +2309,50 @@ var PaymentExperience = ({
2100
2309
  };
2101
2310
  const renderNewTab = () => {
2102
2311
  if (!showNewCard) {
2103
- 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 });
2104
2313
  }
2105
2314
  return /* @__PURE__ */ jsx(
2106
2315
  CardDetailsForm,
2107
2316
  {
2108
2317
  visible: true,
2109
- submitLabel: "Pay now",
2318
+ submitLabel: t.payNow,
2110
2319
  externalError: newCardError,
2111
2320
  onTokenize: handleNewCardTokenize,
2112
- submitting: newCardStatus === "processing"
2321
+ submitting: newCardStatus === "processing",
2322
+ onBillingChange: handleBillingChange,
2323
+ translations: t
2113
2324
  }
2114
2325
  );
2115
2326
  };
2116
2327
  const renderCardExperience = () => /* @__PURE__ */ jsxs(Tabs, { value: activeTab, onValueChange: setActiveTab, children: [
2117
2328
  /* @__PURE__ */ jsxs(TabsList, { className: "w-full rounded-md mb-4", children: [
2118
- /* @__PURE__ */ jsx(TabsTrigger, { className: "cursor-pointer", value: "saved", children: "Use saved card" }),
2119
- /* @__PURE__ */ jsx(TabsTrigger, { className: "cursor-pointer", 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 })
2120
2331
  ] }),
2121
2332
  /* @__PURE__ */ jsx(TabsContent, { value: "saved", children: renderSavedTab() }),
2122
2333
  /* @__PURE__ */ jsx(TabsContent, { value: "new", children: renderNewTab() })
2123
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
+ };
2124
2351
  return /* @__PURE__ */ jsxs("div", { className: "space-y-6 pt-4", children: [
2125
- mode === "cards" && renderCardExperience(),
2352
+ mode === "cards" && /* @__PURE__ */ jsxs(Fragment, { children: [
2353
+ renderCardExperience(),
2354
+ renderCcbillAction()
2355
+ ] }),
2126
2356
  mode === "solana" && enableSolanaPay && /* @__PURE__ */ jsx(
2127
2357
  SolanaPaymentView,
2128
2358
  {
@@ -2174,17 +2404,17 @@ var useSubscriptionActions = () => {
2174
2404
  const subscribeWithCard = useCallback(
2175
2405
  async ({
2176
2406
  priceId,
2177
- processor = "nmi",
2407
+ processor = "mobius",
2178
2408
  provider,
2179
2409
  paymentToken,
2180
2410
  billing,
2181
2411
  idempotencyKey
2182
2412
  }) => {
2183
- const payload = {
2184
- 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 = {
2185
2417
  processor,
2186
- provider,
2187
- payment_token: paymentToken,
2188
2418
  email: billing.email,
2189
2419
  first_name: billing.firstName,
2190
2420
  last_name: billing.lastName,
@@ -2194,6 +2424,17 @@ var useSubscriptionActions = () => {
2194
2424
  zip: billing.postalCode,
2195
2425
  country: billing.country
2196
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
+ };
2197
2438
  return client.checkout(payload, idempotencyKey);
2198
2439
  },
2199
2440
  [client]
@@ -2201,7 +2442,7 @@ var useSubscriptionActions = () => {
2201
2442
  const subscribeWithSavedMethod = useCallback(
2202
2443
  async ({
2203
2444
  priceId,
2204
- processor = "nmi",
2445
+ processor = "mobius",
2205
2446
  provider,
2206
2447
  paymentMethodId,
2207
2448
  email,
@@ -2209,10 +2450,12 @@ var useSubscriptionActions = () => {
2209
2450
  }) => {
2210
2451
  const payload = {
2211
2452
  price_id: ensurePrice(priceId),
2212
- processor,
2213
2453
  provider,
2214
- payment_method_id: paymentMethodId,
2215
- email
2454
+ payment: {
2455
+ processor,
2456
+ payment_method_id: paymentMethodId,
2457
+ email
2458
+ }
2216
2459
  };
2217
2460
  return client.checkout(payload, idempotencyKey);
2218
2461
  },
@@ -2231,12 +2474,14 @@ var useSubscriptionActions = () => {
2231
2474
  }) => {
2232
2475
  const payload = {
2233
2476
  price_id: ensurePrice(priceId),
2234
- processor,
2235
- email,
2236
- first_name: firstName,
2237
- last_name: lastName,
2238
- zip: zipCode,
2239
- country
2477
+ payment: {
2478
+ processor,
2479
+ email,
2480
+ first_name: firstName,
2481
+ last_name: lastName,
2482
+ zip: zipCode,
2483
+ country
2484
+ }
2240
2485
  };
2241
2486
  return client.checkout(payload, idempotencyKey);
2242
2487
  },
@@ -2248,6 +2493,11 @@ var useSubscriptionActions = () => {
2248
2493
  subscribeWithCCBill
2249
2494
  };
2250
2495
  };
2496
+ var defaultTranslations2 = {
2497
+ ...defaultPaymentExperienceTranslations,
2498
+ title: "Checkout",
2499
+ selectPlanMessage: "Select a subscription plan to continue."
2500
+ };
2251
2501
  var SubscriptionCheckoutModal = ({
2252
2502
  open,
2253
2503
  onOpenChange,
@@ -2262,11 +2512,16 @@ var SubscriptionCheckoutModal = ({
2262
2512
  enableSolanaPay = true,
2263
2513
  onSolanaSuccess,
2264
2514
  onSolanaError,
2265
- initialMode = "cards"
2515
+ initialMode = "cards",
2516
+ translations
2266
2517
  }) => {
2267
2518
  const [showSuccess, setShowSuccess] = useState(false);
2268
2519
  const [idempotencyKey, setIdempotencyKey] = useState(() => crypto.randomUUID());
2269
2520
  const { subscribeWithCard, subscribeWithSavedMethod } = useSubscriptionActions();
2521
+ const t = {
2522
+ ...defaultTranslations2,
2523
+ ...translations
2524
+ };
2270
2525
  useEffect(() => {
2271
2526
  if (open) {
2272
2527
  setIdempotencyKey(crypto.randomUUID());
@@ -2290,13 +2545,31 @@ var SubscriptionCheckoutModal = ({
2290
2545
  console.debug("[payments-ui] subscription success", result);
2291
2546
  }
2292
2547
  };
2293
- const assertCheckoutSuccess = (status, message) => {
2294
- if (status === "blocked") {
2295
- 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.");
2551
+ }
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;
2562
+ }
2563
+ if (response.payment?.redirect_url) {
2564
+ if (typeof window !== "undefined") {
2565
+ window.location.assign(response.payment.redirect_url);
2566
+ }
2567
+ return;
2296
2568
  }
2297
- if (status === "redirect_required") {
2298
- throw new Error(message || "Additional action required in an alternate flow.");
2569
+ if (nextAction && nextAction.type !== "none") {
2570
+ throw new Error(response.message || "Unsupported checkout action.");
2299
2571
  }
2572
+ notifySuccess();
2300
2573
  };
2301
2574
  const handleNewCardPayment = async ({ token, billing }) => {
2302
2575
  const response = await subscribeWithCard({
@@ -2306,8 +2579,7 @@ var SubscriptionCheckoutModal = ({
2306
2579
  paymentToken: token,
2307
2580
  priceId: ensurePrice()
2308
2581
  });
2309
- assertCheckoutSuccess(response.status, response.message);
2310
- notifySuccess();
2582
+ handleCheckoutResponse(response);
2311
2583
  };
2312
2584
  const handleSavedMethodPayment = async ({ paymentMethodId }) => {
2313
2585
  const response = await subscribeWithSavedMethod({
@@ -2317,8 +2589,16 @@ var SubscriptionCheckoutModal = ({
2317
2589
  email: userEmail ?? "",
2318
2590
  idempotencyKey
2319
2591
  });
2320
- assertCheckoutSuccess(response.status, response.message);
2321
- 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);
2322
2602
  };
2323
2603
  const solanaSuccess = (result) => {
2324
2604
  notifySuccess(result);
@@ -2334,11 +2614,11 @@ var SubscriptionCheckoutModal = ({
2334
2614
  {
2335
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",
2336
2616
  children: [
2337
- /* @__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 }) }),
2338
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: [
2339
2619
  /* @__PURE__ */ jsx(AlertCircle, { className: "h-4 w-4" }),
2340
2620
  " ",
2341
- /* @__PURE__ */ jsx("span", { children: "Select a subscription plan to continue." })
2621
+ /* @__PURE__ */ jsx("span", { children: t.selectPlanMessage })
2342
2622
  ] }) : /* @__PURE__ */ jsx(
2343
2623
  PaymentExperience,
2344
2624
  {
@@ -2351,7 +2631,9 @@ var SubscriptionCheckoutModal = ({
2351
2631
  enableStoredMethods: Boolean(priceId),
2352
2632
  enableSolanaPay: enableSolanaPay && Boolean(priceId),
2353
2633
  onNewCardPayment: priceId ? handleNewCardPayment : void 0,
2354
- onSavedMethodPayment: priceId ? handleSavedMethodPayment : void 0
2634
+ onSavedMethodPayment: priceId ? handleSavedMethodPayment : void 0,
2635
+ onCcbillPayment: priceId ? handleCcbillPayment : void 0,
2636
+ translations: t
2355
2637
  }
2356
2638
  ) })
2357
2639
  ]
@@ -2361,7 +2643,9 @@ var SubscriptionCheckoutModal = ({
2361
2643
  SubscriptionSuccessDialog,
2362
2644
  {
2363
2645
  open: showSuccess,
2364
- onClose: () => setShowSuccess(false),
2646
+ onClose: () => {
2647
+ setShowSuccess(false);
2648
+ },
2365
2649
  planName,
2366
2650
  amountLabel: amountLabel ?? `$${usdAmount.toFixed(2)}`,
2367
2651
  billingPeriodLabel
@@ -2786,7 +3070,7 @@ var notifyDefault = (payload) => {
2786
3070
  const level = payload.status === "destructive" ? "error" : "info";
2787
3071
  console[level === "error" ? "error" : "log"]("[payments-ui] cancellation", payload);
2788
3072
  };
2789
- var defaultTranslations = {
3073
+ var defaultTranslations3 = {
2790
3074
  buttonLabel: "Cancel Membership",
2791
3075
  title: "Confirm Membership Cancellation",
2792
3076
  description: "You are about to cancel your membership. Please review the consequences:",
@@ -2808,23 +3092,33 @@ var CancelMembershipDialog = ({
2808
3092
  minReasonLength = 15,
2809
3093
  onCancelled,
2810
3094
  onNotify,
2811
- translations: customTranslations
3095
+ translations: customTranslations,
3096
+ open,
3097
+ onOpenChange
2812
3098
  }) => {
2813
3099
  const { client } = usePaymentContext();
2814
3100
  const notify = onNotify ?? notifyDefault;
2815
- const t = { ...defaultTranslations, ...customTranslations };
3101
+ const t = { ...defaultTranslations3, ...customTranslations };
2816
3102
  const [cancelReason, setCancelReason] = useState("");
2817
- const [isOpen, setIsOpen] = useState(false);
3103
+ const [internalOpen, setInternalOpen] = useState(false);
2818
3104
  const [isReasonValid, setIsReasonValid] = useState(false);
2819
3105
  const [hasInteracted, setHasInteracted] = useState(false);
2820
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
+ };
2821
3115
  useEffect(() => {
2822
3116
  const trimmed = cancelReason.trim();
2823
3117
  setIsReasonValid(trimmed.length >= minReasonLength);
2824
3118
  }, [cancelReason, minReasonLength]);
2825
- const handleOpenChange = (open) => {
2826
- setIsOpen(open);
2827
- if (!open) {
3119
+ const handleOpenChange = (open2) => {
3120
+ setOpen(open2);
3121
+ if (!open2) {
2828
3122
  setCancelReason("");
2829
3123
  setIsReasonValid(false);
2830
3124
  setHasInteracted(false);
@@ -2861,7 +3155,7 @@ var CancelMembershipDialog = ({
2861
3155
  };
2862
3156
  const showError = hasInteracted && !isReasonValid;
2863
3157
  return /* @__PURE__ */ jsxs(AlertDialog, { open: isOpen, onOpenChange: handleOpenChange, children: [
2864
- /* @__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: [
2865
3159
  /* @__PURE__ */ jsx(Ban, { className: "mr-2 h-4 w-4" }),
2866
3160
  " ",
2867
3161
  t.buttonLabel
@@ -2928,7 +3222,7 @@ var notifyDefault2 = (payload) => {
2928
3222
  const level = payload.status === "destructive" ? "error" : "info";
2929
3223
  console[level === "error" ? "error" : "log"]("[payments-ui] billing", payload);
2930
3224
  };
2931
- var defaultTranslations2 = {
3225
+ var defaultTranslations4 = {
2932
3226
  title: "Transaction History",
2933
3227
  description: "Record of billing history",
2934
3228
  reviewActivity: "Review your account activity below",
@@ -2950,7 +3244,7 @@ var BillingHistory = ({
2950
3244
  }) => {
2951
3245
  const { client } = usePaymentContext();
2952
3246
  const notify = onNotify ?? notifyDefault2;
2953
- const t = { ...defaultTranslations2, ...customTranslations };
3247
+ const t = { ...defaultTranslations4, ...customTranslations };
2954
3248
  const [isExpanded, setIsExpanded] = useState(false);
2955
3249
  const observerRef = useRef(null);
2956
3250
  const loadMoreRef = useRef(null);
@@ -3059,15 +3353,21 @@ var BillingHistory = ({
3059
3353
  ] }) });
3060
3354
  };
3061
3355
  var formatCardLabel2 = (method) => {
3062
- const brand = method.card_type ? method.card_type.toUpperCase() : "CARD";
3063
- 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 = "";
3064
3364
  return `${brand} ${lastFour}`.trim();
3065
3365
  };
3066
3366
  var notifyDefault3 = (payload) => {
3067
3367
  const level = payload.status === "destructive" ? "error" : "info";
3068
3368
  console[level === "error" ? "error" : "log"]("[payments-ui] notification", payload);
3069
3369
  };
3070
- var defaultTranslations3 = {
3370
+ var defaultTranslations5 = {
3071
3371
  title: "Payment Methods",
3072
3372
  description: "Manage your saved billing cards",
3073
3373
  addCard: "Add card",
@@ -3088,7 +3388,11 @@ var defaultTranslations3 = {
3088
3388
  cardUpdated: "Card updated",
3089
3389
  unableToRemoveCard: "Unable to remove card",
3090
3390
  defaultPaymentMethodUpdated: "Default payment method updated",
3091
- 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"
3092
3396
  };
3093
3397
  var PaymentMethodsSection = ({
3094
3398
  isAuthenticated = true,
@@ -3099,8 +3403,7 @@ var PaymentMethodsSection = ({
3099
3403
  onNotify,
3100
3404
  translations: customTranslations
3101
3405
  }) => {
3102
- const { client } = usePaymentContext();
3103
- const queryClient = useQueryClient();
3406
+ const { client, queryClient } = usePaymentContext();
3104
3407
  const paymentMethods = {
3105
3408
  list: (params) => client.listPaymentMethods({ limit: params.pageSize }),
3106
3409
  create: (payload) => client.createPaymentMethod(payload),
@@ -3110,13 +3413,15 @@ var PaymentMethodsSection = ({
3110
3413
  };
3111
3414
  const [isModalOpen, setIsModalOpen] = useState(false);
3112
3415
  const [deletingId, setDeletingId] = useState(null);
3416
+ const [createErrorMessage, setCreateErrorMessage] = useState(null);
3417
+ const [expandedSubscriptions, setExpandedSubscriptions] = useState({});
3113
3418
  const notify = onNotify ?? notifyDefault3;
3114
- const t = { ...defaultTranslations3, ...customTranslations };
3419
+ const t = { ...defaultTranslations5, ...customTranslations };
3115
3420
  const queryKey = ["payments-ui", "payment-methods"];
3116
3421
  const paymentQuery = useQuery({
3117
3422
  queryKey,
3118
3423
  queryFn: () => paymentMethods.list({ pageSize: 50 }),
3119
- enabled: isAuthenticated,
3424
+ enabled: isAuthenticated && !!client,
3120
3425
  staleTime: 3e4
3121
3426
  });
3122
3427
  const createMutation = useMutation({
@@ -3125,11 +3430,14 @@ var PaymentMethodsSection = ({
3125
3430
  notify({ title: t.cardAddedSuccess, status: "success" });
3126
3431
  setIsModalOpen(false);
3127
3432
  void queryClient.invalidateQueries({ queryKey });
3433
+ setCreateErrorMessage(null);
3128
3434
  },
3129
3435
  onError: (error) => {
3436
+ const message = resolveErrorMessageByCode(error, t.errors, error.message);
3437
+ setCreateErrorMessage(message);
3130
3438
  notify({
3131
3439
  title: t.unableToAddCard,
3132
- description: error.message,
3440
+ description: message,
3133
3441
  status: "destructive"
3134
3442
  });
3135
3443
  }
@@ -3140,6 +3448,9 @@ var PaymentMethodsSection = ({
3140
3448
  onSuccess: () => {
3141
3449
  notify({ title: t.cardRemoved, status: "success" });
3142
3450
  void queryClient.invalidateQueries({ queryKey });
3451
+ if (paymentQuery.refetch) {
3452
+ paymentQuery.refetch();
3453
+ }
3143
3454
  },
3144
3455
  onError: (error) => {
3145
3456
  notify({
@@ -3150,7 +3461,7 @@ var PaymentMethodsSection = ({
3150
3461
  },
3151
3462
  onSettled: () => setDeletingId(null)
3152
3463
  });
3153
- const activateMutation = useMutation({
3464
+ useMutation({
3154
3465
  mutationFn: (id) => paymentMethods.activate(id),
3155
3466
  onSuccess: () => {
3156
3467
  notify({ title: t.defaultPaymentMethodUpdated, status: "success" });
@@ -3167,8 +3478,14 @@ var PaymentMethodsSection = ({
3167
3478
  useEffect(() => {
3168
3479
  if (!isModalOpen) {
3169
3480
  createMutation.reset();
3481
+ setCreateErrorMessage(null);
3482
+ }
3483
+ }, [isModalOpen]);
3484
+ useEffect(() => {
3485
+ if (!isModalOpen && paymentQuery.refetch) {
3486
+ paymentQuery.refetch();
3170
3487
  }
3171
- }, [createMutation, isModalOpen]);
3488
+ }, [isModalOpen]);
3172
3489
  const payments = useMemo(() => paymentQuery.data?.data ?? [], [paymentQuery.data]);
3173
3490
  const loading = paymentQuery.isLoading || paymentQuery.isFetching;
3174
3491
  const buildPayload = (token, billing) => ({
@@ -3182,9 +3499,13 @@ var PaymentMethodsSection = ({
3182
3499
  zip: billing.postalCode,
3183
3500
  country: billing.country,
3184
3501
  email: billing.email,
3185
- 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
3186
3506
  });
3187
3507
  const handleCardTokenize = (token, billing) => {
3508
+ setCreateErrorMessage(null);
3188
3509
  createMutation.mutate(buildPayload(token, billing));
3189
3510
  };
3190
3511
  return /* @__PURE__ */ jsxs(Card, { className: "border-0 bg-black/30 shadow-2xl backdrop-blur-xl", children: [
@@ -3203,32 +3524,19 @@ var PaymentMethodsSection = ({
3203
3524
  /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-5 w-5 animate-spin" }),
3204
3525
  " ",
3205
3526
  t.loadingCards
3206
- ] }) : 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(
3207
- "div",
3208
- {
3209
- className: "rounded-lg border bg-white/5 p-4 shadow-sm",
3210
- children: [
3211
- /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
3212
- /* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
3213
- /* @__PURE__ */ jsx("div", { className: "text-base font-medium text-white", children: formatCardLabel2(method) }),
3214
- /* @__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 }) })
3215
3538
  ] }),
3216
- /* @__PURE__ */ jsx("div", { children: method.failure_reason && /* @__PURE__ */ jsx(Badge, { variant: "destructive", children: method.failure_reason }) })
3217
- ] }),
3218
- /* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap gap-2", children: [
3219
- /* @__PURE__ */ jsxs(
3220
- Button,
3221
- {
3222
- variant: "outline",
3223
- disabled: method.is_active || activateMutation.isPending,
3224
- onClick: () => activateMutation.mutate(method.id),
3225
- children: [
3226
- activateMutation.isPending ? /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null,
3227
- method.is_active ? t.defaultMethod : t.makeDefault
3228
- ]
3229
- }
3230
- ),
3231
- /* @__PURE__ */ jsxs(
3539
+ /* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-wrap gap-2", children: /* @__PURE__ */ jsxs(
3232
3540
  Button,
3233
3541
  {
3234
3542
  variant: "ghost",
@@ -3240,12 +3548,12 @@ var PaymentMethodsSection = ({
3240
3548
  t.remove
3241
3549
  ]
3242
3550
  }
3243
- )
3244
- ] })
3245
- ]
3246
- },
3247
- method.id
3248
- )) }) }),
3551
+ ) })
3552
+ ]
3553
+ },
3554
+ method.id
3555
+ );
3556
+ }) }) }),
3249
3557
  /* @__PURE__ */ jsx(Dialog, { open: isModalOpen, onOpenChange: setIsModalOpen, children: /* @__PURE__ */ jsxs(
3250
3558
  DialogContent,
3251
3559
  {
@@ -3263,12 +3571,13 @@ var PaymentMethodsSection = ({
3263
3571
  collectPrefix,
3264
3572
  onTokenize: handleCardTokenize,
3265
3573
  submitting: createMutation.isPending,
3574
+ translations: t,
3266
3575
  defaultValues: {
3267
3576
  provider,
3268
3577
  email: userEmail ?? "",
3269
3578
  country: defaultCountry
3270
3579
  },
3271
- externalError: createMutation.error?.message ?? null
3580
+ externalError: createErrorMessage
3272
3581
  }
3273
3582
  )
3274
3583
  ]
@@ -3276,6 +3585,387 @@ var PaymentMethodsSection = ({
3276
3585
  ) })
3277
3586
  ] });
3278
3587
  };
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
+ };
3279
3969
  var Checkbox = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3280
3970
  CheckboxPrimitive.Root,
3281
3971
  {
@@ -3791,6 +4481,6 @@ var usePaymentStatus = (options = {}) => {
3791
4481
  };
3792
4482
  };
3793
4483
 
3794
- 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 };
3795
4485
  //# sourceMappingURL=index.js.map
3796
4486
  //# sourceMappingURL=index.js.map