@burdenoff/microfe-billing 2026.531.2 → 2026.531.3

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.
@@ -6,63 +6,80 @@ import "../hooks/index.js";
6
6
  import { useCallback as i, useEffect as a, useRef as o, useState as s } from "react";
7
7
  import { AlertCircle as c, CheckCircle2 as l, CreditCard as u, Loader2 as d, X as ee } from "lucide-react";
8
8
  import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
9
- import { useI18n as h } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
9
+ import { useI18n as te } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
10
10
  //#region src/billing/modules/dashboard/components/AddPaymentMethodModal.tsx
11
- var g = (e, t) => new Promise((n, r) => {
11
+ var h = (e, t) => new Promise((n, r) => {
12
12
  if (document.getElementById(t)) {
13
13
  n();
14
14
  return;
15
15
  }
16
16
  let i = document.createElement("script");
17
17
  i.id = t, i.src = e, i.async = !0, i.onload = () => n(), i.onerror = () => r(/* @__PURE__ */ Error(`Failed to load ${e}`)), document.head.appendChild(i);
18
- }), _ = ({ isOpen: _, onClose: v, billingAccountId: y, currency: b, country: x, onSuccess: S }) => {
19
- let { t: te } = h(), C = (e, t) => {
20
- let n = te(e);
18
+ }), g = ({ isOpen: g, onClose: _, billingAccountId: v, currency: y, country: b, onSuccess: x }) => {
19
+ let { t: S } = te(), C = (e, t) => {
20
+ let n = S(e);
21
21
  return n === e ? t : n;
22
- }, w = o(null), T = o(null), E = o(null), D = o(null), { stripeEnabled: O, stripePublishableKey: k, razorpayEnabled: A, isLoading: ne } = r(!_), { isAddingPaymentMethod: j, verifyPaymentMethod: M } = n(), N = b?.toUpperCase() === "INR" || x?.toUpperCase() === "IN", P = A && N, F = O && !N, [I, L] = s("select-provider"), [R, z] = s(null), [B, V] = s(null), [H, U] = s(!1), [W, G] = s(!1), [K, q] = s(""), [J, Y] = s(!0), [re, X] = s(!1);
22
+ }, ne = o(null), w = o(null), T = o(null), E = o(null), { stripeEnabled: D, stripePublishableKey: O, razorpayEnabled: k, isLoading: A } = r(!g), { isAddingPaymentMethod: j, verifyPaymentMethod: M } = n(), N = y?.toUpperCase() === "INR" || b?.toUpperCase() === "IN", P = k && N, F = D && !N, [I, L] = s("select-provider"), [R, z] = s(null), [B, V] = s(null), [H, U] = s(!1), [W, G] = s(!1), [K, q] = s(""), [J, Y] = s(!0), [re, X] = s(!1), Z = i((e) => {
23
+ z(e), L("add-card"), V(null);
24
+ }, []);
23
25
  a(() => {
24
- _ ? (L("select-provider"), z(null), V(null), q(""), Y(!0), X(!1)) : E.current &&= (E.current.destroy(), null);
25
- }, [_]), a(() => I !== "add-card" || R !== "stripe" || !k ? void 0 : ((async () => {
26
- G(!0);
27
- try {
28
- if (await g("https://js.stripe.com/v3/", "stripe-js"), !window.Stripe) throw Error("Stripe SDK failed to load");
29
- let e = window.Stripe(k);
30
- D.current = e;
31
- let n = 0;
32
- for (; !T.current && n < 50;) await new Promise((e) => setTimeout(e, 100)), n++;
33
- if (!T.current) throw Error("Card element container not found after 5 seconds");
34
- if (E.current) return;
35
- let r = e.elements().create("card", { style: t("system-ui, -apple-system, sans-serif") });
36
- r.mount(T.current), E.current = r, r.on("change", (e) => {
37
- X(e.complete), e.error ? V(e.error.message) : V(null);
38
- });
39
- } catch (e) {
40
- V(e instanceof Error ? e.message : "Failed to load Stripe");
41
- } finally {
42
- G(!1);
26
+ g ? (L("select-provider"), z(null), V(null), q(""), Y(!0), X(!1)) : T.current &&= (T.current.destroy(), null);
27
+ }, [g]), a(() => {
28
+ I !== "select-provider" || A || (F && !P ? Z("stripe") : P && !F && Z("razorpay"));
29
+ }, [
30
+ I,
31
+ A,
32
+ F,
33
+ P,
34
+ Z
35
+ ]), a(() => {
36
+ if (!(I !== "add-card" || R !== "stripe")) {
37
+ if (!O && !A) {
38
+ V("Stripe is not configured. Please contact support.");
39
+ return;
40
+ }
41
+ return O ? ((async () => {
42
+ G(!0);
43
+ try {
44
+ if (await h("https://js.stripe.com/v3/", "stripe-js"), !window.Stripe) throw Error("Stripe SDK failed to load");
45
+ let e = window.Stripe(O);
46
+ E.current = e;
47
+ let n = 0;
48
+ for (; !w.current && n < 50;) await new Promise((e) => setTimeout(e, 100)), n++;
49
+ if (!w.current) throw Error("Card element container not found after 5 seconds");
50
+ if (T.current) return;
51
+ let r = e.elements().create("card", { style: t("system-ui, -apple-system, sans-serif") });
52
+ r.mount(w.current), T.current = r, r.on("change", (e) => {
53
+ X(e.complete), e.error ? V(e.error.message) : V(null);
54
+ });
55
+ } catch (e) {
56
+ V(e instanceof Error ? e.message : "Failed to load Stripe");
57
+ } finally {
58
+ G(!1);
59
+ }
60
+ })(), () => {
61
+ if (T.current) try {
62
+ T.current.destroy(), T.current = null;
63
+ } catch {}
64
+ }) : void 0;
43
65
  }
44
- })(), () => {
45
- if (E.current) try {
46
- E.current.destroy(), E.current = null;
47
- } catch {}
48
- }), [
66
+ }, [
49
67
  I,
50
68
  R,
51
- k
69
+ O,
70
+ A
52
71
  ]), a(() => {
53
72
  let e = (e) => {
54
- e.key === "Escape" && _ && !H && v();
73
+ e.key === "Escape" && g && !H && _();
55
74
  };
56
75
  return document.addEventListener("keydown", e), () => document.removeEventListener("keydown", e);
57
76
  }, [
58
- _,
77
+ g,
59
78
  H,
60
- v
79
+ _
61
80
  ]);
62
- let Z = i((e) => {
63
- z(e), L("add-card"), V(null);
64
- }, []), Q = i(async () => {
65
- if (!D.current || !E.current) {
81
+ let Q = i(async () => {
82
+ if (!E.current || !T.current) {
66
83
  V("Stripe not initialized");
67
84
  return;
68
85
  }
@@ -73,20 +90,20 @@ var g = (e, t) => new Promise((n, r) => {
73
90
  U(!0), V(null);
74
91
  try {
75
92
  let t = await M({
76
- billingAccountId: y,
93
+ billingAccountId: v,
77
94
  currency: "USD",
78
95
  provider: e.STRIPE,
79
96
  setAsDefault: J
80
97
  });
81
98
  if (!t.clientSecret) throw Error("Failed to initiate card verification");
82
- let { setupIntent: n, error: r } = await D.current.confirmCardSetup(t.clientSecret, { payment_method: {
83
- card: E.current,
99
+ let { setupIntent: n, error: r } = await E.current.confirmCardSetup(t.clientSecret, { payment_method: {
100
+ card: T.current,
84
101
  billing_details: { name: K }
85
102
  } });
86
103
  if (r) throw Error(r.message);
87
104
  if (n?.status !== "succeeded") throw Error("Card verification failed");
88
- L("success"), await new Promise((e) => setTimeout(e, 2e3)), S?.(), setTimeout(() => {
89
- v();
105
+ L("success"), await new Promise((e) => setTimeout(e, 2e3)), x?.(), setTimeout(() => {
106
+ _();
90
107
  }, 500);
91
108
  } catch (e) {
92
109
  V(e instanceof Error ? e.message : "Failed to add payment method"), U(!1);
@@ -94,23 +111,23 @@ var g = (e, t) => new Promise((n, r) => {
94
111
  }, [
95
112
  K,
96
113
  J,
97
- y,
114
+ v,
98
115
  M,
99
- S,
100
- v
116
+ x,
117
+ _
101
118
  ]), $ = i(async () => {
102
119
  U(!0), V(null);
103
120
  try {
104
121
  G(!0);
105
122
  try {
106
- await g("https://checkout.razorpay.com/v1/checkout.js", "razorpay-sdk");
123
+ await h("https://checkout.razorpay.com/v1/checkout.js", "razorpay-sdk");
107
124
  } catch {
108
125
  V("Failed to load Razorpay SDK"), U(!1), G(!1);
109
126
  return;
110
127
  }
111
128
  G(!1);
112
129
  let t = await M({
113
- billingAccountId: y,
130
+ billingAccountId: v,
114
131
  currency: "INR",
115
132
  provider: e.RAZORPAY,
116
133
  setAsDefault: J
@@ -141,8 +158,8 @@ var g = (e, t) => new Promise((n, r) => {
141
158
  default_method: J.toString()
142
159
  },
143
160
  handler: async () => {
144
- L("success"), await new Promise((e) => setTimeout(e, 2e3)), S?.(), setTimeout(() => {
145
- v();
161
+ L("success"), await new Promise((e) => setTimeout(e, 2e3)), x?.(), setTimeout(() => {
162
+ _();
146
163
  }, 500);
147
164
  },
148
165
  modal: { ondismiss: () => {
@@ -155,10 +172,10 @@ var g = (e, t) => new Promise((n, r) => {
155
172
  }
156
173
  }, [
157
174
  J,
158
- y,
175
+ v,
159
176
  M,
160
- S,
161
- v
177
+ x,
178
+ _
162
179
  ]), ie = i(() => {
163
180
  R === "stripe" ? Q() : R === "razorpay" && $();
164
181
  }, [
@@ -166,17 +183,17 @@ var g = (e, t) => new Promise((n, r) => {
166
183
  Q,
167
184
  $
168
185
  ]);
169
- return _ ? /* @__PURE__ */ m("div", {
186
+ return g ? /* @__PURE__ */ m("div", {
170
187
  className: "fixed inset-0 z-50 flex items-center justify-center",
171
188
  role: "dialog",
172
189
  "aria-modal": "true",
173
190
  "aria-labelledby": "modal-title",
174
191
  children: [/* @__PURE__ */ p("div", {
175
192
  className: "absolute inset-0 bg-background/80 backdrop-blur-sm",
176
- onClick: H ? void 0 : v,
193
+ onClick: H ? void 0 : _,
177
194
  "aria-hidden": "true"
178
195
  }), /* @__PURE__ */ m("div", {
179
- ref: w,
196
+ ref: ne,
180
197
  className: "relative w-full max-w-md max-h-[90vh] mx-4 bg-card border border-border rounded-lg shadow-xl flex flex-col",
181
198
  children: [
182
199
  /* @__PURE__ */ m("header", {
@@ -201,7 +218,7 @@ var g = (e, t) => new Promise((n, r) => {
201
218
  })] })]
202
219
  }), /* @__PURE__ */ p("button", {
203
220
  type: "button",
204
- onClick: v,
221
+ onClick: _,
205
222
  disabled: H,
206
223
  className: "p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-colors disabled:opacity-50",
207
224
  "aria-label": "Close modal",
@@ -210,7 +227,7 @@ var g = (e, t) => new Promise((n, r) => {
210
227
  }),
211
228
  /* @__PURE__ */ p("div", {
212
229
  className: "p-4 overflow-y-auto flex-1",
213
- children: ne ? /* @__PURE__ */ p("div", {
230
+ children: A ? /* @__PURE__ */ p("div", {
214
231
  className: "flex items-center justify-center py-8",
215
232
  children: /* @__PURE__ */ p(d, { className: "size-6 animate-spin text-muted-foreground" })
216
233
  }) : I === "select-provider" ? /* @__PURE__ */ m("div", {
@@ -295,8 +312,8 @@ var g = (e, t) => new Promise((n, r) => {
295
312
  }), /* @__PURE__ */ m("div", {
296
313
  className: "relative",
297
314
  children: [/* @__PURE__ */ p("div", {
298
- ref: T,
299
- className: "p-3 border border-input rounded-lg bg-background min-h-[44px]"
315
+ ref: w,
316
+ className: "p-3 border border-input rounded-lg bg-background min-h-[56px]"
300
317
  }), W && /* @__PURE__ */ m("div", {
301
318
  className: "absolute inset-0 flex items-center justify-center bg-background/80 rounded-lg",
302
319
  children: [/* @__PURE__ */ p(d, { className: "size-5 animate-spin text-muted-foreground" }), /* @__PURE__ */ p("span", {
@@ -360,7 +377,7 @@ var g = (e, t) => new Promise((n, r) => {
360
377
  children: [I === "add-card" && F && P ? /* @__PURE__ */ p("button", {
361
378
  type: "button",
362
379
  onClick: () => {
363
- L("select-provider"), z(null), V(null), E.current &&= (E.current.destroy(), null);
380
+ L("select-provider"), z(null), V(null), T.current &&= (T.current.destroy(), null);
364
381
  },
365
382
  disabled: H,
366
383
  className: "px-4 py-2 text-sm font-medium text-foreground hover:text-primary transition-colors disabled:opacity-50",
@@ -369,7 +386,7 @@ var g = (e, t) => new Promise((n, r) => {
369
386
  className: "flex items-center gap-3",
370
387
  children: [/* @__PURE__ */ p("button", {
371
388
  type: "button",
372
- onClick: v,
389
+ onClick: _,
373
390
  disabled: H,
374
391
  className: "px-4 py-2 text-sm font-medium text-foreground border border-border rounded-lg hover:bg-muted transition-colors disabled:opacity-50",
375
392
  children: "Cancel"
@@ -387,6 +404,6 @@ var g = (e, t) => new Promise((n, r) => {
387
404
  }) : null;
388
405
  };
389
406
  //#endregion
390
- export { _ as AddPaymentMethodModal };
407
+ export { g as AddPaymentMethodModal };
391
408
 
392
409
  //# sourceMappingURL=AddPaymentMethodModal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AddPaymentMethodModal.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/AddPaymentMethodModal.tsx"],"sourcesContent":["/**\n * Add Payment Method Modal Component\n * Modal for adding a new payment method via Stripe or Razorpay SDKs\n */\n\nimport { type FC, useState, useCallback, useEffect, useRef } from 'react';\nimport { X, CreditCard, Loader2, AlertCircle, CheckCircle2 } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { usePaymentProviderConfig, useDashboardMutations } from '../hooks';\nimport { PaymentProviderPreference } from '../../../shared/types';\nimport { getStripeCardElementStyle } from '../../../shared/utils';\n\n// Stripe types (loaded dynamically)\ndeclare global {\n interface Window {\n Stripe?: (key: string) => StripeInstance;\n Razorpay?: new (options: RazorpayOptions) => RazorpayInstance;\n }\n}\n\ninterface StripeInstance {\n elements: () => StripeElements;\n createPaymentMethod: (options: {\n type: 'card';\n card: StripeCardElement;\n billing_details?: { name?: string };\n }) => Promise<{\n paymentMethod?: {\n id: string;\n card?: { brand: string; last4: string; exp_month: number; exp_year: number };\n };\n error?: { message: string };\n }>;\n confirmCardSetup: (\n clientSecret: string,\n options?: {\n payment_method?: {\n card: StripeCardElement;\n billing_details?: { name?: string };\n };\n }\n ) => Promise<{\n setupIntent?: {\n id: string;\n status: string;\n payment_method?: string;\n };\n error?: { message: string };\n }>;\n}\n\ninterface StripeElements {\n create: (type: 'card', options?: object) => StripeCardElement;\n}\n\ninterface StripeCardElement {\n mount: (element: HTMLElement | string) => void;\n unmount: () => void;\n on: (\n event: string,\n handler: (event: { complete: boolean; error?: { message: string } }) => void\n ) => void;\n destroy: () => void;\n}\n\ninterface RazorpayOptions {\n key: string;\n amount: number;\n currency: string;\n order_id: string;\n customer_id?: string;\n name: string;\n description: string;\n method?: 'card' | 'upi' | 'netbanking' | 'wallet' | 'emi' | 'cardless_emi';\n handler: (response: RazorpayResponse) => void;\n modal?: { ondismiss?: () => void };\n prefill?: { name?: string; email?: string; contact?: string };\n notes?: Record<string, string>;\n config?: {\n display?: {\n hide?: Array<{ method: string }>;\n preferences?: {\n show_default_blocks?: boolean;\n };\n };\n };\n}\n\ninterface RazorpayInstance {\n open: () => void;\n close: () => void;\n}\n\ninterface RazorpayResponse {\n razorpay_payment_id: string;\n razorpay_token?: string;\n}\n\ninterface AddPaymentMethodModalProps {\n isOpen: boolean;\n onClose: () => void;\n billingAccountId: string;\n currency: string; // e.g., 'INR', 'USD'\n country?: string; // e.g., 'IN', 'US' - from billing address\n onSuccess?: () => void;\n}\n\ntype ModalStep = 'select-provider' | 'add-card' | 'processing' | 'success';\n\n// Load external script dynamically\nconst loadScript = (src: string, id: string): Promise<void> => {\n return new Promise((resolve, reject) => {\n if (document.getElementById(id)) {\n resolve();\n return;\n }\n const script = document.createElement('script');\n script.id = id;\n script.src = src;\n script.async = true;\n script.onload = () => resolve();\n script.onerror = () => reject(new Error(`Failed to load ${src}`));\n document.head.appendChild(script);\n });\n};\n\nexport const AddPaymentMethodModal: FC<AddPaymentMethodModalProps> = ({\n isOpen,\n onClose,\n billingAccountId,\n currency,\n country,\n onSuccess,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const modalRef = useRef<HTMLDivElement>(null);\n const cardElementRef = useRef<HTMLDivElement>(null);\n const stripeCardElementRef = useRef<StripeCardElement | null>(null);\n const stripeRef = useRef<StripeInstance | null>(null);\n\n // Only fetch payment provider config when modal is open\n const {\n stripeEnabled,\n stripePublishableKey,\n razorpayEnabled,\n isLoading: isLoadingConfig,\n } = usePaymentProviderConfig(!isOpen);\n const { isAddingPaymentMethod, verifyPaymentMethod } = useDashboardMutations();\n\n // Determine which providers to show based on currency and country\n // Razorpay: India only (INR currency OR IN country)\n // Stripe: International (non-INR OR non-IN country)\n const isIndianAccount = currency?.toUpperCase() === 'INR' || country?.toUpperCase() === 'IN';\n const showRazorpay = razorpayEnabled && isIndianAccount;\n const showStripe = stripeEnabled && !isIndianAccount;\n\n const [step, setStep] = useState<ModalStep>('select-provider');\n const [selectedProvider, setSelectedProvider] = useState<'stripe' | 'razorpay' | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [isProcessing, setIsProcessing] = useState(false);\n const [isLoadingSdk, setIsLoadingSdk] = useState(false);\n const [cardholderName, setCardholderName] = useState('');\n const [setAsDefault, setSetAsDefault] = useState(true);\n const [isCardComplete, setIsCardComplete] = useState(false);\n\n // Reset state when modal opens/closes\n useEffect(() => {\n if (isOpen) {\n // Always start with provider selection\n setStep('select-provider');\n setSelectedProvider(null);\n setError(null);\n setCardholderName('');\n setSetAsDefault(true);\n setIsCardComplete(false);\n } else {\n // Cleanup Stripe element when modal closes\n if (stripeCardElementRef.current) {\n stripeCardElementRef.current.destroy();\n stripeCardElementRef.current = null;\n }\n }\n }, [isOpen]);\n\n // Note: We always show provider selection, even if only one is available\n // This ensures the user explicitly chooses their payment provider\n\n // Initialize Stripe when provider is selected\n useEffect(() => {\n if (step !== 'add-card' || selectedProvider !== 'stripe' || !stripePublishableKey) return;\n\n const initStripe = async () => {\n setIsLoadingSdk(true);\n try {\n await loadScript('https://js.stripe.com/v3/', 'stripe-js');\n\n if (!window.Stripe) {\n throw new Error('Stripe SDK failed to load');\n }\n\n const stripe = window.Stripe(stripePublishableKey);\n stripeRef.current = stripe;\n\n // Wait for the card element container to be mounted in the DOM\n let attempts = 0;\n while (!cardElementRef.current && attempts < 50) {\n await new Promise((resolve) => setTimeout(resolve, 100));\n attempts++;\n }\n\n if (!cardElementRef.current) {\n throw new Error('Card element container not found after 5 seconds');\n }\n\n if (stripeCardElementRef.current) {\n return;\n }\n\n const elements = stripe.elements();\n const cardElement = elements.create('card', {\n style: getStripeCardElementStyle('system-ui, -apple-system, sans-serif'),\n });\n\n cardElement.mount(cardElementRef.current);\n stripeCardElementRef.current = cardElement;\n\n cardElement.on('change', (event) => {\n setIsCardComplete(event.complete);\n if (event.error) {\n setError(event.error.message);\n } else {\n setError(null);\n }\n });\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load Stripe');\n } finally {\n setIsLoadingSdk(false);\n }\n };\n\n initStripe();\n\n return () => {\n if (stripeCardElementRef.current) {\n try {\n stripeCardElementRef.current.destroy();\n stripeCardElementRef.current = null;\n } catch {\n // Stripe element may already be detached during modal teardown.\n }\n }\n };\n }, [step, selectedProvider, stripePublishableKey]);\n\n // Handle escape key\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && isOpen && !isProcessing) {\n onClose();\n }\n };\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [isOpen, isProcessing, onClose]);\n\n const handleSelectProvider = useCallback((provider: 'stripe' | 'razorpay') => {\n setSelectedProvider(provider);\n setStep('add-card');\n setError(null);\n }, []);\n\n const handleStripeSubmit = useCallback(async () => {\n if (!stripeRef.current || !stripeCardElementRef.current) {\n setError('Stripe not initialized');\n return;\n }\n\n if (!cardholderName.trim()) {\n setError('Please enter the cardholder name');\n return;\n }\n\n setIsProcessing(true);\n setError(null);\n\n try {\n // Step 1: Call backend to create SetupIntent and get client secret\n const verificationResult = await verifyPaymentMethod({\n billingAccountId,\n currency: 'USD',\n provider: PaymentProviderPreference.STRIPE,\n setAsDefault,\n });\n\n if (!verificationResult.clientSecret) {\n throw new Error('Failed to initiate card verification');\n }\n\n // Step 2: Confirm card setup using the client secret (no charge)\n const { setupIntent, error: stripeError } = await stripeRef.current.confirmCardSetup(\n verificationResult.clientSecret,\n {\n payment_method: {\n card: stripeCardElementRef.current,\n billing_details: { name: cardholderName },\n },\n }\n );\n\n if (stripeError) {\n throw new Error(stripeError.message);\n }\n\n if (setupIntent?.status !== 'succeeded') {\n throw new Error('Card verification failed');\n }\n\n // Step 3: Show success (webhook will save the payment method)\n setStep('success');\n\n // Wait for webhook to process (2 seconds) before refetching\n await new Promise((resolve) => setTimeout(resolve, 2000));\n onSuccess?.();\n\n // Show success message briefly before closing\n setTimeout(() => {\n onClose();\n }, 500);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to add payment method');\n setIsProcessing(false);\n }\n }, [cardholderName, setAsDefault, billingAccountId, verifyPaymentMethod, onSuccess, onClose]);\n\n const handleRazorpaySubmit = useCallback(async () => {\n setIsProcessing(true);\n setError(null);\n\n try {\n // Load Razorpay SDK\n setIsLoadingSdk(true);\n try {\n await loadScript('https://checkout.razorpay.com/v1/checkout.js', 'razorpay-sdk');\n } catch {\n setError('Failed to load Razorpay SDK');\n setIsProcessing(false);\n setIsLoadingSdk(false);\n return;\n }\n setIsLoadingSdk(false);\n\n // Initiate ₹1 verification payment\n const verificationResult = await verifyPaymentMethod({\n billingAccountId,\n currency: 'INR',\n provider: PaymentProviderPreference.RAZORPAY,\n setAsDefault,\n });\n\n if (\n !verificationResult.success ||\n !verificationResult.orderId ||\n !verificationResult.razorpayKeyId\n ) {\n setError(verificationResult.error || 'Failed to initiate verification');\n setIsProcessing(false);\n return;\n }\n\n // Open Razorpay checkout with ALL payment methods\n if (!window.Razorpay) {\n setError('Razorpay SDK not loaded');\n setIsProcessing(false);\n return;\n }\n\n const options = {\n key: verificationResult.razorpayKeyId,\n amount: (verificationResult.amount ?? 0) * 100, // Convert to paise\n currency: verificationResult.currency.toUpperCase(),\n order_id: verificationResult.orderId,\n customer_id: verificationResult.razorpayCustomerId, // Link to customer for saving\n name: 'Add Payment Method',\n description: `₹${verificationResult.amount} verification charge (refundable)`,\n prefill: {\n name: verificationResult.prefillName,\n email: verificationResult.prefillEmail,\n contact: verificationResult.prefillContact,\n },\n notes: {\n purchaseType: 'PAYMENT_METHOD_VERIFICATION',\n default_method: setAsDefault.toString(),\n },\n handler: async () => {\n // Payment successful - payment method will be saved via webhook\n setStep('success');\n\n // Wait for webhook to process (2 seconds) before refetching\n await new Promise((resolve) => setTimeout(resolve, 2000));\n onSuccess?.();\n\n // Show success message briefly before closing\n setTimeout(() => {\n onClose();\n }, 500);\n },\n modal: {\n ondismiss: () => {\n setIsProcessing(false);\n setError('Payment cancelled');\n },\n },\n };\n\n const rzp = new window.Razorpay(options);\n rzp.open();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to add payment method');\n setIsProcessing(false);\n }\n }, [setAsDefault, billingAccountId, verifyPaymentMethod, onSuccess, onClose]);\n\n const handleSubmit = useCallback(() => {\n if (selectedProvider === 'stripe') {\n handleStripeSubmit();\n } else if (selectedProvider === 'razorpay') {\n handleRazorpaySubmit();\n }\n }, [selectedProvider, handleStripeSubmit, handleRazorpaySubmit]);\n\n if (!isOpen) return null;\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"modal-title\"\n >\n {/* Backdrop */}\n <div\n className=\"absolute inset-0 bg-background/80 backdrop-blur-sm\"\n onClick={!isProcessing ? onClose : undefined}\n aria-hidden=\"true\"\n />\n\n {/* Modal */}\n <div\n ref={modalRef}\n className=\"relative w-full max-w-md max-h-[90vh] mx-4 bg-card border border-border rounded-lg shadow-xl flex flex-col\"\n >\n {/* Header */}\n <header className=\"flex items-center justify-between p-4 border-b border-border flex-shrink-0\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 rounded-lg bg-primary/10 flex items-center justify-center\">\n <CreditCard className=\"size-5 text-primary\" />\n </div>\n <div>\n <h2 id=\"modal-title\" className=\"text-lg font-semibold text-foreground\">\n {step === 'success' ? 'Payment Method Added' : 'Add Payment Method'}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {step === 'select-provider' && 'Choose a payment provider'}\n {step === 'add-card' &&\n (selectedProvider === 'stripe'\n ? 'Add card via Stripe'\n : 'Add payment method with ₹1 verification')}\n {step === 'processing' && 'Processing...'}\n {step === 'success' && 'Your payment method has been saved'}\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={isProcessing}\n className=\"p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-colors disabled:opacity-50\"\n aria-label=\"Close modal\"\n >\n <X className=\"size-5\" />\n </button>\n </header>\n\n {/* Content */}\n <div className=\"p-4 overflow-y-auto flex-1\">\n {isLoadingConfig ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"size-6 animate-spin text-muted-foreground\" />\n </div>\n ) : step === 'select-provider' ? (\n <div className=\"space-y-3\">\n {showStripe && (\n <button\n type=\"button\"\n onClick={() => handleSelectProvider('stripe')}\n className=\"w-full flex items-center gap-4 p-4 border border-border rounded-lg hover:border-primary hover:bg-primary/5 transition-colors text-left\"\n >\n <div className=\"size-12 rounded-lg bg-accent-violet-subtle flex items-center justify-center\">\n <CreditCard className=\"size-6 text-accent-violet\" />\n </div>\n <div>\n <p className=\"font-medium text-foreground\">Stripe</p>\n <p className=\"text-sm text-muted-foreground\">Cards, Apple Pay, Google Pay</p>\n </div>\n </button>\n )}\n {showRazorpay && (\n <button\n type=\"button\"\n onClick={() => handleSelectProvider('razorpay')}\n className=\"w-full flex items-center gap-4 p-4 border border-border rounded-lg hover:border-primary hover:bg-primary/5 transition-colors text-left\"\n >\n <div className=\"size-12 rounded-lg bg-status-info-bg-subtle flex items-center justify-center\">\n <CreditCard className=\"size-6 text-accent-blue\" />\n </div>\n <div>\n <p className=\"font-medium text-foreground\">Razorpay</p>\n <p className=\"text-sm text-muted-foreground\">UPI, Netbanking, Cards</p>\n </div>\n </button>\n )}\n {!showStripe && !showRazorpay && (\n <div className=\"flex flex-col items-center py-8 text-center\">\n <AlertCircle className=\"size-12 text-status-warning-text mb-3\" />\n <p className=\"font-medium text-foreground\">\n {tr('billing.overview.noPaymentProviders', 'No Payment Providers Available')}\n </p>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {isIndianAccount\n ? 'Razorpay is not configured for Indian accounts. Please contact support.'\n : 'Stripe is not configured for international accounts. Please contact support.'}\n </p>\n </div>\n )}\n </div>\n ) : step === 'add-card' ? (\n <div className=\"space-y-4\">\n {/* Error Message */}\n {error && (\n <div className=\"flex items-center gap-2 p-3 rounded-lg bg-destructive/10 border border-destructive/20 text-destructive\">\n <AlertCircle className=\"size-4 flex-shrink-0\" />\n <p className=\"text-sm\">{error}</p>\n </div>\n )}\n\n {/* Cardholder Name - only for Stripe */}\n {selectedProvider === 'stripe' && (\n <div className=\"space-y-1.5\">\n <label\n htmlFor=\"cardholderName\"\n className=\"block text-sm font-medium text-foreground\"\n >\n Cardholder Name\n </label>\n <input\n type=\"text\"\n id=\"cardholderName\"\n value={cardholderName}\n onChange={(e) => setCardholderName(e.target.value)}\n className=\"w-full px-3 py-2 border border-input rounded-lg bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary\"\n placeholder=\"John Doe\"\n disabled={isProcessing}\n />\n </div>\n )}\n\n {/* Stripe Card Element */}\n {selectedProvider === 'stripe' && (\n <div className=\"space-y-1.5\">\n <label className=\"block text-sm font-medium text-foreground\">\n {tr('billing.overview.cardDetails', 'Card Details')}\n </label>\n <div className=\"relative\">\n <div\n ref={cardElementRef}\n className=\"p-3 border border-input rounded-lg bg-background min-h-[44px]\"\n />\n {isLoadingSdk && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-background/80 rounded-lg\">\n <Loader2 className=\"size-5 animate-spin text-muted-foreground\" />\n <span className=\"ml-2 text-sm text-muted-foreground\">Loading Stripe…</span>\n </div>\n )}\n </div>\n </div>\n )}\n\n {/* Razorpay - Verification Info */}\n {selectedProvider === 'razorpay' && (\n <div className=\"p-3 border border-border-subtle rounded-lg bg-status-info-bg-subtle\">\n <p className=\"text-sm text-status-info-text\">\n <strong>Verification Process:</strong> A ₹1 charge will be made to verify your\n payment method. This amount will be refunded immediately after verification.\n </p>\n </div>\n )}\n\n {/* Set as default */}\n <label className=\"flex items-center gap-2 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={setAsDefault}\n onChange={(e) => setSetAsDefault(e.target.checked)}\n className=\"size-4 rounded border-input text-primary focus:ring-primary/20\"\n disabled={isProcessing}\n />\n <span className=\"text-sm text-foreground\">\n {tr('billing.overview.setAsDefault', 'Set as default payment method')}\n </span>\n </label>\n\n {/* Provider badge */}\n <div className=\"flex items-center gap-2 pt-2 border-t border-border\">\n <span className=\"text-xs text-muted-foreground\">Secured by</span>\n <span\n className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${\n selectedProvider === 'stripe'\n ? 'bg-accent-violet-subtle text-accent-violet'\n : 'bg-status-info-bg-subtle text-status-info-text'\n }`}\n >\n {selectedProvider === 'stripe' ? 'Stripe' : 'Razorpay'}\n </span>\n </div>\n </div>\n ) : (\n /* Success state */\n <div className=\"flex flex-col items-center py-8 text-center\">\n <div className=\"size-16 rounded-full bg-status-success-bg-subtle flex items-center justify-center mb-4\">\n <CheckCircle2 className=\"size-8 text-status-success-text\" />\n </div>\n <p className=\"font-medium text-foreground\">\n {tr('billing.overview.paymentMethodAdded', 'Payment method added successfully!')}\n </p>\n <p className=\"text-sm text-muted-foreground mt-1\">\n Your payment method has been securely saved.\n </p>\n </div>\n )}\n </div>\n\n {/* Footer */}\n {step !== 'success' && (\n <footer className=\"flex items-center justify-between gap-3 p-4 border-t border-border bg-muted/30 rounded-b-lg flex-shrink-0\">\n {step === 'add-card' && showStripe && showRazorpay ? (\n <button\n type=\"button\"\n onClick={() => {\n setStep('select-provider');\n setSelectedProvider(null);\n setError(null);\n // Cleanup Stripe element\n if (stripeCardElementRef.current) {\n stripeCardElementRef.current.destroy();\n stripeCardElementRef.current = null;\n }\n }}\n disabled={isProcessing}\n className=\"px-4 py-2 text-sm font-medium text-foreground hover:text-primary transition-colors disabled:opacity-50\"\n >\n Back\n </button>\n ) : (\n <div />\n )}\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={onClose}\n disabled={isProcessing}\n className=\"px-4 py-2 text-sm font-medium text-foreground border border-border rounded-lg hover:bg-muted transition-colors disabled:opacity-50\"\n >\n Cancel\n </button>\n {step === 'add-card' && (\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={\n isProcessing ||\n isAddingPaymentMethod ||\n isLoadingSdk ||\n (selectedProvider === 'stripe' && !isCardComplete)\n }\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors disabled:opacity-50\"\n >\n {isProcessing || isAddingPaymentMethod ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n {selectedProvider === 'razorpay' ? 'Processing...' : 'Saving...'}\n </>\n ) : isLoadingSdk ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n Loading…\n </>\n ) : selectedProvider === 'stripe' ? (\n 'Add Card'\n ) : (\n 'Add Payment Method (₹1)'\n )}\n </button>\n )}\n </div>\n </footer>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AddPaymentMethodModal.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/AddPaymentMethodModal.tsx"],"sourcesContent":["/**\n * Add Payment Method Modal Component\n * Modal for adding a new payment method via Stripe or Razorpay SDKs\n */\n\nimport { type FC, useState, useCallback, useEffect, useRef } from 'react';\nimport { X, CreditCard, Loader2, AlertCircle, CheckCircle2 } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { usePaymentProviderConfig, useDashboardMutations } from '../hooks';\nimport { PaymentProviderPreference } from '../../../shared/types';\nimport { getStripeCardElementStyle } from '../../../shared/utils';\n\n// Stripe types (loaded dynamically)\ndeclare global {\n interface Window {\n Stripe?: (key: string) => StripeInstance;\n Razorpay?: new (options: RazorpayOptions) => RazorpayInstance;\n }\n}\n\ninterface StripeInstance {\n elements: () => StripeElements;\n createPaymentMethod: (options: {\n type: 'card';\n card: StripeCardElement;\n billing_details?: { name?: string };\n }) => Promise<{\n paymentMethod?: {\n id: string;\n card?: { brand: string; last4: string; exp_month: number; exp_year: number };\n };\n error?: { message: string };\n }>;\n confirmCardSetup: (\n clientSecret: string,\n options?: {\n payment_method?: {\n card: StripeCardElement;\n billing_details?: { name?: string };\n };\n }\n ) => Promise<{\n setupIntent?: {\n id: string;\n status: string;\n payment_method?: string;\n };\n error?: { message: string };\n }>;\n}\n\ninterface StripeElements {\n create: (type: 'card', options?: object) => StripeCardElement;\n}\n\ninterface StripeCardElement {\n mount: (element: HTMLElement | string) => void;\n unmount: () => void;\n on: (\n event: string,\n handler: (event: { complete: boolean; error?: { message: string } }) => void\n ) => void;\n destroy: () => void;\n}\n\ninterface RazorpayOptions {\n key: string;\n amount: number;\n currency: string;\n order_id: string;\n customer_id?: string;\n name: string;\n description: string;\n method?: 'card' | 'upi' | 'netbanking' | 'wallet' | 'emi' | 'cardless_emi';\n handler: (response: RazorpayResponse) => void;\n modal?: { ondismiss?: () => void };\n prefill?: { name?: string; email?: string; contact?: string };\n notes?: Record<string, string>;\n config?: {\n display?: {\n hide?: Array<{ method: string }>;\n preferences?: {\n show_default_blocks?: boolean;\n };\n };\n };\n}\n\ninterface RazorpayInstance {\n open: () => void;\n close: () => void;\n}\n\ninterface RazorpayResponse {\n razorpay_payment_id: string;\n razorpay_token?: string;\n}\n\ninterface AddPaymentMethodModalProps {\n isOpen: boolean;\n onClose: () => void;\n billingAccountId: string;\n currency: string; // e.g., 'INR', 'USD'\n country?: string; // e.g., 'IN', 'US' - from billing address\n onSuccess?: () => void;\n}\n\ntype ModalStep = 'select-provider' | 'add-card' | 'processing' | 'success';\n\n// Load external script dynamically\nconst loadScript = (src: string, id: string): Promise<void> => {\n return new Promise((resolve, reject) => {\n if (document.getElementById(id)) {\n resolve();\n return;\n }\n const script = document.createElement('script');\n script.id = id;\n script.src = src;\n script.async = true;\n script.onload = () => resolve();\n script.onerror = () => reject(new Error(`Failed to load ${src}`));\n document.head.appendChild(script);\n });\n};\n\nexport const AddPaymentMethodModal: FC<AddPaymentMethodModalProps> = ({\n isOpen,\n onClose,\n billingAccountId,\n currency,\n country,\n onSuccess,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const modalRef = useRef<HTMLDivElement>(null);\n const cardElementRef = useRef<HTMLDivElement>(null);\n const stripeCardElementRef = useRef<StripeCardElement | null>(null);\n const stripeRef = useRef<StripeInstance | null>(null);\n\n // Only fetch payment provider config when modal is open\n const {\n stripeEnabled,\n stripePublishableKey,\n razorpayEnabled,\n isLoading: isLoadingConfig,\n } = usePaymentProviderConfig(!isOpen);\n const { isAddingPaymentMethod, verifyPaymentMethod } = useDashboardMutations();\n\n // Determine which providers to show based on currency and country\n // Razorpay: India only (INR currency OR IN country)\n // Stripe: International (non-INR OR non-IN country)\n const isIndianAccount = currency?.toUpperCase() === 'INR' || country?.toUpperCase() === 'IN';\n const showRazorpay = razorpayEnabled && isIndianAccount;\n const showStripe = stripeEnabled && !isIndianAccount;\n\n const [step, setStep] = useState<ModalStep>('select-provider');\n const [selectedProvider, setSelectedProvider] = useState<'stripe' | 'razorpay' | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [isProcessing, setIsProcessing] = useState(false);\n const [isLoadingSdk, setIsLoadingSdk] = useState(false);\n const [cardholderName, setCardholderName] = useState('');\n const [setAsDefault, setSetAsDefault] = useState(true);\n const [isCardComplete, setIsCardComplete] = useState(false);\n\n // Must be declared before the effects that reference it\n const handleSelectProvider = useCallback((provider: 'stripe' | 'razorpay') => {\n setSelectedProvider(provider);\n setStep('add-card');\n setError(null);\n }, []);\n\n // Reset state when modal opens/closes\n useEffect(() => {\n if (isOpen) {\n setStep('select-provider');\n setSelectedProvider(null);\n setError(null);\n setCardholderName('');\n setSetAsDefault(true);\n setIsCardComplete(false);\n } else {\n // Cleanup Stripe element when modal closes\n if (stripeCardElementRef.current) {\n stripeCardElementRef.current.destroy();\n stripeCardElementRef.current = null;\n }\n }\n }, [isOpen]);\n\n // Auto-select provider when only one is available (skip the select-provider screen)\n useEffect(() => {\n if (step !== 'select-provider' || isLoadingConfig) return;\n if (showStripe && !showRazorpay) {\n handleSelectProvider('stripe');\n } else if (showRazorpay && !showStripe) {\n handleSelectProvider('razorpay');\n }\n }, [step, isLoadingConfig, showStripe, showRazorpay, handleSelectProvider]);\n\n // Initialize Stripe when provider is selected\n useEffect(() => {\n if (step !== 'add-card' || selectedProvider !== 'stripe') return;\n\n // Show error if config loaded but key is missing\n if (!stripePublishableKey && !isLoadingConfig) {\n setError('Stripe is not configured. Please contact support.');\n return;\n }\n\n if (!stripePublishableKey) return;\n\n const initStripe = async () => {\n setIsLoadingSdk(true);\n try {\n await loadScript('https://js.stripe.com/v3/', 'stripe-js');\n\n if (!window.Stripe) {\n throw new Error('Stripe SDK failed to load');\n }\n\n const stripe = window.Stripe(stripePublishableKey);\n stripeRef.current = stripe;\n\n // Wait for the card element container to be mounted in the DOM\n let attempts = 0;\n while (!cardElementRef.current && attempts < 50) {\n await new Promise((resolve) => setTimeout(resolve, 100));\n attempts++;\n }\n\n if (!cardElementRef.current) {\n throw new Error('Card element container not found after 5 seconds');\n }\n\n if (stripeCardElementRef.current) {\n return;\n }\n\n const elements = stripe.elements();\n const cardElement = elements.create('card', {\n style: getStripeCardElementStyle('system-ui, -apple-system, sans-serif'),\n });\n\n cardElement.mount(cardElementRef.current);\n stripeCardElementRef.current = cardElement;\n\n cardElement.on('change', (event) => {\n setIsCardComplete(event.complete);\n if (event.error) {\n setError(event.error.message);\n } else {\n setError(null);\n }\n });\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load Stripe');\n } finally {\n setIsLoadingSdk(false);\n }\n };\n\n initStripe();\n\n return () => {\n if (stripeCardElementRef.current) {\n try {\n stripeCardElementRef.current.destroy();\n stripeCardElementRef.current = null;\n } catch {\n // Stripe element may already be detached during modal teardown.\n }\n }\n };\n }, [step, selectedProvider, stripePublishableKey, isLoadingConfig]);\n\n // Handle escape key\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && isOpen && !isProcessing) {\n onClose();\n }\n };\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [isOpen, isProcessing, onClose]);\n\n const handleStripeSubmit = useCallback(async () => {\n if (!stripeRef.current || !stripeCardElementRef.current) {\n setError('Stripe not initialized');\n return;\n }\n\n if (!cardholderName.trim()) {\n setError('Please enter the cardholder name');\n return;\n }\n\n setIsProcessing(true);\n setError(null);\n\n try {\n // Step 1: Call backend to create SetupIntent and get client secret\n const verificationResult = await verifyPaymentMethod({\n billingAccountId,\n currency: 'USD',\n provider: PaymentProviderPreference.STRIPE,\n setAsDefault,\n });\n\n if (!verificationResult.clientSecret) {\n throw new Error('Failed to initiate card verification');\n }\n\n // Step 2: Confirm card setup using the client secret (no charge)\n const { setupIntent, error: stripeError } = await stripeRef.current.confirmCardSetup(\n verificationResult.clientSecret,\n {\n payment_method: {\n card: stripeCardElementRef.current,\n billing_details: { name: cardholderName },\n },\n }\n );\n\n if (stripeError) {\n throw new Error(stripeError.message);\n }\n\n if (setupIntent?.status !== 'succeeded') {\n throw new Error('Card verification failed');\n }\n\n // Step 3: Show success (webhook will save the payment method)\n setStep('success');\n\n // Wait for webhook to process (2 seconds) before refetching\n await new Promise((resolve) => setTimeout(resolve, 2000));\n onSuccess?.();\n\n // Show success message briefly before closing\n setTimeout(() => {\n onClose();\n }, 500);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to add payment method');\n setIsProcessing(false);\n }\n }, [cardholderName, setAsDefault, billingAccountId, verifyPaymentMethod, onSuccess, onClose]);\n\n const handleRazorpaySubmit = useCallback(async () => {\n setIsProcessing(true);\n setError(null);\n\n try {\n // Load Razorpay SDK\n setIsLoadingSdk(true);\n try {\n await loadScript('https://checkout.razorpay.com/v1/checkout.js', 'razorpay-sdk');\n } catch {\n setError('Failed to load Razorpay SDK');\n setIsProcessing(false);\n setIsLoadingSdk(false);\n return;\n }\n setIsLoadingSdk(false);\n\n // Initiate ₹1 verification payment\n const verificationResult = await verifyPaymentMethod({\n billingAccountId,\n currency: 'INR',\n provider: PaymentProviderPreference.RAZORPAY,\n setAsDefault,\n });\n\n if (\n !verificationResult.success ||\n !verificationResult.orderId ||\n !verificationResult.razorpayKeyId\n ) {\n setError(verificationResult.error || 'Failed to initiate verification');\n setIsProcessing(false);\n return;\n }\n\n // Open Razorpay checkout with ALL payment methods\n if (!window.Razorpay) {\n setError('Razorpay SDK not loaded');\n setIsProcessing(false);\n return;\n }\n\n const options = {\n key: verificationResult.razorpayKeyId,\n amount: (verificationResult.amount ?? 0) * 100, // Convert to paise\n currency: verificationResult.currency.toUpperCase(),\n order_id: verificationResult.orderId,\n customer_id: verificationResult.razorpayCustomerId, // Link to customer for saving\n name: 'Add Payment Method',\n description: `₹${verificationResult.amount} verification charge (refundable)`,\n prefill: {\n name: verificationResult.prefillName,\n email: verificationResult.prefillEmail,\n contact: verificationResult.prefillContact,\n },\n notes: {\n purchaseType: 'PAYMENT_METHOD_VERIFICATION',\n default_method: setAsDefault.toString(),\n },\n handler: async () => {\n // Payment successful - payment method will be saved via webhook\n setStep('success');\n\n // Wait for webhook to process (2 seconds) before refetching\n await new Promise((resolve) => setTimeout(resolve, 2000));\n onSuccess?.();\n\n // Show success message briefly before closing\n setTimeout(() => {\n onClose();\n }, 500);\n },\n modal: {\n ondismiss: () => {\n setIsProcessing(false);\n setError('Payment cancelled');\n },\n },\n };\n\n const rzp = new window.Razorpay(options);\n rzp.open();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to add payment method');\n setIsProcessing(false);\n }\n }, [setAsDefault, billingAccountId, verifyPaymentMethod, onSuccess, onClose]);\n\n const handleSubmit = useCallback(() => {\n if (selectedProvider === 'stripe') {\n handleStripeSubmit();\n } else if (selectedProvider === 'razorpay') {\n handleRazorpaySubmit();\n }\n }, [selectedProvider, handleStripeSubmit, handleRazorpaySubmit]);\n\n if (!isOpen) return null;\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"modal-title\"\n >\n {/* Backdrop */}\n <div\n className=\"absolute inset-0 bg-background/80 backdrop-blur-sm\"\n onClick={!isProcessing ? onClose : undefined}\n aria-hidden=\"true\"\n />\n\n {/* Modal */}\n <div\n ref={modalRef}\n className=\"relative w-full max-w-md max-h-[90vh] mx-4 bg-card border border-border rounded-lg shadow-xl flex flex-col\"\n >\n {/* Header */}\n <header className=\"flex items-center justify-between p-4 border-b border-border flex-shrink-0\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 rounded-lg bg-primary/10 flex items-center justify-center\">\n <CreditCard className=\"size-5 text-primary\" />\n </div>\n <div>\n <h2 id=\"modal-title\" className=\"text-lg font-semibold text-foreground\">\n {step === 'success' ? 'Payment Method Added' : 'Add Payment Method'}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {step === 'select-provider' && 'Choose a payment provider'}\n {step === 'add-card' &&\n (selectedProvider === 'stripe'\n ? 'Add card via Stripe'\n : 'Add payment method with ₹1 verification')}\n {step === 'processing' && 'Processing...'}\n {step === 'success' && 'Your payment method has been saved'}\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={isProcessing}\n className=\"p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-colors disabled:opacity-50\"\n aria-label=\"Close modal\"\n >\n <X className=\"size-5\" />\n </button>\n </header>\n\n {/* Content */}\n <div className=\"p-4 overflow-y-auto flex-1\">\n {isLoadingConfig ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"size-6 animate-spin text-muted-foreground\" />\n </div>\n ) : step === 'select-provider' ? (\n <div className=\"space-y-3\">\n {showStripe && (\n <button\n type=\"button\"\n onClick={() => handleSelectProvider('stripe')}\n className=\"w-full flex items-center gap-4 p-4 border border-border rounded-lg hover:border-primary hover:bg-primary/5 transition-colors text-left\"\n >\n <div className=\"size-12 rounded-lg bg-accent-violet-subtle flex items-center justify-center\">\n <CreditCard className=\"size-6 text-accent-violet\" />\n </div>\n <div>\n <p className=\"font-medium text-foreground\">Stripe</p>\n <p className=\"text-sm text-muted-foreground\">Cards, Apple Pay, Google Pay</p>\n </div>\n </button>\n )}\n {showRazorpay && (\n <button\n type=\"button\"\n onClick={() => handleSelectProvider('razorpay')}\n className=\"w-full flex items-center gap-4 p-4 border border-border rounded-lg hover:border-primary hover:bg-primary/5 transition-colors text-left\"\n >\n <div className=\"size-12 rounded-lg bg-status-info-bg-subtle flex items-center justify-center\">\n <CreditCard className=\"size-6 text-accent-blue\" />\n </div>\n <div>\n <p className=\"font-medium text-foreground\">Razorpay</p>\n <p className=\"text-sm text-muted-foreground\">UPI, Netbanking, Cards</p>\n </div>\n </button>\n )}\n {!showStripe && !showRazorpay && (\n <div className=\"flex flex-col items-center py-8 text-center\">\n <AlertCircle className=\"size-12 text-status-warning-text mb-3\" />\n <p className=\"font-medium text-foreground\">\n {tr('billing.overview.noPaymentProviders', 'No Payment Providers Available')}\n </p>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {isIndianAccount\n ? 'Razorpay is not configured for Indian accounts. Please contact support.'\n : 'Stripe is not configured for international accounts. Please contact support.'}\n </p>\n </div>\n )}\n </div>\n ) : step === 'add-card' ? (\n <div className=\"space-y-4\">\n {/* Error Message */}\n {error && (\n <div className=\"flex items-center gap-2 p-3 rounded-lg bg-destructive/10 border border-destructive/20 text-destructive\">\n <AlertCircle className=\"size-4 flex-shrink-0\" />\n <p className=\"text-sm\">{error}</p>\n </div>\n )}\n\n {/* Cardholder Name - only for Stripe */}\n {selectedProvider === 'stripe' && (\n <div className=\"space-y-1.5\">\n <label\n htmlFor=\"cardholderName\"\n className=\"block text-sm font-medium text-foreground\"\n >\n Cardholder Name\n </label>\n <input\n type=\"text\"\n id=\"cardholderName\"\n value={cardholderName}\n onChange={(e) => setCardholderName(e.target.value)}\n className=\"w-full px-3 py-2 border border-input rounded-lg bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary\"\n placeholder=\"John Doe\"\n disabled={isProcessing}\n />\n </div>\n )}\n\n {/* Stripe Card Element */}\n {selectedProvider === 'stripe' && (\n <div className=\"space-y-1.5\">\n <label className=\"block text-sm font-medium text-foreground\">\n {tr('billing.overview.cardDetails', 'Card Details')}\n </label>\n <div className=\"relative\">\n <div\n ref={cardElementRef}\n className=\"p-3 border border-input rounded-lg bg-background min-h-[56px]\"\n />\n {isLoadingSdk && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-background/80 rounded-lg\">\n <Loader2 className=\"size-5 animate-spin text-muted-foreground\" />\n <span className=\"ml-2 text-sm text-muted-foreground\">Loading Stripe…</span>\n </div>\n )}\n </div>\n </div>\n )}\n\n {/* Razorpay - Verification Info */}\n {selectedProvider === 'razorpay' && (\n <div className=\"p-3 border border-border-subtle rounded-lg bg-status-info-bg-subtle\">\n <p className=\"text-sm text-status-info-text\">\n <strong>Verification Process:</strong> A ₹1 charge will be made to verify your\n payment method. This amount will be refunded immediately after verification.\n </p>\n </div>\n )}\n\n {/* Set as default */}\n <label className=\"flex items-center gap-2 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={setAsDefault}\n onChange={(e) => setSetAsDefault(e.target.checked)}\n className=\"size-4 rounded border-input text-primary focus:ring-primary/20\"\n disabled={isProcessing}\n />\n <span className=\"text-sm text-foreground\">\n {tr('billing.overview.setAsDefault', 'Set as default payment method')}\n </span>\n </label>\n\n {/* Provider badge */}\n <div className=\"flex items-center gap-2 pt-2 border-t border-border\">\n <span className=\"text-xs text-muted-foreground\">Secured by</span>\n <span\n className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${\n selectedProvider === 'stripe'\n ? 'bg-accent-violet-subtle text-accent-violet'\n : 'bg-status-info-bg-subtle text-status-info-text'\n }`}\n >\n {selectedProvider === 'stripe' ? 'Stripe' : 'Razorpay'}\n </span>\n </div>\n </div>\n ) : (\n /* Success state */\n <div className=\"flex flex-col items-center py-8 text-center\">\n <div className=\"size-16 rounded-full bg-status-success-bg-subtle flex items-center justify-center mb-4\">\n <CheckCircle2 className=\"size-8 text-status-success-text\" />\n </div>\n <p className=\"font-medium text-foreground\">\n {tr('billing.overview.paymentMethodAdded', 'Payment method added successfully!')}\n </p>\n <p className=\"text-sm text-muted-foreground mt-1\">\n Your payment method has been securely saved.\n </p>\n </div>\n )}\n </div>\n\n {/* Footer */}\n {step !== 'success' && (\n <footer className=\"flex items-center justify-between gap-3 p-4 border-t border-border bg-muted/30 rounded-b-lg flex-shrink-0\">\n {step === 'add-card' && showStripe && showRazorpay ? (\n <button\n type=\"button\"\n onClick={() => {\n setStep('select-provider');\n setSelectedProvider(null);\n setError(null);\n // Cleanup Stripe element\n if (stripeCardElementRef.current) {\n stripeCardElementRef.current.destroy();\n stripeCardElementRef.current = null;\n }\n }}\n disabled={isProcessing}\n className=\"px-4 py-2 text-sm font-medium text-foreground hover:text-primary transition-colors disabled:opacity-50\"\n >\n Back\n </button>\n ) : (\n <div />\n )}\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={onClose}\n disabled={isProcessing}\n className=\"px-4 py-2 text-sm font-medium text-foreground border border-border rounded-lg hover:bg-muted transition-colors disabled:opacity-50\"\n >\n Cancel\n </button>\n {step === 'add-card' && (\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={\n isProcessing ||\n isAddingPaymentMethod ||\n isLoadingSdk ||\n (selectedProvider === 'stripe' && !isCardComplete)\n }\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors disabled:opacity-50\"\n >\n {isProcessing || isAddingPaymentMethod ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n {selectedProvider === 'razorpay' ? 'Processing...' : 'Saving...'}\n </>\n ) : isLoadingSdk ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n Loading…\n </>\n ) : selectedProvider === 'stripe' ? (\n 'Add Card'\n ) : (\n 'Add Payment Method (₹1)'\n )}\n </button>\n )}\n </div>\n </footer>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;AA8GA,IAAM,KAAc,GAAa,MACxB,IAAI,SAAS,GAAS,MAAW;AACtC,KAAI,SAAS,eAAe,EAAG,EAAE;AAC/B,KAAS;AACT;;CAEF,IAAM,IAAS,SAAS,cAAc,SAAS;AAM/C,CALA,EAAO,KAAK,GACZ,EAAO,MAAM,GACb,EAAO,QAAQ,IACf,EAAO,eAAe,GAAS,EAC/B,EAAO,gBAAgB,EAAO,gBAAI,MAAM,kBAAkB,IAAM,CAAC,EACjE,SAAS,KAAK,YAAY,EAAO;EACjC,EAGS,KAAyD,EACpE,WACA,YACA,qBACA,aACA,YACA,mBACI;CACJ,IAAM,EAAE,SAAM,IAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAEnC,KAAW,EAAuB,KAAK,EACvC,IAAiB,EAAuB,KAAK,EAC7C,IAAuB,EAAiC,KAAK,EAC7D,IAAY,EAA8B,KAAK,EAG/C,EACJ,kBACA,yBACA,oBACA,WAAW,MACT,EAAyB,CAAC,EAAO,EAC/B,EAAE,0BAAuB,2BAAwB,GAAuB,EAKxE,IAAkB,GAAU,aAAa,KAAK,SAAS,GAAS,aAAa,KAAK,MAClF,IAAe,KAAmB,GAClC,IAAa,KAAiB,CAAC,GAE/B,CAAC,GAAM,KAAW,EAAoB,kBAAkB,EACxD,CAAC,GAAkB,KAAuB,EAAuC,KAAK,EACtF,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,CAAC,GAAc,KAAmB,EAAS,GAAM,EACjD,CAAC,GAAc,KAAmB,EAAS,GAAM,EACjD,CAAC,GAAgB,KAAqB,EAAS,GAAG,EAClD,CAAC,GAAc,KAAmB,EAAS,GAAK,EAChD,CAAC,IAAgB,KAAqB,EAAS,GAAM,EAGrD,IAAuB,GAAa,MAAoC;AAG5E,EAFA,EAAoB,EAAS,EAC7B,EAAQ,WAAW,EACnB,EAAS,KAAK;IACb,EAAE,CAAC;AA2GN,CAxGA,QAAgB;AACd,EAAI,KACF,EAAQ,kBAAkB,EAC1B,EAAoB,KAAK,EACzB,EAAS,KAAK,EACd,EAAkB,GAAG,EACrB,EAAgB,GAAK,EACrB,EAAkB,GAAM,IAKtB,EAAqB,aADrB,EAAqB,QAAQ,SAAS,EACP;IAGlC,CAAC,EAAO,CAAC,EAGZ,QAAgB;AACV,QAAS,qBAAqB,MAC9B,KAAc,CAAC,IACjB,EAAqB,SAAS,GACrB,KAAgB,CAAC,KAC1B,EAAqB,WAAW;IAEjC;EAAC;EAAM;EAAiB;EAAY;EAAc;EAAqB,CAAC,EAG3E,QAAgB;AACV,cAAS,cAAc,MAAqB,WAGhD;OAAI,CAAC,KAAwB,CAAC,GAAiB;AAC7C,MAAS,oDAAoD;AAC7D;;AAyDF,UAtDK,MAEc,YAAY;AAC7B,MAAgB,GAAK;AACrB,QAAI;AAGF,SAFA,MAAM,EAAW,6BAA6B,YAAY,EAEtD,CAAC,OAAO,OACV,OAAU,MAAM,4BAA4B;KAG9C,IAAM,IAAS,OAAO,OAAO,EAAqB;AAClD,OAAU,UAAU;KAGpB,IAAI,IAAW;AACf,YAAO,CAAC,EAAe,WAAW,IAAW,IAE3C,CADA,MAAM,IAAI,SAAS,MAAY,WAAW,GAAS,IAAI,CAAC,EACxD;AAGF,SAAI,CAAC,EAAe,QAClB,OAAU,MAAM,mDAAmD;AAGrE,SAAI,EAAqB,QACvB;KAIF,IAAM,IADW,EAAO,UAAU,CACL,OAAO,QAAQ,EAC1C,OAAO,EAA0B,uCAAuC,EACzE,CAAC;AAKF,KAHA,EAAY,MAAM,EAAe,QAAQ,EACzC,EAAqB,UAAU,GAE/B,EAAY,GAAG,WAAW,MAAU;AAElC,MADA,EAAkB,EAAM,SAAS,EAC7B,EAAM,QACR,EAAS,EAAM,MAAM,QAAQ,GAE7B,EAAS,KAAK;OAEhB;aACK,GAAK;AACZ,OAAS,aAAe,QAAQ,EAAI,UAAU,wBAAwB;cAC9D;AACR,OAAgB,GAAM;;OAId,QAEC;AACX,QAAI,EAAqB,QACvB,KAAI;AAEF,KADA,EAAqB,QAAQ,SAAS,EACtC,EAAqB,UAAU;YACzB;QA3De;;IAgE1B;EAAC;EAAM;EAAkB;EAAsB;EAAgB,CAAC,EAGnE,QAAgB;EACd,IAAM,KAAiB,MAAqB;AAC1C,GAAI,EAAE,QAAQ,YAAY,KAAU,CAAC,KACnC,GAAS;;AAIb,SADA,SAAS,iBAAiB,WAAW,EAAc,QACtC,SAAS,oBAAoB,WAAW,EAAc;IAClE;EAAC;EAAQ;EAAc;EAAQ,CAAC;CAEnC,IAAM,IAAqB,EAAY,YAAY;AACjD,MAAI,CAAC,EAAU,WAAW,CAAC,EAAqB,SAAS;AACvD,KAAS,yBAAyB;AAClC;;AAGF,MAAI,CAAC,EAAe,MAAM,EAAE;AAC1B,KAAS,mCAAmC;AAC5C;;AAIF,EADA,EAAgB,GAAK,EACrB,EAAS,KAAK;AAEd,MAAI;GAEF,IAAM,IAAqB,MAAM,EAAoB;IACnD;IACA,UAAU;IACV,UAAU,EAA0B;IACpC;IACD,CAAC;AAEF,OAAI,CAAC,EAAmB,aACtB,OAAU,MAAM,uCAAuC;GAIzD,IAAM,EAAE,gBAAa,OAAO,MAAgB,MAAM,EAAU,QAAQ,iBAClE,EAAmB,cACnB,EACE,gBAAgB;IACd,MAAM,EAAqB;IAC3B,iBAAiB,EAAE,MAAM,GAAgB;IAC1C,EACF,CACF;AAED,OAAI,EACF,OAAU,MAAM,EAAY,QAAQ;AAGtC,OAAI,GAAa,WAAW,YAC1B,OAAU,MAAM,2BAA2B;AAW7C,GAPA,EAAQ,UAAU,EAGlB,MAAM,IAAI,SAAS,MAAY,WAAW,GAAS,IAAK,CAAC,EACzD,KAAa,EAGb,iBAAiB;AACf,OAAS;MACR,IAAI;WACA,GAAK;AAEZ,GADA,EAAS,aAAe,QAAQ,EAAI,UAAU,+BAA+B,EAC7E,EAAgB,GAAM;;IAEvB;EAAC;EAAgB;EAAc;EAAkB;EAAqB;EAAW;EAAQ,CAAC,EAEvF,IAAuB,EAAY,YAAY;AAEnD,EADA,EAAgB,GAAK,EACrB,EAAS,KAAK;AAEd,MAAI;AAEF,KAAgB,GAAK;AACrB,OAAI;AACF,UAAM,EAAW,gDAAgD,eAAe;WAC1E;AAGN,IAFA,EAAS,8BAA8B,EACvC,EAAgB,GAAM,EACtB,EAAgB,GAAM;AACtB;;AAEF,KAAgB,GAAM;GAGtB,IAAM,IAAqB,MAAM,EAAoB;IACnD;IACA,UAAU;IACV,UAAU,EAA0B;IACpC;IACD,CAAC;AAEF,OACE,CAAC,EAAmB,WACpB,CAAC,EAAmB,WACpB,CAAC,EAAmB,eACpB;AAEA,IADA,EAAS,EAAmB,SAAS,kCAAkC,EACvE,EAAgB,GAAM;AACtB;;AAIF,OAAI,CAAC,OAAO,UAAU;AAEpB,IADA,EAAS,0BAA0B,EACnC,EAAgB,GAAM;AACtB;;GAGF,IAAM,IAAU;IACd,KAAK,EAAmB;IACxB,SAAS,EAAmB,UAAU,KAAK;IAC3C,UAAU,EAAmB,SAAS,aAAa;IACnD,UAAU,EAAmB;IAC7B,aAAa,EAAmB;IAChC,MAAM;IACN,aAAa,IAAI,EAAmB,OAAO;IAC3C,SAAS;KACP,MAAM,EAAmB;KACzB,OAAO,EAAmB;KAC1B,SAAS,EAAmB;KAC7B;IACD,OAAO;KACL,cAAc;KACd,gBAAgB,EAAa,UAAU;KACxC;IACD,SAAS,YAAY;AASnB,KAPA,EAAQ,UAAU,EAGlB,MAAM,IAAI,SAAS,MAAY,WAAW,GAAS,IAAK,CAAC,EACzD,KAAa,EAGb,iBAAiB;AACf,SAAS;QACR,IAAI;;IAET,OAAO,EACL,iBAAiB;AAEf,KADA,EAAgB,GAAM,EACtB,EAAS,oBAAoB;OAEhC;IACF;AAEW,OAAI,OAAO,SAAS,EAAQ,CACpC,MAAM;WACH,GAAK;AAEZ,GADA,EAAS,aAAe,QAAQ,EAAI,UAAU,+BAA+B,EAC7E,EAAgB,GAAM;;IAEvB;EAAC;EAAc;EAAkB;EAAqB;EAAW;EAAQ,CAAC,EAEvE,KAAe,QAAkB;AACrC,EAAI,MAAqB,WACvB,GAAoB,GACX,MAAqB,cAC9B,GAAsB;IAEvB;EAAC;EAAkB;EAAoB;EAAqB,CAAC;AAIhE,QAFK,IAGH,kBAAC,OAAD;EACE,WAAU;EACV,MAAK;EACL,cAAW;EACX,mBAAgB;YAJlB,CAOE,kBAAC,OAAD;GACE,WAAU;GACV,SAAU,IAAyB,KAAA,IAAV;GACzB,eAAY;GACZ,CAAA,EAGF,kBAAC,OAAD;GACE,KAAK;GACL,WAAU;aAFZ;IAKE,kBAAC,UAAD;KAAQ,WAAU;eAAlB,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAY,WAAU,uBAAwB,CAAA;OAC1C,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,IAAG;OAAc,WAAU;iBAC5B,MAAS,YAAY,yBAAyB;OAC5C,CAAA,EACL,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,MAAS,qBAAqB;QAC9B,MAAS,eACP,MAAqB,WAClB,wBACA;QACL,MAAS,gBAAgB;QACzB,MAAS,aAAa;QACrB;SACA,EAAA,CAAA,CACF;SACN,kBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,UAAU;MACV,WAAU;MACV,cAAW;gBAEX,kBAAC,IAAD,EAAG,WAAU,UAAW,CAAA;MACjB,CAAA,CACF;;IAGT,kBAAC,OAAD;KAAK,WAAU;eACZ,IACC,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD,EAAS,WAAU,6CAA8C,CAAA;MAC7D,CAAA,GACJ,MAAS,oBACX,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACG,KACC,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAqB,SAAS;QAC7C,WAAU;kBAHZ,CAKE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,GAAD,EAAY,WAAU,6BAA8B,CAAA;SAChD,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAA8B;SAAU,CAAA,EACrD,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAAgC,CAAA,CACzE,EAAA,CAAA,CACC;;OAEV,KACC,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAqB,WAAW;QAC/C,WAAU;kBAHZ,CAKE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,GAAD,EAAY,WAAU,2BAA4B,CAAA;SAC9C,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAA8B;SAAY,CAAA,EACvD,kBAAC,KAAD;SAAG,WAAU;mBAAgC;SAA0B,CAAA,CACnE,EAAA,CAAA,CACC;;OAEV,CAAC,KAAc,CAAC,KACf,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,GAAD,EAAa,WAAU,yCAA0C,CAAA;SACjE,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAG,uCAAuC,iCAAiC;UAC1E,CAAA;SACJ,kBAAC,KAAD;UAAG,WAAU;oBACV,IACG,4EACA;UACF,CAAA;SACA;;OAEJ;UACJ,MAAS,aACX,kBAAC,OAAD;MAAK,WAAU;gBAAf;OAEG,KACC,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAa,WAAU,wBAAyB,CAAA,EAChD,kBAAC,KAAD;SAAG,WAAU;mBAAW;SAAU,CAAA,CAC9B;;OAIP,MAAqB,YACpB,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,SAAD;SACE,SAAQ;SACR,WAAU;mBACX;SAEO,CAAA,EACR,kBAAC,SAAD;SACE,MAAK;SACL,IAAG;SACH,OAAO;SACP,WAAW,MAAM,EAAkB,EAAE,OAAO,MAAM;SAClD,WAAU;SACV,aAAY;SACZ,UAAU;SACV,CAAA,CACE;;OAIP,MAAqB,YACpB,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,SAAD;SAAO,WAAU;mBACd,EAAG,gCAAgC,eAAe;SAC7C,CAAA,EACR,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UACE,KAAK;UACL,WAAU;UACV,CAAA,EACD,KACC,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD,EAAS,WAAU,6CAA8C,CAAA,EACjE,kBAAC,QAAD;WAAM,WAAU;qBAAqC;WAAsB,CAAA,CACvE;YAEJ;WACF;;OAIP,MAAqB,cACpB,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,KAAD;SAAG,WAAU;mBAAb,CACE,kBAAC,UAAD,EAAA,UAAQ,yBAA8B,CAAA,EAAA,wHAEpC;;QACA,CAAA;OAIR,kBAAC,SAAD;QAAO,WAAU;kBAAjB,CACE,kBAAC,SAAD;SACE,MAAK;SACL,SAAS;SACT,WAAW,MAAM,EAAgB,EAAE,OAAO,QAAQ;SAClD,WAAU;SACV,UAAU;SACV,CAAA,EACF,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAG,iCAAiC,gCAAgC;SAChE,CAAA,CACD;;OAGR,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAgC;SAAiB,CAAA,EACjE,kBAAC,QAAD;SACE,WAAW,oEACT,MAAqB,WACjB,+CACA;mBAGL,MAAqB,WAAW,WAAW;SACvC,CAAA,CACH;;OACF;UAGN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD,EAAc,WAAU,mCAAoC,CAAA;QACxD,CAAA;OACN,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAG,uCAAuC,qCAAqC;QAC9E,CAAA;OACJ,kBAAC,KAAD;QAAG,WAAU;kBAAqC;QAE9C,CAAA;OACA;;KAEJ,CAAA;IAGL,MAAS,aACR,kBAAC,UAAD;KAAQ,WAAU;eAAlB,CACG,MAAS,cAAc,KAAc,IACpC,kBAAC,UAAD;MACE,MAAK;MACL,eAAe;AAKb,OAJA,EAAQ,kBAAkB,EAC1B,EAAoB,KAAK,EACzB,EAAS,KAAK,EAEd,AAEE,EAAqB,aADrB,EAAqB,QAAQ,SAAS,EACP;;MAGnC,UAAU;MACV,WAAU;gBACX;MAEQ,CAAA,GAET,kBAAC,OAAD,EAAO,CAAA,EAET,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,UAAU;OACV,WAAU;iBACX;OAEQ,CAAA,EACR,MAAS,cACR,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,UACE,KACA,KACA,KACC,MAAqB,YAAY,CAAC;OAErC,WAAU;iBAET,KAAgB,IACf,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAS,WAAU,uBAAwB,CAAA,EAC1C,MAAqB,aAAa,kBAAkB,YACpD,EAAA,CAAA,GACD,IACF,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAS,WAAU,uBAAwB,CAAA,EAAA,WAE1C,EAAA,CAAA,GACD,MAAqB,WACvB,aAEA;OAEK,CAAA,CAEP;QACC;;IAEP;KACF;MArRY"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-billing",
3
- "version": "2026.531.2",
3
+ "version": "2026.531.3",
4
4
  "description": "Billing microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [