@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.cjs CHANGED
@@ -281,6 +281,7 @@ var createClient = (config) => {
281
281
  return normalizeList(result);
282
282
  },
283
283
  createPaymentMethod(payload) {
284
+ console.log("Creating payment method with payload:", payload);
284
285
  return request("POST", "/me/payment-methods", {
285
286
  body: payload
286
287
  });
@@ -298,10 +299,10 @@ var createClient = (config) => {
298
299
  },
299
300
  checkout(payload, idempotencyKey) {
300
301
  const key = idempotencyKey ?? crypto.randomUUID();
301
- return request("POST", "/me/checkout", {
302
+ return request("POST", "/checkout", {
302
303
  body: payload,
303
304
  headers: {
304
- "Idempotency-Key": key
305
+ "X-Idempotency-Key": key
305
306
  }
306
307
  });
307
308
  },
@@ -310,6 +311,36 @@ var createClient = (config) => {
310
311
  body: feedback ? { feedback } : void 0
311
312
  });
312
313
  },
314
+ async listSubscriptions(params) {
315
+ const result = await request(
316
+ "GET",
317
+ "/me/subscriptions",
318
+ {
319
+ query: {
320
+ status: params?.status,
321
+ limit: params?.limit,
322
+ offset: params?.offset
323
+ }
324
+ }
325
+ );
326
+ return normalizeList(result);
327
+ },
328
+ updateSubscriptionPaymentMethod(payload) {
329
+ return request("PUT", "/me/subscriptions/payment-method", {
330
+ body: {
331
+ subscription_id: payload.subscription_id,
332
+ payment_method_id: payload.payment_method_id
333
+ }
334
+ });
335
+ },
336
+ resumeSubscription() {
337
+ return request("POST", "/me/subscriptions/resume");
338
+ },
339
+ changeSubscription(payload) {
340
+ return request("POST", "/me/subscriptions/change", {
341
+ body: payload
342
+ });
343
+ },
313
344
  async getPaymentHistory(params) {
314
345
  const result = await request("GET", "/me/payments", {
315
346
  query: {
@@ -455,6 +486,19 @@ function DialogHeader({ className, ...props }) {
455
486
  }
456
487
  );
457
488
  }
489
+ function DialogFooter({ className, ...props }) {
490
+ return /* @__PURE__ */ jsxRuntime.jsx(
491
+ "div",
492
+ {
493
+ "data-slot": "dialog-footer",
494
+ className: cn(
495
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
496
+ className
497
+ ),
498
+ ...props
499
+ }
500
+ );
501
+ }
458
502
  function DialogTitle({
459
503
  className,
460
504
  ...props
@@ -709,6 +753,28 @@ var defaultBillingDetails = {
709
753
  email: "",
710
754
  provider: "mobius"
711
755
  };
756
+ var defaultCardDetailsFormTranslations = {
757
+ firstName: "First name",
758
+ lastName: "Last name",
759
+ email: "Email",
760
+ address: "Address",
761
+ city: "City",
762
+ state: "State / Region",
763
+ postalCode: "Postal code",
764
+ country: "Country",
765
+ cardNumber: "Card number",
766
+ expiry: "Expiry",
767
+ cvv: "CVV",
768
+ submit: "Submit",
769
+ processing: "Processing\u2026",
770
+ errorRequiredFields: "Please complete all required billing fields.",
771
+ errorTokenization: "Payment tokenization failed. Please try again.",
772
+ errorFormNotReady: "Payment form is not ready. Please try again later.",
773
+ infoSecure: "Your payment information is encrypted and processed securely.",
774
+ cancel: "Cancel",
775
+ editEmail: "Edit email",
776
+ selectCountry: "Select a country"
777
+ };
712
778
  var buildSelector = (prefix, field) => `#${prefix}-${field}`;
713
779
  var CardDetailsForm = ({
714
780
  visible,
@@ -720,8 +786,10 @@ var CardDetailsForm = ({
720
786
  collectPrefix = "card-form",
721
787
  className,
722
788
  onBillingChange,
723
- submitDisabled = false
789
+ submitDisabled = false,
790
+ translations
724
791
  }) => {
792
+ const t = { ...defaultCardDetailsFormTranslations, ...translations };
725
793
  const { config } = usePaymentContext();
726
794
  const defaultValuesKey = React4.useMemo(() => JSON.stringify(defaultValues ?? {}), [defaultValues]);
727
795
  const mergedDefaults = React4.useMemo(
@@ -762,8 +830,11 @@ var CardDetailsForm = ({
762
830
  if (!visible) {
763
831
  setLocalError(null);
764
832
  setIsTokenizing(false);
833
+ if (typeof window !== "undefined" && window.__doujinsCollectConfigured && collectPrefix) {
834
+ window.__doujinsCollectConfigured[collectPrefix] = false;
835
+ }
765
836
  }
766
- }, [visible]);
837
+ }, [visible, collectPrefix]);
767
838
  React4.useEffect(() => {
768
839
  if (!visible) return;
769
840
  setFirstName(mergedDefaults.firstName);
@@ -825,6 +896,8 @@ var CardDetailsForm = ({
825
896
  setLocalError("Payment tokenization failed. Please try again.");
826
897
  return;
827
898
  }
899
+ let rawExp = response.card?.exp;
900
+ let formattedExp = rawExp && rawExp.length === 4 ? `${rawExp.slice(0, 2)}/${rawExp.slice(2)}` : rawExp;
828
901
  const billing = {
829
902
  firstName,
830
903
  lastName,
@@ -834,7 +907,10 @@ var CardDetailsForm = ({
834
907
  postalCode,
835
908
  country,
836
909
  email,
837
- provider: mergedDefaults.provider ?? "mobius"
910
+ provider: mergedDefaults.provider ?? "mobius",
911
+ last_four: response.card?.number,
912
+ card_type: response.card?.type,
913
+ expiry_date: formattedExp
838
914
  };
839
915
  onTokenize(response.token, billing);
840
916
  };
@@ -875,7 +951,7 @@ var CardDetailsForm = ({
875
951
  ]);
876
952
  const validate = () => {
877
953
  if (!firstName.trim() || !lastName.trim() || !address1.trim() || !city.trim() || !postalCode.trim() || !country.trim() || !email.trim()) {
878
- setLocalError("Please complete all required billing fields.");
954
+ setLocalError(t.errorRequiredFields);
879
955
  return false;
880
956
  }
881
957
  setLocalError(null);
@@ -885,7 +961,7 @@ var CardDetailsForm = ({
885
961
  event.preventDefault();
886
962
  if (!validate()) return;
887
963
  if (!window.CollectJS) {
888
- setLocalError("Payment form is not ready. Please try again later.");
964
+ setLocalError(t.errorFormNotReady);
889
965
  return;
890
966
  }
891
967
  setIsTokenizing(true);
@@ -905,7 +981,7 @@ var CardDetailsForm = ({
905
981
  errorMessage && /* @__PURE__ */ jsxRuntime.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 }),
906
982
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 md:flex-row", children: [
907
983
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 space-y-2", children: [
908
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "firstName", children: "First name" }),
984
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "firstName", children: t.firstName }),
909
985
  /* @__PURE__ */ jsxRuntime.jsx(
910
986
  Input,
911
987
  {
@@ -917,7 +993,7 @@ var CardDetailsForm = ({
917
993
  )
918
994
  ] }),
919
995
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 space-y-2", children: [
920
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "lastName", children: "Last name" }),
996
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "lastName", children: t.lastName }),
921
997
  /* @__PURE__ */ jsxRuntime.jsx(
922
998
  Input,
923
999
  {
@@ -930,7 +1006,7 @@ var CardDetailsForm = ({
930
1006
  ] })
931
1007
  ] }),
932
1008
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
933
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "email", children: "Email" }),
1009
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "email", children: t.email }),
934
1010
  showEmailInput ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 items-center", children: [
935
1011
  /* @__PURE__ */ jsxRuntime.jsx(
936
1012
  Input,
@@ -954,7 +1030,7 @@ var CardDetailsForm = ({
954
1030
  setEmail(mergedDefaults.email ?? "");
955
1031
  },
956
1032
  className: "px-3 text-xs text-muted-foreground hover:text-foreground",
957
- children: "Cancel"
1033
+ children: t.cancel
958
1034
  }
959
1035
  )
960
1036
  ] }) : /* @__PURE__ */ jsxRuntime.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: [
@@ -965,14 +1041,14 @@ var CardDetailsForm = ({
965
1041
  type: "button",
966
1042
  onClick: () => setIsEditingEmail(true),
967
1043
  className: "text-muted-foreground hover:text-foreground transition-colors",
968
- "aria-label": "Edit email",
1044
+ "aria-label": t.editEmail,
969
1045
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-4 w-4" })
970
1046
  }
971
1047
  )
972
1048
  ] })
973
1049
  ] }),
974
1050
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
975
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "address1", children: "Address" }),
1051
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "address1", children: t.address }),
976
1052
  /* @__PURE__ */ jsxRuntime.jsx(
977
1053
  Input,
978
1054
  {
@@ -985,7 +1061,7 @@ var CardDetailsForm = ({
985
1061
  ] }),
986
1062
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-2 md:grid-cols-2", children: [
987
1063
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
988
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "city", children: "City" }),
1064
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "city", children: t.city }),
989
1065
  /* @__PURE__ */ jsxRuntime.jsx(
990
1066
  Input,
991
1067
  {
@@ -997,7 +1073,7 @@ var CardDetailsForm = ({
997
1073
  )
998
1074
  ] }),
999
1075
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1000
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "state", children: "State / Region" }),
1076
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "state", children: t.state }),
1001
1077
  /* @__PURE__ */ jsxRuntime.jsx(
1002
1078
  Input,
1003
1079
  {
@@ -1010,7 +1086,7 @@ var CardDetailsForm = ({
1010
1086
  ] }),
1011
1087
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-2 md:grid-cols-2", children: [
1012
1088
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1013
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "postal", children: "Postal code" }),
1089
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "postal", children: t.postalCode }),
1014
1090
  /* @__PURE__ */ jsxRuntime.jsx(
1015
1091
  Input,
1016
1092
  {
@@ -1022,24 +1098,24 @@ var CardDetailsForm = ({
1022
1098
  )
1023
1099
  ] }),
1024
1100
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1025
- /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Country" }),
1101
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: t.country }),
1026
1102
  /* @__PURE__ */ jsxRuntime.jsxs(Select, { value: country, onValueChange: setCountry, children: [
1027
- /* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "Select a country" }) }),
1103
+ /* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: t.selectCountry }) }),
1028
1104
  /* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: countries.map((option) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: option.code, children: option.name }, option.code)) })
1029
1105
  ] })
1030
1106
  ] })
1031
1107
  ] }),
1032
1108
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1033
- /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Card number" }),
1109
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: t.cardNumber }),
1034
1110
  /* @__PURE__ */ jsxRuntime.jsx("div", { id: buildSelector(collectPrefix, "ccnumber").slice(1), className: collectFieldClass })
1035
1111
  ] }),
1036
1112
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-2 md:grid-cols-2", children: [
1037
1113
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1038
- /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Expiry" }),
1114
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: t.expiry }),
1039
1115
  /* @__PURE__ */ jsxRuntime.jsx("div", { id: buildSelector(collectPrefix, "ccexp").slice(1), className: collectFieldClass })
1040
1116
  ] }),
1041
1117
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1042
- /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "CVV" }),
1118
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { children: t.cvv }),
1043
1119
  /* @__PURE__ */ jsxRuntime.jsx("div", { id: buildSelector(collectPrefix, "cvv").slice(1), className: collectFieldClass })
1044
1120
  ] })
1045
1121
  ] }),
@@ -1051,11 +1127,12 @@ var CardDetailsForm = ({
1051
1127
  disabled: submitting || submitDisabled || isTokenizing,
1052
1128
  children: submitting || isTokenizing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1053
1129
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
1054
- " Processing\u2026"
1055
- ] }) : submitLabel
1130
+ " ",
1131
+ t.processing
1132
+ ] }) : submitLabel || t.submit
1056
1133
  }
1057
1134
  ),
1058
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-white/60", children: "Your payment information is encrypted and processed securely." })
1135
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-white/60", children: t.infoSecure })
1059
1136
  ]
1060
1137
  }
1061
1138
  );
@@ -1092,7 +1169,9 @@ var usePaymentMethods = () => {
1092
1169
  zip: billing.postalCode,
1093
1170
  country: billing.country,
1094
1171
  email: billing.email,
1095
- provider: billing.provider
1172
+ provider: billing.provider,
1173
+ last_four: billing.last_four,
1174
+ card_type: billing.card_type
1096
1175
  };
1097
1176
  return client.createPaymentMethod(payload);
1098
1177
  },
@@ -1161,21 +1240,34 @@ var ScrollBar = React4__namespace.forwardRef(({ className, orientation = "vertic
1161
1240
  }
1162
1241
  ));
1163
1242
  ScrollBar.displayName = ScrollAreaPrimitive__namespace.ScrollAreaScrollbar.displayName;
1243
+ var defaultTranslations = {
1244
+ loadingCards: "Loading cards...",
1245
+ noSavedMethods: "No saved payment methods yet.",
1246
+ selectedLabel: "Selected",
1247
+ useCardLabel: "Use card"
1248
+ };
1164
1249
  var formatCardLabel = (method) => {
1165
- const brand = method.card_type ? method.card_type.toUpperCase() : "CARD";
1166
- const lastFour = method.last_four ? `\u2022\u2022\u2022\u2022 ${method.last_four}` : "";
1167
- return `${brand} ${lastFour}`.trim();
1250
+ if (method.card) {
1251
+ const brand = method.card.brand ? method.card.brand.toUpperCase() : "CARD";
1252
+ const lastFour = method.card.last4 ? `\u2022\u2022\u2022\u2022 ${method.card.last4}` : "";
1253
+ 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)}` : "";
1254
+ return `${brand} ${lastFour}${exp}`.trim();
1255
+ }
1256
+ return "CARD";
1168
1257
  };
1169
1258
  var StoredPaymentMethods = ({
1170
1259
  selectedMethodId,
1171
- onMethodSelect
1260
+ onMethodSelect,
1261
+ translations
1172
1262
  }) => {
1173
1263
  const { listQuery } = usePaymentMethods();
1174
1264
  const payments = React4.useMemo(() => listQuery.data?.data ?? [], [listQuery.data]);
1265
+ const t = { ...defaultTranslations, ...translations };
1175
1266
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: listQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center py-4 text-sm text-muted-foreground", children: [
1176
1267
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
1177
- " Loading cards..."
1178
- ] }) : payments.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 text-center text-sm text-muted-foreground", children: "No saved payment methods yet." }) : /* @__PURE__ */ jsxRuntime.jsx(ScrollArea, { className: "max-h-[320px] pr-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: payments.map((method) => {
1268
+ " ",
1269
+ t.loadingCards
1270
+ ] }) : payments.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 text-center text-sm text-muted-foreground", children: t.noSavedMethods }) : /* @__PURE__ */ jsxRuntime.jsx(ScrollArea, { className: "max-h-[320px] pr-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: payments.map((method) => {
1179
1271
  const isSelected = selectedMethodId === method.id;
1180
1272
  return /* @__PURE__ */ jsxRuntime.jsxs(
1181
1273
  "div",
@@ -1198,7 +1290,7 @@ var StoredPaymentMethods = ({
1198
1290
  disabled: isSelected,
1199
1291
  onClick: () => onMethodSelect(method),
1200
1292
  className: clsx2__default.default("px-3", { "bg-muted/90": !isSelected, "bg-inherit": isSelected }),
1201
- children: isSelected ? "Selected" : "Use card"
1293
+ children: isSelected ? t.selectedLabel : t.useCardLabel
1202
1294
  }
1203
1295
  )
1204
1296
  ] })
@@ -1208,6 +1300,26 @@ var StoredPaymentMethods = ({
1208
1300
  );
1209
1301
  }) }) }) });
1210
1302
  };
1303
+
1304
+ // src/utils/errorMessages.ts
1305
+ var resolveErrorMessageByCode = (error, translationErrors, fallbackMessage) => {
1306
+ const errors = translationErrors ?? {};
1307
+ const defaultMessage = fallbackMessage ?? (error instanceof Error ? error.message : typeof error === "string" ? error : "An unexpected error occurred");
1308
+ if (error instanceof ClientApiError) {
1309
+ const payload = error.body;
1310
+ const code = payload?.code ?? payload?.error?.code;
1311
+ if (code && errors[code]) return errors[code];
1312
+ if (typeof payload?.error?.message === "string") return payload.error.message;
1313
+ return error.message;
1314
+ }
1315
+ if (typeof error === "object" && error !== null && "code" in error) {
1316
+ const code = error.code;
1317
+ if (typeof code === "string" && errors[code]) return errors[code];
1318
+ }
1319
+ if (error instanceof Error) return error.message;
1320
+ if (typeof error === "string") return error;
1321
+ return defaultMessage;
1322
+ };
1211
1323
  function Tabs({
1212
1324
  className,
1213
1325
  ...props
@@ -2003,20 +2115,45 @@ var SolanaPaymentView = ({
2003
2115
  renderBody()
2004
2116
  ] });
2005
2117
  };
2118
+ var defaultPaymentExperienceTranslations = {
2119
+ ...defaultCardDetailsFormTranslations,
2120
+ useSavedCardTab: "Use saved card",
2121
+ addNewCardTab: "Add new card",
2122
+ savedUnavailable: "Saved payment methods are unavailable right now. Add a new card to get started.",
2123
+ payWithSavedCard: "Pay with selected card",
2124
+ processingSavedCard: "Processing...",
2125
+ savedErrorFallback: "Unable to complete payment with saved card",
2126
+ newCardUnavailable: "Select a subscription plan to add a new card.",
2127
+ payNow: "Pay now",
2128
+ storedLoadingCards: "Loading cards...",
2129
+ storedNoSavedMethods: "No saved payment methods yet.",
2130
+ storedSelectedLabel: "Selected",
2131
+ storedUseCardLabel: "Use card",
2132
+ payWithCcbill: "Pay with CCBill",
2133
+ processingCcbill: "Redirecting...",
2134
+ errors: {}
2135
+ };
2006
2136
  var PaymentExperience = ({
2007
2137
  priceId,
2008
2138
  usdAmount,
2009
2139
  onNewCardPayment,
2010
2140
  onSavedMethodPayment,
2141
+ onCcbillPayment,
2011
2142
  enableNewCard = true,
2012
2143
  enableStoredMethods = true,
2013
2144
  enableSolanaPay = true,
2014
2145
  onSolanaSuccess,
2015
2146
  onSolanaError,
2016
- initialMode = "cards"
2147
+ initialMode = "cards",
2148
+ translations
2017
2149
  }) => {
2150
+ const t = {
2151
+ ...defaultPaymentExperienceTranslations,
2152
+ ...translations
2153
+ };
2018
2154
  const showNewCard = enableNewCard && Boolean(onNewCardPayment);
2019
2155
  const showStored = enableStoredMethods && Boolean(onSavedMethodPayment);
2156
+ const showCcbill = showNewCard && Boolean(onCcbillPayment);
2020
2157
  const defaultTab = showStored ? "saved" : "new";
2021
2158
  const [activeTab, setActiveTab] = React4.useState(defaultTab);
2022
2159
  const [mode, setMode] = React4.useState(
@@ -2027,6 +2164,9 @@ var PaymentExperience = ({
2027
2164
  const [savedError, setSavedError] = React4.useState(null);
2028
2165
  const [newCardStatus, setNewCardStatus] = React4.useState("idle");
2029
2166
  const [newCardError, setNewCardError] = React4.useState(null);
2167
+ const [billingDetails, setBillingDetails] = React4.useState(null);
2168
+ const [ccbillStatus, setCcbillStatus] = React4.useState("idle");
2169
+ const [ccbillError, setCcbillError] = React4.useState(null);
2030
2170
  const { notifyStatus, notifyError } = usePaymentNotifications();
2031
2171
  React4.useEffect(() => {
2032
2172
  setActiveTab(showStored ? "saved" : "new");
@@ -2042,6 +2182,24 @@ var PaymentExperience = ({
2042
2182
  setMode("cards");
2043
2183
  }
2044
2184
  }, [enableSolanaPay, initialMode]);
2185
+ React4.useEffect(() => {
2186
+ if (!showNewCard) {
2187
+ setBillingDetails(null);
2188
+ setCcbillStatus("idle");
2189
+ setCcbillError(null);
2190
+ }
2191
+ }, [showNewCard]);
2192
+ const handleBillingChange = React4.useCallback((billing) => {
2193
+ setBillingDetails(billing);
2194
+ setCcbillError(null);
2195
+ setCcbillStatus("idle");
2196
+ }, []);
2197
+ const isBillingComplete = React4.useCallback((billing) => {
2198
+ if (!billing) return false;
2199
+ return Boolean(
2200
+ billing.firstName.trim() && billing.lastName.trim() && billing.address1.trim() && billing.city.trim() && billing.postalCode.trim() && billing.country.trim() && billing.email.trim()
2201
+ );
2202
+ }, []);
2045
2203
  const handleMethodSelect = React4.useCallback((method) => {
2046
2204
  setSelectedMethodId(method.id);
2047
2205
  setSavedStatus("idle");
@@ -2060,13 +2218,17 @@ var PaymentExperience = ({
2060
2218
  setSavedStatus("success");
2061
2219
  notifyStatus("success", { source: "saved-payment" });
2062
2220
  } catch (error) {
2063
- const message = error instanceof Error ? error.message : "Unable to complete payment with saved card";
2221
+ const message = resolveErrorMessageByCode(
2222
+ error,
2223
+ t.errors,
2224
+ t.savedErrorFallback
2225
+ );
2064
2226
  setSavedStatus("error");
2065
2227
  setSavedError(message);
2066
2228
  notifyStatus("error", { source: "saved-payment" });
2067
2229
  notifyError(message);
2068
2230
  }
2069
- }, [notifyError, notifyStatus, onSavedMethodPayment, selectedMethodId, usdAmount]);
2231
+ }, [notifyError, notifyStatus, onSavedMethodPayment, selectedMethodId, t, usdAmount]);
2070
2232
  const handleNewCardTokenize = React4.useCallback(
2071
2233
  async (token, billing) => {
2072
2234
  if (!onNewCardPayment) return;
@@ -2078,15 +2240,56 @@ var PaymentExperience = ({
2078
2240
  setNewCardStatus("success");
2079
2241
  notifyStatus("success", { source: "new-card" });
2080
2242
  } catch (error) {
2081
- const message = error instanceof Error ? error.message : "Unable to complete payment";
2243
+ const message = resolveErrorMessageByCode(
2244
+ error,
2245
+ t.errors,
2246
+ "Unable to complete payment"
2247
+ );
2082
2248
  setNewCardStatus("error");
2083
2249
  setNewCardError(message);
2084
2250
  notifyStatus("error", { source: "new-card" });
2085
2251
  notifyError(message);
2086
2252
  }
2087
2253
  },
2088
- [notifyError, notifyStatus, onNewCardPayment]
2254
+ [notifyError, notifyStatus, onNewCardPayment, t]
2089
2255
  );
2256
+ const handleCcbillPayment = React4.useCallback(async () => {
2257
+ if (!onCcbillPayment) return;
2258
+ if (!billingDetails || !isBillingComplete(billingDetails)) {
2259
+ const message = t.errorRequiredFields;
2260
+ setActiveTab("new");
2261
+ setCcbillStatus("error");
2262
+ setCcbillError(message);
2263
+ notifyStatus("error", { source: "ccbill" });
2264
+ notifyError(message);
2265
+ return;
2266
+ }
2267
+ try {
2268
+ setCcbillStatus("processing");
2269
+ setCcbillError(null);
2270
+ notifyStatus("processing", { source: "ccbill" });
2271
+ await onCcbillPayment({ billing: billingDetails });
2272
+ setCcbillStatus("success");
2273
+ notifyStatus("success", { source: "ccbill" });
2274
+ } catch (error) {
2275
+ const message = resolveErrorMessageByCode(
2276
+ error,
2277
+ t.errors,
2278
+ "Unable to start CCBill checkout"
2279
+ );
2280
+ setCcbillStatus("error");
2281
+ setCcbillError(message);
2282
+ notifyStatus("error", { source: "ccbill" });
2283
+ notifyError(message);
2284
+ }
2285
+ }, [
2286
+ billingDetails,
2287
+ isBillingComplete,
2288
+ notifyError,
2289
+ notifyStatus,
2290
+ onCcbillPayment,
2291
+ t
2292
+ ]);
2090
2293
  React4.useCallback(() => {
2091
2294
  if (!enableSolanaPay) return;
2092
2295
  setMode("solana");
@@ -2109,14 +2312,20 @@ var PaymentExperience = ({
2109
2312
  );
2110
2313
  const renderSavedTab = () => {
2111
2314
  if (!showStored) {
2112
- return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Saved payment methods are unavailable right now. Add a new card to get started." });
2315
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: t.savedUnavailable });
2113
2316
  }
2114
2317
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
2115
2318
  /* @__PURE__ */ jsxRuntime.jsx(
2116
2319
  StoredPaymentMethods,
2117
2320
  {
2118
2321
  selectedMethodId,
2119
- onMethodSelect: handleMethodSelect
2322
+ onMethodSelect: handleMethodSelect,
2323
+ translations: {
2324
+ loadingCards: t.storedLoadingCards,
2325
+ noSavedMethods: t.storedNoSavedMethods,
2326
+ selectedLabel: t.storedSelectedLabel,
2327
+ useCardLabel: t.storedUseCardLabel
2328
+ }
2120
2329
  }
2121
2330
  ),
2122
2331
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2125,7 +2334,7 @@ var PaymentExperience = ({
2125
2334
  className: "w-full",
2126
2335
  disabled: !selectedMethodId || savedStatus === "processing",
2127
2336
  onClick: handleSavedPayment,
2128
- children: savedStatus === "processing" ? "Processing..." : "Pay with selected card"
2337
+ children: savedStatus === "processing" ? t.processingSavedCard : t.payWithSavedCard
2129
2338
  }
2130
2339
  ),
2131
2340
  savedError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: savedError })
@@ -2133,29 +2342,50 @@ var PaymentExperience = ({
2133
2342
  };
2134
2343
  const renderNewTab = () => {
2135
2344
  if (!showNewCard) {
2136
- return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Select a subscription plan to add a new card." });
2345
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: t.newCardUnavailable });
2137
2346
  }
2138
2347
  return /* @__PURE__ */ jsxRuntime.jsx(
2139
2348
  CardDetailsForm,
2140
2349
  {
2141
2350
  visible: true,
2142
- submitLabel: "Pay now",
2351
+ submitLabel: t.payNow,
2143
2352
  externalError: newCardError,
2144
2353
  onTokenize: handleNewCardTokenize,
2145
- submitting: newCardStatus === "processing"
2354
+ submitting: newCardStatus === "processing",
2355
+ onBillingChange: handleBillingChange,
2356
+ translations: t
2146
2357
  }
2147
2358
  );
2148
2359
  };
2149
2360
  const renderCardExperience = () => /* @__PURE__ */ jsxRuntime.jsxs(Tabs, { value: activeTab, onValueChange: setActiveTab, children: [
2150
2361
  /* @__PURE__ */ jsxRuntime.jsxs(TabsList, { className: "w-full rounded-md mb-4", children: [
2151
- /* @__PURE__ */ jsxRuntime.jsx(TabsTrigger, { className: "cursor-pointer", value: "saved", children: "Use saved card" }),
2152
- /* @__PURE__ */ jsxRuntime.jsx(TabsTrigger, { className: "cursor-pointer", value: "new", children: "Add new card" })
2362
+ /* @__PURE__ */ jsxRuntime.jsx(TabsTrigger, { className: "cursor-pointer", value: "saved", children: t.useSavedCardTab }),
2363
+ /* @__PURE__ */ jsxRuntime.jsx(TabsTrigger, { className: "cursor-pointer", value: "new", children: t.addNewCardTab })
2153
2364
  ] }),
2154
2365
  /* @__PURE__ */ jsxRuntime.jsx(TabsContent, { value: "saved", children: renderSavedTab() }),
2155
2366
  /* @__PURE__ */ jsxRuntime.jsx(TabsContent, { value: "new", children: renderNewTab() })
2156
2367
  ] });
2368
+ const renderCcbillAction = () => {
2369
+ if (!showCcbill) return null;
2370
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 border-t border-white/10 pt-4", children: [
2371
+ /* @__PURE__ */ jsxRuntime.jsx(
2372
+ Button,
2373
+ {
2374
+ className: "w-full",
2375
+ variant: "outline",
2376
+ disabled: ccbillStatus === "processing",
2377
+ onClick: handleCcbillPayment,
2378
+ children: ccbillStatus === "processing" ? t.processingCcbill : t.payWithCcbill
2379
+ }
2380
+ ),
2381
+ ccbillError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: ccbillError })
2382
+ ] });
2383
+ };
2157
2384
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6 pt-4", children: [
2158
- mode === "cards" && renderCardExperience(),
2385
+ mode === "cards" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2386
+ renderCardExperience(),
2387
+ renderCcbillAction()
2388
+ ] }),
2159
2389
  mode === "solana" && enableSolanaPay && /* @__PURE__ */ jsxRuntime.jsx(
2160
2390
  SolanaPaymentView,
2161
2391
  {
@@ -2207,17 +2437,17 @@ var useSubscriptionActions = () => {
2207
2437
  const subscribeWithCard = React4.useCallback(
2208
2438
  async ({
2209
2439
  priceId,
2210
- processor = "nmi",
2440
+ processor = "mobius",
2211
2441
  provider,
2212
2442
  paymentToken,
2213
2443
  billing,
2214
2444
  idempotencyKey
2215
2445
  }) => {
2216
- const payload = {
2217
- price_id: ensurePrice(priceId),
2446
+ if (processor !== "ccbill" && !paymentToken) {
2447
+ throw new Error("payments-ui: payment token is required for card checkout");
2448
+ }
2449
+ const payment = {
2218
2450
  processor,
2219
- provider,
2220
- payment_token: paymentToken,
2221
2451
  email: billing.email,
2222
2452
  first_name: billing.firstName,
2223
2453
  last_name: billing.lastName,
@@ -2227,6 +2457,17 @@ var useSubscriptionActions = () => {
2227
2457
  zip: billing.postalCode,
2228
2458
  country: billing.country
2229
2459
  };
2460
+ if (paymentToken) {
2461
+ payment.payment_token = paymentToken;
2462
+ payment.last_four = billing.last_four;
2463
+ payment.card_type = billing.card_type;
2464
+ payment.expiry_date = billing.expiry_date;
2465
+ }
2466
+ const payload = {
2467
+ price_id: ensurePrice(priceId),
2468
+ provider,
2469
+ payment
2470
+ };
2230
2471
  return client.checkout(payload, idempotencyKey);
2231
2472
  },
2232
2473
  [client]
@@ -2234,7 +2475,7 @@ var useSubscriptionActions = () => {
2234
2475
  const subscribeWithSavedMethod = React4.useCallback(
2235
2476
  async ({
2236
2477
  priceId,
2237
- processor = "nmi",
2478
+ processor = "mobius",
2238
2479
  provider,
2239
2480
  paymentMethodId,
2240
2481
  email,
@@ -2242,10 +2483,12 @@ var useSubscriptionActions = () => {
2242
2483
  }) => {
2243
2484
  const payload = {
2244
2485
  price_id: ensurePrice(priceId),
2245
- processor,
2246
2486
  provider,
2247
- payment_method_id: paymentMethodId,
2248
- email
2487
+ payment: {
2488
+ processor,
2489
+ payment_method_id: paymentMethodId,
2490
+ email
2491
+ }
2249
2492
  };
2250
2493
  return client.checkout(payload, idempotencyKey);
2251
2494
  },
@@ -2264,12 +2507,14 @@ var useSubscriptionActions = () => {
2264
2507
  }) => {
2265
2508
  const payload = {
2266
2509
  price_id: ensurePrice(priceId),
2267
- processor,
2268
- email,
2269
- first_name: firstName,
2270
- last_name: lastName,
2271
- zip: zipCode,
2272
- country
2510
+ payment: {
2511
+ processor,
2512
+ email,
2513
+ first_name: firstName,
2514
+ last_name: lastName,
2515
+ zip: zipCode,
2516
+ country
2517
+ }
2273
2518
  };
2274
2519
  return client.checkout(payload, idempotencyKey);
2275
2520
  },
@@ -2281,6 +2526,11 @@ var useSubscriptionActions = () => {
2281
2526
  subscribeWithCCBill
2282
2527
  };
2283
2528
  };
2529
+ var defaultTranslations2 = {
2530
+ ...defaultPaymentExperienceTranslations,
2531
+ title: "Checkout",
2532
+ selectPlanMessage: "Select a subscription plan to continue."
2533
+ };
2284
2534
  var SubscriptionCheckoutModal = ({
2285
2535
  open,
2286
2536
  onOpenChange,
@@ -2295,11 +2545,16 @@ var SubscriptionCheckoutModal = ({
2295
2545
  enableSolanaPay = true,
2296
2546
  onSolanaSuccess,
2297
2547
  onSolanaError,
2298
- initialMode = "cards"
2548
+ initialMode = "cards",
2549
+ translations
2299
2550
  }) => {
2300
2551
  const [showSuccess, setShowSuccess] = React4.useState(false);
2301
2552
  const [idempotencyKey, setIdempotencyKey] = React4.useState(() => crypto.randomUUID());
2302
2553
  const { subscribeWithCard, subscribeWithSavedMethod } = useSubscriptionActions();
2554
+ const t = {
2555
+ ...defaultTranslations2,
2556
+ ...translations
2557
+ };
2303
2558
  React4.useEffect(() => {
2304
2559
  if (open) {
2305
2560
  setIdempotencyKey(crypto.randomUUID());
@@ -2323,13 +2578,31 @@ var SubscriptionCheckoutModal = ({
2323
2578
  console.debug("[payments-ui] subscription success", result);
2324
2579
  }
2325
2580
  };
2326
- const assertCheckoutSuccess = (status, message) => {
2327
- if (status === "blocked") {
2328
- throw new Error(message || "This subscription cannot be completed right now.");
2581
+ const handleCheckoutResponse = (response) => {
2582
+ if (response.status === "blocked") {
2583
+ throw new Error(response.message || "This subscription cannot be completed right now.");
2584
+ }
2585
+ const nextAction = response.next_action;
2586
+ if (nextAction?.type === "redirect_to_url") {
2587
+ const redirectUrl = nextAction.redirect_to_url?.url || response.payment?.redirect_url;
2588
+ if (!redirectUrl) {
2589
+ throw new Error(response.message || "Checkout requires a redirect URL.");
2590
+ }
2591
+ if (typeof window !== "undefined") {
2592
+ window.location.assign(redirectUrl);
2593
+ }
2594
+ return;
2595
+ }
2596
+ if (response.payment?.redirect_url) {
2597
+ if (typeof window !== "undefined") {
2598
+ window.location.assign(response.payment.redirect_url);
2599
+ }
2600
+ return;
2329
2601
  }
2330
- if (status === "redirect_required") {
2331
- throw new Error(message || "Additional action required in an alternate flow.");
2602
+ if (nextAction && nextAction.type !== "none") {
2603
+ throw new Error(response.message || "Unsupported checkout action.");
2332
2604
  }
2605
+ notifySuccess();
2333
2606
  };
2334
2607
  const handleNewCardPayment = async ({ token, billing }) => {
2335
2608
  const response = await subscribeWithCard({
@@ -2339,8 +2612,7 @@ var SubscriptionCheckoutModal = ({
2339
2612
  paymentToken: token,
2340
2613
  priceId: ensurePrice()
2341
2614
  });
2342
- assertCheckoutSuccess(response.status, response.message);
2343
- notifySuccess();
2615
+ handleCheckoutResponse(response);
2344
2616
  };
2345
2617
  const handleSavedMethodPayment = async ({ paymentMethodId }) => {
2346
2618
  const response = await subscribeWithSavedMethod({
@@ -2350,8 +2622,16 @@ var SubscriptionCheckoutModal = ({
2350
2622
  email: userEmail ?? "",
2351
2623
  idempotencyKey
2352
2624
  });
2353
- assertCheckoutSuccess(response.status, response.message);
2354
- notifySuccess();
2625
+ handleCheckoutResponse(response);
2626
+ };
2627
+ const handleCcbillPayment = async ({ billing }) => {
2628
+ const response = await subscribeWithCard({
2629
+ billing,
2630
+ idempotencyKey,
2631
+ processor: "ccbill",
2632
+ priceId: ensurePrice()
2633
+ });
2634
+ handleCheckoutResponse(response);
2355
2635
  };
2356
2636
  const solanaSuccess = (result) => {
2357
2637
  notifySuccess(result);
@@ -2367,11 +2647,11 @@ var SubscriptionCheckoutModal = ({
2367
2647
  {
2368
2648
  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",
2369
2649
  children: [
2370
- /* @__PURE__ */ jsxRuntime.jsx(DialogHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { className: "flex items-center gap-2 text-foreground", children: "Checkout" }) }),
2650
+ /* @__PURE__ */ jsxRuntime.jsx(DialogHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { className: "flex items-center gap-2 text-foreground", children: t.title }) }),
2371
2651
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: !priceId ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-center px-3 py-2 text-sm text-destructive", children: [
2372
2652
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-4 w-4" }),
2373
2653
  " ",
2374
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Select a subscription plan to continue." })
2654
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t.selectPlanMessage })
2375
2655
  ] }) : /* @__PURE__ */ jsxRuntime.jsx(
2376
2656
  PaymentExperience,
2377
2657
  {
@@ -2384,7 +2664,9 @@ var SubscriptionCheckoutModal = ({
2384
2664
  enableStoredMethods: Boolean(priceId),
2385
2665
  enableSolanaPay: enableSolanaPay && Boolean(priceId),
2386
2666
  onNewCardPayment: priceId ? handleNewCardPayment : void 0,
2387
- onSavedMethodPayment: priceId ? handleSavedMethodPayment : void 0
2667
+ onSavedMethodPayment: priceId ? handleSavedMethodPayment : void 0,
2668
+ onCcbillPayment: priceId ? handleCcbillPayment : void 0,
2669
+ translations: t
2388
2670
  }
2389
2671
  ) })
2390
2672
  ]
@@ -2394,7 +2676,9 @@ var SubscriptionCheckoutModal = ({
2394
2676
  SubscriptionSuccessDialog,
2395
2677
  {
2396
2678
  open: showSuccess,
2397
- onClose: () => setShowSuccess(false),
2679
+ onClose: () => {
2680
+ setShowSuccess(false);
2681
+ },
2398
2682
  planName,
2399
2683
  amountLabel: amountLabel ?? `$${usdAmount.toFixed(2)}`,
2400
2684
  billingPeriodLabel
@@ -2819,7 +3103,7 @@ var notifyDefault = (payload) => {
2819
3103
  const level = payload.status === "destructive" ? "error" : "info";
2820
3104
  console[level === "error" ? "error" : "log"]("[payments-ui] cancellation", payload);
2821
3105
  };
2822
- var defaultTranslations = {
3106
+ var defaultTranslations3 = {
2823
3107
  buttonLabel: "Cancel Membership",
2824
3108
  title: "Confirm Membership Cancellation",
2825
3109
  description: "You are about to cancel your membership. Please review the consequences:",
@@ -2841,23 +3125,33 @@ var CancelMembershipDialog = ({
2841
3125
  minReasonLength = 15,
2842
3126
  onCancelled,
2843
3127
  onNotify,
2844
- translations: customTranslations
3128
+ translations: customTranslations,
3129
+ open,
3130
+ onOpenChange
2845
3131
  }) => {
2846
3132
  const { client } = usePaymentContext();
2847
3133
  const notify = onNotify ?? notifyDefault;
2848
- const t = { ...defaultTranslations, ...customTranslations };
3134
+ const t = { ...defaultTranslations3, ...customTranslations };
2849
3135
  const [cancelReason, setCancelReason] = React4.useState("");
2850
- const [isOpen, setIsOpen] = React4.useState(false);
3136
+ const [internalOpen, setInternalOpen] = React4.useState(false);
2851
3137
  const [isReasonValid, setIsReasonValid] = React4.useState(false);
2852
3138
  const [hasInteracted, setHasInteracted] = React4.useState(false);
2853
3139
  const [isSubmitting, setIsSubmitting] = React4.useState(false);
3140
+ const isControlled = typeof open === "boolean";
3141
+ const isOpen = isControlled ? open : internalOpen;
3142
+ const setOpen = (next) => {
3143
+ if (!isControlled) {
3144
+ setInternalOpen(next);
3145
+ }
3146
+ onOpenChange?.(next);
3147
+ };
2854
3148
  React4.useEffect(() => {
2855
3149
  const trimmed = cancelReason.trim();
2856
3150
  setIsReasonValid(trimmed.length >= minReasonLength);
2857
3151
  }, [cancelReason, minReasonLength]);
2858
- const handleOpenChange = (open) => {
2859
- setIsOpen(open);
2860
- if (!open) {
3152
+ const handleOpenChange = (open2) => {
3153
+ setOpen(open2);
3154
+ if (!open2) {
2861
3155
  setCancelReason("");
2862
3156
  setIsReasonValid(false);
2863
3157
  setHasInteracted(false);
@@ -2894,7 +3188,7 @@ var CancelMembershipDialog = ({
2894
3188
  };
2895
3189
  const showError = hasInteracted && !isReasonValid;
2896
3190
  return /* @__PURE__ */ jsxRuntime.jsxs(AlertDialog, { open: isOpen, onOpenChange: handleOpenChange, children: [
2897
- /* @__PURE__ */ jsxRuntime.jsx(AlertDialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "outline", className: "border-destructive/50 text-destructive", children: [
3191
+ /* @__PURE__ */ jsxRuntime.jsx(AlertDialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { className: "bg-destructive text-destructive-foreground border-destructive/50 hover:bg-destructive/90", children: [
2898
3192
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Ban, { className: "mr-2 h-4 w-4" }),
2899
3193
  " ",
2900
3194
  t.buttonLabel
@@ -2961,7 +3255,7 @@ var notifyDefault2 = (payload) => {
2961
3255
  const level = payload.status === "destructive" ? "error" : "info";
2962
3256
  console[level === "error" ? "error" : "log"]("[payments-ui] billing", payload);
2963
3257
  };
2964
- var defaultTranslations2 = {
3258
+ var defaultTranslations4 = {
2965
3259
  title: "Transaction History",
2966
3260
  description: "Record of billing history",
2967
3261
  reviewActivity: "Review your account activity below",
@@ -2983,7 +3277,7 @@ var BillingHistory = ({
2983
3277
  }) => {
2984
3278
  const { client } = usePaymentContext();
2985
3279
  const notify = onNotify ?? notifyDefault2;
2986
- const t = { ...defaultTranslations2, ...customTranslations };
3280
+ const t = { ...defaultTranslations4, ...customTranslations };
2987
3281
  const [isExpanded, setIsExpanded] = React4.useState(false);
2988
3282
  const observerRef = React4.useRef(null);
2989
3283
  const loadMoreRef = React4.useRef(null);
@@ -3092,15 +3386,21 @@ var BillingHistory = ({
3092
3386
  ] }) });
3093
3387
  };
3094
3388
  var formatCardLabel2 = (method) => {
3095
- const brand = method.card_type ? method.card_type.toUpperCase() : "CARD";
3096
- const lastFour = method.last_four ? `\u2022\u2022\u2022\u2022 ${method.last_four}` : "";
3389
+ if (method.card) {
3390
+ const brand2 = method.card.brand ? method.card.brand.toUpperCase() : "CARD";
3391
+ const lastFour2 = method.card.last4 ? `\u2022\u2022\u2022\u2022 ${method.card.last4}` : "";
3392
+ 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)}` : "";
3393
+ return `${brand2} ${lastFour2}${exp}`.trim();
3394
+ }
3395
+ const brand = "CARD";
3396
+ const lastFour = "";
3097
3397
  return `${brand} ${lastFour}`.trim();
3098
3398
  };
3099
3399
  var notifyDefault3 = (payload) => {
3100
3400
  const level = payload.status === "destructive" ? "error" : "info";
3101
3401
  console[level === "error" ? "error" : "log"]("[payments-ui] notification", payload);
3102
3402
  };
3103
- var defaultTranslations3 = {
3403
+ var defaultTranslations5 = {
3104
3404
  title: "Payment Methods",
3105
3405
  description: "Manage your saved billing cards",
3106
3406
  addCard: "Add card",
@@ -3121,7 +3421,11 @@ var defaultTranslations3 = {
3121
3421
  cardUpdated: "Card updated",
3122
3422
  unableToRemoveCard: "Unable to remove card",
3123
3423
  defaultPaymentMethodUpdated: "Default payment method updated",
3124
- unableToSetDefault: "Unable to set default payment method"
3424
+ unableToSetDefault: "Unable to set default payment method",
3425
+ errors: {},
3426
+ activeSubscriptions: "Active subscriptions",
3427
+ showSubscriptions: "Show",
3428
+ hideSubscriptions: "Hide"
3125
3429
  };
3126
3430
  var PaymentMethodsSection = ({
3127
3431
  isAuthenticated = true,
@@ -3132,8 +3436,7 @@ var PaymentMethodsSection = ({
3132
3436
  onNotify,
3133
3437
  translations: customTranslations
3134
3438
  }) => {
3135
- const { client } = usePaymentContext();
3136
- const queryClient = reactQuery.useQueryClient();
3439
+ const { client, queryClient } = usePaymentContext();
3137
3440
  const paymentMethods = {
3138
3441
  list: (params) => client.listPaymentMethods({ limit: params.pageSize }),
3139
3442
  create: (payload) => client.createPaymentMethod(payload),
@@ -3143,13 +3446,15 @@ var PaymentMethodsSection = ({
3143
3446
  };
3144
3447
  const [isModalOpen, setIsModalOpen] = React4.useState(false);
3145
3448
  const [deletingId, setDeletingId] = React4.useState(null);
3449
+ const [createErrorMessage, setCreateErrorMessage] = React4.useState(null);
3450
+ const [expandedSubscriptions, setExpandedSubscriptions] = React4.useState({});
3146
3451
  const notify = onNotify ?? notifyDefault3;
3147
- const t = { ...defaultTranslations3, ...customTranslations };
3452
+ const t = { ...defaultTranslations5, ...customTranslations };
3148
3453
  const queryKey = ["payments-ui", "payment-methods"];
3149
3454
  const paymentQuery = reactQuery.useQuery({
3150
3455
  queryKey,
3151
3456
  queryFn: () => paymentMethods.list({ pageSize: 50 }),
3152
- enabled: isAuthenticated,
3457
+ enabled: isAuthenticated && !!client,
3153
3458
  staleTime: 3e4
3154
3459
  });
3155
3460
  const createMutation = reactQuery.useMutation({
@@ -3158,11 +3463,14 @@ var PaymentMethodsSection = ({
3158
3463
  notify({ title: t.cardAddedSuccess, status: "success" });
3159
3464
  setIsModalOpen(false);
3160
3465
  void queryClient.invalidateQueries({ queryKey });
3466
+ setCreateErrorMessage(null);
3161
3467
  },
3162
3468
  onError: (error) => {
3469
+ const message = resolveErrorMessageByCode(error, t.errors, error.message);
3470
+ setCreateErrorMessage(message);
3163
3471
  notify({
3164
3472
  title: t.unableToAddCard,
3165
- description: error.message,
3473
+ description: message,
3166
3474
  status: "destructive"
3167
3475
  });
3168
3476
  }
@@ -3173,6 +3481,9 @@ var PaymentMethodsSection = ({
3173
3481
  onSuccess: () => {
3174
3482
  notify({ title: t.cardRemoved, status: "success" });
3175
3483
  void queryClient.invalidateQueries({ queryKey });
3484
+ if (paymentQuery.refetch) {
3485
+ paymentQuery.refetch();
3486
+ }
3176
3487
  },
3177
3488
  onError: (error) => {
3178
3489
  notify({
@@ -3183,7 +3494,7 @@ var PaymentMethodsSection = ({
3183
3494
  },
3184
3495
  onSettled: () => setDeletingId(null)
3185
3496
  });
3186
- const activateMutation = reactQuery.useMutation({
3497
+ reactQuery.useMutation({
3187
3498
  mutationFn: (id) => paymentMethods.activate(id),
3188
3499
  onSuccess: () => {
3189
3500
  notify({ title: t.defaultPaymentMethodUpdated, status: "success" });
@@ -3200,8 +3511,14 @@ var PaymentMethodsSection = ({
3200
3511
  React4.useEffect(() => {
3201
3512
  if (!isModalOpen) {
3202
3513
  createMutation.reset();
3514
+ setCreateErrorMessage(null);
3515
+ }
3516
+ }, [isModalOpen]);
3517
+ React4.useEffect(() => {
3518
+ if (!isModalOpen && paymentQuery.refetch) {
3519
+ paymentQuery.refetch();
3203
3520
  }
3204
- }, [createMutation, isModalOpen]);
3521
+ }, [isModalOpen]);
3205
3522
  const payments = React4.useMemo(() => paymentQuery.data?.data ?? [], [paymentQuery.data]);
3206
3523
  const loading = paymentQuery.isLoading || paymentQuery.isFetching;
3207
3524
  const buildPayload = (token, billing) => ({
@@ -3215,9 +3532,13 @@ var PaymentMethodsSection = ({
3215
3532
  zip: billing.postalCode,
3216
3533
  country: billing.country,
3217
3534
  email: billing.email,
3218
- provider: billing.provider
3535
+ provider: billing.provider,
3536
+ last_four: billing.last_four,
3537
+ card_type: billing.card_type,
3538
+ expiry_date: billing.expiry_date
3219
3539
  });
3220
3540
  const handleCardTokenize = (token, billing) => {
3541
+ setCreateErrorMessage(null);
3221
3542
  createMutation.mutate(buildPayload(token, billing));
3222
3543
  };
3223
3544
  return /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "border-0 bg-black/30 shadow-2xl backdrop-blur-xl", children: [
@@ -3236,32 +3557,19 @@ var PaymentMethodsSection = ({
3236
3557
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-2 h-5 w-5 animate-spin" }),
3237
3558
  " ",
3238
3559
  t.loadingCards
3239
- ] }) : payments.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 text-sm text-center", children: t.noPaymentMethods }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: payments.map((method) => /* @__PURE__ */ jsxRuntime.jsxs(
3240
- "div",
3241
- {
3242
- className: "rounded-lg border bg-white/5 p-4 shadow-sm",
3243
- children: [
3244
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
3245
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between", children: [
3246
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base font-medium text-white", children: formatCardLabel2(method) }),
3247
- /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: method.is_active ? "default" : "secondary", children: method.is_active ? t.active : t.inactive })
3560
+ ] }) : payments.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 text-sm text-center", children: t.noPaymentMethods }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: payments.map((method) => {
3561
+ (method.subscriptions?.length ?? 0) > 0;
3562
+ expandedSubscriptions[method.id] ?? false;
3563
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3564
+ "div",
3565
+ {
3566
+ className: "rounded-lg border bg-white/5 p-4 shadow-sm",
3567
+ children: [
3568
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
3569
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-between", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base font-medium text-white", children: formatCardLabel2(method) }) }),
3570
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: method.failure_reason && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "destructive", children: method.failure_reason }) })
3248
3571
  ] }),
3249
- /* @__PURE__ */ jsxRuntime.jsx("div", { children: method.failure_reason && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "destructive", children: method.failure_reason }) })
3250
- ] }),
3251
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 flex flex-wrap gap-2", children: [
3252
- /* @__PURE__ */ jsxRuntime.jsxs(
3253
- Button,
3254
- {
3255
- variant: "outline",
3256
- disabled: method.is_active || activateMutation.isPending,
3257
- onClick: () => activateMutation.mutate(method.id),
3258
- children: [
3259
- activateMutation.isPending ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null,
3260
- method.is_active ? t.defaultMethod : t.makeDefault
3261
- ]
3262
- }
3263
- ),
3264
- /* @__PURE__ */ jsxRuntime.jsxs(
3572
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 flex flex-wrap gap-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
3265
3573
  Button,
3266
3574
  {
3267
3575
  variant: "ghost",
@@ -3273,12 +3581,12 @@ var PaymentMethodsSection = ({
3273
3581
  t.remove
3274
3582
  ]
3275
3583
  }
3276
- )
3277
- ] })
3278
- ]
3279
- },
3280
- method.id
3281
- )) }) }),
3584
+ ) })
3585
+ ]
3586
+ },
3587
+ method.id
3588
+ );
3589
+ }) }) }),
3282
3590
  /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open: isModalOpen, onOpenChange: setIsModalOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(
3283
3591
  DialogContent,
3284
3592
  {
@@ -3296,12 +3604,13 @@ var PaymentMethodsSection = ({
3296
3604
  collectPrefix,
3297
3605
  onTokenize: handleCardTokenize,
3298
3606
  submitting: createMutation.isPending,
3607
+ translations: t,
3299
3608
  defaultValues: {
3300
3609
  provider,
3301
3610
  email: userEmail ?? "",
3302
3611
  country: defaultCountry
3303
3612
  },
3304
- externalError: createMutation.error?.message ?? null
3613
+ externalError: createErrorMessage
3305
3614
  }
3306
3615
  )
3307
3616
  ]
@@ -3309,6 +3618,387 @@ var PaymentMethodsSection = ({
3309
3618
  ) })
3310
3619
  ] });
3311
3620
  };
3621
+ var notifyDefault4 = (payload) => {
3622
+ const level = payload.status === "destructive" ? "error" : "info";
3623
+ console[level === "error" ? "error" : "log"]("[payments-ui] notification", payload);
3624
+ };
3625
+ var defaultTranslations6 = {
3626
+ title: "Subscriptions",
3627
+ description: "Manage your active and recent subscriptions.",
3628
+ loading: "Loading subscriptions...",
3629
+ noSubscriptions: "No subscriptions found.",
3630
+ status: "Status",
3631
+ active: "Active",
3632
+ cancelled: "Cancelled",
3633
+ pastDue: "Past due",
3634
+ pending: "Pending",
3635
+ paymentMethodLabel: "Payment method",
3636
+ changePaymentMethod: "Change payment method",
3637
+ paymentMethodUpdated: "Payment method updated",
3638
+ paymentMethodUpdateFailed: "Unable to update payment method",
3639
+ resume: "Resume subscription",
3640
+ changePlan: "Change plan",
3641
+ changePlanPlaceholder: "Enter a new price ID",
3642
+ planChanged: "Subscription updated",
3643
+ planChangeFailed: "Unable to update subscription",
3644
+ planChangeUnavailable: "Plan changes are unavailable for this subscription.",
3645
+ resumeSuccess: "Resume requested",
3646
+ resumeFailed: "Unable to resume subscription",
3647
+ update: "Update",
3648
+ product: "Product",
3649
+ price: "Price",
3650
+ currentPeriod: "Current period",
3651
+ refresh: "Refresh",
3652
+ manage: "Manage",
3653
+ close: "Close",
3654
+ paymentMethodTab: "Payment method",
3655
+ changePlanTab: "Change plan",
3656
+ statusTab: "Details",
3657
+ cancelTab: "Cancel/Resume"
3658
+ };
3659
+ var formatCardLabel3 = (method) => {
3660
+ if (method.card) {
3661
+ const brand2 = method.card.brand ? method.card.brand.toUpperCase() : "CARD";
3662
+ const lastFour2 = method.card.last4 ? `\u2022\u2022\u2022\u2022 ${method.card.last4}` : "";
3663
+ 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)}` : "";
3664
+ return `${brand2} ${lastFour2}${exp}`.trim();
3665
+ }
3666
+ const brand = "CARD";
3667
+ const lastFour = "";
3668
+ return `${brand} ${lastFour}`.trim();
3669
+ };
3670
+ var SubscriptionsSection = ({
3671
+ isAuthenticated = true,
3672
+ translations: customTranslations,
3673
+ onNotify,
3674
+ statusFilter,
3675
+ cancelDialogTranslations,
3676
+ onCancelled
3677
+ }) => {
3678
+ const { client, queryClient } = usePaymentContext();
3679
+ const notify = onNotify ?? notifyDefault4;
3680
+ const t = { ...defaultTranslations6, ...customTranslations };
3681
+ const cancelTranslations = cancelDialogTranslations ?? defaultTranslations3;
3682
+ const [paymentSelections, setPaymentSelections] = React4.useState({});
3683
+ const [priceInputs, setPriceInputs] = React4.useState({});
3684
+ const [activeSubId, setActiveSubId] = React4.useState(null);
3685
+ const [cancelDialogOpen, setCancelDialogOpen] = React4.useState(false);
3686
+ const [sectionsOpen, setSectionsOpen] = React4.useState({
3687
+ status: true,
3688
+ payment: false,
3689
+ plan: false,
3690
+ cancel: false
3691
+ });
3692
+ const normalizedStatusFilter = statusFilter ?? "all";
3693
+ const subscriptionsQueryKey = ["payments-ui", "subscriptions", normalizedStatusFilter];
3694
+ const paymentMethodsQueryKey = ["payments-ui", "payment-methods"];
3695
+ const subscriptionsQuery = reactQuery.useQuery({
3696
+ queryKey: subscriptionsQueryKey,
3697
+ queryFn: () => client.listSubscriptions({
3698
+ status: normalizedStatusFilter,
3699
+ limit: 50
3700
+ }),
3701
+ enabled: isAuthenticated && !!client,
3702
+ staleTime: 3e4
3703
+ });
3704
+ const paymentMethodsQuery = reactQuery.useQuery({
3705
+ queryKey: paymentMethodsQueryKey,
3706
+ queryFn: () => client.listPaymentMethods({ limit: 50 }),
3707
+ enabled: isAuthenticated && !!client,
3708
+ staleTime: 3e4
3709
+ });
3710
+ const subscriptions = React4.useMemo(() => subscriptionsQuery.data?.data ?? [], [subscriptionsQuery.data]);
3711
+ const paymentMethods = React4.useMemo(() => paymentMethodsQuery.data?.data ?? [], [paymentMethodsQuery.data]);
3712
+ const activeSubscription = React4.useMemo(
3713
+ () => subscriptions.find((sub) => sub.id === activeSubId),
3714
+ [subscriptions, activeSubId]
3715
+ );
3716
+ React4.useEffect(() => {
3717
+ setPaymentSelections((prev) => {
3718
+ const next = {};
3719
+ subscriptions.forEach((sub) => {
3720
+ next[sub.id] = prev[sub.id] ?? "";
3721
+ });
3722
+ return next;
3723
+ });
3724
+ }, [subscriptions]);
3725
+ React4.useEffect(() => {
3726
+ setCancelDialogOpen(false);
3727
+ }, [activeSubId]);
3728
+ const updatePaymentMethodMutation = reactQuery.useMutation({
3729
+ mutationFn: (payload) => client.updateSubscriptionPaymentMethod({
3730
+ subscription_id: payload.subscriptionId,
3731
+ payment_method_id: payload.paymentMethodId
3732
+ }),
3733
+ onSuccess: () => {
3734
+ notify({ title: t.paymentMethodUpdated, status: "success" });
3735
+ void queryClient.invalidateQueries({ queryKey: subscriptionsQueryKey });
3736
+ },
3737
+ onError: (error) => {
3738
+ notify({
3739
+ title: t.paymentMethodUpdateFailed,
3740
+ description: resolveErrorMessageByCode(error, {}, error.message),
3741
+ status: "destructive"
3742
+ });
3743
+ }
3744
+ });
3745
+ const resumeSubscriptionMutation = reactQuery.useMutation({
3746
+ mutationFn: () => client.resumeSubscription(),
3747
+ onSuccess: () => {
3748
+ notify({ title: t.resumeSuccess, status: "success" });
3749
+ void queryClient.invalidateQueries({ queryKey: subscriptionsQueryKey });
3750
+ },
3751
+ onError: (error) => {
3752
+ notify({
3753
+ title: t.resumeFailed,
3754
+ description: resolveErrorMessageByCode(error, {}, error.message),
3755
+ status: "destructive"
3756
+ });
3757
+ }
3758
+ });
3759
+ reactQuery.useMutation({
3760
+ mutationFn: (payload) => client.changeSubscription({ price_id: payload.priceId }),
3761
+ onSuccess: () => {
3762
+ notify({ title: t.planChanged, status: "success" });
3763
+ void queryClient.invalidateQueries({ queryKey: subscriptionsQueryKey });
3764
+ },
3765
+ onError: (error) => {
3766
+ notify({
3767
+ title: t.planChangeFailed,
3768
+ description: resolveErrorMessageByCode(error, {}, error.message),
3769
+ status: "destructive"
3770
+ });
3771
+ }
3772
+ });
3773
+ const isLoading = subscriptionsQuery.isLoading || subscriptionsQuery.isFetching;
3774
+ const isError = subscriptionsQuery.isError;
3775
+ const errorMessage = subscriptionsQuery.error instanceof Error ? subscriptionsQuery.error.message : void 0;
3776
+ React4.useEffect(() => {
3777
+ if (subscriptionsQuery.refetch) {
3778
+ void subscriptionsQuery.refetch();
3779
+ }
3780
+ }, []);
3781
+ const formatPrice = (sub) => {
3782
+ const price = sub.price;
3783
+ if (!price) return t.price;
3784
+ const amount = (price.amount / 100).toFixed(2);
3785
+ const currency = price.currency ? price.currency?.toUpperCase() : "";
3786
+ return `${price.display_name ?? t.price} \u2014 ${currency} ${amount}`;
3787
+ };
3788
+ const renderStatusBadge = (status) => {
3789
+ const normalized = status.toLowerCase();
3790
+ const label = normalized === "active" ? t.active : normalized === "past_due" ? t.pastDue : normalized === "pending" ? t.pending : t.cancelled;
3791
+ const variant = normalized === "active" ? "default" : normalized === "past_due" ? "destructive" : normalized === "pending" ? "outline" : "secondary";
3792
+ return /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant, children: label });
3793
+ };
3794
+ const handleUpdatePaymentMethod = (subscriptionId) => {
3795
+ const paymentMethodId = paymentSelections[subscriptionId];
3796
+ const currentPaymentMethodId = "pm_" + subscriptions.find((s) => s.id === subscriptionId)?.payment_method_id;
3797
+ if (!paymentMethodId) return;
3798
+ if (currentPaymentMethodId && paymentMethodId === currentPaymentMethodId) {
3799
+ notify({
3800
+ title: t.paymentMethodUpdateFailed,
3801
+ description: t.paymentMethodUpdateFailed,
3802
+ status: "destructive"
3803
+ });
3804
+ return;
3805
+ }
3806
+ updatePaymentMethodMutation.mutate({ subscriptionId, paymentMethodId });
3807
+ };
3808
+ const toggleSection = (key) => {
3809
+ setSectionsOpen((prev) => ({
3810
+ ...prev,
3811
+ [key]: !prev[key]
3812
+ }));
3813
+ };
3814
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3815
+ /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: "border-0 bg-black/30 shadow-2xl backdrop-blur-xl", children: [
3816
+ /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "flex flex-col gap-4 md:flex-row md:items-center md:justify-between", children: [
3817
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3818
+ /* @__PURE__ */ jsxRuntime.jsxs(CardTitle, { className: "flex items-center gap-2", children: [
3819
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.WalletCards, { className: "h-5 w-5" }),
3820
+ t.title
3821
+ ] }),
3822
+ /* @__PURE__ */ jsxRuntime.jsx(CardDescription, { children: t.description })
3823
+ ] }),
3824
+ /* @__PURE__ */ jsxRuntime.jsxs(
3825
+ Button,
3826
+ {
3827
+ variant: "ghost",
3828
+ size: "sm",
3829
+ onClick: () => subscriptionsQuery.refetch?.(),
3830
+ disabled: subscriptionsQuery.isFetching,
3831
+ children: [
3832
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { className: "mr-2 h-4 w-4" }),
3833
+ " ",
3834
+ subscriptionsQuery.isFetching ? t.loading : t.refresh
3835
+ ]
3836
+ }
3837
+ )
3838
+ ] }),
3839
+ /* @__PURE__ */ jsxRuntime.jsx(CardContent, { className: "space-y-4", children: isLoading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center py-10 text-white/60", children: [
3840
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-2 h-5 w-5 animate-spin" }),
3841
+ " ",
3842
+ t.loading
3843
+ ] }) : isError ? /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("div", { className: "p-6 text-sm text-center", children: t.noSubscriptions }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: subscriptions.map((subscription) => {
3844
+ return /* @__PURE__ */ jsxRuntime.jsx(
3845
+ "div",
3846
+ {
3847
+ className: "rounded-lg border bg-white/5 p-4 shadow-sm",
3848
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-4", children: [
3849
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3850
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base font-semibold text-white", children: subscription.product?.display_name ?? t.product }),
3851
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-white/80", children: formatPrice(subscription) })
3852
+ ] }),
3853
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
3854
+ renderStatusBadge(subscription.status),
3855
+ /* @__PURE__ */ jsxRuntime.jsx(
3856
+ Button,
3857
+ {
3858
+ variant: "default",
3859
+ size: "sm",
3860
+ className: "rounded-full bg-foreground/10 text-muted-foreground hover:bg-foreground/20 hover:text-foreground",
3861
+ onClick: () => setActiveSubId(subscription.id),
3862
+ children: t.update
3863
+ }
3864
+ )
3865
+ ] })
3866
+ ] })
3867
+ },
3868
+ subscription.id
3869
+ );
3870
+ }) }) })
3871
+ ] }),
3872
+ /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open: !!activeSubscription, onOpenChange: (open) => setActiveSubId(open ? activeSubId : null), children: /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { className: "max-w-2xl min-h-[300px] border border-white/20 bg-background-regular p-6 text-foreground", children: [
3873
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
3874
+ /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { className: "flex items-center gap-2", children: activeSubscription?.product?.display_name ?? t.product }),
3875
+ /* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { className: "text-white/70", children: activeSubscription ? formatPrice(activeSubscription) : null })
3876
+ ] }),
3877
+ activeSubscription && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 space-y-3", children: [
3878
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "overflow-hidden rounded-lg border border-white/15 bg-white/5", children: [
3879
+ /* @__PURE__ */ jsxRuntime.jsxs(
3880
+ "button",
3881
+ {
3882
+ type: "button",
3883
+ className: "flex w-full items-center justify-between px-4 py-3 text-left text-white hover:bg-white/10",
3884
+ onClick: () => toggleSection("status"),
3885
+ children: [
3886
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold", children: t.statusTab }),
3887
+ /* @__PURE__ */ jsxRuntime.jsx(
3888
+ lucideReact.ChevronDown,
3889
+ {
3890
+ className: `h-4 w-4 transition-transform ${sectionsOpen.status ? "rotate-180" : ""}`
3891
+ }
3892
+ )
3893
+ ]
3894
+ }
3895
+ ),
3896
+ sectionsOpen.status ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3 border-t border-white/10 px-4 py-3", children: [
3897
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between rounded-lg border border-white/10 bg-white/5 p-3", children: [
3898
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium text-white/80", children: t.status }),
3899
+ renderStatusBadge(activeSubscription.status)
3900
+ ] }),
3901
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-white/60", children: [
3902
+ t.currentPeriod,
3903
+ ": ",
3904
+ activeSubscription.started_at ? new Date(activeSubscription.started_at).toLocaleDateString() : "\u2014",
3905
+ " \u2192",
3906
+ " ",
3907
+ activeSubscription.current_period_ends_at ?? "\u2014"
3908
+ ] }),
3909
+ activeSubscription.status.toLowerCase() === "cancelled" ? /* @__PURE__ */ jsxRuntime.jsxs(
3910
+ Button,
3911
+ {
3912
+ variant: "secondary",
3913
+ onClick: () => resumeSubscriptionMutation.mutate(),
3914
+ disabled: resumeSubscriptionMutation.isPending,
3915
+ className: "rounded-full px-4",
3916
+ children: [
3917
+ resumeSubscriptionMutation.isPending ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null,
3918
+ t.resume
3919
+ ]
3920
+ }
3921
+ ) : null
3922
+ ] }) : null
3923
+ ] }),
3924
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "overflow-hidden rounded-lg border border-white/15 bg-white/5", children: [
3925
+ /* @__PURE__ */ jsxRuntime.jsxs(
3926
+ "button",
3927
+ {
3928
+ type: "button",
3929
+ className: "flex w-full items-center justify-between px-4 py-3 text-left text-white hover:bg-white/10",
3930
+ onClick: () => toggleSection("payment"),
3931
+ children: [
3932
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold", children: t.paymentMethodTab }),
3933
+ /* @__PURE__ */ jsxRuntime.jsx(
3934
+ lucideReact.ChevronDown,
3935
+ {
3936
+ className: `h-4 w-4 transition-transform ${sectionsOpen.payment ? "rotate-180" : ""}`
3937
+ }
3938
+ )
3939
+ ]
3940
+ }
3941
+ ),
3942
+ sectionsOpen.payment ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 border-t border-white/10 px-4 py-3", children: [
3943
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs uppercase tracking-wide text-white/60", children: t.paymentMethodLabel }),
3944
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 md:flex-row md:items-center md:gap-2", children: [
3945
+ /* @__PURE__ */ jsxRuntime.jsxs(
3946
+ Select,
3947
+ {
3948
+ value: paymentSelections[activeSubscription.id] || "pm_" + activeSubscription.payment_method_id || "",
3949
+ onValueChange: (value) => setPaymentSelections((prev) => ({ ...prev, [activeSubscription.id]: value })),
3950
+ disabled: paymentMethodsQuery.isLoading || paymentMethods.length === 0,
3951
+ children: [
3952
+ /* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "w-full md:w-64 text-white", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: paymentMethodsQuery.isLoading ? t.loading : "" }) }),
3953
+ /* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: paymentMethods.filter((method) => method.id !== activeSubscription.payment_method_id).map((method) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: method.id, children: formatCardLabel3(method) }, method.id)) })
3954
+ ]
3955
+ }
3956
+ ),
3957
+ /* @__PURE__ */ jsxRuntime.jsxs(
3958
+ Button,
3959
+ {
3960
+ size: "sm",
3961
+ onClick: () => handleUpdatePaymentMethod(activeSubscription.id),
3962
+ disabled: updatePaymentMethodMutation.isPending || !paymentSelections[activeSubscription.id] || paymentSelections[activeSubscription.id] === activeSubscription.payment_method_id,
3963
+ className: "border-0 bg-green-bg text-white hover:bg-green-bg/80 disabled:opacity-50",
3964
+ children: [
3965
+ updatePaymentMethodMutation.isPending ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null,
3966
+ t.update
3967
+ ]
3968
+ }
3969
+ )
3970
+ ] })
3971
+ ] }) : null
3972
+ ] })
3973
+ ] }),
3974
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogFooter, { className: "flex flex-wrap gap-2", children: [
3975
+ /* @__PURE__ */ jsxRuntime.jsx(
3976
+ CancelMembershipDialog,
3977
+ {
3978
+ translations: cancelTranslations,
3979
+ onNotify,
3980
+ open: cancelDialogOpen,
3981
+ onOpenChange: (openState) => setCancelDialogOpen(openState),
3982
+ onCancelled: () => {
3983
+ void queryClient.invalidateQueries({ queryKey: subscriptionsQueryKey });
3984
+ onCancelled?.();
3985
+ setActiveSubId(null);
3986
+ }
3987
+ }
3988
+ ),
3989
+ /* @__PURE__ */ jsxRuntime.jsx(
3990
+ Button,
3991
+ {
3992
+ variant: "secondary",
3993
+ onClick: () => setActiveSubId(null),
3994
+ className: "border-white/20 bg-transparent text-foreground hover:bg-foreground/10 hover:text-foreground",
3995
+ children: t.close
3996
+ }
3997
+ )
3998
+ ] })
3999
+ ] }) })
4000
+ ] });
4001
+ };
3312
4002
  var Checkbox = React4__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
3313
4003
  CheckboxPrimitive__namespace.Root,
3314
4004
  {
@@ -3838,9 +4528,13 @@ exports.SolanaPaymentView = SolanaPaymentView;
3838
4528
  exports.StoredPaymentMethods = StoredPaymentMethods;
3839
4529
  exports.SubscriptionCheckoutModal = SubscriptionCheckoutModal;
3840
4530
  exports.SubscriptionSuccessDialog = SubscriptionSuccessDialog;
4531
+ exports.SubscriptionsSection = SubscriptionsSection;
3841
4532
  exports.WalletDialog = WalletDialog;
3842
4533
  exports.WalletModal = WalletModal;
3843
4534
  exports.createClient = createClient;
4535
+ exports.defaultCardDetailsFormTranslations = defaultCardDetailsFormTranslations;
4536
+ exports.defaultPaymentExperienceTranslations = defaultPaymentExperienceTranslations;
4537
+ exports.defaultTranslations = defaultTranslations3;
3844
4538
  exports.usePaymentContext = usePaymentContext;
3845
4539
  exports.usePaymentDialogs = usePaymentDialogs;
3846
4540
  exports.usePaymentMethods = usePaymentMethods;