@burdenoff/microfe-billing 2026.912.1 → 2026.912.2
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/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js +1 -1
- package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js.map +1 -1
- package/dist/billing/modules/checkout/pages/CheckoutPage.js +30 -31
- package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ import { ChevronDown as t, Coins as n, CreditCard as r, Wallet as i } from "luci
|
|
|
3
3
|
import { Fragment as a, jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
4
4
|
import { useI18n as c } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
5
5
|
//#region src/billing/modules/checkout/components/UnifiedPaymentMethodSelector.tsx
|
|
6
|
-
var l = (e) => o(e === "WALLET" ? i : r, { className: "size-4" }), u = (e) => `${e.type === "CREDIT_CARD" ? "Credit Card" : e.type === "DEBIT_CARD" ? "Debit Card" : e.type}${e.last4 ? ` •••• ${e.last4}` : ""}${e.brand ? ` (${e.brand})` : ""}`, d = ({ savedPaymentMethods: i, selectedPaymentMethodId: d, onSelectSavedCard: f, selectedProvider: p, onSelectProvider: m, stripeEnabled: h, razorpayEnabled: g, canPayWithCredits: _, availableCredits: v,
|
|
6
|
+
var l = (e) => o(e === "WALLET" ? i : r, { className: "size-4" }), u = (e) => `${e.type === "CREDIT_CARD" ? "Credit Card" : e.type === "DEBIT_CARD" ? "Debit Card" : e.type}${e.last4 ? ` •••• ${e.last4}` : ""}${e.brand ? ` (${e.brand})` : ""}`, d = ({ savedPaymentMethods: i, selectedPaymentMethodId: d, onSelectSavedCard: f, selectedProvider: p, onSelectProvider: m, stripeEnabled: h, razorpayEnabled: g, canPayWithCredits: _, availableCredits: v, creditsRequired: y, billingCountry: b, saveCard: x, onSaveCardChange: S, autoRenewEnabled: C = !1, isLoading: w = !1 }) => {
|
|
7
7
|
let { t: T } = c(), E = (e, t) => {
|
|
8
8
|
let n = T(e);
|
|
9
9
|
return n === e ? t : n;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UnifiedPaymentMethodSelector.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/UnifiedPaymentMethodSelector.tsx"],"sourcesContent":["/**\n * Unified Payment Method Selector\n * Combines payment providers and saved payment methods into one clear interface\n */\n\nimport { type FC, useState } from 'react';\nimport { Coins, CreditCard, Wallet, ChevronDown } from 'lucide-react';\nimport type { SavedPaymentMethod } from '../types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface UnifiedPaymentMethodSelectorProps {\n // Saved cards\n savedPaymentMethods: SavedPaymentMethod[];\n selectedPaymentMethodId: string | null;\n onSelectSavedCard: (id: string | null) => void;\n\n // Provider selection (for new cards)\n selectedProvider: 'CREDITS' | 'STRIPE' | 'RAZORPAY' | 'AUTO';\n onSelectProvider: (provider: 'CREDITS' | 'STRIPE' | 'RAZORPAY') => void;\n\n // Provider availability\n stripeEnabled: boolean;\n razorpayEnabled: boolean;\n canPayWithCredits: boolean;\n availableCredits: number;\n totalAmount: number;\n billingCountry?: string;\n\n // Save card option\n saveCard: boolean;\n onSaveCardChange: (save: boolean) => void;\n\n // Auto-renewal (when enabled, card must be saved - checkbox hidden)\n autoRenewEnabled?: boolean;\n\n isLoading?: boolean;\n}\n\nconst getCardIcon = (type: string) => {\n if (type === 'WALLET') return <Wallet className=\"size-4\" />;\n return <CreditCard className=\"size-4\" />;\n};\n\nconst getCardDisplay = (pm: SavedPaymentMethod): string => {\n const type =\n pm.type === 'CREDIT_CARD' ? 'Credit Card' : pm.type === 'DEBIT_CARD' ? 'Debit Card' : pm.type;\n const last4 = pm.last4 ? ` •••• ${pm.last4}` : '';\n const brand = pm.brand ? ` (${pm.brand})` : '';\n return `${type}${last4}${brand}`;\n};\n\nexport const UnifiedPaymentMethodSelector: FC<UnifiedPaymentMethodSelectorProps> = ({\n savedPaymentMethods,\n selectedPaymentMethodId,\n onSelectSavedCard,\n selectedProvider,\n onSelectProvider,\n stripeEnabled,\n razorpayEnabled,\n canPayWithCredits,\n availableCredits,\n totalAmount,\n billingCountry,\n saveCard,\n onSaveCardChange,\n autoRenewEnabled = false,\n isLoading = false,\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 [showAllSavedCards, setShowAllSavedCards] = useState(false);\n const hasEnoughCredits = availableCredits >= totalAmount;\n const isIndia = billingCountry === 'IN';\n\n // Get default card or first card for collapsible view\n const defaultSavedCard = savedPaymentMethods.find((pm) => pm.isDefault) || savedPaymentMethods[0];\n const otherSavedCards = savedPaymentMethods.filter((pm) => pm.id !== defaultSavedCard?.id);\n\n const handleSelect = (type: 'saved' | 'new', value: string) => {\n if (type === 'saved') {\n // When selecting saved card, set it and auto-determine provider\n const selectedCard = savedPaymentMethods.find((pm) => pm.id === value);\n if (selectedCard) {\n onSelectSavedCard(value);\n // Auto-set provider based on card type\n if (selectedCard.razorpayPaymentMethodId) {\n onSelectProvider('RAZORPAY');\n } else if (selectedCard.stripePaymentMethodId) {\n onSelectProvider('STRIPE');\n }\n }\n } else {\n // When selecting new card payment, clear saved card selection\n onSelectSavedCard(null);\n onSelectProvider(value as 'CREDITS' | 'STRIPE' | 'RAZORPAY');\n }\n };\n\n const isSelected = (type: 'saved' | 'new', value: string): boolean => {\n if (type === 'saved') {\n return selectedPaymentMethodId === value;\n } else {\n return !selectedPaymentMethodId && selectedProvider === value;\n }\n };\n\n return (\n <div className=\"space-y-3\">\n <h3 className=\"text-sm font-medium text-text-primary\">\n {tr('billing.checkout.paymentMethod', 'Payment Method')}\n </h3>\n\n {isLoading ? (\n <div className=\"space-y-2\">\n <div className=\"h-16 rounded-lg border border-border-subtle bg-bg-sunken animate-pulse\" />\n <div className=\"h-16 rounded-lg border border-border-subtle bg-bg-sunken animate-pulse\" />\n </div>\n ) : (\n <div className=\"space-y-2\">\n {/* Credits Option */}\n {canPayWithCredits && (\n <button\n type=\"button\"\n onClick={() => handleSelect('new', 'CREDITS')}\n disabled={!hasEnoughCredits}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('new', 'CREDITS')\n ? 'border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle hover:border-border-strong hover:bg-bg-sunken'\n }\n ${!hasEnoughCredits && 'opacity-50 cursor-not-allowed'}\n `}\n >\n <Coins\n className={`size-5 shrink-0 ${isSelected('new', 'CREDITS') ? 'text-text-link' : 'text-text-secondary'}`}\n />\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">Pay with Credits</div>\n <div className=\"text-xs text-text-secondary\">\n {hasEnoughCredits\n ? `Available: ${availableCredits.toLocaleString()} credits`\n : `Insufficient credits (need ${totalAmount.toLocaleString()})`}\n </div>\n </div>\n {hasEnoughCredits && (\n <span className=\"text-xs px-2 py-0.5 rounded bg-success/10 text-success\">\n Instant\n </span>\n )}\n </button>\n )}\n\n {/* Saved Cards Section */}\n {savedPaymentMethods.length > 0 && (\n <>\n <div className=\"text-xs font-medium text-text-secondary uppercase tracking-wide pt-2\">\n Saved Cards\n </div>\n\n {/* Default/First Card - Always Shown */}\n {defaultSavedCard && (\n <button\n type=\"button\"\n onClick={() => handleSelect('saved', defaultSavedCard.id)}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('saved', defaultSavedCard.id)\n ? 'border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle hover:border-border-strong hover:bg-bg-sunken'\n }\n `}\n >\n {getCardIcon(defaultSavedCard.type)}\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">\n {getCardDisplay(defaultSavedCard)}\n </div>\n {defaultSavedCard.expiryMonth && defaultSavedCard.expiryYear && (\n <div className=\"text-xs text-text-secondary\">\n Expires {defaultSavedCard.expiryMonth.toString().padStart(2, '0')}/\n {defaultSavedCard.expiryYear}\n </div>\n )}\n </div>\n {defaultSavedCard.isDefault && (\n <span className=\"text-xs px-2 py-0.5 rounded bg-[var(--color-accent-soft)] text-text-link\">\n Default\n </span>\n )}\n </button>\n )}\n\n {/* Other Cards - Collapsible */}\n {otherSavedCards.length > 0 && (\n <>\n {showAllSavedCards ? (\n <>\n {otherSavedCards.map((pm) => (\n <button\n key={pm.id}\n type=\"button\"\n onClick={() => handleSelect('saved', pm.id)}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('saved', pm.id)\n ? 'border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle hover:border-border-strong hover:bg-bg-sunken'\n }\n `}\n >\n {getCardIcon(pm.type)}\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">\n {getCardDisplay(pm)}\n </div>\n {pm.expiryMonth && pm.expiryYear && (\n <div className=\"text-xs text-text-secondary\">\n Expires {pm.expiryMonth.toString().padStart(2, '0')}/{pm.expiryYear}\n </div>\n )}\n </div>\n </button>\n ))}\n <button\n type=\"button\"\n onClick={() => setShowAllSavedCards(false)}\n className=\"text-xs text-text-link hover:underline flex items-center gap-1 pl-1\"\n >\n <ChevronDown className=\"size-3 rotate-180\" />\n Hide other cards\n </button>\n </>\n ) : (\n <button\n type=\"button\"\n onClick={() => setShowAllSavedCards(true)}\n className=\"text-xs text-text-link hover:underline flex items-center gap-1 pl-1\"\n >\n <ChevronDown className=\"size-3\" />\n Show {otherSavedCards.length} more card\n {otherSavedCards.length > 1 ? 's' : ''}\n </button>\n )}\n </>\n )}\n </>\n )}\n\n {/* New Card Options */}\n <div className=\"text-xs font-medium text-text-secondary uppercase tracking-wide pt-2\">\n {savedPaymentMethods.length > 0\n ? 'Or use new card'\n : canPayWithCredits\n ? 'Or pay with card'\n : 'Pay with Card'}\n </div>\n\n {/* Razorpay (India) */}\n {razorpayEnabled && isIndia && (\n <button\n type=\"button\"\n onClick={() => handleSelect('new', 'RAZORPAY')}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('new', 'RAZORPAY')\n ? 'border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle hover:border-border-strong hover:bg-bg-sunken'\n }\n `}\n >\n <CreditCard\n className={`size-5 shrink-0 ${isSelected('new', 'RAZORPAY') ? 'text-text-link' : 'text-text-secondary'}`}\n />\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">\n UPI / Cards / Netbanking\n </div>\n <div className=\"text-xs text-text-secondary\">Razorpay</div>\n </div>\n <span className=\"text-xs px-2 py-0.5 rounded bg-accent-blue-subtle text-accent-blue\">\n Recommended\n </span>\n </button>\n )}\n\n {/* Stripe (International) */}\n {stripeEnabled && (\n <button\n type=\"button\"\n onClick={() => handleSelect('new', 'STRIPE')}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('new', 'STRIPE')\n ? 'border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle hover:border-border-strong hover:bg-bg-sunken'\n }\n `}\n >\n <CreditCard\n className={`size-5 shrink-0 ${isSelected('new', 'STRIPE') ? 'text-text-link' : 'text-text-secondary'}`}\n />\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">Credit / Debit Cards</div>\n <div className=\"text-xs text-text-secondary\">Stripe - Multi-currency</div>\n </div>\n {!isIndia && (\n <span className=\"text-xs px-2 py-0.5 rounded bg-accent-blue-subtle text-accent-blue\">\n Recommended\n </span>\n )}\n </button>\n )}\n\n {/* Save Card Checkbox - Only show when using new card (not saved card, not credits) */}\n {!selectedPaymentMethodId &&\n selectedProvider !== 'CREDITS' &&\n selectedProvider !== 'AUTO' &&\n !autoRenewEnabled && (\n <div className=\"mt-4 pt-4 border-t border-border-subtle\">\n <label className=\"flex items-start gap-3 cursor-pointer group\">\n <input\n type=\"checkbox\"\n checked={saveCard}\n onChange={(e) => onSaveCardChange(e.target.checked)}\n className=\"mt-0.5 size-4 rounded border-border-subtle text-text-link focus:ring-[var(--color-focus-ring)] focus:ring-offset-0\"\n />\n <div className=\"flex-1\">\n <div className=\"text-sm font-medium text-text-primary group-hover:text-text-link transition-colors\">\n Save card for future use\n </div>\n <div className=\"text-xs text-text-secondary mt-0.5\">\n Securely save this payment method for faster checkout next time\n </div>\n </div>\n </label>\n </div>\n )}\n\n {/* Auto-renewal notice - Show when auto-renewal is enabled */}\n {!selectedPaymentMethodId &&\n selectedProvider !== 'CREDITS' &&\n selectedProvider !== 'AUTO' &&\n autoRenewEnabled && (\n <div className=\"mt-4 pt-4 border-t border-border-subtle\">\n <div className=\"flex items-start gap-2 text-xs text-text-secondary bg-bg-sunken p-3 rounded-lg\">\n <svg\n className=\"size-4 mt-0.5 flex-shrink-0\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <div>Your payment method will be securely saved for auto-renewal</div>\n </div>\n </div>\n )}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"UnifiedPaymentMethodSelector.js","names":[],"sources":["../../../../../src/billing/modules/checkout/components/UnifiedPaymentMethodSelector.tsx"],"sourcesContent":["/**\n * Unified Payment Method Selector\n * Combines payment providers and saved payment methods into one clear interface\n */\n\nimport { type FC, useState } from 'react';\nimport { Coins, CreditCard, Wallet, ChevronDown } from 'lucide-react';\nimport type { SavedPaymentMethod } from '../types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface UnifiedPaymentMethodSelectorProps {\n // Saved cards\n savedPaymentMethods: SavedPaymentMethod[];\n selectedPaymentMethodId: string | null;\n onSelectSavedCard: (id: string | null) => void;\n\n // Provider selection (for new cards)\n selectedProvider: 'CREDITS' | 'STRIPE' | 'RAZORPAY' | 'AUTO';\n onSelectProvider: (provider: 'CREDITS' | 'STRIPE' | 'RAZORPAY') => void;\n\n // Provider availability\n stripeEnabled: boolean;\n razorpayEnabled: boolean;\n canPayWithCredits: boolean;\n availableCredits: number;\n /** Credits (not a currency amount) needed to pay for this purchase —\n * comparable directly to availableCredits. Passing a raw USD/local-\n * currency total here compares two different units and silently lets\n * a purchase proceed with insufficient credits (reproduced live,\n * BOFF-7192: 800 available credits read as \"enough\" for a 2,474-credit\n * item because the prop held $24.74, not 2,474). */\n creditsRequired: number;\n billingCountry?: string;\n\n // Save card option\n saveCard: boolean;\n onSaveCardChange: (save: boolean) => void;\n\n // Auto-renewal (when enabled, card must be saved - checkbox hidden)\n autoRenewEnabled?: boolean;\n\n isLoading?: boolean;\n}\n\nconst getCardIcon = (type: string) => {\n if (type === 'WALLET') return <Wallet className=\"size-4\" />;\n return <CreditCard className=\"size-4\" />;\n};\n\nconst getCardDisplay = (pm: SavedPaymentMethod): string => {\n const type =\n pm.type === 'CREDIT_CARD' ? 'Credit Card' : pm.type === 'DEBIT_CARD' ? 'Debit Card' : pm.type;\n const last4 = pm.last4 ? ` •••• ${pm.last4}` : '';\n const brand = pm.brand ? ` (${pm.brand})` : '';\n return `${type}${last4}${brand}`;\n};\n\nexport const UnifiedPaymentMethodSelector: FC<UnifiedPaymentMethodSelectorProps> = ({\n savedPaymentMethods,\n selectedPaymentMethodId,\n onSelectSavedCard,\n selectedProvider,\n onSelectProvider,\n stripeEnabled,\n razorpayEnabled,\n canPayWithCredits,\n availableCredits,\n creditsRequired,\n billingCountry,\n saveCard,\n onSaveCardChange,\n autoRenewEnabled = false,\n isLoading = false,\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 [showAllSavedCards, setShowAllSavedCards] = useState(false);\n const hasEnoughCredits = availableCredits >= creditsRequired;\n const isIndia = billingCountry === 'IN';\n\n // Get default card or first card for collapsible view\n const defaultSavedCard = savedPaymentMethods.find((pm) => pm.isDefault) || savedPaymentMethods[0];\n const otherSavedCards = savedPaymentMethods.filter((pm) => pm.id !== defaultSavedCard?.id);\n\n const handleSelect = (type: 'saved' | 'new', value: string) => {\n if (type === 'saved') {\n // When selecting saved card, set it and auto-determine provider\n const selectedCard = savedPaymentMethods.find((pm) => pm.id === value);\n if (selectedCard) {\n onSelectSavedCard(value);\n // Auto-set provider based on card type\n if (selectedCard.razorpayPaymentMethodId) {\n onSelectProvider('RAZORPAY');\n } else if (selectedCard.stripePaymentMethodId) {\n onSelectProvider('STRIPE');\n }\n }\n } else {\n // When selecting new card payment, clear saved card selection\n onSelectSavedCard(null);\n onSelectProvider(value as 'CREDITS' | 'STRIPE' | 'RAZORPAY');\n }\n };\n\n const isSelected = (type: 'saved' | 'new', value: string): boolean => {\n if (type === 'saved') {\n return selectedPaymentMethodId === value;\n } else {\n return !selectedPaymentMethodId && selectedProvider === value;\n }\n };\n\n return (\n <div className=\"space-y-3\">\n <h3 className=\"text-sm font-medium text-text-primary\">\n {tr('billing.checkout.paymentMethod', 'Payment Method')}\n </h3>\n\n {isLoading ? (\n <div className=\"space-y-2\">\n <div className=\"h-16 rounded-lg border border-border-subtle bg-bg-sunken animate-pulse\" />\n <div className=\"h-16 rounded-lg border border-border-subtle bg-bg-sunken animate-pulse\" />\n </div>\n ) : (\n <div className=\"space-y-2\">\n {/* Credits Option */}\n {canPayWithCredits && (\n <button\n type=\"button\"\n onClick={() => handleSelect('new', 'CREDITS')}\n disabled={!hasEnoughCredits}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('new', 'CREDITS')\n ? 'border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle hover:border-border-strong hover:bg-bg-sunken'\n }\n ${!hasEnoughCredits && 'opacity-50 cursor-not-allowed'}\n `}\n >\n <Coins\n className={`size-5 shrink-0 ${isSelected('new', 'CREDITS') ? 'text-text-link' : 'text-text-secondary'}`}\n />\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">Pay with Credits</div>\n <div className=\"text-xs text-text-secondary\">\n {hasEnoughCredits\n ? `Available: ${availableCredits.toLocaleString()} credits`\n : `Insufficient credits (need ${creditsRequired.toLocaleString()})`}\n </div>\n </div>\n {hasEnoughCredits && (\n <span className=\"text-xs px-2 py-0.5 rounded bg-success/10 text-success\">\n Instant\n </span>\n )}\n </button>\n )}\n\n {/* Saved Cards Section */}\n {savedPaymentMethods.length > 0 && (\n <>\n <div className=\"text-xs font-medium text-text-secondary uppercase tracking-wide pt-2\">\n Saved Cards\n </div>\n\n {/* Default/First Card - Always Shown */}\n {defaultSavedCard && (\n <button\n type=\"button\"\n onClick={() => handleSelect('saved', defaultSavedCard.id)}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('saved', defaultSavedCard.id)\n ? 'border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle hover:border-border-strong hover:bg-bg-sunken'\n }\n `}\n >\n {getCardIcon(defaultSavedCard.type)}\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">\n {getCardDisplay(defaultSavedCard)}\n </div>\n {defaultSavedCard.expiryMonth && defaultSavedCard.expiryYear && (\n <div className=\"text-xs text-text-secondary\">\n Expires {defaultSavedCard.expiryMonth.toString().padStart(2, '0')}/\n {defaultSavedCard.expiryYear}\n </div>\n )}\n </div>\n {defaultSavedCard.isDefault && (\n <span className=\"text-xs px-2 py-0.5 rounded bg-[var(--color-accent-soft)] text-text-link\">\n Default\n </span>\n )}\n </button>\n )}\n\n {/* Other Cards - Collapsible */}\n {otherSavedCards.length > 0 && (\n <>\n {showAllSavedCards ? (\n <>\n {otherSavedCards.map((pm) => (\n <button\n key={pm.id}\n type=\"button\"\n onClick={() => handleSelect('saved', pm.id)}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('saved', pm.id)\n ? 'border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle hover:border-border-strong hover:bg-bg-sunken'\n }\n `}\n >\n {getCardIcon(pm.type)}\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">\n {getCardDisplay(pm)}\n </div>\n {pm.expiryMonth && pm.expiryYear && (\n <div className=\"text-xs text-text-secondary\">\n Expires {pm.expiryMonth.toString().padStart(2, '0')}/{pm.expiryYear}\n </div>\n )}\n </div>\n </button>\n ))}\n <button\n type=\"button\"\n onClick={() => setShowAllSavedCards(false)}\n className=\"text-xs text-text-link hover:underline flex items-center gap-1 pl-1\"\n >\n <ChevronDown className=\"size-3 rotate-180\" />\n Hide other cards\n </button>\n </>\n ) : (\n <button\n type=\"button\"\n onClick={() => setShowAllSavedCards(true)}\n className=\"text-xs text-text-link hover:underline flex items-center gap-1 pl-1\"\n >\n <ChevronDown className=\"size-3\" />\n Show {otherSavedCards.length} more card\n {otherSavedCards.length > 1 ? 's' : ''}\n </button>\n )}\n </>\n )}\n </>\n )}\n\n {/* New Card Options */}\n <div className=\"text-xs font-medium text-text-secondary uppercase tracking-wide pt-2\">\n {savedPaymentMethods.length > 0\n ? 'Or use new card'\n : canPayWithCredits\n ? 'Or pay with card'\n : 'Pay with Card'}\n </div>\n\n {/* Razorpay (India) */}\n {razorpayEnabled && isIndia && (\n <button\n type=\"button\"\n onClick={() => handleSelect('new', 'RAZORPAY')}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('new', 'RAZORPAY')\n ? 'border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle hover:border-border-strong hover:bg-bg-sunken'\n }\n `}\n >\n <CreditCard\n className={`size-5 shrink-0 ${isSelected('new', 'RAZORPAY') ? 'text-text-link' : 'text-text-secondary'}`}\n />\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">\n UPI / Cards / Netbanking\n </div>\n <div className=\"text-xs text-text-secondary\">Razorpay</div>\n </div>\n <span className=\"text-xs px-2 py-0.5 rounded bg-accent-blue-subtle text-accent-blue\">\n Recommended\n </span>\n </button>\n )}\n\n {/* Stripe (International) */}\n {stripeEnabled && (\n <button\n type=\"button\"\n onClick={() => handleSelect('new', 'STRIPE')}\n className={`\n w-full flex items-center gap-3 p-4 rounded-lg border transition-all text-left\n ${\n isSelected('new', 'STRIPE')\n ? 'border-border-strong bg-[var(--color-accent-soft)] ring-1 ring-[var(--color-focus-ring)]'\n : 'border-border-subtle hover:border-border-strong hover:bg-bg-sunken'\n }\n `}\n >\n <CreditCard\n className={`size-5 shrink-0 ${isSelected('new', 'STRIPE') ? 'text-text-link' : 'text-text-secondary'}`}\n />\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-sm font-medium text-text-primary\">Credit / Debit Cards</div>\n <div className=\"text-xs text-text-secondary\">Stripe - Multi-currency</div>\n </div>\n {!isIndia && (\n <span className=\"text-xs px-2 py-0.5 rounded bg-accent-blue-subtle text-accent-blue\">\n Recommended\n </span>\n )}\n </button>\n )}\n\n {/* Save Card Checkbox - Only show when using new card (not saved card, not credits) */}\n {!selectedPaymentMethodId &&\n selectedProvider !== 'CREDITS' &&\n selectedProvider !== 'AUTO' &&\n !autoRenewEnabled && (\n <div className=\"mt-4 pt-4 border-t border-border-subtle\">\n <label className=\"flex items-start gap-3 cursor-pointer group\">\n <input\n type=\"checkbox\"\n checked={saveCard}\n onChange={(e) => onSaveCardChange(e.target.checked)}\n className=\"mt-0.5 size-4 rounded border-border-subtle text-text-link focus:ring-[var(--color-focus-ring)] focus:ring-offset-0\"\n />\n <div className=\"flex-1\">\n <div className=\"text-sm font-medium text-text-primary group-hover:text-text-link transition-colors\">\n Save card for future use\n </div>\n <div className=\"text-xs text-text-secondary mt-0.5\">\n Securely save this payment method for faster checkout next time\n </div>\n </div>\n </label>\n </div>\n )}\n\n {/* Auto-renewal notice - Show when auto-renewal is enabled */}\n {!selectedPaymentMethodId &&\n selectedProvider !== 'CREDITS' &&\n selectedProvider !== 'AUTO' &&\n autoRenewEnabled && (\n <div className=\"mt-4 pt-4 border-t border-border-subtle\">\n <div className=\"flex items-start gap-2 text-xs text-text-secondary bg-bg-sunken p-3 rounded-lg\">\n <svg\n className=\"size-4 mt-0.5 flex-shrink-0\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <div>Your payment method will be securely saved for auto-renewal</div>\n </div>\n </div>\n )}\n </div>\n )}\n </div>\n );\n};\n"],"mappings":";;;;;AA4CA,IAAM,KAAe,MACW,EAA1B,MAAS,WAAkB,IACvB,GADsB,EAAQ,WAAU,SAAU,CAAA,GAItD,KAAkB,MAKf,GAHL,EAAG,SAAS,gBAAgB,gBAAgB,EAAG,SAAS,eAAe,eAAe,EAAG,OAC7E,EAAG,QAAQ,SAAS,EAAG,UAAU,KACjC,EAAG,QAAQ,KAAK,EAAG,MAAM,KAAK,MAIjC,KAAuE,EAClF,wBACA,4BACA,sBACA,qBACA,qBACA,kBACA,oBACA,sBACA,qBACA,oBACA,mBACA,aACA,qBACA,sBAAmB,IACnB,eAAY,SACR;CACJ,IAAM,EAAE,SAAM,EAAQ,GAChB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,CAAG;EACxB,OAAO,MAAe,IAAM,IAAW;CACzC,GACM,CAAC,GAAmB,KAAwB,EAAS,EAAK,GAC1D,IAAmB,KAAoB,GACvC,IAAU,MAAmB,MAG7B,IAAmB,EAAoB,MAAM,MAAO,EAAG,SAAS,KAAK,EAAoB,IACzF,IAAkB,EAAoB,QAAQ,MAAO,EAAG,OAAO,GAAkB,EAAE,GAEnF,KAAgB,GAAuB,MAAkB;EAC7D,IAAI,MAAS,SAAS;GAEpB,IAAM,IAAe,EAAoB,MAAM,MAAO,EAAG,OAAO,CAAK;GACrE,AAAI,MACF,EAAkB,CAAK,GAEnB,EAAa,0BACf,EAAiB,UAAU,IAClB,EAAa,yBACtB,EAAiB,QAAQ;EAG/B,OAGE,AADA,EAAkB,IAAI,GACtB,EAAiB,CAA0C;CAE/D,GAEM,KAAc,GAAuB,MACrC,MAAS,UACJ,MAA4B,IAE5B,CAAC,KAA2B,MAAqB;CAI5D,OACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,MAAD;GAAI,WAAU;aACX,EAAG,kCAAkC,gBAAgB;EACpD,CAAA,GAEH,IACC,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD,EAAK,WAAU,yEAA0E,CAAA,GACzF,kBAAC,OAAD,EAAK,WAAU,yEAA0E,CAAA,CACtF;OAEL,kBAAC,OAAD;GAAK,WAAU;aAAf;IAEG,KACC,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAa,OAAO,SAAS;KAC5C,UAAU,CAAC;KACX,WAAW;;kBAGP,EAAW,OAAO,SAAS,IACvB,6FACA,qEACL;kBACC,CAAC,KAAoB,gCAAgC;;eAX3D;MAcE,kBAAC,GAAD,EACE,WAAW,mBAAmB,EAAW,OAAO,SAAS,IAAI,mBAAmB,wBACjF,CAAA;MACD,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAwC;OAAqB,CAAA,GAC5E,kBAAC,OAAD;QAAK,WAAU;kBACZ,IACG,cAAc,EAAiB,eAAe,EAAE,YAChD,8BAA8B,EAAgB,eAAe,EAAE;OAChE,CAAA,CACF;;MACJ,KACC,kBAAC,QAAD;OAAM,WAAU;iBAAyD;MAEnE,CAAA;KAEF;;IAIT,EAAoB,SAAS,KAC5B,kBAAA,GAAA,EAAA,UAAA;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAuE;KAEjF,CAAA;KAGJ,KACC,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAa,SAAS,EAAiB,EAAE;MACxD,WAAW;;sBAGP,EAAW,SAAS,EAAiB,EAAE,IACnC,6FACA,qEACL;;gBATL;OAYG,EAAY,EAAiB,IAAI;OAClC,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAe,CAAgB;QAC7B,CAAA,GACJ,EAAiB,eAAe,EAAiB,cAChD,kBAAC,OAAD;SAAK,WAAU;mBAAf;UAA6C;UAClC,EAAiB,YAAY,SAAS,EAAE,SAAS,GAAG,GAAG;UAAE;UACjE,EAAiB;SACf;UAEJ;;OACJ,EAAiB,aAChB,kBAAC,QAAD;QAAM,WAAU;kBAA2E;OAErF,CAAA;MAEF;;KAIT,EAAgB,SAAS,KACxB,kBAAA,GAAA,EAAA,UACG,IACC,kBAAA,GAAA,EAAA,UAAA,CACG,EAAgB,KAAK,MACpB,kBAAC,UAAD;MAEE,MAAK;MACL,eAAe,EAAa,SAAS,EAAG,EAAE;MAC1C,WAAW;;8BAGP,EAAW,SAAS,EAAG,EAAE,IACrB,6FACA,qEACL;;gBAVL,CAaG,EAAY,EAAG,IAAI,GACpB,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACZ,EAAe,CAAE;OACf,CAAA,GACJ,EAAG,eAAe,EAAG,cACpB,kBAAC,OAAD;QAAK,WAAU;kBAAf;SAA6C;SAClC,EAAG,YAAY,SAAS,EAAE,SAAS,GAAG,GAAG;SAAE;SAAE,EAAG;QACtD;SAEJ;QACC;QAvBD,EAAG,EAuBF,CACT,GACD,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAqB,EAAK;MACzC,WAAU;gBAHZ,CAKE,kBAAC,GAAD,EAAa,WAAU,oBAAqB,CAAA,GAAC,kBAEvC;OACR,EAAA,CAAA,IAEF,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAqB,EAAI;MACxC,WAAU;gBAHZ;OAKE,kBAAC,GAAD,EAAa,WAAU,SAAU,CAAA;OAAC;OAC5B,EAAgB;OAAO;OAC5B,EAAgB,SAAS,IAAI,MAAM;MAC9B;QAEV,CAAA;IAEJ,EAAA,CAAA;IAIJ,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAoB,SAAS,IAC1B,oBACA,IACE,qBACA;IACH,CAAA;IAGJ,KAAmB,KAClB,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAa,OAAO,UAAU;KAC7C,WAAW;;kBAGP,EAAW,OAAO,UAAU,IACxB,6FACA,qEACL;;eATL;MAYE,kBAAC,GAAD,EACE,WAAW,mBAAmB,EAAW,OAAO,UAAU,IAAI,mBAAmB,wBAClF,CAAA;MACD,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAwC;OAElD,CAAA,GACL,kBAAC,OAAD;QAAK,WAAU;kBAA8B;OAAa,CAAA,CACvD;;MACL,kBAAC,QAAD;OAAM,WAAU;iBAAqE;MAE/E,CAAA;KACA;;IAIT,KACC,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAa,OAAO,QAAQ;KAC3C,WAAW;;kBAGP,EAAW,OAAO,QAAQ,IACtB,6FACA,qEACL;;eATL;MAYE,kBAAC,GAAD,EACE,WAAW,mBAAmB,EAAW,OAAO,QAAQ,IAAI,mBAAmB,wBAChF,CAAA;MACD,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAwC;OAAyB,CAAA,GAChF,kBAAC,OAAD;QAAK,WAAU;kBAA8B;OAA4B,CAAA,CACtE;;MACJ,CAAC,KACA,kBAAC,QAAD;OAAM,WAAU;iBAAqE;MAE/E,CAAA;KAEF;;IAIT,CAAC,KACA,MAAqB,aACrB,MAAqB,UACrB,CAAC,KACC,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,SAAD;MAAO,WAAU;gBAAjB,CACE,kBAAC,SAAD;OACE,MAAK;OACL,SAAS;OACT,WAAW,MAAM,EAAiB,EAAE,OAAO,OAAO;OAClD,WAAU;MACX,CAAA,GACD,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAqF;OAE/F,CAAA,GACL,kBAAC,OAAD;QAAK,WAAU;kBAAqC;OAE/C,CAAA,CACF;QACA;;IACJ,CAAA;IAIR,CAAC,KACA,MAAqB,aACrB,MAAqB,UACrB,KACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OACE,WAAU;OACV,MAAK;OACL,SAAQ;OACR,QAAO;iBAEP,kBAAC,QAAD;QACE,eAAc;QACd,gBAAe;QACf,aAAa;QACb,GAAE;OACH,CAAA;MACE,CAAA,GACL,kBAAC,OAAD,EAAA,UAAK,8DAAgE,CAAA,CAClE;;IACF,CAAA;GAEN;IAEJ;;AAET"}
|
|
@@ -200,19 +200,19 @@ var $e = () => {
|
|
|
200
200
|
kt,
|
|
201
201
|
Jt
|
|
202
202
|
]);
|
|
203
|
-
let {
|
|
203
|
+
let { billingAccount: Xt, canPayWithCredits: Zt, hasEnoughCredits: Qt, selectedCurrency: J, selectedPaymentMethodId: $t, selectedProvider: en, setCurrency: Y, setPaymentProvider: X, setSelectedPaymentMethodId: tn } = F;
|
|
204
204
|
l(() => {
|
|
205
|
-
if (!
|
|
206
|
-
let e =
|
|
205
|
+
if (!Xt?.currency || q === "IN") return;
|
|
206
|
+
let e = Xt.currency;
|
|
207
207
|
e !== "INR" && J !== e && Y(e);
|
|
208
208
|
}, [
|
|
209
209
|
q,
|
|
210
|
-
|
|
210
|
+
Xt,
|
|
211
211
|
J,
|
|
212
212
|
Y
|
|
213
213
|
]), l(() => {
|
|
214
214
|
if (!(en !== "AUTO" && en !== null)) {
|
|
215
|
-
if (
|
|
215
|
+
if (Zt && Qt) {
|
|
216
216
|
X("CREDITS"), tn(null);
|
|
217
217
|
return;
|
|
218
218
|
}
|
|
@@ -225,9 +225,8 @@ var $e = () => {
|
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
}, [
|
|
228
|
-
|
|
228
|
+
Zt,
|
|
229
229
|
Qt,
|
|
230
|
-
nn,
|
|
231
230
|
G,
|
|
232
231
|
$t,
|
|
233
232
|
en,
|
|
@@ -490,7 +489,7 @@ var $e = () => {
|
|
|
490
489
|
v?.billingAccountId,
|
|
491
490
|
b
|
|
492
491
|
]);
|
|
493
|
-
let
|
|
492
|
+
let nn = !!(v?.upgradeFromSubscriptionId || v?.downgradeFromSubscriptionId);
|
|
494
493
|
l(() => {
|
|
495
494
|
w && C && S.length === 0 && !Z && _("/addons", { replace: !0 });
|
|
496
495
|
}, [
|
|
@@ -500,11 +499,11 @@ var $e = () => {
|
|
|
500
499
|
Z,
|
|
501
500
|
_
|
|
502
501
|
]);
|
|
503
|
-
let
|
|
502
|
+
let rn = fe({
|
|
504
503
|
billingAccountId: F.billingAccount?.id,
|
|
505
504
|
purchaseType: E,
|
|
506
505
|
productId: D
|
|
507
|
-
}),
|
|
506
|
+
}), an = c(async () => {
|
|
508
507
|
if (!F.billingAccount) return;
|
|
509
508
|
let e = ht.current;
|
|
510
509
|
if (ht.current = !1, !e && !Ee(F.billingAccount)) {
|
|
@@ -589,7 +588,7 @@ var $e = () => {
|
|
|
589
588
|
g,
|
|
590
589
|
Rt,
|
|
591
590
|
Bt
|
|
592
|
-
]),
|
|
591
|
+
]), on = c((e) => {
|
|
593
592
|
y("billing.payment.succeeded", {
|
|
594
593
|
route: "/billing/checkout",
|
|
595
594
|
entityId: A?.paymentId ?? A?.orderId,
|
|
@@ -599,7 +598,7 @@ var $e = () => {
|
|
|
599
598
|
y,
|
|
600
599
|
A?.orderId,
|
|
601
600
|
A?.paymentId
|
|
602
|
-
]),
|
|
601
|
+
]), sn = c((e) => {
|
|
603
602
|
y("billing.payment.failed", {
|
|
604
603
|
route: "/billing/checkout",
|
|
605
604
|
entityId: A?.paymentId ?? A?.orderId,
|
|
@@ -610,7 +609,7 @@ var $e = () => {
|
|
|
610
609
|
y,
|
|
611
610
|
A?.orderId,
|
|
612
611
|
A?.paymentId
|
|
613
|
-
]),
|
|
612
|
+
]), cn = c((e) => {
|
|
614
613
|
F.updateItemQuantity("credits-purchase", e);
|
|
615
614
|
}, [F]);
|
|
616
615
|
if (!rt.canProcessPayment) return /* @__PURE__ */ p(t, { message: "You don't have permission to make purchases." });
|
|
@@ -618,12 +617,12 @@ var $e = () => {
|
|
|
618
617
|
purchaseType: E,
|
|
619
618
|
productId: D,
|
|
620
619
|
billingAccountId: F.billingAccount?.id,
|
|
621
|
-
nativeCheckout:
|
|
620
|
+
nativeCheckout: rn,
|
|
622
621
|
onBack: () => _("dashboard")
|
|
623
622
|
});
|
|
624
|
-
let
|
|
623
|
+
let ln = Nt || Ft && E === "PLAN" || It && E === "ADDON" && !w || w && !C;
|
|
625
624
|
if (w && C && S.length === 0 && !Z) return null;
|
|
626
|
-
let
|
|
625
|
+
let un = () => {
|
|
627
626
|
if (w) {
|
|
628
627
|
let e = F.items.length;
|
|
629
628
|
return `Purchase ${e} Add-on${e === 1 ? "" : "s"}`;
|
|
@@ -634,7 +633,7 @@ var $e = () => {
|
|
|
634
633
|
case "ADDON": return "Purchase Add-on";
|
|
635
634
|
default: return "Checkout";
|
|
636
635
|
}
|
|
637
|
-
},
|
|
636
|
+
}, dn = () => {
|
|
638
637
|
switch (E) {
|
|
639
638
|
case "CREDITS": return /* @__PURE__ */ p(Le, { className: "size-6" });
|
|
640
639
|
case "PLAN": return /* @__PURE__ */ p(Be, { className: "size-6" });
|
|
@@ -642,7 +641,7 @@ var $e = () => {
|
|
|
642
641
|
default: return /* @__PURE__ */ p(Ue, { className: "size-6" });
|
|
643
642
|
}
|
|
644
643
|
};
|
|
645
|
-
if (
|
|
644
|
+
if (ln) return /* @__PURE__ */ p("div", {
|
|
646
645
|
className: "flex items-center justify-center min-h-[400px]",
|
|
647
646
|
children: /* @__PURE__ */ m("div", {
|
|
648
647
|
className: "text-center space-y-4",
|
|
@@ -1034,8 +1033,8 @@ var $e = () => {
|
|
|
1034
1033
|
publishableKey: it || Dt?.stripePublishableKey || "",
|
|
1035
1034
|
amount: Math.round((A.total ?? F.total) * 100),
|
|
1036
1035
|
currency: A.currency || F.selectedCurrency,
|
|
1037
|
-
onSuccess:
|
|
1038
|
-
onError:
|
|
1036
|
+
onSuccess: on,
|
|
1037
|
+
onError: sn,
|
|
1039
1038
|
onCancel: () => k("select"),
|
|
1040
1039
|
isCompletingExistingPayment: !!F.selectedPaymentMethodId
|
|
1041
1040
|
})
|
|
@@ -1067,8 +1066,8 @@ var $e = () => {
|
|
|
1067
1066
|
email: A.prefillEmail,
|
|
1068
1067
|
contact: A.prefillContact
|
|
1069
1068
|
},
|
|
1070
|
-
onSuccess: (e) =>
|
|
1071
|
-
onError:
|
|
1069
|
+
onSuccess: (e) => on(e.razorpay_payment_id),
|
|
1070
|
+
onError: sn,
|
|
1072
1071
|
onCancel: () => k("select")
|
|
1073
1072
|
})]
|
|
1074
1073
|
})
|
|
@@ -1089,10 +1088,10 @@ var $e = () => {
|
|
|
1089
1088
|
className: "flex items-center gap-3 mb-6",
|
|
1090
1089
|
children: [/* @__PURE__ */ p("div", {
|
|
1091
1090
|
className: "p-2 rounded-lg bg-[var(--color-accent-soft)] text-text-link",
|
|
1092
|
-
children:
|
|
1091
|
+
children: dn()
|
|
1093
1092
|
}), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("h1", {
|
|
1094
1093
|
className: "text-2xl font-bold text-text-primary",
|
|
1095
|
-
children:
|
|
1094
|
+
children: un()
|
|
1096
1095
|
}), /* @__PURE__ */ p("p", {
|
|
1097
1096
|
className: "text-sm text-text-secondary",
|
|
1098
1097
|
children: "Complete your purchase securely"
|
|
@@ -1163,7 +1162,7 @@ var $e = () => {
|
|
|
1163
1162
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4 sm:p-6",
|
|
1164
1163
|
children: /* @__PURE__ */ p(ye, {
|
|
1165
1164
|
value: F.items[0].quantity,
|
|
1166
|
-
onChange:
|
|
1165
|
+
onChange: cn,
|
|
1167
1166
|
costPerCredit: W,
|
|
1168
1167
|
currency: F.selectedCurrency,
|
|
1169
1168
|
disabled: qt
|
|
@@ -1176,8 +1175,8 @@ var $e = () => {
|
|
|
1176
1175
|
selectedAccountId: F.billingAccount?.id ?? null,
|
|
1177
1176
|
onSelect: F.setBillingAccount,
|
|
1178
1177
|
isLoading: Nt,
|
|
1179
|
-
disabled:
|
|
1180
|
-
disabledMessage:
|
|
1178
|
+
disabled: nn,
|
|
1179
|
+
disabledMessage: nn ? "Billing account is locked for plan changes" : void 0
|
|
1181
1180
|
}), E === "ADDON" && !T && F.billingAccount && /* @__PURE__ */ p(Se, {
|
|
1182
1181
|
subscriptions: V,
|
|
1183
1182
|
selectedSubscriptionId: F.selectedSubscriptionId,
|
|
@@ -1203,7 +1202,7 @@ var $e = () => {
|
|
|
1203
1202
|
razorpayEnabled: kt,
|
|
1204
1203
|
canPayWithCredits: F.canPayWithCredits,
|
|
1205
1204
|
availableCredits: F.availableCredits,
|
|
1206
|
-
|
|
1205
|
+
creditsRequired: F.creditsRequired,
|
|
1207
1206
|
billingCountry: q,
|
|
1208
1207
|
saveCard: F.saveCard,
|
|
1209
1208
|
onSaveCardChange: F.setSaveCard,
|
|
@@ -1379,8 +1378,8 @@ var $e = () => {
|
|
|
1379
1378
|
}),
|
|
1380
1379
|
/* @__PURE__ */ p("button", {
|
|
1381
1380
|
type: "button",
|
|
1382
|
-
onClick:
|
|
1383
|
-
disabled: F.isProcessing || !F.billingAccount || F.items.length === 0 || E === "ADDON" && !T && Wt || E === "ADDON" && !T && V.length === 0 || E === "ADDON" && !T && !F.selectedSubscriptionId || z && (!B.shippingAddressId || !B.shippingMethodId),
|
|
1381
|
+
onClick: an,
|
|
1382
|
+
disabled: F.isProcessing || !F.billingAccount || F.items.length === 0 || F.selectedProvider === "CREDITS" && !Qt || E === "ADDON" && !T && Wt || E === "ADDON" && !T && V.length === 0 || E === "ADDON" && !T && !F.selectedSubscriptionId || z && (!B.shippingAddressId || !B.shippingMethodId),
|
|
1384
1383
|
className: "w-full px-6 py-4 rounded-lg bg-action-primary-bg text-action-primary-text font-semibold\n hover:bg-action-primary-bgHover disabled:opacity-50 disabled:cursor-not-allowed\n transition-colors flex items-center justify-center gap-2",
|
|
1385
1384
|
children: F.isProcessing ? /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p(ze, { className: "size-5 animate-spin" }), "Processing…"] }) : F.selectedProvider === "CREDITS" ? "Pay with Credits" : "Proceed to Payment"
|
|
1386
1385
|
}),
|
|
@@ -1397,7 +1396,7 @@ var $e = () => {
|
|
|
1397
1396
|
hasPhone: !!F.billingAccount.contactNumber?.trim(),
|
|
1398
1397
|
hasAddress: !!(F.billingAccount.billingAddresses?.[0]?.line1?.trim() && F.billingAccount.billingAddresses?.[0]?.postalCode?.trim()),
|
|
1399
1398
|
onCompleted: () => {
|
|
1400
|
-
ht.current = !0, mt(!1),
|
|
1399
|
+
ht.current = !0, mt(!1), an(), Pt().then((e) => {
|
|
1401
1400
|
let t = (e.data?.myBillingAccounts ?? []).map(Qe).find((e) => e?.id === F.billingAccount?.id);
|
|
1402
1401
|
t && F.setBillingAccount(t);
|
|
1403
1402
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CheckoutPage.js","names":[],"sources":["../../../../../src/billing/modules/checkout/pages/CheckoutPage.tsx"],"sourcesContent":["/**\n * Checkout Page Component\n * Main checkout flow for purchasing credits, plans, and add-ons\n * Supports single item checkout and multi-addon cart checkout\n */\n\nimport { type FC, useState, useEffect, useCallback, useMemo, useRef } from 'react';\nimport { useParams, useLocation } from 'react-router';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport {\n ArrowLeft,\n ShoppingCart,\n Loader2,\n CheckCircle,\n XCircle,\n Coins,\n Package,\n Blocks,\n Wallet,\n CreditCard,\n ArrowRight,\n Receipt,\n RefreshCw,\n} from 'lucide-react';\n\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { AccessDenied, PageHeader } from '../../../shared/components';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useAddonCartStore } from '../../addons/store/addonCartStore';\n\nimport {\n CREDITS_PER_USD,\n useCheckout,\n useNativeCheckout,\n usePaymentProviders,\n usePaymentStatusPolling,\n} from '../hooks';\nimport { isNativeIAPAvailable } from '../../../lib/native-purchases';\nimport { NativeCheckoutView } from '../components/NativeCheckoutView';\nimport { getStoredBillingAccountId } from '../../../hooks/useBillingAccountSelection';\nimport {\n useGetBillingAccountsCheckoutQuery,\n useGetPlanCheckoutQuery,\n useGetSelectedAddonCheckoutQuery,\n useGetPaymentMethodsCheckoutQuery,\n useGetActiveSubscriptionsCheckoutQuery,\n useGetCostOfCreditsCheckoutQuery,\n useGetStoreOrderCheckoutQuery,\n type GetActiveSubscriptionsCheckoutQuery,\n type GetBillingAccountsCheckoutQuery,\n} from '../../../../generated/global-operations';\nimport {\n BillingAccountSelector,\n OrderSummary,\n CreditsAmountInput,\n StripePaymentForm,\n RazorpayPaymentButton,\n SubscriptionSelector,\n SimpleAutoPayToggle,\n UnifiedPaymentMethodSelector,\n PromoCodeInput,\n CompleteBillingAccountModal,\n isBillingAccountComplete,\n StoreShippingSection,\n type StoreShippingSelection,\n} from '../components';\nimport {\n WorkspaceQuotaAssignmentModal,\n isWorkspaceScopedQuota,\n type WorkspaceQuotaToAssign,\n type WorkspaceOption,\n} from '../components/WorkspaceQuotaAssignmentModal';\nimport {\n buildStoreOrderCheckoutItems,\n normalizeStoreOrderCurrency,\n type StoreOrderLineItem,\n} from '../utils/storeOrder';\nimport { useSetStoreOrderShipping, STORE_SHIPPING_ITEM_ID } from '../hooks/useStoreShipping';\nimport type { SavedPaymentMethod } from '../types';\n\nimport type { CheckoutItem, CheckoutBillingAccount, PurchaseType } from '../types';\n\ntype RawCheckoutBillingAccount = GetBillingAccountsCheckoutQuery['myBillingAccounts'][number];\n\nfunction normalizeCheckoutBillingAccount(\n account: RawCheckoutBillingAccount\n): CheckoutBillingAccount | null {\n if (\n typeof account.id !== 'string' ||\n typeof account.name !== 'string' ||\n typeof account.email !== 'string' ||\n typeof account.currency !== 'string' ||\n typeof account.creditAmount !== 'number' ||\n typeof account.isActive !== 'boolean'\n ) {\n return null;\n }\n\n return {\n ...account,\n id: account.id,\n name: account.name,\n email: account.email,\n currency: account.currency,\n creditAmount: account.creditAmount,\n isActive: account.isActive,\n billingAddresses:\n account.billingAddresses === null\n ? null\n : account.billingAddresses?.flatMap((address) => (address ? [{ ...address }] : [])),\n };\n}\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useAppShell } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { getProfileScopedKey } from '@burdenoff/fe-libs/shared/utils';\nimport { recordAffiliateEvent } from '../../affiliate/api';\n\ntype CheckoutMode = 'select' | 'payment' | 'processing' | 'success' | 'error';\n\ninterface LocationState {\n purchaseType?: PurchaseType;\n itemId?: string;\n quantity?: number;\n upgradeFromSubscriptionId?: string;\n downgradeFromSubscriptionId?: string;\n currentPlanName?: string;\n billingAccountId?: string;\n}\n\nexport const CheckoutPage: FC = () => {\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 { planId, addonId, storeOrderId } = useParams<{\n planId?: string;\n addonId?: string;\n storeOrderId?: string;\n }>();\n const navigateTo = useBillingNavigate();\n const location = useLocation();\n const locationState = location.state as LocationState | undefined;\n\n const permissions = useBillingPermissions();\n const { emit } = useBillingEventEmitter();\n const {\n orgId,\n stripePublishableKey,\n razorpayKey,\n apiGatewayUrl,\n authToken,\n currentUser,\n workspaceId,\n } = useBilling();\n const { productSlug } = useAppShell();\n const referralEventReportedRef = useRef<string | null>(null);\n // Workspace options for the assignment modal — single workspace for now.\n // Multi-workspace support requires a listWorkspaces query (future enhancement).\n const workspaceOptions: WorkspaceOption[] = workspaceId\n ? [{ id: workspaceId, name: 'Current workspace' }]\n : [];\n\n // Addon cart store for multi-addon checkout\n const { items: cartItems, clearCart, _hasHydrated: cartHydrated } = useAddonCartStore();\n\n // Check if this is a cart-based checkout (route: /checkout/addons)\n const isCartCheckout = location.pathname.includes('/checkout/addons') && !addonId;\n\n // Check if this is a store order checkout (route: /checkout/store/:storeOrderId)\n const isStoreCheckout = location.pathname.includes('/checkout/store') && !!storeOrderId;\n\n // Determine what we're purchasing\n const purchaseType: PurchaseType = isCartCheckout\n ? 'ADDON'\n : isStoreCheckout\n ? 'ORDER' // Store orders use ORDER purchase type\n : (locationState?.purchaseType ?? (planId ? 'PLAN' : addonId ? 'ADDON' : 'CREDITS'));\n const itemId = locationState?.itemId ?? planId ?? addonId ?? storeOrderId;\n\n // Billing account completion gate\n const [showCompleteAccountModal, setShowCompleteAccountModal] = useState(false);\n // Bypass the isBillingAccountComplete gate for the call that follows modal completion.\n // The gate uses stale Apollo cache (contactNumber/billingAddresses are not returned by\n // updateBillingAccount mutation), so we skip it once immediately after the modal saves.\n const bypassBillingGate = useRef(false);\n\n // Workspace quota assignment modal state\n const [showAssignModal, setShowAssignModal] = useState(false);\n const [pendingWorkspaceQuotas, setPendingWorkspaceQuotas] = useState<WorkspaceQuotaToAssign[]>(\n []\n );\n const [pendingSubscriptionId, setPendingSubscriptionId] = useState<string | null>(null);\n\n // Checkout state\n const [mode, setMode] = useState<CheckoutMode>('select');\n const [paymentResponse, setPaymentResponse] = useState<{\n clientSecret?: string;\n orderId?: string;\n razorpayKeyId?: string;\n razorpayCustomerId?: string;\n paymentId?: string;\n paymentComplete?: boolean;\n prefillName?: string;\n prefillEmail?: string;\n prefillContact?: string;\n total?: number; // Total in target currency from backend (e.g., INR)\n currency?: string;\n } | null>(null);\n const [paymentErrorMessage, setPaymentErrorMessage] = useState<string | null>(null);\n\n // Store purchase details for success page (captured before clearing)\n const [purchaseDetails, setPurchaseDetails] = useState<{\n items: typeof checkout.items;\n total: number;\n billingAccountName: string;\n billingAccountId: string;\n previousBalance: number;\n newBalance: number;\n paymentMethod: 'credits' | 'card';\n currency: string;\n transactionId?: string;\n } | null>(null);\n\n // Track payment type for polling (card vs credits)\n const [pollingPaymentType, setPollingPaymentType] = useState<'card' | 'credits'>('card');\n\n useEffect(() => {\n if (mode !== 'success' || !purchaseDetails) {\n return;\n }\n\n const rawAttribution =\n typeof window !== 'undefined'\n ? window.localStorage.getItem(getProfileScopedKey('boff-referral-attribution'))\n : null;\n if (!rawAttribution) {\n return;\n }\n\n let attribution: { journeyId?: string; codeId?: string };\n try {\n attribution = JSON.parse(rawAttribution) as { journeyId?: string; codeId?: string };\n } catch {\n return;\n }\n if (!attribution.journeyId && !attribution.codeId) {\n return;\n }\n\n const uniqueReference =\n purchaseDetails.transactionId ||\n paymentResponse?.paymentId ||\n paymentResponse?.orderId ||\n storeOrderId ||\n itemId ||\n purchaseType;\n\n const signature = `${attribution.journeyId ?? 'journey'}:${uniqueReference}`;\n if (referralEventReportedRef.current === signature) {\n return;\n }\n referralEventReportedRef.current = signature;\n\n void recordAffiliateEvent(\n {\n journeyId: attribution.journeyId,\n codeId: attribution.codeId,\n eventType: purchaseType === 'ORDER' ? 'STORE_PURCHASE' : 'PAID_CONVERSION',\n productSlug,\n actorUserId: currentUser?.id,\n orderId:\n purchaseDetails.transactionId || paymentResponse?.paymentId || paymentResponse?.orderId,\n billingAccountId: purchaseDetails.billingAccountId,\n storeOrderId: purchaseType === 'ORDER' ? storeOrderId : undefined,\n valueAmount: purchaseDetails.total,\n currency: purchaseDetails.currency,\n mode: 'billing-checkout',\n metadata: {\n purchaseType,\n billingAccountName: purchaseDetails.billingAccountName,\n },\n },\n { apiGatewayUrl, authToken, orgId }\n ).catch(() => {\n referralEventReportedRef.current = null;\n });\n }, [\n apiGatewayUrl,\n authToken,\n currentUser?.id,\n itemId,\n mode,\n orgId,\n paymentResponse?.orderId,\n paymentResponse?.paymentId,\n productSlug,\n purchaseDetails,\n purchaseType,\n storeOrderId,\n ]);\n\n // Per-seat plan seat count\n const [seatCount, setSeatCount] = useState<number>(1);\n\n // Use checkout hook - pass upgrade/downgrade context if this is a plan change\n const checkout = useCheckout({\n defaultCurrency: 'USD',\n upgradeFromSubscriptionId: locationState?.upgradeFromSubscriptionId,\n downgradeFromSubscriptionId: locationState?.downgradeFromSubscriptionId,\n });\n const { config: providerConfig, stripeEnabled, razorpayEnabled } = usePaymentProviders();\n\n // Payment status polling for both card and credit payments\n const {\n // isPolling - available but UI relies on mode state instead\n elapsedTime,\n error: pollingError,\n // paymentResult - available but not directly used (callbacks handle results)\n } = usePaymentStatusPolling({\n paymentId: mode === 'processing' ? (paymentResponse?.paymentId ?? null) : null,\n billingAccountId: checkout.billingAccount?.id ?? purchaseDetails?.billingAccountId ?? null,\n paymentType: pollingPaymentType, // 'card' or 'credits' based on selected provider\n pollingInterval: 5000, // Poll every 5 seconds\n maxPollingTime: 300000, // 5 minutes timeout\n onSuccess: (result) => {\n // Update transaction ID if available from polling result\n if (result.transactionId) {\n setPurchaseDetails((prev) =>\n prev ? { ...prev, transactionId: result.transactionId } : prev\n );\n }\n setMode('success');\n if (isCartCheckout) {\n clearCart();\n }\n\n // Derive workspace-scoped quotas from the purchased plan's features.\n // We only show the modal for PLAN and ADDON purchases that include workspace quotas.\n // A feature's own context.quantity is a per-plan \"absolute limit\" override\n // (set via the Plan admin form) that takes precedence over the quota\n // catalog's generic limits.value default — must match buildWorkspaceQuotaInputs\n // (the backend function that actually grants the quota) or this preview\n // promises a different number than what the customer actually receives.\n if ((purchaseType === 'PLAN' || purchaseType === 'ADDON') && workspaceId) {\n type PlanFeature = {\n context?: Record<string, unknown> | null;\n quota?: { name?: string | null; limits?: Record<string, unknown> | null } | null;\n };\n const rawFeatures: Array<PlanFeature | null | undefined> =\n planData?.getPlan?.features ?? [];\n const features: PlanFeature[] = rawFeatures.filter((f): f is PlanFeature => f != null);\n const wsQuotas: WorkspaceQuotaToAssign[] = features\n .filter((f: PlanFeature) => f.quota?.name && isWorkspaceScopedQuota(f.quota.name))\n .map((f: PlanFeature) => {\n const limits = f.quota?.limits as Record<string, unknown> | null | undefined;\n const limitsType = limits && 'type' in limits ? String(limits.type) : 'number';\n const overrideQuantity =\n f.context && 'quantity' in f.context ? Number(f.context.quantity) : null;\n const catalogValue = limits && 'value' in limits ? Number(limits.value) : null;\n const limitsValue = overrideQuantity ?? catalogValue;\n return {\n quotaName: f.quota!.name!,\n productId:\n ((planData?.getPlan?.context as Record<string, unknown> | null)?.[\n 'productId'\n ] as string) ?? 'platform',\n limit: limitsType === 'number' ? limitsValue : null,\n noLimit: limitsType !== 'number',\n resetPeriod: 'NEVER',\n quotaMode: 'dedicated' as const,\n };\n });\n\n if (wsQuotas.length > 0) {\n setPendingWorkspaceQuotas(wsQuotas);\n // Refetch subscriptions to resolve the subscriptionId for the modal\n void refetchSubscriptions().then(\n (res: { data?: GetActiveSubscriptionsCheckoutQuery }) => {\n const match = res.data?.getActiveSubscriptions?.find(\n (s: { planId: string; id: string }) => s.planId === itemId\n );\n if (match) setPendingSubscriptionId(match.id);\n }\n );\n // With exactly one workspace there's no real choice to make — the\n // backend's autoActivateSingleWorkspaceForSubscription already\n // assigned these quotas to it at purchase time (see\n // global-billing-svc's Razorpay/Stripe webhook handlers), so this\n // modal would just be asking the customer to confirm something\n // that already happened. Only show it when there's a genuine\n // choice — 2+ workspaces — which is exactly the ambiguity it\n // exists to resolve.\n if (workspaceOptions.length > 1) {\n setShowAssignModal(true);\n }\n }\n }\n\n // Auto-redirect is handled by useEffect\n },\n onFailure: (result) => {\n setPaymentErrorMessage(result.failureReason || 'Payment failed');\n setMode('error');\n },\n onTimeout: () => {\n setPaymentErrorMessage(\n 'Payment verification timed out. Please check your billing history for the payment status.'\n );\n setMode('error');\n },\n });\n\n // Fetch billing accounts\n const {\n data: accountsData,\n loading: accountsLoading,\n refetch: refetchAccounts,\n } = useGetBillingAccountsCheckoutQuery({});\n\n // Fetch plan if purchasing a plan\n const {\n data: planData,\n loading: planLoading,\n // error: planError - available but not displayed (loading/empty states suffice)\n } = useGetPlanCheckoutQuery({\n variables: { id: itemId! },\n skip: purchaseType !== 'PLAN' || !itemId,\n });\n\n // Fetch addon if purchasing an addon\n const { data: addonData, loading: addonLoading } = useGetSelectedAddonCheckoutQuery({\n variables: { addonIDs: itemId ? [itemId] : [] },\n skip: purchaseType !== 'ADDON' || !itemId || isStoreCheckout, // Skip for store checkout\n });\n\n // Fetch store order (for store checkout)\n const { data: storeOrderData } = useGetStoreOrderCheckoutQuery({\n variables: { id: storeOrderId! },\n skip: !isStoreCheckout || !storeOrderId,\n });\n\n // ── Physical store-order shipping (physical produce only) ─────────────────\n // All shipping UI + logic below is gated on `isPhysicalStoreOrder`, which\n // REQUIRES isStoreCheckout (purchaseType 'ORDER') AND a PHYSICAL line item.\n // The digital-app / plan / addon / credits paths — and digital-only store\n // orders — never see any of it and behave byte-for-byte as before.\n //\n // getStoreOrder.lineItems is typed Record<string,unknown> by codegen but is a\n // StoreOrderLineItem[] at runtime — cast defensively behind an Array.isArray\n // guard. itemType is written LOWERCASE by microfe-store (CartPage), so compare\n // case-insensitively or the gate silently never fires.\n const storeOrderLineItems = useMemo<StoreOrderLineItem[]>(() => {\n const raw = storeOrderData?.getStoreOrder?.lineItems;\n return Array.isArray(raw) ? (raw as StoreOrderLineItem[]) : [];\n }, [storeOrderData]);\n\n const isPhysicalStoreOrder =\n isStoreCheckout &&\n storeOrderLineItems.some((li) => String(li.itemType).toUpperCase() === 'PHYSICAL');\n\n // The order currency drives the synthetic shipping line so it converts in\n // lockstep with the product ORDER items (see currency-conversion effect below).\n const storeOrderCurrency = normalizeStoreOrderCurrency(storeOrderData?.getStoreOrder?.currency);\n\n // Buyer's shipping selection, surfaced by <StoreShippingSection />. Cost is\n // DISPLAY-ONLY here; the backend recomputes it authoritatively on submit.\n const [shipping, setShipping] = useState<StoreShippingSelection>({\n shippingAddressId: null,\n shippingMethodId: null,\n shippingCost: 0,\n destinationCountry: null,\n });\n\n // NEW backend mutation — see hooks/useStoreShipping.ts. Inert until\n // global-store-svc ships `setStoreOrderShipping`.\n const [setStoreOrderShipping] = useSetStoreOrderShipping();\n\n // Fetch saved payment methods for selected billing account\n const { data: paymentMethodsData, loading: paymentMethodsLoading } =\n useGetPaymentMethodsCheckoutQuery({\n variables: { billingAccountId: checkout.billingAccount?.id! },\n skip: !checkout.billingAccount?.id,\n });\n\n // Fetch active subscriptions for addon purchases (needed to select which subscription to attach addon to)\n // Skip for store checkout - store items don't require an active subscription\n // Also load for PLAN purchases so we can resolve subscriptionId after payment success\n const {\n data: subscriptionsData,\n loading: subscriptionsLoading,\n refetch: refetchSubscriptions,\n } = useGetActiveSubscriptionsCheckoutQuery({\n variables: { billingAccountId: checkout.billingAccount?.id! },\n skip:\n !checkout.billingAccount?.id ||\n (purchaseType !== 'ADDON' && purchaseType !== 'PLAN') ||\n isStoreCheckout,\n });\n\n // Memoized subscriptions list\n const activeSubscriptions = useMemo(() => {\n return subscriptionsData?.getActiveSubscriptions ?? [];\n }, [subscriptionsData]);\n\n // Selected subscription for addon purchases\n const selectedSubscription = useMemo(() => {\n if (!checkout.selectedSubscriptionId || activeSubscriptions.length === 0) return null;\n return (\n activeSubscriptions.find((s: { id: string }) => s.id === checkout.selectedSubscriptionId) ??\n null\n );\n }, [checkout.selectedSubscriptionId, activeSubscriptions]);\n\n // Fetch credit cost from backend (for credits purchase)\n // Use current quantity from items, or default to 100 for initial query\n // Query in selected currency to get correct pricing\n const creditQuantity = checkout.items[0]?.type === 'CREDITS' ? checkout.items[0].quantity : 100;\n const { data: creditCostData, loading: creditCostLoading } = useGetCostOfCreditsCheckoutQuery({\n variables: { creditAmount: creditQuantity, currency: checkout.selectedCurrency || 'USD' },\n skip: purchaseType !== 'CREDITS',\n });\n\n // Calculate cost per credit from backend response\n const costPerCredit = useMemo(() => {\n if (creditCostData?.getCostOfCredits?.amount && creditQuantity > 0) {\n return creditCostData.getCostOfCredits.amount / creditQuantity;\n }\n return 0.01; // Default: 100 credits = $1\n }, [creditCostData, creditQuantity]);\n\n // Memoized saved payment methods\n const savedPaymentMethods = useMemo<SavedPaymentMethod[]>(() => {\n return paymentMethodsData?.getPaymentMethods ?? [];\n }, [paymentMethodsData]);\n\n // Memoized billing accounts list\n const billingAccounts = useMemo<CheckoutBillingAccount[]>(() => {\n return accountsData?.myBillingAccounts ?? [];\n }, [accountsData]);\n\n // Extract billing country from selected billing account's first billing address\n // This is used to restrict currency and payment provider options\n const billingCountry = useMemo<string | undefined>(() => {\n if (!checkout.billingAccount) return undefined;\n\n // Find the full account data with billing addresses\n const fullAccount = billingAccounts.find((acc) => acc.id === checkout.billingAccount?.id);\n const billingAddresses = fullAccount?.billingAddresses;\n\n if (billingAddresses && billingAddresses.length > 0) {\n return billingAddresses[0]?.country || undefined;\n }\n return undefined;\n }, [checkout.billingAccount, billingAccounts]);\n\n // Track if initial provider selection has been done for this billing country\n const [initialProviderSet, setInitialProviderSet] = useState<string | null>(null);\n\n // Auto-select appropriate currency based on billing country and billing account currency\n // - India: INR only (for Zoho Books GST invoice compatibility)\n // - International: Prefer billing account currency, or USD (INR not allowed for Stripe invoice compatibility)\n useEffect(() => {\n if (!billingCountry) return;\n\n if (billingCountry === 'IN') {\n // Indian billing: Force INR currency\n if (checkout.selectedCurrency !== 'INR') {\n checkout.setCurrency('INR');\n }\n // Auto-select Razorpay ONLY on first load for this billing country (don't override user's choice)\n if (\n razorpayEnabled &&\n initialProviderSet !== billingCountry &&\n checkout.selectedProvider !== 'CREDITS'\n ) {\n checkout.setPaymentProvider('RAZORPAY');\n setInitialProviderSet(billingCountry);\n }\n } else {\n // International billing: Force non-INR currency\n if (checkout.selectedCurrency === 'INR') {\n // Prefer billing account's currency if it's not INR, otherwise default to USD\n const accountCurrency = checkout.billingAccount?.currency;\n const preferredCurrency =\n accountCurrency && accountCurrency !== 'INR' ? accountCurrency : 'USD';\n checkout.setCurrency(preferredCurrency);\n }\n // For international, Stripe is the only option (Razorpay is India-only)\n if (checkout.selectedProvider === 'RAZORPAY') {\n checkout.setPaymentProvider('STRIPE');\n setInitialProviderSet(billingCountry);\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- Using specific checkout properties is intentional for granular updates\n }, [\n billingCountry,\n checkout.selectedCurrency,\n checkout.selectedProvider,\n checkout.setCurrency,\n checkout.setPaymentProvider,\n checkout.billingAccount?.currency,\n razorpayEnabled,\n initialProviderSet,\n ]);\n\n const {\n availableCredits,\n billingAccount: checkoutBillingAccount,\n canPayWithCredits,\n selectedCurrency,\n selectedPaymentMethodId,\n selectedProvider,\n setCurrency,\n setPaymentProvider,\n setSelectedPaymentMethodId,\n total: checkoutTotal,\n } = checkout;\n\n // Auto-select billing account's currency when billing account changes (for international)\n useEffect(() => {\n if (!checkoutBillingAccount?.currency || billingCountry === 'IN') return;\n\n const accountCurrency = checkoutBillingAccount.currency;\n // Update to billing account's currency if it's different and valid (not INR for international)\n if (accountCurrency !== 'INR' && selectedCurrency !== accountCurrency) {\n setCurrency(accountCurrency);\n }\n }, [billingCountry, checkoutBillingAccount, selectedCurrency, setCurrency]);\n\n // Auto-select payment method with priority: Credits > Saved Card > New Card\n useEffect(() => {\n // Skip if already manually selected something (not AUTO or null)\n if (selectedProvider !== 'AUTO' && selectedProvider !== null) {\n return; // Already selected, don't override user choice\n }\n\n const hasEnoughCredits = availableCredits >= checkoutTotal;\n\n // Priority 1: Credits (if available and sufficient)\n if (canPayWithCredits && hasEnoughCredits) {\n setPaymentProvider('CREDITS');\n setSelectedPaymentMethodId(null);\n return;\n }\n\n // Priority 2: Default saved card\n if (savedPaymentMethods.length > 0 && !selectedPaymentMethodId) {\n const defaultMethod = savedPaymentMethods.find((pm) => pm.isDefault && pm.isVerified);\n const firstVerified = savedPaymentMethods.find((pm) => pm.isVerified);\n const methodToSelect = defaultMethod || firstVerified;\n\n if (methodToSelect) {\n setSelectedPaymentMethodId(methodToSelect.id);\n // Auto-set provider based on card type\n if (methodToSelect.razorpayPaymentMethodId) {\n setPaymentProvider('RAZORPAY');\n } else if (methodToSelect.stripePaymentMethodId) {\n setPaymentProvider('STRIPE');\n }\n return;\n }\n }\n\n // Priority 3: New card (auto-select provider based on country)\n // This is already handled by the currency auto-selection effect above\n }, [\n availableCredits,\n canPayWithCredits,\n checkoutTotal,\n savedPaymentMethods,\n selectedPaymentMethodId,\n selectedProvider,\n setPaymentProvider,\n setSelectedPaymentMethodId,\n ]);\n\n // Track if item was already added to prevent duplicates\n const [itemAdded, setItemAdded] = useState(false);\n\n useEffect(() => {\n if (!isStoreCheckout || itemAdded || !storeOrderData?.getStoreOrder) {\n return;\n }\n\n const orderCurrency = normalizeStoreOrderCurrency(storeOrderData.getStoreOrder.currency);\n if (selectedCurrency !== orderCurrency) {\n setCurrency(orderCurrency);\n }\n }, [isStoreCheckout, itemAdded, selectedCurrency, setCurrency, storeOrderData]);\n\n // Initialize checkout with items\n // Initialize seat count from plan minSeats when plan data loads\n useEffect(() => {\n if (purchaseType === 'PLAN' && planData?.getPlan?.pricingModel === 'PER_SEAT') {\n const minSeats = planData.getPlan.minSeats ?? 1;\n setSeatCount((prev) => (prev < minSeats ? minSeats : prev));\n }\n }, [planData, purchaseType]);\n\n // Recalculate checkout item when seat count changes for PER_SEAT plans\n useEffect(() => {\n if (purchaseType === 'PLAN' && planData?.getPlan?.pricingModel === 'PER_SEAT' && itemAdded) {\n setItemAdded(false); // triggers re-add with new seatCount\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [seatCount]);\n\n useEffect(() => {\n // Skip if already added items\n if (itemAdded) {\n return;\n }\n\n // Handle cart-based checkout (multiple addons from cart)\n // Wait for cart to hydrate before processing\n if (isCartCheckout && cartHydrated && cartItems.length > 0) {\n // Clear existing items first to prevent duplicates (React strict mode can run effects twice)\n checkout.clearItems();\n\n for (const cartItem of cartItems) {\n const addon = cartItem.addon;\n\n // Extract features from addon (using SubscriptionFeatures type with quota).\n // context.quantity is the amount this addon actually grants per unit\n // purchased — takes precedence over the quota catalog's generic\n // limits.value, same as the other purchase paths on this page.\n const features =\n addon.features?.map((f) => {\n const limits = f.quota?.limits as Record<string, number | string | boolean> | undefined;\n const overrideQuantity =\n f.context && 'quantity' in f.context ? Number(f.context.quantity) : undefined;\n return {\n id: f.quota?.id || f.id,\n name: f.quota?.name || 'Feature',\n limits:\n overrideQuantity !== undefined && limits && 'type' in limits\n ? { ...limits, value: overrideQuantity }\n : limits,\n };\n }) || [];\n\n // Use stored currency price if available, otherwise fallback to base USD price\n const selectedCurrency = checkout.selectedCurrency || 'USD';\n const currencyPrices = (addon as { currencyPrices?: Record<string, number> })\n .currencyPrices;\n const priceInSelectedCurrency = currencyPrices?.[selectedCurrency] ?? addon.price;\n // addon.price/currency are always localized to the caller's currency\n // context (e.g. an INR request gets addon.price in INR, regardless of\n // the addon's stored base currency) — currencyPrices.USD is the one\n // field that's reliably actually USD, needed for credit calculations.\n const usdPrice = currencyPrices?.USD ?? addon.price;\n\n const item: CheckoutItem = {\n id: addon.id,\n type: 'ADDON',\n name: addon.name,\n description:\n typeof addon.context === 'object' && addon.context !== null\n ? (addon.context as { description?: string }).description\n : typeof addon.context === 'string'\n ? addon.context\n : undefined,\n price: priceInSelectedCurrency,\n priceUSD: usdPrice,\n currency: selectedCurrency,\n quantity: cartItem.quantity,\n features,\n metadata: { addonId: addon.id },\n };\n checkout.addItem(item);\n }\n setItemAdded(true);\n\n return;\n }\n\n if (purchaseType === 'PLAN' && planData?.getPlan) {\n const plan = planData.getPlan;\n\n // Extract features from plan. A feature's own context.quantity is a\n // per-plan \"absolute limit\" override (set via the Plan admin form) that\n // takes precedence over the quota catalog's generic limits.value\n // default — must match buildWorkspaceQuotaInputs (the backend function\n // that actually grants the quota) or this order summary promises a\n // different number than what the customer actually receives.\n const features =\n plan.features?.map(\n (f: {\n id: string;\n context?: Record<string, unknown> | null;\n quota?: { id: string; name: string; limits?: Record<string, unknown> };\n }) => {\n const limits = f.quota?.limits;\n const overrideQuantity =\n f.context && 'quantity' in f.context ? Number(f.context.quantity) : undefined;\n return {\n id: f.quota?.id || f.id,\n name: f.quota?.name || 'Feature',\n limits:\n overrideQuantity !== undefined && limits && 'type' in limits\n ? { ...limits, value: overrideQuantity }\n : limits,\n };\n }\n ) || [];\n\n // Use stored currency price if available, otherwise fallback to base USD price\n const selectedCurrency = checkout.selectedCurrency || 'USD';\n const currencyPrices = plan.currencyPrices as Record<\n string,\n number | { perSeat?: number }\n > | null;\n const isPerSeat = plan.pricingModel === 'PER_SEAT';\n\n let priceInSelectedCurrency: number;\n if (isPerSeat && plan.pricePerSeat) {\n // PER_SEAT: per-seat rate × seat count\n const currencyEntry = currencyPrices?.[selectedCurrency];\n const perSeatRate =\n typeof currencyEntry === 'object' && currencyEntry?.perSeat\n ? currencyEntry.perSeat\n : plan.pricePerSeat;\n priceInSelectedCurrency = perSeatRate * seatCount;\n } else {\n const currencyEntry = currencyPrices?.[selectedCurrency];\n priceInSelectedCurrency = typeof currencyEntry === 'number' ? currencyEntry : plan.price;\n }\n\n // USD price for credit-balance calculations. plan.price/pricePerSeat are\n // only reliably USD when the plan's own base currency is USD — a plan\n // whose base currency is e.g. INR (an admin can pick any currency when\n // creating a plan) stores that INR amount directly in plan.price, not a\n // USD figure. currencyPrices.USD (present for every plan regardless of\n // base currency) is the one field that's always actually USD.\n const usdCurrencyEntry = currencyPrices?.USD;\n const usdFlatPrice = typeof usdCurrencyEntry === 'number' ? usdCurrencyEntry : plan.price;\n const usdPerSeatRate =\n typeof usdCurrencyEntry === 'object' && usdCurrencyEntry?.perSeat\n ? usdCurrencyEntry.perSeat\n : plan.pricePerSeat;\n\n const resolvedSeatCount = isPerSeat ? seatCount : 1;\n\n const item: CheckoutItem = {\n id: plan.id,\n type: 'PLAN',\n name: plan.name,\n description:\n typeof plan.context === 'object' && plan.context !== null\n ? (plan.context as { description?: string }).description\n : typeof plan.context === 'string'\n ? plan.context\n : undefined,\n price: priceInSelectedCurrency,\n priceUSD:\n isPerSeat && plan.pricePerSeat\n ? (usdPerSeatRate ?? plan.pricePerSeat) * resolvedSeatCount\n : usdFlatPrice,\n currency: selectedCurrency,\n quantity: resolvedSeatCount,\n features,\n metadata: {\n planId: plan.id,\n duration: plan.duration,\n seatCount: isPerSeat ? resolvedSeatCount : undefined,\n },\n };\n\n checkout.addItem(item);\n setItemAdded(true);\n } else if (purchaseType === 'ADDON' && !isCartCheckout && addonData?.getSelectedAddons?.[0]) {\n const addon = addonData.getSelectedAddons[0];\n\n // Extract features from addon. context.quantity is the amount this\n // addon actually grants per unit purchased — takes precedence over the\n // quota catalog's generic limits.value, same as the plan path above.\n const features =\n addon.features?.map(\n (f: {\n id: string;\n context?: Record<string, unknown> | null;\n quota?: { id: string; name: string; limits?: Record<string, unknown> };\n }) => {\n const limits = f.quota?.limits;\n const overrideQuantity =\n f.context && 'quantity' in f.context ? Number(f.context.quantity) : undefined;\n return {\n id: f.quota?.id || f.id,\n name: f.quota?.name || 'Feature',\n limits:\n overrideQuantity !== undefined && limits && 'type' in limits\n ? { ...limits, value: overrideQuantity }\n : limits,\n };\n }\n ) || [];\n\n // Use stored currency price if available, otherwise fallback to base USD price\n const selectedCurrency = checkout.selectedCurrency || 'USD';\n const currencyPrices = addon.currencyPrices as Record<string, number> | null;\n const priceInSelectedCurrency = currencyPrices?.[selectedCurrency] ?? addon.price;\n // addon.price/currency are always localized to the caller's currency\n // context (e.g. an INR request gets addon.price in INR, regardless of\n // the addon's stored base currency) — currencyPrices.USD is the one\n // field that's reliably actually USD, needed for credit calculations.\n const usdPrice = currencyPrices?.USD ?? addon.price;\n\n const item: CheckoutItem = {\n id: addon.id,\n type: 'ADDON',\n name: addon.name,\n description:\n typeof addon.context === 'object' && addon.context !== null\n ? (addon.context as { description?: string }).description\n : typeof addon.context === 'string'\n ? addon.context\n : undefined,\n price: priceInSelectedCurrency,\n priceUSD: usdPrice,\n currency: selectedCurrency,\n quantity: locationState?.quantity ?? 1,\n features,\n metadata: { addonId: addon.id },\n };\n checkout.addItem(item);\n setItemAdded(true);\n } else if (isStoreCheckout && storeOrderData?.getStoreOrder) {\n // Load store order items into checkout\n const storeOrder = storeOrderData.getStoreOrder;\n const items = buildStoreOrderCheckoutItems(storeOrder);\n\n for (const item of items) {\n checkout.addItem(item);\n }\n setItemAdded(true);\n } else if (purchaseType === 'CREDITS') {\n const selectedCurrency = checkout.selectedCurrency || 'USD';\n const item: CheckoutItem = {\n id: 'credits-purchase',\n type: 'CREDITS',\n name: 'Credits',\n description: 'Purchase credits for your account',\n price: costPerCredit, // Use actual cost per credit from backend (in selected currency)\n currency: selectedCurrency,\n quantity: 100, // Default amount\n };\n checkout.addItem(item);\n setItemAdded(true);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- Using specific checkout properties is intentional for granular updates\n }, [\n purchaseType,\n planData,\n addonData,\n storeOrderData,\n checkout.addItem,\n checkout.clearItems,\n locationState?.quantity,\n itemAdded,\n isCartCheckout,\n isStoreCheckout,\n cartItems,\n cartHydrated,\n costPerCredit,\n checkout.selectedCurrency, // Re-add items when currency changes to use correct prices\n ]);\n\n // Update store-order item prices when a payment currency change requires conversion.\n // If the persisted order currency already matches the selected currency this is a no-op.\n useEffect(() => {\n // convertedAmount !== null signals a real rate has arrived for the current\n // currency pair — conversionRate's own magnitude can't be used for this\n // (a from-INR-to-USD rate is legitimately < 1, unlike the from-USD rates\n // this used to always fetch — see BOFF-7127).\n if (!isStoreCheckout || !itemAdded || checkout.convertedAmount === null) return;\n\n for (const item of checkout.items) {\n if (item.type === 'ORDER' && item.currency !== checkout.selectedCurrency) {\n const convertedPrice = item.price * checkout.conversionRate;\n checkout.updateItemPrice(item.id, convertedPrice, checkout.selectedCurrency);\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- checkout.items intentionally excluded to avoid infinite loop\n }, [\n isStoreCheckout,\n itemAdded,\n checkout.convertedAmount,\n checkout.conversionRate,\n checkout.selectedCurrency,\n checkout.updateItemPrice,\n ]);\n\n // Fold the buyer's shipping cost into the billing total as a single synthetic\n // ORDER line item, upserted-by-id so it survives React strict-mode double\n // effects and is never an editable/removable line (OrderSummary disables qty\n // editing for ORDER purchases). Free shipping (cost 0) intentionally adds no\n // line.\n //\n // The line is ALWAYS written in the selected currency, pre-converting from the\n // order currency with the SAME formula the store-order conversion effect above\n // uses (price * conversionRate, only when order currency !== selected currency\n // and a rate is active). Because its currency always equals the selected\n // currency, that conversion effect skips it (`item.currency !== selectedCurrency`\n // is false) — so shipping is counted EXACTLY once and can never be double-applied.\n // This effect owns re-pricing on cost / currency / rate changes.\n //\n // This is the ONLY place shipping enters the billing charge; the backend's\n // StoreOrder.total bump is store-side reconciliation only and is never read\n // back here (no double count).\n const shippingLineRef = useRef<{ price: number; currency: string } | null>(null);\n useEffect(() => {\n if (!isPhysicalStoreOrder) return;\n\n if (shipping.shippingCost > 0) {\n const selectedCurrency = checkout.selectedCurrency;\n const needsConversion =\n storeOrderCurrency !== selectedCurrency && checkout.convertedAmount !== null;\n const price = needsConversion\n ? shipping.shippingCost * checkout.conversionRate\n : shipping.shippingCost;\n\n const prev = shippingLineRef.current;\n if (prev && prev.price === price && prev.currency === selectedCurrency) {\n return; // nothing changed\n }\n if (prev === null) {\n checkout.addItem({\n id: STORE_SHIPPING_ITEM_ID,\n type: 'ORDER',\n name: 'Shipping',\n price,\n currency: selectedCurrency,\n quantity: 1,\n metadata: { storeOrderId, isShipping: true },\n });\n } else {\n checkout.updateItemPrice(STORE_SHIPPING_ITEM_ID, price, selectedCurrency);\n }\n shippingLineRef.current = { price, currency: selectedCurrency };\n } else if (shippingLineRef.current !== null) {\n shippingLineRef.current = null;\n checkout.removeItem(STORE_SHIPPING_ITEM_ID);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- add/update decided via ref; checkout.items excluded to avoid an update loop\n }, [\n isPhysicalStoreOrder,\n shipping.shippingCost,\n storeOrderCurrency,\n checkout.selectedCurrency,\n checkout.conversionRate,\n checkout.convertedAmount,\n storeOrderId,\n checkout.addItem,\n checkout.updateItemPrice,\n checkout.removeItem,\n ]);\n\n // Update item price when backend credit cost changes (after item is already added)\n // This handles cases where the rate might differ based on quantity or currency\n useEffect(() => {\n if (purchaseType === 'CREDITS' && itemAdded && costPerCredit > 0) {\n const currentItem = checkout.items.find((i) => i.id === 'credits-purchase');\n // Only update if price actually changed\n if (currentItem && currentItem.price !== costPerCredit) {\n checkout.updateItemPrice('credits-purchase', costPerCredit);\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- Using specific checkout properties is intentional for granular updates\n }, [purchaseType, itemAdded, costPerCredit, checkout.items, checkout.updateItemPrice]);\n\n // Update item prices when currency changes (for plans and addons with fixed currencyPrices)\n // This handles mid-checkout currency changes by updating prices from stored currencyPrices\n useEffect(() => {\n if (!itemAdded || checkout.items.length === 0) return;\n\n const selectedCurrency = checkout.selectedCurrency || 'USD';\n\n for (const item of checkout.items) {\n if (item.type === 'PLAN' && planData?.getPlan) {\n const currencyPrices = planData.getPlan.currencyPrices as Record<string, number> | null;\n const priceInSelectedCurrency =\n currencyPrices?.[selectedCurrency] ?? planData.getPlan.price;\n // Update both price AND currency so conversion check works correctly\n if (item.price !== priceInSelectedCurrency || item.currency !== selectedCurrency) {\n checkout.updateItemPrice(item.id, priceInSelectedCurrency, selectedCurrency);\n }\n } else if (item.type === 'ADDON') {\n // For single addon checkout\n if (!isCartCheckout && addonData?.getSelectedAddons?.[0]) {\n const addon = addonData.getSelectedAddons[0];\n const currencyPrices = addon.currencyPrices as Record<string, number> | null;\n const priceInSelectedCurrency = currencyPrices?.[selectedCurrency] ?? addon.price;\n if (item.price !== priceInSelectedCurrency || item.currency !== selectedCurrency) {\n checkout.updateItemPrice(item.id, priceInSelectedCurrency, selectedCurrency);\n }\n }\n // For cart checkout, find the addon in cartItems\n if (isCartCheckout) {\n const cartItem = cartItems.find((ci) => ci.addon.id === item.id);\n if (cartItem) {\n const currencyPrices = (cartItem.addon as { currencyPrices?: Record<string, number> })\n .currencyPrices;\n const priceInSelectedCurrency =\n currencyPrices?.[selectedCurrency] ?? cartItem.addon.price;\n if (item.price !== priceInSelectedCurrency || item.currency !== selectedCurrency) {\n checkout.updateItemPrice(item.id, priceInSelectedCurrency, selectedCurrency);\n }\n }\n }\n }\n // CREDITS are handled by the credit cost effect above\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- Using specific checkout properties is intentional for granular updates\n }, [\n checkout.selectedCurrency,\n itemAdded,\n checkout.items,\n checkout.updateItemPrice,\n planData,\n addonData,\n cartItems,\n isCartCheckout,\n ]);\n\n // Set default billing account\n // Priority: 1. Location state (for upgrade flows), 2. localStorage (user's last selection), 3. First account\n useEffect(() => {\n if (billingAccounts.length > 0 && !checkout.billingAccount) {\n // 1. If this is an upgrade, find and select the specific billing account from location state\n if (locationState?.billingAccountId) {\n const targetAccount = billingAccounts.find(\n (acc) => acc.id === locationState.billingAccountId\n );\n if (targetAccount) {\n checkout.setBillingAccount(targetAccount);\n return;\n }\n }\n\n // 2. Check localStorage for previously selected billing account (set from /overview page)\n if (orgId) {\n const storedAccountId = getStoredBillingAccountId(orgId);\n if (storedAccountId) {\n const storedAccount = billingAccounts.find((acc) => acc.id === storedAccountId);\n if (storedAccount) {\n checkout.setBillingAccount(storedAccount);\n return;\n }\n }\n }\n\n // 3. Default: select first account\n checkout.setBillingAccount(billingAccounts[0]);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- Using specific checkout properties is intentional for granular updates\n }, [\n billingAccounts,\n checkout.billingAccount,\n checkout.setBillingAccount,\n locationState?.billingAccountId,\n orgId,\n ]);\n\n // Determine if billing account selector should be locked (for upgrade/downgrade flows)\n const isBillingAccountLocked = Boolean(\n locationState?.upgradeFromSubscriptionId || locationState?.downgradeFromSubscriptionId\n );\n\n // Redirect to addons page if cart is empty for cart checkout.\n // Navigation must happen after render to avoid React setState-in-render warnings.\n useEffect(() => {\n if (isCartCheckout && cartHydrated && cartItems.length === 0 && !itemAdded) {\n navigateTo('/addons', { replace: true });\n }\n }, [cartHydrated, cartItems.length, isCartCheckout, itemAdded, navigateTo]);\n\n const nativeCheckout = useNativeCheckout({\n billingAccountId: checkout.billingAccount?.id,\n purchaseType,\n productId: itemId,\n });\n\n const handleProceedToPayment = useCallback(async () => {\n if (!checkout.billingAccount) {\n return;\n }\n\n // Gate: require complete billing details before payment.\n // Skip once after the CompleteBillingAccountModal saves — Apollo cache still holds stale\n // contactNumber/billingAddresses because the mutation selection doesn't return those fields.\n const skipGate = bypassBillingGate.current;\n bypassBillingGate.current = false;\n if (!skipGate && !isBillingAccountComplete(checkout.billingAccount)) {\n setShowCompleteAccountModal(true);\n return;\n }\n\n setPaymentErrorMessage(null);\n\n // Physical store orders: persist the chosen shipping address + method on the\n // store order BEFORE billing creates its immutable order snapshot. The store\n // service recomputes and persists the shipping cost authoritatively; only IDs\n // are sent from this page. This whole block is skipped for non-physical paths.\n if (isPhysicalStoreOrder) {\n try {\n const shippingResult = await setStoreOrderShipping({\n variables: {\n input: {\n orderId: storeOrderId!,\n shippingMethodId: shipping.shippingMethodId!,\n ...(shipping.shippingAddressId\n ? { shippingAddressId: shipping.shippingAddressId }\n : {}),\n },\n },\n });\n const serverCost = shippingResult.data?.setStoreOrderShipping?.shippingCost;\n if (typeof serverCost === 'number' && serverCost !== shipping.shippingCost) {\n // Keep the display in sync if the authoritative quote differs from\n // the estimate shown before the user submitted the order.\n setShipping((prev) => ({ ...prev, shippingCost: serverCost }));\n }\n } catch {\n setPaymentErrorMessage(\n 'Could not save your shipping details. Please review your delivery address and try again.'\n );\n setMode('error');\n return;\n }\n }\n\n // Capture purchase details before payment (for success page)\n const previousBalance = checkout.billingAccount.creditAmount ?? 0;\n const purchasedItems = [...checkout.items];\n // checkout.total is in the selected DISPLAY currency (e.g. INR) — never\n // the right number for a credits purchase, which always charges\n // creditsRequired (subtotalUSD × CREDITS_PER_USD) regardless of display\n // currency. The spendCredits mutation itself doesn't return the amount\n // it charged, so this is recomputed the same way the backend charges it,\n // not read back from the response.\n const purchaseTotal = checkout.total;\n const creditsCharged = checkout.creditsRequired;\n\n // The checkout hook asks the store service to create the billing order from\n // the persisted catalog and shipping snapshots. No browser-calculated total\n // or line item is used to create the payable order.\n const response = await checkout.initiatePayment();\n\n if (response.success) {\n emit('billing.checkout.initiated', {\n route: '/billing/checkout',\n entityId: response.paymentId ?? response.orderId,\n source: 'checkout',\n amount: response.total || purchaseTotal,\n currency: response.currency || checkout.selectedCurrency,\n });\n // For credit payments, go to processing mode and poll for transaction status\n if (checkout.selectedProvider === 'CREDITS') {\n emit('billing.credits.spent', {\n route: '/billing/checkout',\n entityId: response.orderId,\n source: 'checkout',\n amount: creditsCharged,\n currency: 'CREDITS',\n });\n // Store purchase details for success page\n setPurchaseDetails({\n items: purchasedItems,\n total: creditsCharged,\n billingAccountName: checkout.billingAccount.name,\n billingAccountId: checkout.billingAccount.id,\n previousBalance,\n newBalance: previousBalance - creditsCharged,\n paymentMethod: 'credits',\n currency: 'Credits', // Credit payments use credits as currency\n transactionId: response.orderId, // This is creditTransactionID from spendCredits\n });\n\n // Set payment type for polling and start verification\n setPollingPaymentType('credits');\n setPaymentResponse({\n paymentId: response.orderId, // creditTransactionID from spendCredits\n });\n setMode('processing');\n return;\n }\n\n // For card payments, proceed to payment form\n // Store partial details (will be completed after payment confirmation)\n setPurchaseDetails({\n items: purchasedItems,\n total: response.total || purchaseTotal, // Use response total which includes tax\n billingAccountName: checkout.billingAccount.name,\n billingAccountId: checkout.billingAccount.id,\n previousBalance,\n newBalance: previousBalance, // Card payment doesn't affect credits\n paymentMethod: 'card',\n currency: response.currency || checkout.selectedCurrency, // Use actual currency from response\n transactionId: response.paymentId, // Backend payment ID\n });\n\n setPollingPaymentType('card');\n setPaymentResponse({\n clientSecret: response.clientSecret,\n orderId: response.orderId, // Gateway order ID (for Razorpay)\n razorpayKeyId: response.razorpayKeyId,\n razorpayCustomerId: response.razorpayCustomerId, // For Razorpay saved cards\n paymentId: response.paymentId, // Backend payment ID for polling\n paymentComplete: response.paymentComplete, // For saved card one-click payments\n prefillName: response.prefillName,\n prefillEmail: response.prefillEmail,\n prefillContact: response.prefillContact,\n total: response.total, // Total in target currency (from backend, e.g., INR amount)\n currency: response.currency, // Currency code from backend\n });\n\n // If payment completed immediately (saved card), go to processing for verification\n if (response.paymentComplete) {\n setMode('processing');\n } else {\n setMode('payment');\n }\n } else {\n emit('billing.checkout.failed', {\n route: '/billing/checkout',\n source: 'checkout',\n reason: response.error || 'Payment initiation failed',\n });\n setPaymentErrorMessage(response.error || 'Payment initiation failed');\n setMode('error');\n }\n }, [\n checkout,\n emit,\n isPhysicalStoreOrder,\n shipping,\n storeOrderId,\n storeOrderCurrency,\n setStoreOrderShipping,\n ]);\n\n const handlePaymentSuccess = useCallback(\n (_gatewayPaymentId: string) => {\n emit('billing.payment.succeeded', {\n route: '/billing/checkout',\n entityId: paymentResponse?.paymentId ?? paymentResponse?.orderId,\n source: 'checkout',\n });\n // Keep using the backend payment ID (orderId) that was already set\n // The _gatewayPaymentId is for reference only, not for our polling\n setPaymentResponse((prev) => {\n return prev; // Keep existing paymentId (orderId from initiatePayment)\n });\n setMode('processing');\n },\n [emit, paymentResponse?.orderId, paymentResponse?.paymentId]\n );\n\n const handlePaymentError = useCallback(\n (error: string) => {\n emit('billing.payment.failed', {\n route: '/billing/checkout',\n entityId: paymentResponse?.paymentId ?? paymentResponse?.orderId,\n source: 'checkout',\n reason: error,\n });\n setPaymentErrorMessage(error);\n setMode('error');\n },\n [emit, paymentResponse?.orderId, paymentResponse?.paymentId]\n );\n\n const handleCreditsAmountChange = useCallback(\n (amount: number) => {\n checkout.updateItemQuantity('credits-purchase', amount);\n },\n [checkout]\n );\n\n if (!permissions.canProcessPayment) {\n return <AccessDenied message=\"You don't have permission to make purchases.\" />;\n }\n\n if (isNativeIAPAvailable()) {\n return (\n <NativeCheckoutView\n purchaseType={purchaseType}\n productId={itemId}\n billingAccountId={checkout.billingAccount?.id}\n nativeCheckout={nativeCheckout}\n onBack={() => navigateTo('dashboard')}\n />\n );\n }\n\n const isLoading =\n accountsLoading ||\n (planLoading && purchaseType === 'PLAN') ||\n (addonLoading && purchaseType === 'ADDON' && !isCartCheckout) ||\n (isCartCheckout && !cartHydrated);\n\n if (isCartCheckout && cartHydrated && cartItems.length === 0 && !itemAdded) {\n return null;\n }\n\n // Get page title based on purchase type\n const getPageTitle = () => {\n if (isCartCheckout) {\n const addonCount = checkout.items.length;\n return `Purchase ${addonCount} Add-on${addonCount !== 1 ? 's' : ''}`;\n }\n switch (purchaseType) {\n case 'CREDITS':\n return 'Buy Credits';\n case 'PLAN':\n return 'Subscribe to Plan';\n case 'ADDON':\n return 'Purchase Add-on';\n default:\n return 'Checkout';\n }\n };\n\n const getPageIcon = () => {\n switch (purchaseType) {\n case 'CREDITS':\n return <Coins className=\"size-6\" />;\n case 'PLAN':\n return <Package className=\"size-6\" />;\n case 'ADDON':\n return <Blocks className=\"size-6\" />;\n default:\n return <ShoppingCart className=\"size-6\" />;\n }\n };\n\n // Render loading state\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <Loader2 className=\"size-8 animate-spin mx-auto text-text-link\" />\n <p className=\"text-text-secondary\">\n {tr('billing.checkout.loadingCheckout', 'Loading checkout...')}\n </p>\n </div>\n </div>\n );\n }\n\n // Render success state\n if (mode === 'success') {\n // Format amount based on payment method and currency\n const formatAmount = (amount: number) => {\n if (!purchaseDetails) return `${amount.toFixed(2)}`;\n\n const currency = purchaseDetails.currency;\n\n // For credits, show \"X.XX Credits\"\n if (currency === 'Credits') {\n return `${amount.toFixed(2)} Credits`;\n }\n\n // For real currencies, show with symbol\n const currencySymbols: Record<string, string> = {\n INR: '₹',\n USD: '$',\n EUR: '€',\n GBP: '£',\n };\n const symbol = currencySymbols[currency] || currency + ' ';\n return `${symbol}${amount.toFixed(2)}`;\n };\n\n // Keep formatCredits for credit balance section\n const formatCredits = (amount: number) => `${amount.toFixed(2)} Credits`;\n\n return (\n <>\n <div className=\"max-w-2xl mx-auto p-6 space-y-6\">\n {/* Success Header */}\n <div className=\"text-center space-y-4\">\n <div className=\"inline-flex p-4 rounded-full bg-status-success-bg-subtle text-status-success-text\">\n <CheckCircle className=\"size-12\" />\n </div>\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {tr('billing.checkout.paymentSuccessful', 'Payment Successful!')}\n </h1>\n <p className=\"text-text-secondary mt-2\">\n {tr(\n 'billing.checkout.purchaseCompleted',\n 'Your purchase has been completed successfully.'\n )}\n </p>\n </div>\n </div>\n\n {/* Purchase Details Card */}\n {purchaseDetails && (\n <div className=\"bg-bg-surface border border-border-subtle rounded-lg overflow-hidden\">\n {/* Items Purchased */}\n <div className=\"p-4 border-b border-border-subtle\">\n <h3 className=\"font-semibold text-text-primary mb-3 flex items-center gap-2\">\n <Receipt className=\"size-4\" />\n {tr('billing.checkout.itemsPurchased', 'Items Purchased')}\n </h3>\n <div className=\"space-y-2\">\n {purchaseDetails.items.map((item) => (\n <div key={item.id} className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n {item.type === 'PLAN' && <Package className=\"size-4 text-accent-blue\" />}\n {item.type === 'ADDON' && <Blocks className=\"size-4 text-accent-purple\" />}\n {item.type === 'CREDITS' && (\n <Coins className=\"size-4 text-status-warning-text\" />\n )}\n {item.type === 'ORDER' && (\n <ShoppingCart className=\"size-4 text-status-success-text\" />\n )}\n <span className=\"text-text-primary\">{item.name}</span>\n {item.quantity > 1 && (\n <span className=\"text-xs text-text-secondary\">x{item.quantity}</span>\n )}\n </div>\n <span className=\"text-text-secondary\">\n {formatAmount(\n purchaseDetails.paymentMethod === 'credits'\n ? (item.priceUSD ?? item.price) * CREDITS_PER_USD * item.quantity\n : item.price * item.quantity\n )}\n </span>\n </div>\n ))}\n </div>\n </div>\n\n {/* Payment Info */}\n <div className=\"p-4 border-b border-border-subtle bg-bg-sunken\">\n <div className=\"grid grid-cols-2 gap-4\">\n {/* Payment Method */}\n <div>\n <p className=\"text-xs text-text-secondary mb-1\">\n {tr('billing.checkout.paymentMethod', 'Payment Method')}\n </p>\n <div className=\"flex items-center gap-2\">\n {purchaseDetails.paymentMethod === 'credits' ? (\n <>\n <Wallet className=\"size-4 text-status-warning-text\" />\n <span className=\"font-medium text-text-primary\">\n {tr('billing.checkout.credits', 'Credits')}\n </span>\n </>\n ) : (\n <>\n <CreditCard className=\"size-4 text-accent-blue\" />\n <span className=\"font-medium text-text-primary\">\n {tr('billing.checkout.card', 'Card')}\n </span>\n </>\n )}\n </div>\n </div>\n {/* Total */}\n <div className=\"text-right\">\n <p className=\"text-xs text-text-secondary mb-1\">\n {tr('billing.checkout.totalPaid', 'Total Paid')}\n </p>\n <p className=\"font-bold text-lg text-text-primary\">\n {formatAmount(purchaseDetails.total)}\n </p>\n </div>\n </div>\n </div>\n\n {/* Credit Balance (only for credit payments) */}\n {purchaseDetails.paymentMethod === 'credits' && (\n <div className=\"p-4 border-b border-border-subtle\">\n <h3 className=\"font-semibold text-text-primary mb-3 flex items-center gap-2\">\n <Wallet className=\"size-4\" />\n {tr('billing.checkout.creditBalance', 'Credit Balance')}\n </h3>\n <div className=\"flex items-center gap-4\">\n <div className=\"flex-1 text-center p-3 rounded-lg bg-bg-sunken\">\n <p className=\"text-xs text-text-secondary mb-1\">\n {tr('billing.checkout.previousBalance', 'Previous Balance')}\n </p>\n <p className=\"font-semibold text-text-primary\">\n {formatCredits(purchaseDetails.previousBalance)}\n </p>\n </div>\n <ArrowRight className=\"size-5 text-text-secondary shrink-0\" />\n <div className=\"flex-1 text-center p-3 rounded-lg bg-status-success-bg-subtle\">\n <p className=\"text-xs text-text-secondary mb-1\">\n {tr('billing.checkout.newBalance', 'New Balance')}\n </p>\n <p className=\"font-semibold text-status-success-text\">\n {formatCredits(purchaseDetails.newBalance)}\n </p>\n </div>\n </div>\n </div>\n )}\n\n {/* Billing Account Info */}\n <div className=\"p-4 bg-bg-sunken\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-xs text-text-secondary mb-1\">\n {tr('billing.checkout.billingAccount', 'Billing Account')}\n </p>\n <p className=\"font-medium text-text-primary\">\n {purchaseDetails.billingAccountName}\n </p>\n </div>\n {purchaseDetails.transactionId && (\n <div className=\"text-right\">\n <p className=\"text-xs text-text-secondary mb-1\">\n {tr('billing.checkout.transactionId', 'Transaction ID')}\n </p>\n <p className=\"font-mono text-xs text-text-secondary\">\n {purchaseDetails.transactionId.slice(0, 8)}...\n </p>\n </div>\n )}\n </div>\n </div>\n </div>\n )}\n\n {/* Action Buttons */}\n <div className=\"flex flex-col sm:flex-row gap-3 justify-center pt-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/overview')}\n className=\"px-6 py-3 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-lg hover:bg-action-primary-bgHover transition-colors\"\n >\n {tr('billing.checkout.backToOverview', 'Back to Overview')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/subscriptions')}\n className=\"px-6 py-3 text-sm font-medium border border-border-subtle text-text-primary rounded-lg hover:bg-bg-sunken transition-colors\"\n >\n {tr('billing.checkout.viewSubscriptions', 'View Subscriptions')}\n </button>\n </div>\n </div>\n\n {/* Workspace Quota Assignment Modal */}\n {showAssignModal &&\n pendingWorkspaceQuotas.length > 0 &&\n workspaceId &&\n pendingSubscriptionId && (\n <WorkspaceQuotaAssignmentModal\n billingAccountId={purchaseDetails?.billingAccountId ?? ''}\n subscriptionId={pendingSubscriptionId}\n quotas={pendingWorkspaceQuotas}\n workspaces={workspaceOptions}\n currentWorkspaceId={workspaceId}\n onAssigned={() => setShowAssignModal(false)}\n onSkip={() => setShowAssignModal(false)}\n />\n )}\n </>\n );\n }\n\n // Render error state\n if (mode === 'error') {\n return (\n <div className=\"max-w-lg mx-auto p-6\">\n <div className=\"text-center space-y-6\">\n <div className=\"inline-flex p-4 rounded-full bg-status-error-bg-subtle text-status-error-text\">\n <XCircle className=\"size-12\" />\n </div>\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {tr('billing.checkout.paymentFailed', 'Payment Failed')}\n </h1>\n <p className=\"text-text-secondary mt-2\">\n {paymentErrorMessage ||\n pollingError ||\n checkout.error ||\n tr('billing.checkout.genericError', 'Something went wrong. Please try again.')}\n </p>\n </div>\n <div className=\"flex gap-3 justify-center\">\n <button\n type=\"button\"\n onClick={() => navigateTo(-1)}\n className=\"px-4 py-2 rounded-lg border border-border-subtle hover:bg-bg-sunken\"\n >\n {tr('billing.checkout.goBack', 'Go Back')}\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setMode('select');\n setPaymentErrorMessage(null);\n checkout.reset();\n }}\n className=\"px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bgHover\"\n >\n {tr('billing.checkout.tryAgain', 'Try Again')}\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n // Render processing state (waiting for payment confirmation)\n if (mode === 'processing') {\n const formatTime = (ms: number) => {\n const seconds = Math.floor(ms / 1000);\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = seconds % 60;\n return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;\n };\n\n return (\n <div className=\"max-w-lg mx-auto p-6\">\n <div className=\"text-center space-y-6\">\n <div className=\"inline-flex p-4 rounded-full bg-[var(--color-accent-soft)] text-text-link\">\n <Loader2 className=\"size-12 animate-spin\" />\n </div>\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {tr('billing.checkout.verifyingPayment', 'Verifying Payment')}\n </h1>\n <p className=\"text-text-secondary mt-2\">\n {tr(\n 'billing.checkout.verifyingPaymentDescription',\n 'Please wait while we confirm your payment...'\n )}\n </p>\n </div>\n\n {/* Elapsed time */}\n <div className=\"bg-bg-sunken rounded-lg p-4\">\n <p className=\"text-sm text-text-secondary\">\n {tr('billing.checkout.timeElapsed', 'Time elapsed:')}{' '}\n <span className=\"font-mono font-medium text-text-primary\">\n {formatTime(elapsedTime)}\n </span>\n </p>\n <p className=\"text-xs text-text-secondary mt-1\">\n {tr(\n 'billing.checkout.keepPageOpen',\n \"This may take a few moments. Please don't close this page.\"\n )}\n </p>\n </div>\n\n {/* Progress indicator */}\n <div className=\"w-full bg-bg-sunken rounded-full h-2 overflow-hidden\">\n <div\n className=\"bg-action-primary-bg h-2 rounded-full transition-all duration-1000\"\n style={{ width: `${Math.min((elapsedTime / 300000) * 100, 100)}%` }}\n />\n </div>\n\n {/* Cancel button - only show after 30 seconds */}\n {elapsedTime > 30000 && (\n <button\n type=\"button\"\n onClick={() => {\n setMode('error');\n setPaymentErrorMessage(\n 'Payment verification was cancelled. Please check your billing history.'\n );\n }}\n className=\"text-sm text-text-secondary hover:text-text-primary underline\"\n >\n {tr('billing.checkout.cancelAndCheckHistory', 'Cancel and check billing history')}\n </button>\n )}\n </div>\n </div>\n );\n }\n\n // Render payment form\n if (mode === 'payment' && paymentResponse) {\n // Format currency for display\n const formatDisplayCurrency = (amount: number, currencyCode: string) => {\n const symbols: Record<string, string> = { INR: '₹', USD: '$', EUR: '€', GBP: '£' };\n const symbol = symbols[currencyCode] || currencyCode + ' ';\n return `${symbol}${amount.toFixed(2)}`;\n };\n\n const displayCurrency = paymentResponse.currency || checkout.selectedCurrency;\n const displayTotal = paymentResponse.total ?? checkout.total;\n\n return (\n <div className=\"max-w-2xl mx-auto p-4 sm:p-6 space-y-6\">\n <button\n type=\"button\"\n onClick={() => setMode('select')}\n className=\"flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('billing.checkout.backToCheckout', 'Back to checkout')}\n </button>\n\n <PageHeader\n title={tr('billing.checkout.completePayment', 'Complete Payment')}\n description={tr(\n 'billing.checkout.completePaymentDescription',\n 'Enter your payment details to complete the purchase'\n )}\n />\n\n {/* Payment Summary with correct tax calculation */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 space-y-4\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Receipt className=\"size-4\" />\n {tr('billing.checkout.paymentSummary', 'Payment Summary')}\n </h3>\n\n {/* Billing Account Info */}\n {checkout.billingAccount && (\n <div className=\"p-3 bg-bg-sunken rounded-lg space-y-1\">\n <div className=\"text-xs text-text-secondary uppercase tracking-wide\">\n {tr('billing.checkout.billingAccount', 'Billing Account')}\n </div>\n <div className=\"font-medium text-text-primary\">{checkout.billingAccount.name}</div>\n <div className=\"text-sm text-text-secondary\">{checkout.billingAccount.email}</div>\n </div>\n )}\n\n {/* Subscription Info (for addon purchases) */}\n {purchaseType === 'ADDON' && checkout.selectedSubscriptionId && selectedSubscription && (\n <div className=\"p-3 bg-bg-sunken rounded-lg space-y-1\">\n <div className=\"text-xs text-text-secondary uppercase tracking-wide\">\n {tr('billing.checkout.addingToSubscription', 'Adding to Subscription')}\n </div>\n <div className=\"font-medium text-text-primary\">\n {selectedSubscription.plan?.name ||\n tr('billing.checkout.subscription', 'Subscription')}\n </div>\n {selectedSubscription.plan?.product?.name && (\n <div className=\"text-sm text-text-secondary\">\n {selectedSubscription.plan.product.name}\n </div>\n )}\n </div>\n )}\n\n {/* Price Breakdown */}\n <div className=\"space-y-2 text-sm\">\n {/* Calculate display values correctly */}\n {(() => {\n const taxRate = checkout.taxRate;\n // For non-USD: calculate subtotal and tax from the total\n const calcSubtotal = taxRate > 0 ? displayTotal / (1 + taxRate) : displayTotal;\n const calcTax = taxRate > 0 ? displayTotal - calcSubtotal : 0;\n\n return (\n <>\n {/* Subtotal */}\n <div className=\"flex justify-between\">\n <span className=\"text-text-secondary\">\n {tr('billing.checkout.subtotal', 'Subtotal')}\n </span>\n <span className=\"text-text-primary\">\n {formatDisplayCurrency(calcSubtotal, displayCurrency)}\n </span>\n </div>\n {/* Discount */}\n {checkout.discountAmount > 0 && (\n <div className=\"flex justify-between text-status-success-text\">\n <span>{tr('billing.checkout.discount', 'Discount')}</span>\n <span>\n -{formatDisplayCurrency(checkout.discountAmount, displayCurrency)}\n </span>\n </div>\n )}\n {/* Tax */}\n {calcTax > 0 && (\n <div className=\"flex justify-between\">\n <span className=\"text-text-secondary\">\n {checkout.taxType || tr('billing.checkout.tax', 'Tax')} (\n {(taxRate * 100).toFixed(0)}%)\n </span>\n <span className=\"text-text-primary\">\n {formatDisplayCurrency(calcTax, displayCurrency)}\n </span>\n </div>\n )}\n </>\n );\n })()}\n {/* Divider */}\n <div className=\"border-t border-border-subtle my-2\" />\n {/* Total */}\n <div className=\"flex justify-between font-semibold\">\n <span className=\"text-text-primary\">{tr('billing.checkout.total', 'Total')}</span>\n <span className=\"text-lg text-text-primary\">\n {formatDisplayCurrency(displayTotal, displayCurrency)}\n </span>\n </div>\n </div>\n </div>\n\n {checkout.selectedProvider === 'STRIPE' &&\n paymentResponse.clientSecret &&\n (() => {\n // NOTE: Saved cards for Stripe are temporarily disabled on the backend\n // NOTE: Card saving disabled — re-enable when backend supports setup_future_usage on PaymentIntents\n // const stripeCards = savedPaymentMethods.filter(pm => pm.stripePaymentMethodId && pm.isVerified);\n\n return (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface overflow-hidden\">\n {/* Stripe Elements form for new card */}\n <div className=\"p-4 space-y-4\">\n {/* Save card checkbox - disabled until backend supports it */}\n {/* <label className=\"flex items-center gap-2 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={checkout.saveCard}\n onChange={(e) => checkout.setSaveCard(e.target.checked)}\n className=\"size-4 rounded border-border-subtle text-text-link focus:ring-[var(--color-focus-ring)]\"\n />\n <span className=\"text-sm text-text-secondary\">\n Save card for future purchases\n </span>\n </label> */}\n\n <StripePaymentForm\n clientSecret={paymentResponse.clientSecret}\n publishableKey={\n stripePublishableKey || providerConfig?.stripePublishableKey || ''\n }\n amount={Math.round((paymentResponse.total ?? checkout.total) * 100)}\n currency={paymentResponse.currency || checkout.selectedCurrency}\n onSuccess={handlePaymentSuccess}\n onError={handlePaymentError}\n onCancel={() => setMode('select')}\n isCompletingExistingPayment={!!checkout.selectedPaymentMethodId}\n />\n </div>\n </div>\n );\n })()}\n\n {checkout.selectedProvider === 'RAZORPAY' && paymentResponse.orderId && (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface overflow-hidden\">\n {/* Razorpay Payment Section */}\n <div className=\"p-4 space-y-4\">\n {/* Save card checkbox for new card payments */}\n <label className=\"flex items-center gap-2 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={checkout.saveCard}\n onChange={(e) => checkout.setSaveCard(e.target.checked)}\n className=\"size-4 rounded border-border-subtle text-text-link focus:ring-[var(--color-focus-ring)]\"\n />\n <span className=\"text-sm text-text-secondary\">Save card for future purchases</span>\n </label>\n\n <RazorpayPaymentButton\n orderId={paymentResponse.orderId}\n keyId={paymentResponse.razorpayKeyId || razorpayKey || ''}\n amount={Math.round((paymentResponse.total ?? checkout.total) * 100)}\n currency={paymentResponse.currency || 'INR'}\n customerId={paymentResponse.razorpayCustomerId}\n prefill={{\n name: paymentResponse.prefillName,\n email: paymentResponse.prefillEmail,\n contact: paymentResponse.prefillContact,\n }}\n onSuccess={(response) => handlePaymentSuccess(response.razorpay_payment_id)}\n onError={handlePaymentError}\n onCancel={() => setMode('select')}\n />\n </div>\n </div>\n )}\n </div>\n );\n }\n\n // Main checkout flow\n return (\n <div className=\"max-w-6xl mx-auto p-4 sm:p-6\">\n {/* Back button */}\n <button\n type=\"button\"\n onClick={() => navigateTo(-1)}\n className=\"flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary mb-4\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n\n {/* Header */}\n <div className=\"flex items-center gap-3 mb-6\">\n <div className=\"p-2 rounded-lg bg-[var(--color-accent-soft)] text-text-link\">\n {getPageIcon()}\n </div>\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">{getPageTitle()}</h1>\n <p className=\"text-sm text-text-secondary\">Complete your purchase securely</p>\n </div>\n </div>\n\n <PagePurpose className=\"mb-6\">\n {tr(\n 'billing.checkout.purpose',\n 'Review what you are buying — plan, add-ons or credits — pick the billing account and payment method, apply any discount code, and pay. Taxes and the final total are shown before you confirm.'\n )}\n </PagePurpose>\n\n {/* Upgrade Banner */}\n {locationState?.upgradeFromSubscriptionId && locationState?.currentPlanName && (\n <div className=\"mb-6 p-4 rounded-lg border border-border-subtle bg-status-info-bg-subtle\">\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 rounded-full bg-status-info-bg-subtle\">\n <RefreshCw className=\"size-4 text-accent-blue\" />\n </div>\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-status-info-text\">\n Upgrading from:{' '}\n <span className=\"font-semibold\">{locationState.currentPlanName}</span>\n </p>\n <p className=\"text-xs text-status-info-text mt-0.5\">\n Your current subscription will be marked as upgraded and quotas will transfer to the\n new plan.\n </p>\n </div>\n </div>\n </div>\n )}\n\n {/* Downgrade Banner */}\n {locationState?.downgradeFromSubscriptionId && locationState?.currentPlanName && (\n <div className=\"mb-6 p-4 rounded-lg border border-status-warning-border bg-status-warning-bg-subtle\">\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 rounded-full bg-status-warning-bg-subtle\">\n <RefreshCw className=\"size-4 text-status-warning-text\" />\n </div>\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-status-warning-text\">\n Downgrading from:{' '}\n <span className=\"font-semibold\">{locationState.currentPlanName}</span>\n </p>\n <p className=\"text-xs text-status-warning-text mt-0.5\">\n Your current plan stays active until it expires, you keep all its features until\n then. After expiry, only the new plan applies.\n </p>\n </div>\n </div>\n </div>\n )}\n\n {/* Main content - responsive grid. On mobile the order summary appears first\n so users see the total before filling in payment details. */}\n <div className=\"grid gap-6 lg:grid-cols-5\">\n {/* Left column - Configuration (renders second on mobile) */}\n <div className=\"lg:col-span-3 space-y-6 order-2 lg:order-1\">\n {/* Credits amount (for credits purchase) */}\n {purchaseType === 'CREDITS' && checkout.items[0] && (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 sm:p-6\">\n <CreditsAmountInput\n value={checkout.items[0].quantity}\n onChange={handleCreditsAmountChange}\n costPerCredit={costPerCredit}\n currency={checkout.selectedCurrency}\n disabled={creditCostLoading}\n />\n </div>\n )}\n\n {/* Billing Account Selection */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 sm:p-6 space-y-4\">\n <BillingAccountSelector\n accounts={billingAccounts}\n selectedAccountId={checkout.billingAccount?.id ?? null}\n onSelect={checkout.setBillingAccount}\n isLoading={accountsLoading}\n disabled={isBillingAccountLocked}\n disabledMessage={\n isBillingAccountLocked ? 'Billing account is locked for plan changes' : undefined\n }\n />\n\n {/* Subscription Selection - inline for addon purchases (only shows if multiple subs or no subs) */}\n {/* Skip subscription selection for store checkout - store items don't require an active subscription */}\n {purchaseType === 'ADDON' && !isStoreCheckout && checkout.billingAccount && (\n <SubscriptionSelector\n subscriptions={activeSubscriptions}\n selectedSubscriptionId={checkout.selectedSubscriptionId}\n onSelect={checkout.setSelectedSubscriptionId}\n isLoading={subscriptionsLoading}\n />\n )}\n </div>\n\n {/* Shipping — physical store orders only. Non-physical flows render\n nothing new here. */}\n {isPhysicalStoreOrder && (\n <StoreShippingSection\n storeOrderId={storeOrderId!}\n currency={checkout.selectedCurrency}\n onChange={setShipping}\n />\n )}\n\n {/* Unified Payment Method Selection */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 sm:p-6 space-y-4\">\n {/* canPayWithCredits below gates whether credit payment applies to\n this purchase at all (e.g. not already buying credits) —\n whether the balance is *sufficient* is the child's own\n concern: it already computes hasEnoughCredits internally and\n shows a disabled option with an \"Insufficient credits\"\n message rather than hiding the option outright. */}\n <UnifiedPaymentMethodSelector\n savedPaymentMethods={savedPaymentMethods}\n selectedPaymentMethodId={checkout.selectedPaymentMethodId}\n onSelectSavedCard={checkout.setSelectedPaymentMethodId}\n selectedProvider={checkout.selectedProvider}\n onSelectProvider={checkout.setPaymentProvider}\n stripeEnabled={stripeEnabled}\n razorpayEnabled={razorpayEnabled}\n canPayWithCredits={checkout.canPayWithCredits}\n availableCredits={checkout.availableCredits}\n totalAmount={checkout.subtotalUSD}\n billingCountry={billingCountry}\n saveCard={checkout.saveCard}\n onSaveCardChange={checkout.setSaveCard}\n autoRenewEnabled={purchaseType === 'PLAN' && checkout.autoRenew}\n isLoading={paymentMethodsLoading}\n />\n\n {/* Currency Info - Locked to Billing Account */}\n {checkout.selectedProvider !== 'CREDITS' && checkout.selectedProvider !== 'AUTO' && (\n <>\n <div className=\"border-t border-border-subtle\" />\n <div className=\"bg-bg-sunken p-4 rounded-button\">\n <div className=\"flex items-start gap-3\">\n <svg\n className=\"size-5 text-text-secondary mt-0.5\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <div className=\"flex-1 text-sm\">\n <p className=\"font-medium text-text-primary mb-1\">\n Currency: {checkout.selectedCurrency}\n </p>\n <p className=\"text-text-secondary text-xs\">\n All prices are shown in{' '}\n <strong className=\"text-text-primary\">{checkout.selectedCurrency}</strong>{' '}\n based on your billing account settings.\n {checkout.selectedCurrency === 'INR' && ' GST will be applied at checkout.'}\n </p>\n </div>\n </div>\n </div>\n </>\n )}\n\n {/* Seat count selector — only for PER_SEAT plans */}\n {purchaseType === 'PLAN' && planData?.getPlan?.pricingModel === 'PER_SEAT' && (\n <div className=\"border-t border-border-subtle pt-4\">\n <div className=\"space-y-3\">\n <div>\n <label className=\"text-sm font-medium text-text-primary\">Number of seats</label>\n {planData.getPlan.minSeats && (\n <p className=\"text-xs text-text-secondary mt-0.5\">\n Minimum {planData.getPlan.minSeats} seat\n {planData.getPlan.minSeats !== 1 ? 's' : ''} required\n </p>\n )}\n </div>\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() =>\n setSeatCount((n) => Math.max(planData.getPlan?.minSeats ?? 1, n - 1))\n }\n disabled={seatCount <= (planData.getPlan.minSeats ?? 1)}\n className=\"size-9 rounded-button border border-border-subtle flex items-center justify-center hover:bg-action-ghost-bgHover disabled:opacity-40 disabled:cursor-not-allowed transition-colors text-text-primary\"\n >\n −\n </button>\n <input\n type=\"number\"\n value={seatCount}\n min={planData.getPlan.minSeats ?? 1}\n max={planData.getPlan.maxSeats ?? 999}\n onChange={(e) => {\n const v = parseInt(e.target.value, 10);\n if (!isNaN(v)) setSeatCount(Math.max(planData.getPlan?.minSeats ?? 1, v));\n }}\n className=\"w-20 h-9 text-center border border-border-subtle rounded-button bg-bg-surface text-text-primary text-sm focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n <button\n type=\"button\"\n onClick={() =>\n setSeatCount((n) => Math.min(planData.getPlan?.maxSeats ?? 999, n + 1))\n }\n disabled={seatCount >= (planData.getPlan.maxSeats ?? 999)}\n className=\"size-9 rounded-button border border-border-subtle flex items-center justify-center hover:bg-action-ghost-bgHover disabled:opacity-40 disabled:cursor-not-allowed transition-colors text-text-primary\"\n >\n +\n </button>\n {planData.getPlan.pricePerSeat && (\n <span className=\"text-sm text-text-secondary\">\n ×{' '}\n {planData.getPlan.pricePerSeat.toLocaleString('en', {\n style: 'currency',\n currency: planData.getPlan.currency ?? 'USD',\n })}{' '}\n ={' '}\n {(planData.getPlan.pricePerSeat * seatCount).toLocaleString('en', {\n style: 'currency',\n currency: planData.getPlan.currency ?? 'USD',\n })}\n /mo\n </span>\n )}\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n\n {/* Right column - Order Summary (sticky on desktop, shown first on mobile) */}\n <div className=\"lg:col-span-2 order-1 lg:order-2\">\n <div className=\"lg:sticky lg:top-6 space-y-4\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface\">\n <div className=\"p-4\">\n <OrderSummary\n items={checkout.items}\n subtotal={checkout.subtotal}\n discountAmount={checkout.discountAmount}\n taxAmount={checkout.taxAmount}\n taxRate={checkout.taxRate}\n taxType={checkout.taxType}\n total={checkout.total}\n currency={checkout.selectedCurrency}\n isCalculatingTax={checkout.isCalculatingTax}\n showInCredits={checkout.selectedProvider === 'CREDITS'}\n onUpdateQuantity={\n purchaseType !== 'PLAN' && purchaseType !== 'ORDER'\n ? checkout.updateItemQuantity\n : undefined\n }\n onRemoveItem={checkout.removeItem}\n isEditable={purchaseType !== 'PLAN' && purchaseType !== 'ORDER'}\n />\n </div>\n\n {/* Promo Code Input — not supported for credits purchases yet (BOFF-7192) */}\n {checkout.selectedProvider !== 'CREDITS' && (\n <div className=\"px-4 pb-4 border-t border-border-subtle pt-4\">\n <PromoCodeInput\n value={checkout.promoCode}\n onChange={checkout.setPromoCode}\n onValidate={async () => {\n const result = await checkout.validatePromoCode();\n return result;\n }}\n onClear={checkout.clearPromoCode}\n isApplied={checkout.promoDiscount > 0}\n discountValue={checkout.promoDiscount}\n discountType={checkout.promoDiscountType}\n isValidating={checkout.isProcessing}\n />\n </div>\n )}\n\n {/* Simple Auto-Pay Toggle - only for PLAN purchases */}\n {purchaseType === 'PLAN' &&\n checkout.selectedProvider !== 'CREDITS' &&\n checkout.selectedProvider !== 'AUTO' &&\n (() => {\n // Determine payment method display\n let paymentMethodDisplay = '';\n let hasPaymentMethod = false;\n let isNewCard = false;\n\n if (checkout.selectedPaymentMethodId) {\n // Saved card selected\n const selectedCard = savedPaymentMethods.find(\n (pm) => pm.id === checkout.selectedPaymentMethodId\n );\n if (selectedCard) {\n const type =\n selectedCard.type === 'CREDIT_CARD'\n ? 'Card'\n : selectedCard.type === 'DEBIT_CARD'\n ? 'Card'\n : selectedCard.type;\n const last4 = selectedCard.last4 ? ` •••• ${selectedCard.last4}` : '';\n paymentMethodDisplay = `${type}${last4}`;\n hasPaymentMethod = true;\n isNewCard = false;\n }\n } else if (\n checkout.selectedProvider === 'RAZORPAY' ||\n checkout.selectedProvider === 'STRIPE'\n ) {\n // New card selected\n paymentMethodDisplay =\n checkout.selectedProvider === 'RAZORPAY'\n ? 'new card (Razorpay)'\n : 'new card (Stripe)';\n hasPaymentMethod = true;\n isNewCard = true;\n }\n\n return (\n <div className=\"px-4 pb-4\">\n <SimpleAutoPayToggle\n enabled={checkout.autoRenew}\n onToggle={(enabled) => {\n checkout.setAutoRenew(enabled);\n // When enabling auto-pay with saved card, also set it as auto-renew method\n if (enabled && checkout.selectedPaymentMethodId) {\n checkout.setAutoRenewPaymentMethodId(checkout.selectedPaymentMethodId);\n }\n }}\n hasPaymentMethodSelected={hasPaymentMethod}\n selectedPaymentMethodDisplay={paymentMethodDisplay}\n isNewCard={isNewCard}\n />\n </div>\n );\n })()}\n </div>\n\n {/* Checkout Button */}\n <button\n type=\"button\"\n onClick={handleProceedToPayment}\n disabled={\n checkout.isProcessing ||\n !checkout.billingAccount ||\n checkout.items.length === 0 ||\n // For addon purchases: require active subscription to be selected (skip for store checkout)\n (purchaseType === 'ADDON' && !isStoreCheckout && subscriptionsLoading) ||\n (purchaseType === 'ADDON' &&\n !isStoreCheckout &&\n activeSubscriptions.length === 0) ||\n (purchaseType === 'ADDON' &&\n !isStoreCheckout &&\n !checkout.selectedSubscriptionId) ||\n // Physical store orders: require a delivery address + method\n // (with a resolved estimate) before payment.\n (isPhysicalStoreOrder &&\n (!shipping.shippingAddressId || !shipping.shippingMethodId))\n }\n className=\"w-full px-6 py-4 rounded-lg bg-action-primary-bg text-action-primary-text font-semibold\n hover:bg-action-primary-bgHover disabled:opacity-50 disabled:cursor-not-allowed\n transition-colors flex items-center justify-center gap-2\"\n >\n {checkout.isProcessing ? (\n <>\n <Loader2 className=\"size-5 animate-spin\" />\n Processing…\n </>\n ) : checkout.selectedProvider === 'CREDITS' ? (\n 'Pay with Credits'\n ) : (\n 'Proceed to Payment'\n )}\n </button>\n\n {/* Security badge */}\n <p className=\"text-center text-xs text-text-secondary\">\n 🔒 Secure checkout with 256-bit SSL encryption\n </p>\n </div>\n </div>\n </div>\n\n {/* Billing account completion gate */}\n {showCompleteAccountModal && checkout.billingAccount && (\n <CompleteBillingAccountModal\n billingAccountId={checkout.billingAccount.id}\n hasPhone={Boolean(checkout.billingAccount.contactNumber?.trim())}\n hasAddress={Boolean(\n checkout.billingAccount.billingAddresses?.[0]?.line1?.trim() &&\n checkout.billingAccount.billingAddresses?.[0]?.postalCode?.trim()\n )}\n onCompleted={() => {\n // Set bypass BEFORE closing modal so the re-triggered payment skips the stale check\n bypassBillingGate.current = true;\n setShowCompleteAccountModal(false);\n void handleProceedToPayment();\n // Refetch accounts in background so future gate checks use fresh contactNumber/address\n void refetchAccounts().then((result: { data?: GetBillingAccountsCheckoutQuery }) => {\n const freshAccount = (result.data?.myBillingAccounts ?? [])\n .map(normalizeCheckoutBillingAccount)\n .find((account) => account?.id === checkout.billingAccount?.id);\n if (freshAccount) checkout.setBillingAccount(freshAccount);\n });\n }}\n onCancel={() => setShowCompleteAccountModal(false)}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CheckoutPage.js","names":[],"sources":["../../../../../src/billing/modules/checkout/pages/CheckoutPage.tsx"],"sourcesContent":["/**\n * Checkout Page Component\n * Main checkout flow for purchasing credits, plans, and add-ons\n * Supports single item checkout and multi-addon cart checkout\n */\n\nimport { type FC, useState, useEffect, useCallback, useMemo, useRef } from 'react';\nimport { useParams, useLocation } from 'react-router';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport {\n ArrowLeft,\n ShoppingCart,\n Loader2,\n CheckCircle,\n XCircle,\n Coins,\n Package,\n Blocks,\n Wallet,\n CreditCard,\n ArrowRight,\n Receipt,\n RefreshCw,\n} from 'lucide-react';\n\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { AccessDenied, PageHeader } from '../../../shared/components';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useAddonCartStore } from '../../addons/store/addonCartStore';\n\nimport {\n CREDITS_PER_USD,\n useCheckout,\n useNativeCheckout,\n usePaymentProviders,\n usePaymentStatusPolling,\n} from '../hooks';\nimport { isNativeIAPAvailable } from '../../../lib/native-purchases';\nimport { NativeCheckoutView } from '../components/NativeCheckoutView';\nimport { getStoredBillingAccountId } from '../../../hooks/useBillingAccountSelection';\nimport {\n useGetBillingAccountsCheckoutQuery,\n useGetPlanCheckoutQuery,\n useGetSelectedAddonCheckoutQuery,\n useGetPaymentMethodsCheckoutQuery,\n useGetActiveSubscriptionsCheckoutQuery,\n useGetCostOfCreditsCheckoutQuery,\n useGetStoreOrderCheckoutQuery,\n type GetActiveSubscriptionsCheckoutQuery,\n type GetBillingAccountsCheckoutQuery,\n} from '../../../../generated/global-operations';\nimport {\n BillingAccountSelector,\n OrderSummary,\n CreditsAmountInput,\n StripePaymentForm,\n RazorpayPaymentButton,\n SubscriptionSelector,\n SimpleAutoPayToggle,\n UnifiedPaymentMethodSelector,\n PromoCodeInput,\n CompleteBillingAccountModal,\n isBillingAccountComplete,\n StoreShippingSection,\n type StoreShippingSelection,\n} from '../components';\nimport {\n WorkspaceQuotaAssignmentModal,\n isWorkspaceScopedQuota,\n type WorkspaceQuotaToAssign,\n type WorkspaceOption,\n} from '../components/WorkspaceQuotaAssignmentModal';\nimport {\n buildStoreOrderCheckoutItems,\n normalizeStoreOrderCurrency,\n type StoreOrderLineItem,\n} from '../utils/storeOrder';\nimport { useSetStoreOrderShipping, STORE_SHIPPING_ITEM_ID } from '../hooks/useStoreShipping';\nimport type { SavedPaymentMethod } from '../types';\n\nimport type { CheckoutItem, CheckoutBillingAccount, PurchaseType } from '../types';\n\ntype RawCheckoutBillingAccount = GetBillingAccountsCheckoutQuery['myBillingAccounts'][number];\n\nfunction normalizeCheckoutBillingAccount(\n account: RawCheckoutBillingAccount\n): CheckoutBillingAccount | null {\n if (\n typeof account.id !== 'string' ||\n typeof account.name !== 'string' ||\n typeof account.email !== 'string' ||\n typeof account.currency !== 'string' ||\n typeof account.creditAmount !== 'number' ||\n typeof account.isActive !== 'boolean'\n ) {\n return null;\n }\n\n return {\n ...account,\n id: account.id,\n name: account.name,\n email: account.email,\n currency: account.currency,\n creditAmount: account.creditAmount,\n isActive: account.isActive,\n billingAddresses:\n account.billingAddresses === null\n ? null\n : account.billingAddresses?.flatMap((address) => (address ? [{ ...address }] : [])),\n };\n}\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useAppShell } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { getProfileScopedKey } from '@burdenoff/fe-libs/shared/utils';\nimport { recordAffiliateEvent } from '../../affiliate/api';\n\ntype CheckoutMode = 'select' | 'payment' | 'processing' | 'success' | 'error';\n\ninterface LocationState {\n purchaseType?: PurchaseType;\n itemId?: string;\n quantity?: number;\n upgradeFromSubscriptionId?: string;\n downgradeFromSubscriptionId?: string;\n currentPlanName?: string;\n billingAccountId?: string;\n}\n\nexport const CheckoutPage: FC = () => {\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 { planId, addonId, storeOrderId } = useParams<{\n planId?: string;\n addonId?: string;\n storeOrderId?: string;\n }>();\n const navigateTo = useBillingNavigate();\n const location = useLocation();\n const locationState = location.state as LocationState | undefined;\n\n const permissions = useBillingPermissions();\n const { emit } = useBillingEventEmitter();\n const {\n orgId,\n stripePublishableKey,\n razorpayKey,\n apiGatewayUrl,\n authToken,\n currentUser,\n workspaceId,\n } = useBilling();\n const { productSlug } = useAppShell();\n const referralEventReportedRef = useRef<string | null>(null);\n // Workspace options for the assignment modal — single workspace for now.\n // Multi-workspace support requires a listWorkspaces query (future enhancement).\n const workspaceOptions: WorkspaceOption[] = workspaceId\n ? [{ id: workspaceId, name: 'Current workspace' }]\n : [];\n\n // Addon cart store for multi-addon checkout\n const { items: cartItems, clearCart, _hasHydrated: cartHydrated } = useAddonCartStore();\n\n // Check if this is a cart-based checkout (route: /checkout/addons)\n const isCartCheckout = location.pathname.includes('/checkout/addons') && !addonId;\n\n // Check if this is a store order checkout (route: /checkout/store/:storeOrderId)\n const isStoreCheckout = location.pathname.includes('/checkout/store') && !!storeOrderId;\n\n // Determine what we're purchasing\n const purchaseType: PurchaseType = isCartCheckout\n ? 'ADDON'\n : isStoreCheckout\n ? 'ORDER' // Store orders use ORDER purchase type\n : (locationState?.purchaseType ?? (planId ? 'PLAN' : addonId ? 'ADDON' : 'CREDITS'));\n const itemId = locationState?.itemId ?? planId ?? addonId ?? storeOrderId;\n\n // Billing account completion gate\n const [showCompleteAccountModal, setShowCompleteAccountModal] = useState(false);\n // Bypass the isBillingAccountComplete gate for the call that follows modal completion.\n // The gate uses stale Apollo cache (contactNumber/billingAddresses are not returned by\n // updateBillingAccount mutation), so we skip it once immediately after the modal saves.\n const bypassBillingGate = useRef(false);\n\n // Workspace quota assignment modal state\n const [showAssignModal, setShowAssignModal] = useState(false);\n const [pendingWorkspaceQuotas, setPendingWorkspaceQuotas] = useState<WorkspaceQuotaToAssign[]>(\n []\n );\n const [pendingSubscriptionId, setPendingSubscriptionId] = useState<string | null>(null);\n\n // Checkout state\n const [mode, setMode] = useState<CheckoutMode>('select');\n const [paymentResponse, setPaymentResponse] = useState<{\n clientSecret?: string;\n orderId?: string;\n razorpayKeyId?: string;\n razorpayCustomerId?: string;\n paymentId?: string;\n paymentComplete?: boolean;\n prefillName?: string;\n prefillEmail?: string;\n prefillContact?: string;\n total?: number; // Total in target currency from backend (e.g., INR)\n currency?: string;\n } | null>(null);\n const [paymentErrorMessage, setPaymentErrorMessage] = useState<string | null>(null);\n\n // Store purchase details for success page (captured before clearing)\n const [purchaseDetails, setPurchaseDetails] = useState<{\n items: typeof checkout.items;\n total: number;\n billingAccountName: string;\n billingAccountId: string;\n previousBalance: number;\n newBalance: number;\n paymentMethod: 'credits' | 'card';\n currency: string;\n transactionId?: string;\n } | null>(null);\n\n // Track payment type for polling (card vs credits)\n const [pollingPaymentType, setPollingPaymentType] = useState<'card' | 'credits'>('card');\n\n useEffect(() => {\n if (mode !== 'success' || !purchaseDetails) {\n return;\n }\n\n const rawAttribution =\n typeof window !== 'undefined'\n ? window.localStorage.getItem(getProfileScopedKey('boff-referral-attribution'))\n : null;\n if (!rawAttribution) {\n return;\n }\n\n let attribution: { journeyId?: string; codeId?: string };\n try {\n attribution = JSON.parse(rawAttribution) as { journeyId?: string; codeId?: string };\n } catch {\n return;\n }\n if (!attribution.journeyId && !attribution.codeId) {\n return;\n }\n\n const uniqueReference =\n purchaseDetails.transactionId ||\n paymentResponse?.paymentId ||\n paymentResponse?.orderId ||\n storeOrderId ||\n itemId ||\n purchaseType;\n\n const signature = `${attribution.journeyId ?? 'journey'}:${uniqueReference}`;\n if (referralEventReportedRef.current === signature) {\n return;\n }\n referralEventReportedRef.current = signature;\n\n void recordAffiliateEvent(\n {\n journeyId: attribution.journeyId,\n codeId: attribution.codeId,\n eventType: purchaseType === 'ORDER' ? 'STORE_PURCHASE' : 'PAID_CONVERSION',\n productSlug,\n actorUserId: currentUser?.id,\n orderId:\n purchaseDetails.transactionId || paymentResponse?.paymentId || paymentResponse?.orderId,\n billingAccountId: purchaseDetails.billingAccountId,\n storeOrderId: purchaseType === 'ORDER' ? storeOrderId : undefined,\n valueAmount: purchaseDetails.total,\n currency: purchaseDetails.currency,\n mode: 'billing-checkout',\n metadata: {\n purchaseType,\n billingAccountName: purchaseDetails.billingAccountName,\n },\n },\n { apiGatewayUrl, authToken, orgId }\n ).catch(() => {\n referralEventReportedRef.current = null;\n });\n }, [\n apiGatewayUrl,\n authToken,\n currentUser?.id,\n itemId,\n mode,\n orgId,\n paymentResponse?.orderId,\n paymentResponse?.paymentId,\n productSlug,\n purchaseDetails,\n purchaseType,\n storeOrderId,\n ]);\n\n // Per-seat plan seat count\n const [seatCount, setSeatCount] = useState<number>(1);\n\n // Use checkout hook - pass upgrade/downgrade context if this is a plan change\n const checkout = useCheckout({\n defaultCurrency: 'USD',\n upgradeFromSubscriptionId: locationState?.upgradeFromSubscriptionId,\n downgradeFromSubscriptionId: locationState?.downgradeFromSubscriptionId,\n });\n const { config: providerConfig, stripeEnabled, razorpayEnabled } = usePaymentProviders();\n\n // Payment status polling for both card and credit payments\n const {\n // isPolling - available but UI relies on mode state instead\n elapsedTime,\n error: pollingError,\n // paymentResult - available but not directly used (callbacks handle results)\n } = usePaymentStatusPolling({\n paymentId: mode === 'processing' ? (paymentResponse?.paymentId ?? null) : null,\n billingAccountId: checkout.billingAccount?.id ?? purchaseDetails?.billingAccountId ?? null,\n paymentType: pollingPaymentType, // 'card' or 'credits' based on selected provider\n pollingInterval: 5000, // Poll every 5 seconds\n maxPollingTime: 300000, // 5 minutes timeout\n onSuccess: (result) => {\n // Update transaction ID if available from polling result\n if (result.transactionId) {\n setPurchaseDetails((prev) =>\n prev ? { ...prev, transactionId: result.transactionId } : prev\n );\n }\n setMode('success');\n if (isCartCheckout) {\n clearCart();\n }\n\n // Derive workspace-scoped quotas from the purchased plan's features.\n // We only show the modal for PLAN and ADDON purchases that include workspace quotas.\n // A feature's own context.quantity is a per-plan \"absolute limit\" override\n // (set via the Plan admin form) that takes precedence over the quota\n // catalog's generic limits.value default — must match buildWorkspaceQuotaInputs\n // (the backend function that actually grants the quota) or this preview\n // promises a different number than what the customer actually receives.\n if ((purchaseType === 'PLAN' || purchaseType === 'ADDON') && workspaceId) {\n type PlanFeature = {\n context?: Record<string, unknown> | null;\n quota?: { name?: string | null; limits?: Record<string, unknown> | null } | null;\n };\n const rawFeatures: Array<PlanFeature | null | undefined> =\n planData?.getPlan?.features ?? [];\n const features: PlanFeature[] = rawFeatures.filter((f): f is PlanFeature => f != null);\n const wsQuotas: WorkspaceQuotaToAssign[] = features\n .filter((f: PlanFeature) => f.quota?.name && isWorkspaceScopedQuota(f.quota.name))\n .map((f: PlanFeature) => {\n const limits = f.quota?.limits as Record<string, unknown> | null | undefined;\n const limitsType = limits && 'type' in limits ? String(limits.type) : 'number';\n const overrideQuantity =\n f.context && 'quantity' in f.context ? Number(f.context.quantity) : null;\n const catalogValue = limits && 'value' in limits ? Number(limits.value) : null;\n const limitsValue = overrideQuantity ?? catalogValue;\n return {\n quotaName: f.quota!.name!,\n productId:\n ((planData?.getPlan?.context as Record<string, unknown> | null)?.[\n 'productId'\n ] as string) ?? 'platform',\n limit: limitsType === 'number' ? limitsValue : null,\n noLimit: limitsType !== 'number',\n resetPeriod: 'NEVER',\n quotaMode: 'dedicated' as const,\n };\n });\n\n if (wsQuotas.length > 0) {\n setPendingWorkspaceQuotas(wsQuotas);\n // Refetch subscriptions to resolve the subscriptionId for the modal\n void refetchSubscriptions().then(\n (res: { data?: GetActiveSubscriptionsCheckoutQuery }) => {\n const match = res.data?.getActiveSubscriptions?.find(\n (s: { planId: string; id: string }) => s.planId === itemId\n );\n if (match) setPendingSubscriptionId(match.id);\n }\n );\n // With exactly one workspace there's no real choice to make — the\n // backend's autoActivateSingleWorkspaceForSubscription already\n // assigned these quotas to it at purchase time (see\n // global-billing-svc's Razorpay/Stripe webhook handlers), so this\n // modal would just be asking the customer to confirm something\n // that already happened. Only show it when there's a genuine\n // choice — 2+ workspaces — which is exactly the ambiguity it\n // exists to resolve.\n if (workspaceOptions.length > 1) {\n setShowAssignModal(true);\n }\n }\n }\n\n // Auto-redirect is handled by useEffect\n },\n onFailure: (result) => {\n setPaymentErrorMessage(result.failureReason || 'Payment failed');\n setMode('error');\n },\n onTimeout: () => {\n setPaymentErrorMessage(\n 'Payment verification timed out. Please check your billing history for the payment status.'\n );\n setMode('error');\n },\n });\n\n // Fetch billing accounts\n const {\n data: accountsData,\n loading: accountsLoading,\n refetch: refetchAccounts,\n } = useGetBillingAccountsCheckoutQuery({});\n\n // Fetch plan if purchasing a plan\n const {\n data: planData,\n loading: planLoading,\n // error: planError - available but not displayed (loading/empty states suffice)\n } = useGetPlanCheckoutQuery({\n variables: { id: itemId! },\n skip: purchaseType !== 'PLAN' || !itemId,\n });\n\n // Fetch addon if purchasing an addon\n const { data: addonData, loading: addonLoading } = useGetSelectedAddonCheckoutQuery({\n variables: { addonIDs: itemId ? [itemId] : [] },\n skip: purchaseType !== 'ADDON' || !itemId || isStoreCheckout, // Skip for store checkout\n });\n\n // Fetch store order (for store checkout)\n const { data: storeOrderData } = useGetStoreOrderCheckoutQuery({\n variables: { id: storeOrderId! },\n skip: !isStoreCheckout || !storeOrderId,\n });\n\n // ── Physical store-order shipping (physical produce only) ─────────────────\n // All shipping UI + logic below is gated on `isPhysicalStoreOrder`, which\n // REQUIRES isStoreCheckout (purchaseType 'ORDER') AND a PHYSICAL line item.\n // The digital-app / plan / addon / credits paths — and digital-only store\n // orders — never see any of it and behave byte-for-byte as before.\n //\n // getStoreOrder.lineItems is typed Record<string,unknown> by codegen but is a\n // StoreOrderLineItem[] at runtime — cast defensively behind an Array.isArray\n // guard. itemType is written LOWERCASE by microfe-store (CartPage), so compare\n // case-insensitively or the gate silently never fires.\n const storeOrderLineItems = useMemo<StoreOrderLineItem[]>(() => {\n const raw = storeOrderData?.getStoreOrder?.lineItems;\n return Array.isArray(raw) ? (raw as StoreOrderLineItem[]) : [];\n }, [storeOrderData]);\n\n const isPhysicalStoreOrder =\n isStoreCheckout &&\n storeOrderLineItems.some((li) => String(li.itemType).toUpperCase() === 'PHYSICAL');\n\n // The order currency drives the synthetic shipping line so it converts in\n // lockstep with the product ORDER items (see currency-conversion effect below).\n const storeOrderCurrency = normalizeStoreOrderCurrency(storeOrderData?.getStoreOrder?.currency);\n\n // Buyer's shipping selection, surfaced by <StoreShippingSection />. Cost is\n // DISPLAY-ONLY here; the backend recomputes it authoritatively on submit.\n const [shipping, setShipping] = useState<StoreShippingSelection>({\n shippingAddressId: null,\n shippingMethodId: null,\n shippingCost: 0,\n destinationCountry: null,\n });\n\n // NEW backend mutation — see hooks/useStoreShipping.ts. Inert until\n // global-store-svc ships `setStoreOrderShipping`.\n const [setStoreOrderShipping] = useSetStoreOrderShipping();\n\n // Fetch saved payment methods for selected billing account\n const { data: paymentMethodsData, loading: paymentMethodsLoading } =\n useGetPaymentMethodsCheckoutQuery({\n variables: { billingAccountId: checkout.billingAccount?.id! },\n skip: !checkout.billingAccount?.id,\n });\n\n // Fetch active subscriptions for addon purchases (needed to select which subscription to attach addon to)\n // Skip for store checkout - store items don't require an active subscription\n // Also load for PLAN purchases so we can resolve subscriptionId after payment success\n const {\n data: subscriptionsData,\n loading: subscriptionsLoading,\n refetch: refetchSubscriptions,\n } = useGetActiveSubscriptionsCheckoutQuery({\n variables: { billingAccountId: checkout.billingAccount?.id! },\n skip:\n !checkout.billingAccount?.id ||\n (purchaseType !== 'ADDON' && purchaseType !== 'PLAN') ||\n isStoreCheckout,\n });\n\n // Memoized subscriptions list\n const activeSubscriptions = useMemo(() => {\n return subscriptionsData?.getActiveSubscriptions ?? [];\n }, [subscriptionsData]);\n\n // Selected subscription for addon purchases\n const selectedSubscription = useMemo(() => {\n if (!checkout.selectedSubscriptionId || activeSubscriptions.length === 0) return null;\n return (\n activeSubscriptions.find((s: { id: string }) => s.id === checkout.selectedSubscriptionId) ??\n null\n );\n }, [checkout.selectedSubscriptionId, activeSubscriptions]);\n\n // Fetch credit cost from backend (for credits purchase)\n // Use current quantity from items, or default to 100 for initial query\n // Query in selected currency to get correct pricing\n const creditQuantity = checkout.items[0]?.type === 'CREDITS' ? checkout.items[0].quantity : 100;\n const { data: creditCostData, loading: creditCostLoading } = useGetCostOfCreditsCheckoutQuery({\n variables: { creditAmount: creditQuantity, currency: checkout.selectedCurrency || 'USD' },\n skip: purchaseType !== 'CREDITS',\n });\n\n // Calculate cost per credit from backend response\n const costPerCredit = useMemo(() => {\n if (creditCostData?.getCostOfCredits?.amount && creditQuantity > 0) {\n return creditCostData.getCostOfCredits.amount / creditQuantity;\n }\n return 0.01; // Default: 100 credits = $1\n }, [creditCostData, creditQuantity]);\n\n // Memoized saved payment methods\n const savedPaymentMethods = useMemo<SavedPaymentMethod[]>(() => {\n return paymentMethodsData?.getPaymentMethods ?? [];\n }, [paymentMethodsData]);\n\n // Memoized billing accounts list\n const billingAccounts = useMemo<CheckoutBillingAccount[]>(() => {\n return accountsData?.myBillingAccounts ?? [];\n }, [accountsData]);\n\n // Extract billing country from selected billing account's first billing address\n // This is used to restrict currency and payment provider options\n const billingCountry = useMemo<string | undefined>(() => {\n if (!checkout.billingAccount) return undefined;\n\n // Find the full account data with billing addresses\n const fullAccount = billingAccounts.find((acc) => acc.id === checkout.billingAccount?.id);\n const billingAddresses = fullAccount?.billingAddresses;\n\n if (billingAddresses && billingAddresses.length > 0) {\n return billingAddresses[0]?.country || undefined;\n }\n return undefined;\n }, [checkout.billingAccount, billingAccounts]);\n\n // Track if initial provider selection has been done for this billing country\n const [initialProviderSet, setInitialProviderSet] = useState<string | null>(null);\n\n // Auto-select appropriate currency based on billing country and billing account currency\n // - India: INR only (for Zoho Books GST invoice compatibility)\n // - International: Prefer billing account currency, or USD (INR not allowed for Stripe invoice compatibility)\n useEffect(() => {\n if (!billingCountry) return;\n\n if (billingCountry === 'IN') {\n // Indian billing: Force INR currency\n if (checkout.selectedCurrency !== 'INR') {\n checkout.setCurrency('INR');\n }\n // Auto-select Razorpay ONLY on first load for this billing country (don't override user's choice)\n if (\n razorpayEnabled &&\n initialProviderSet !== billingCountry &&\n checkout.selectedProvider !== 'CREDITS'\n ) {\n checkout.setPaymentProvider('RAZORPAY');\n setInitialProviderSet(billingCountry);\n }\n } else {\n // International billing: Force non-INR currency\n if (checkout.selectedCurrency === 'INR') {\n // Prefer billing account's currency if it's not INR, otherwise default to USD\n const accountCurrency = checkout.billingAccount?.currency;\n const preferredCurrency =\n accountCurrency && accountCurrency !== 'INR' ? accountCurrency : 'USD';\n checkout.setCurrency(preferredCurrency);\n }\n // For international, Stripe is the only option (Razorpay is India-only)\n if (checkout.selectedProvider === 'RAZORPAY') {\n checkout.setPaymentProvider('STRIPE');\n setInitialProviderSet(billingCountry);\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- Using specific checkout properties is intentional for granular updates\n }, [\n billingCountry,\n checkout.selectedCurrency,\n checkout.selectedProvider,\n checkout.setCurrency,\n checkout.setPaymentProvider,\n checkout.billingAccount?.currency,\n razorpayEnabled,\n initialProviderSet,\n ]);\n\n const {\n billingAccount: checkoutBillingAccount,\n canPayWithCredits,\n hasEnoughCredits,\n selectedCurrency,\n selectedPaymentMethodId,\n selectedProvider,\n setCurrency,\n setPaymentProvider,\n setSelectedPaymentMethodId,\n } = checkout;\n\n // Auto-select billing account's currency when billing account changes (for international)\n useEffect(() => {\n if (!checkoutBillingAccount?.currency || billingCountry === 'IN') return;\n\n const accountCurrency = checkoutBillingAccount.currency;\n // Update to billing account's currency if it's different and valid (not INR for international)\n if (accountCurrency !== 'INR' && selectedCurrency !== accountCurrency) {\n setCurrency(accountCurrency);\n }\n }, [billingCountry, checkoutBillingAccount, selectedCurrency, setCurrency]);\n\n // Auto-select payment method with priority: Credits > Saved Card > New Card\n useEffect(() => {\n // Skip if already manually selected something (not AUTO or null)\n if (selectedProvider !== 'AUTO' && selectedProvider !== null) {\n return; // Already selected, don't override user choice\n }\n\n // Priority 1: Credits (if available and sufficient)\n if (canPayWithCredits && hasEnoughCredits) {\n setPaymentProvider('CREDITS');\n setSelectedPaymentMethodId(null);\n return;\n }\n\n // Priority 2: Default saved card\n if (savedPaymentMethods.length > 0 && !selectedPaymentMethodId) {\n const defaultMethod = savedPaymentMethods.find((pm) => pm.isDefault && pm.isVerified);\n const firstVerified = savedPaymentMethods.find((pm) => pm.isVerified);\n const methodToSelect = defaultMethod || firstVerified;\n\n if (methodToSelect) {\n setSelectedPaymentMethodId(methodToSelect.id);\n // Auto-set provider based on card type\n if (methodToSelect.razorpayPaymentMethodId) {\n setPaymentProvider('RAZORPAY');\n } else if (methodToSelect.stripePaymentMethodId) {\n setPaymentProvider('STRIPE');\n }\n return;\n }\n }\n\n // Priority 3: New card (auto-select provider based on country)\n // This is already handled by the currency auto-selection effect above\n }, [\n canPayWithCredits,\n hasEnoughCredits,\n savedPaymentMethods,\n selectedPaymentMethodId,\n selectedProvider,\n setPaymentProvider,\n setSelectedPaymentMethodId,\n ]);\n\n // Track if item was already added to prevent duplicates\n const [itemAdded, setItemAdded] = useState(false);\n\n useEffect(() => {\n if (!isStoreCheckout || itemAdded || !storeOrderData?.getStoreOrder) {\n return;\n }\n\n const orderCurrency = normalizeStoreOrderCurrency(storeOrderData.getStoreOrder.currency);\n if (selectedCurrency !== orderCurrency) {\n setCurrency(orderCurrency);\n }\n }, [isStoreCheckout, itemAdded, selectedCurrency, setCurrency, storeOrderData]);\n\n // Initialize checkout with items\n // Initialize seat count from plan minSeats when plan data loads\n useEffect(() => {\n if (purchaseType === 'PLAN' && planData?.getPlan?.pricingModel === 'PER_SEAT') {\n const minSeats = planData.getPlan.minSeats ?? 1;\n setSeatCount((prev) => (prev < minSeats ? minSeats : prev));\n }\n }, [planData, purchaseType]);\n\n // Recalculate checkout item when seat count changes for PER_SEAT plans\n useEffect(() => {\n if (purchaseType === 'PLAN' && planData?.getPlan?.pricingModel === 'PER_SEAT' && itemAdded) {\n setItemAdded(false); // triggers re-add with new seatCount\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [seatCount]);\n\n useEffect(() => {\n // Skip if already added items\n if (itemAdded) {\n return;\n }\n\n // Handle cart-based checkout (multiple addons from cart)\n // Wait for cart to hydrate before processing\n if (isCartCheckout && cartHydrated && cartItems.length > 0) {\n // Clear existing items first to prevent duplicates (React strict mode can run effects twice)\n checkout.clearItems();\n\n for (const cartItem of cartItems) {\n const addon = cartItem.addon;\n\n // Extract features from addon (using SubscriptionFeatures type with quota).\n // context.quantity is the amount this addon actually grants per unit\n // purchased — takes precedence over the quota catalog's generic\n // limits.value, same as the other purchase paths on this page.\n const features =\n addon.features?.map((f) => {\n const limits = f.quota?.limits as Record<string, number | string | boolean> | undefined;\n const overrideQuantity =\n f.context && 'quantity' in f.context ? Number(f.context.quantity) : undefined;\n return {\n id: f.quota?.id || f.id,\n name: f.quota?.name || 'Feature',\n limits:\n overrideQuantity !== undefined && limits && 'type' in limits\n ? { ...limits, value: overrideQuantity }\n : limits,\n };\n }) || [];\n\n // Use stored currency price if available, otherwise fallback to base USD price\n const selectedCurrency = checkout.selectedCurrency || 'USD';\n const currencyPrices = (addon as { currencyPrices?: Record<string, number> })\n .currencyPrices;\n const priceInSelectedCurrency = currencyPrices?.[selectedCurrency] ?? addon.price;\n // addon.price/currency are always localized to the caller's currency\n // context (e.g. an INR request gets addon.price in INR, regardless of\n // the addon's stored base currency) — currencyPrices.USD is the one\n // field that's reliably actually USD, needed for credit calculations.\n const usdPrice = currencyPrices?.USD ?? addon.price;\n\n const item: CheckoutItem = {\n id: addon.id,\n type: 'ADDON',\n name: addon.name,\n description:\n typeof addon.context === 'object' && addon.context !== null\n ? (addon.context as { description?: string }).description\n : typeof addon.context === 'string'\n ? addon.context\n : undefined,\n price: priceInSelectedCurrency,\n priceUSD: usdPrice,\n currency: selectedCurrency,\n quantity: cartItem.quantity,\n features,\n metadata: { addonId: addon.id },\n };\n checkout.addItem(item);\n }\n setItemAdded(true);\n\n return;\n }\n\n if (purchaseType === 'PLAN' && planData?.getPlan) {\n const plan = planData.getPlan;\n\n // Extract features from plan. A feature's own context.quantity is a\n // per-plan \"absolute limit\" override (set via the Plan admin form) that\n // takes precedence over the quota catalog's generic limits.value\n // default — must match buildWorkspaceQuotaInputs (the backend function\n // that actually grants the quota) or this order summary promises a\n // different number than what the customer actually receives.\n const features =\n plan.features?.map(\n (f: {\n id: string;\n context?: Record<string, unknown> | null;\n quota?: { id: string; name: string; limits?: Record<string, unknown> };\n }) => {\n const limits = f.quota?.limits;\n const overrideQuantity =\n f.context && 'quantity' in f.context ? Number(f.context.quantity) : undefined;\n return {\n id: f.quota?.id || f.id,\n name: f.quota?.name || 'Feature',\n limits:\n overrideQuantity !== undefined && limits && 'type' in limits\n ? { ...limits, value: overrideQuantity }\n : limits,\n };\n }\n ) || [];\n\n // Use stored currency price if available, otherwise fallback to base USD price\n const selectedCurrency = checkout.selectedCurrency || 'USD';\n const currencyPrices = plan.currencyPrices as Record<\n string,\n number | { perSeat?: number }\n > | null;\n const isPerSeat = plan.pricingModel === 'PER_SEAT';\n\n let priceInSelectedCurrency: number;\n if (isPerSeat && plan.pricePerSeat) {\n // PER_SEAT: per-seat rate × seat count\n const currencyEntry = currencyPrices?.[selectedCurrency];\n const perSeatRate =\n typeof currencyEntry === 'object' && currencyEntry?.perSeat\n ? currencyEntry.perSeat\n : plan.pricePerSeat;\n priceInSelectedCurrency = perSeatRate * seatCount;\n } else {\n const currencyEntry = currencyPrices?.[selectedCurrency];\n priceInSelectedCurrency = typeof currencyEntry === 'number' ? currencyEntry : plan.price;\n }\n\n // USD price for credit-balance calculations. plan.price/pricePerSeat are\n // only reliably USD when the plan's own base currency is USD — a plan\n // whose base currency is e.g. INR (an admin can pick any currency when\n // creating a plan) stores that INR amount directly in plan.price, not a\n // USD figure. currencyPrices.USD (present for every plan regardless of\n // base currency) is the one field that's always actually USD.\n const usdCurrencyEntry = currencyPrices?.USD;\n const usdFlatPrice = typeof usdCurrencyEntry === 'number' ? usdCurrencyEntry : plan.price;\n const usdPerSeatRate =\n typeof usdCurrencyEntry === 'object' && usdCurrencyEntry?.perSeat\n ? usdCurrencyEntry.perSeat\n : plan.pricePerSeat;\n\n const resolvedSeatCount = isPerSeat ? seatCount : 1;\n\n const item: CheckoutItem = {\n id: plan.id,\n type: 'PLAN',\n name: plan.name,\n description:\n typeof plan.context === 'object' && plan.context !== null\n ? (plan.context as { description?: string }).description\n : typeof plan.context === 'string'\n ? plan.context\n : undefined,\n price: priceInSelectedCurrency,\n priceUSD:\n isPerSeat && plan.pricePerSeat\n ? (usdPerSeatRate ?? plan.pricePerSeat) * resolvedSeatCount\n : usdFlatPrice,\n currency: selectedCurrency,\n quantity: resolvedSeatCount,\n features,\n metadata: {\n planId: plan.id,\n duration: plan.duration,\n seatCount: isPerSeat ? resolvedSeatCount : undefined,\n },\n };\n\n checkout.addItem(item);\n setItemAdded(true);\n } else if (purchaseType === 'ADDON' && !isCartCheckout && addonData?.getSelectedAddons?.[0]) {\n const addon = addonData.getSelectedAddons[0];\n\n // Extract features from addon. context.quantity is the amount this\n // addon actually grants per unit purchased — takes precedence over the\n // quota catalog's generic limits.value, same as the plan path above.\n const features =\n addon.features?.map(\n (f: {\n id: string;\n context?: Record<string, unknown> | null;\n quota?: { id: string; name: string; limits?: Record<string, unknown> };\n }) => {\n const limits = f.quota?.limits;\n const overrideQuantity =\n f.context && 'quantity' in f.context ? Number(f.context.quantity) : undefined;\n return {\n id: f.quota?.id || f.id,\n name: f.quota?.name || 'Feature',\n limits:\n overrideQuantity !== undefined && limits && 'type' in limits\n ? { ...limits, value: overrideQuantity }\n : limits,\n };\n }\n ) || [];\n\n // Use stored currency price if available, otherwise fallback to base USD price\n const selectedCurrency = checkout.selectedCurrency || 'USD';\n const currencyPrices = addon.currencyPrices as Record<string, number> | null;\n const priceInSelectedCurrency = currencyPrices?.[selectedCurrency] ?? addon.price;\n // addon.price/currency are always localized to the caller's currency\n // context (e.g. an INR request gets addon.price in INR, regardless of\n // the addon's stored base currency) — currencyPrices.USD is the one\n // field that's reliably actually USD, needed for credit calculations.\n const usdPrice = currencyPrices?.USD ?? addon.price;\n\n const item: CheckoutItem = {\n id: addon.id,\n type: 'ADDON',\n name: addon.name,\n description:\n typeof addon.context === 'object' && addon.context !== null\n ? (addon.context as { description?: string }).description\n : typeof addon.context === 'string'\n ? addon.context\n : undefined,\n price: priceInSelectedCurrency,\n priceUSD: usdPrice,\n currency: selectedCurrency,\n quantity: locationState?.quantity ?? 1,\n features,\n metadata: { addonId: addon.id },\n };\n checkout.addItem(item);\n setItemAdded(true);\n } else if (isStoreCheckout && storeOrderData?.getStoreOrder) {\n // Load store order items into checkout\n const storeOrder = storeOrderData.getStoreOrder;\n const items = buildStoreOrderCheckoutItems(storeOrder);\n\n for (const item of items) {\n checkout.addItem(item);\n }\n setItemAdded(true);\n } else if (purchaseType === 'CREDITS') {\n const selectedCurrency = checkout.selectedCurrency || 'USD';\n const item: CheckoutItem = {\n id: 'credits-purchase',\n type: 'CREDITS',\n name: 'Credits',\n description: 'Purchase credits for your account',\n price: costPerCredit, // Use actual cost per credit from backend (in selected currency)\n currency: selectedCurrency,\n quantity: 100, // Default amount\n };\n checkout.addItem(item);\n setItemAdded(true);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- Using specific checkout properties is intentional for granular updates\n }, [\n purchaseType,\n planData,\n addonData,\n storeOrderData,\n checkout.addItem,\n checkout.clearItems,\n locationState?.quantity,\n itemAdded,\n isCartCheckout,\n isStoreCheckout,\n cartItems,\n cartHydrated,\n costPerCredit,\n checkout.selectedCurrency, // Re-add items when currency changes to use correct prices\n ]);\n\n // Update store-order item prices when a payment currency change requires conversion.\n // If the persisted order currency already matches the selected currency this is a no-op.\n useEffect(() => {\n // convertedAmount !== null signals a real rate has arrived for the current\n // currency pair — conversionRate's own magnitude can't be used for this\n // (a from-INR-to-USD rate is legitimately < 1, unlike the from-USD rates\n // this used to always fetch — see BOFF-7127).\n if (!isStoreCheckout || !itemAdded || checkout.convertedAmount === null) return;\n\n for (const item of checkout.items) {\n if (item.type === 'ORDER' && item.currency !== checkout.selectedCurrency) {\n const convertedPrice = item.price * checkout.conversionRate;\n checkout.updateItemPrice(item.id, convertedPrice, checkout.selectedCurrency);\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- checkout.items intentionally excluded to avoid infinite loop\n }, [\n isStoreCheckout,\n itemAdded,\n checkout.convertedAmount,\n checkout.conversionRate,\n checkout.selectedCurrency,\n checkout.updateItemPrice,\n ]);\n\n // Fold the buyer's shipping cost into the billing total as a single synthetic\n // ORDER line item, upserted-by-id so it survives React strict-mode double\n // effects and is never an editable/removable line (OrderSummary disables qty\n // editing for ORDER purchases). Free shipping (cost 0) intentionally adds no\n // line.\n //\n // The line is ALWAYS written in the selected currency, pre-converting from the\n // order currency with the SAME formula the store-order conversion effect above\n // uses (price * conversionRate, only when order currency !== selected currency\n // and a rate is active). Because its currency always equals the selected\n // currency, that conversion effect skips it (`item.currency !== selectedCurrency`\n // is false) — so shipping is counted EXACTLY once and can never be double-applied.\n // This effect owns re-pricing on cost / currency / rate changes.\n //\n // This is the ONLY place shipping enters the billing charge; the backend's\n // StoreOrder.total bump is store-side reconciliation only and is never read\n // back here (no double count).\n const shippingLineRef = useRef<{ price: number; currency: string } | null>(null);\n useEffect(() => {\n if (!isPhysicalStoreOrder) return;\n\n if (shipping.shippingCost > 0) {\n const selectedCurrency = checkout.selectedCurrency;\n const needsConversion =\n storeOrderCurrency !== selectedCurrency && checkout.convertedAmount !== null;\n const price = needsConversion\n ? shipping.shippingCost * checkout.conversionRate\n : shipping.shippingCost;\n\n const prev = shippingLineRef.current;\n if (prev && prev.price === price && prev.currency === selectedCurrency) {\n return; // nothing changed\n }\n if (prev === null) {\n checkout.addItem({\n id: STORE_SHIPPING_ITEM_ID,\n type: 'ORDER',\n name: 'Shipping',\n price,\n currency: selectedCurrency,\n quantity: 1,\n metadata: { storeOrderId, isShipping: true },\n });\n } else {\n checkout.updateItemPrice(STORE_SHIPPING_ITEM_ID, price, selectedCurrency);\n }\n shippingLineRef.current = { price, currency: selectedCurrency };\n } else if (shippingLineRef.current !== null) {\n shippingLineRef.current = null;\n checkout.removeItem(STORE_SHIPPING_ITEM_ID);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- add/update decided via ref; checkout.items excluded to avoid an update loop\n }, [\n isPhysicalStoreOrder,\n shipping.shippingCost,\n storeOrderCurrency,\n checkout.selectedCurrency,\n checkout.conversionRate,\n checkout.convertedAmount,\n storeOrderId,\n checkout.addItem,\n checkout.updateItemPrice,\n checkout.removeItem,\n ]);\n\n // Update item price when backend credit cost changes (after item is already added)\n // This handles cases where the rate might differ based on quantity or currency\n useEffect(() => {\n if (purchaseType === 'CREDITS' && itemAdded && costPerCredit > 0) {\n const currentItem = checkout.items.find((i) => i.id === 'credits-purchase');\n // Only update if price actually changed\n if (currentItem && currentItem.price !== costPerCredit) {\n checkout.updateItemPrice('credits-purchase', costPerCredit);\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- Using specific checkout properties is intentional for granular updates\n }, [purchaseType, itemAdded, costPerCredit, checkout.items, checkout.updateItemPrice]);\n\n // Update item prices when currency changes (for plans and addons with fixed currencyPrices)\n // This handles mid-checkout currency changes by updating prices from stored currencyPrices\n useEffect(() => {\n if (!itemAdded || checkout.items.length === 0) return;\n\n const selectedCurrency = checkout.selectedCurrency || 'USD';\n\n for (const item of checkout.items) {\n if (item.type === 'PLAN' && planData?.getPlan) {\n const currencyPrices = planData.getPlan.currencyPrices as Record<string, number> | null;\n const priceInSelectedCurrency =\n currencyPrices?.[selectedCurrency] ?? planData.getPlan.price;\n // Update both price AND currency so conversion check works correctly\n if (item.price !== priceInSelectedCurrency || item.currency !== selectedCurrency) {\n checkout.updateItemPrice(item.id, priceInSelectedCurrency, selectedCurrency);\n }\n } else if (item.type === 'ADDON') {\n // For single addon checkout\n if (!isCartCheckout && addonData?.getSelectedAddons?.[0]) {\n const addon = addonData.getSelectedAddons[0];\n const currencyPrices = addon.currencyPrices as Record<string, number> | null;\n const priceInSelectedCurrency = currencyPrices?.[selectedCurrency] ?? addon.price;\n if (item.price !== priceInSelectedCurrency || item.currency !== selectedCurrency) {\n checkout.updateItemPrice(item.id, priceInSelectedCurrency, selectedCurrency);\n }\n }\n // For cart checkout, find the addon in cartItems\n if (isCartCheckout) {\n const cartItem = cartItems.find((ci) => ci.addon.id === item.id);\n if (cartItem) {\n const currencyPrices = (cartItem.addon as { currencyPrices?: Record<string, number> })\n .currencyPrices;\n const priceInSelectedCurrency =\n currencyPrices?.[selectedCurrency] ?? cartItem.addon.price;\n if (item.price !== priceInSelectedCurrency || item.currency !== selectedCurrency) {\n checkout.updateItemPrice(item.id, priceInSelectedCurrency, selectedCurrency);\n }\n }\n }\n }\n // CREDITS are handled by the credit cost effect above\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- Using specific checkout properties is intentional for granular updates\n }, [\n checkout.selectedCurrency,\n itemAdded,\n checkout.items,\n checkout.updateItemPrice,\n planData,\n addonData,\n cartItems,\n isCartCheckout,\n ]);\n\n // Set default billing account\n // Priority: 1. Location state (for upgrade flows), 2. localStorage (user's last selection), 3. First account\n useEffect(() => {\n if (billingAccounts.length > 0 && !checkout.billingAccount) {\n // 1. If this is an upgrade, find and select the specific billing account from location state\n if (locationState?.billingAccountId) {\n const targetAccount = billingAccounts.find(\n (acc) => acc.id === locationState.billingAccountId\n );\n if (targetAccount) {\n checkout.setBillingAccount(targetAccount);\n return;\n }\n }\n\n // 2. Check localStorage for previously selected billing account (set from /overview page)\n if (orgId) {\n const storedAccountId = getStoredBillingAccountId(orgId);\n if (storedAccountId) {\n const storedAccount = billingAccounts.find((acc) => acc.id === storedAccountId);\n if (storedAccount) {\n checkout.setBillingAccount(storedAccount);\n return;\n }\n }\n }\n\n // 3. Default: select first account\n checkout.setBillingAccount(billingAccounts[0]);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- Using specific checkout properties is intentional for granular updates\n }, [\n billingAccounts,\n checkout.billingAccount,\n checkout.setBillingAccount,\n locationState?.billingAccountId,\n orgId,\n ]);\n\n // Determine if billing account selector should be locked (for upgrade/downgrade flows)\n const isBillingAccountLocked = Boolean(\n locationState?.upgradeFromSubscriptionId || locationState?.downgradeFromSubscriptionId\n );\n\n // Redirect to addons page if cart is empty for cart checkout.\n // Navigation must happen after render to avoid React setState-in-render warnings.\n useEffect(() => {\n if (isCartCheckout && cartHydrated && cartItems.length === 0 && !itemAdded) {\n navigateTo('/addons', { replace: true });\n }\n }, [cartHydrated, cartItems.length, isCartCheckout, itemAdded, navigateTo]);\n\n const nativeCheckout = useNativeCheckout({\n billingAccountId: checkout.billingAccount?.id,\n purchaseType,\n productId: itemId,\n });\n\n const handleProceedToPayment = useCallback(async () => {\n if (!checkout.billingAccount) {\n return;\n }\n\n // Gate: require complete billing details before payment.\n // Skip once after the CompleteBillingAccountModal saves — Apollo cache still holds stale\n // contactNumber/billingAddresses because the mutation selection doesn't return those fields.\n const skipGate = bypassBillingGate.current;\n bypassBillingGate.current = false;\n if (!skipGate && !isBillingAccountComplete(checkout.billingAccount)) {\n setShowCompleteAccountModal(true);\n return;\n }\n\n setPaymentErrorMessage(null);\n\n // Physical store orders: persist the chosen shipping address + method on the\n // store order BEFORE billing creates its immutable order snapshot. The store\n // service recomputes and persists the shipping cost authoritatively; only IDs\n // are sent from this page. This whole block is skipped for non-physical paths.\n if (isPhysicalStoreOrder) {\n try {\n const shippingResult = await setStoreOrderShipping({\n variables: {\n input: {\n orderId: storeOrderId!,\n shippingMethodId: shipping.shippingMethodId!,\n ...(shipping.shippingAddressId\n ? { shippingAddressId: shipping.shippingAddressId }\n : {}),\n },\n },\n });\n const serverCost = shippingResult.data?.setStoreOrderShipping?.shippingCost;\n if (typeof serverCost === 'number' && serverCost !== shipping.shippingCost) {\n // Keep the display in sync if the authoritative quote differs from\n // the estimate shown before the user submitted the order.\n setShipping((prev) => ({ ...prev, shippingCost: serverCost }));\n }\n } catch {\n setPaymentErrorMessage(\n 'Could not save your shipping details. Please review your delivery address and try again.'\n );\n setMode('error');\n return;\n }\n }\n\n // Capture purchase details before payment (for success page)\n const previousBalance = checkout.billingAccount.creditAmount ?? 0;\n const purchasedItems = [...checkout.items];\n // checkout.total is in the selected DISPLAY currency (e.g. INR) — never\n // the right number for a credits purchase, which always charges\n // creditsRequired (subtotalUSD × CREDITS_PER_USD) regardless of display\n // currency. The spendCredits mutation itself doesn't return the amount\n // it charged, so this is recomputed the same way the backend charges it,\n // not read back from the response.\n const purchaseTotal = checkout.total;\n const creditsCharged = checkout.creditsRequired;\n\n // The checkout hook asks the store service to create the billing order from\n // the persisted catalog and shipping snapshots. No browser-calculated total\n // or line item is used to create the payable order.\n const response = await checkout.initiatePayment();\n\n if (response.success) {\n emit('billing.checkout.initiated', {\n route: '/billing/checkout',\n entityId: response.paymentId ?? response.orderId,\n source: 'checkout',\n amount: response.total || purchaseTotal,\n currency: response.currency || checkout.selectedCurrency,\n });\n // For credit payments, go to processing mode and poll for transaction status\n if (checkout.selectedProvider === 'CREDITS') {\n emit('billing.credits.spent', {\n route: '/billing/checkout',\n entityId: response.orderId,\n source: 'checkout',\n amount: creditsCharged,\n currency: 'CREDITS',\n });\n // Store purchase details for success page\n setPurchaseDetails({\n items: purchasedItems,\n total: creditsCharged,\n billingAccountName: checkout.billingAccount.name,\n billingAccountId: checkout.billingAccount.id,\n previousBalance,\n newBalance: previousBalance - creditsCharged,\n paymentMethod: 'credits',\n currency: 'Credits', // Credit payments use credits as currency\n transactionId: response.orderId, // This is creditTransactionID from spendCredits\n });\n\n // Set payment type for polling and start verification\n setPollingPaymentType('credits');\n setPaymentResponse({\n paymentId: response.orderId, // creditTransactionID from spendCredits\n });\n setMode('processing');\n return;\n }\n\n // For card payments, proceed to payment form\n // Store partial details (will be completed after payment confirmation)\n setPurchaseDetails({\n items: purchasedItems,\n total: response.total || purchaseTotal, // Use response total which includes tax\n billingAccountName: checkout.billingAccount.name,\n billingAccountId: checkout.billingAccount.id,\n previousBalance,\n newBalance: previousBalance, // Card payment doesn't affect credits\n paymentMethod: 'card',\n currency: response.currency || checkout.selectedCurrency, // Use actual currency from response\n transactionId: response.paymentId, // Backend payment ID\n });\n\n setPollingPaymentType('card');\n setPaymentResponse({\n clientSecret: response.clientSecret,\n orderId: response.orderId, // Gateway order ID (for Razorpay)\n razorpayKeyId: response.razorpayKeyId,\n razorpayCustomerId: response.razorpayCustomerId, // For Razorpay saved cards\n paymentId: response.paymentId, // Backend payment ID for polling\n paymentComplete: response.paymentComplete, // For saved card one-click payments\n prefillName: response.prefillName,\n prefillEmail: response.prefillEmail,\n prefillContact: response.prefillContact,\n total: response.total, // Total in target currency (from backend, e.g., INR amount)\n currency: response.currency, // Currency code from backend\n });\n\n // If payment completed immediately (saved card), go to processing for verification\n if (response.paymentComplete) {\n setMode('processing');\n } else {\n setMode('payment');\n }\n } else {\n emit('billing.checkout.failed', {\n route: '/billing/checkout',\n source: 'checkout',\n reason: response.error || 'Payment initiation failed',\n });\n setPaymentErrorMessage(response.error || 'Payment initiation failed');\n setMode('error');\n }\n }, [\n checkout,\n emit,\n isPhysicalStoreOrder,\n shipping,\n storeOrderId,\n storeOrderCurrency,\n setStoreOrderShipping,\n ]);\n\n const handlePaymentSuccess = useCallback(\n (_gatewayPaymentId: string) => {\n emit('billing.payment.succeeded', {\n route: '/billing/checkout',\n entityId: paymentResponse?.paymentId ?? paymentResponse?.orderId,\n source: 'checkout',\n });\n // Keep using the backend payment ID (orderId) that was already set\n // The _gatewayPaymentId is for reference only, not for our polling\n setPaymentResponse((prev) => {\n return prev; // Keep existing paymentId (orderId from initiatePayment)\n });\n setMode('processing');\n },\n [emit, paymentResponse?.orderId, paymentResponse?.paymentId]\n );\n\n const handlePaymentError = useCallback(\n (error: string) => {\n emit('billing.payment.failed', {\n route: '/billing/checkout',\n entityId: paymentResponse?.paymentId ?? paymentResponse?.orderId,\n source: 'checkout',\n reason: error,\n });\n setPaymentErrorMessage(error);\n setMode('error');\n },\n [emit, paymentResponse?.orderId, paymentResponse?.paymentId]\n );\n\n const handleCreditsAmountChange = useCallback(\n (amount: number) => {\n checkout.updateItemQuantity('credits-purchase', amount);\n },\n [checkout]\n );\n\n if (!permissions.canProcessPayment) {\n return <AccessDenied message=\"You don't have permission to make purchases.\" />;\n }\n\n if (isNativeIAPAvailable()) {\n return (\n <NativeCheckoutView\n purchaseType={purchaseType}\n productId={itemId}\n billingAccountId={checkout.billingAccount?.id}\n nativeCheckout={nativeCheckout}\n onBack={() => navigateTo('dashboard')}\n />\n );\n }\n\n const isLoading =\n accountsLoading ||\n (planLoading && purchaseType === 'PLAN') ||\n (addonLoading && purchaseType === 'ADDON' && !isCartCheckout) ||\n (isCartCheckout && !cartHydrated);\n\n if (isCartCheckout && cartHydrated && cartItems.length === 0 && !itemAdded) {\n return null;\n }\n\n // Get page title based on purchase type\n const getPageTitle = () => {\n if (isCartCheckout) {\n const addonCount = checkout.items.length;\n return `Purchase ${addonCount} Add-on${addonCount !== 1 ? 's' : ''}`;\n }\n switch (purchaseType) {\n case 'CREDITS':\n return 'Buy Credits';\n case 'PLAN':\n return 'Subscribe to Plan';\n case 'ADDON':\n return 'Purchase Add-on';\n default:\n return 'Checkout';\n }\n };\n\n const getPageIcon = () => {\n switch (purchaseType) {\n case 'CREDITS':\n return <Coins className=\"size-6\" />;\n case 'PLAN':\n return <Package className=\"size-6\" />;\n case 'ADDON':\n return <Blocks className=\"size-6\" />;\n default:\n return <ShoppingCart className=\"size-6\" />;\n }\n };\n\n // Render loading state\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <Loader2 className=\"size-8 animate-spin mx-auto text-text-link\" />\n <p className=\"text-text-secondary\">\n {tr('billing.checkout.loadingCheckout', 'Loading checkout...')}\n </p>\n </div>\n </div>\n );\n }\n\n // Render success state\n if (mode === 'success') {\n // Format amount based on payment method and currency\n const formatAmount = (amount: number) => {\n if (!purchaseDetails) return `${amount.toFixed(2)}`;\n\n const currency = purchaseDetails.currency;\n\n // For credits, show \"X.XX Credits\"\n if (currency === 'Credits') {\n return `${amount.toFixed(2)} Credits`;\n }\n\n // For real currencies, show with symbol\n const currencySymbols: Record<string, string> = {\n INR: '₹',\n USD: '$',\n EUR: '€',\n GBP: '£',\n };\n const symbol = currencySymbols[currency] || currency + ' ';\n return `${symbol}${amount.toFixed(2)}`;\n };\n\n // Keep formatCredits for credit balance section\n const formatCredits = (amount: number) => `${amount.toFixed(2)} Credits`;\n\n return (\n <>\n <div className=\"max-w-2xl mx-auto p-6 space-y-6\">\n {/* Success Header */}\n <div className=\"text-center space-y-4\">\n <div className=\"inline-flex p-4 rounded-full bg-status-success-bg-subtle text-status-success-text\">\n <CheckCircle className=\"size-12\" />\n </div>\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {tr('billing.checkout.paymentSuccessful', 'Payment Successful!')}\n </h1>\n <p className=\"text-text-secondary mt-2\">\n {tr(\n 'billing.checkout.purchaseCompleted',\n 'Your purchase has been completed successfully.'\n )}\n </p>\n </div>\n </div>\n\n {/* Purchase Details Card */}\n {purchaseDetails && (\n <div className=\"bg-bg-surface border border-border-subtle rounded-lg overflow-hidden\">\n {/* Items Purchased */}\n <div className=\"p-4 border-b border-border-subtle\">\n <h3 className=\"font-semibold text-text-primary mb-3 flex items-center gap-2\">\n <Receipt className=\"size-4\" />\n {tr('billing.checkout.itemsPurchased', 'Items Purchased')}\n </h3>\n <div className=\"space-y-2\">\n {purchaseDetails.items.map((item) => (\n <div key={item.id} className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n {item.type === 'PLAN' && <Package className=\"size-4 text-accent-blue\" />}\n {item.type === 'ADDON' && <Blocks className=\"size-4 text-accent-purple\" />}\n {item.type === 'CREDITS' && (\n <Coins className=\"size-4 text-status-warning-text\" />\n )}\n {item.type === 'ORDER' && (\n <ShoppingCart className=\"size-4 text-status-success-text\" />\n )}\n <span className=\"text-text-primary\">{item.name}</span>\n {item.quantity > 1 && (\n <span className=\"text-xs text-text-secondary\">x{item.quantity}</span>\n )}\n </div>\n <span className=\"text-text-secondary\">\n {formatAmount(\n purchaseDetails.paymentMethod === 'credits'\n ? (item.priceUSD ?? item.price) * CREDITS_PER_USD * item.quantity\n : item.price * item.quantity\n )}\n </span>\n </div>\n ))}\n </div>\n </div>\n\n {/* Payment Info */}\n <div className=\"p-4 border-b border-border-subtle bg-bg-sunken\">\n <div className=\"grid grid-cols-2 gap-4\">\n {/* Payment Method */}\n <div>\n <p className=\"text-xs text-text-secondary mb-1\">\n {tr('billing.checkout.paymentMethod', 'Payment Method')}\n </p>\n <div className=\"flex items-center gap-2\">\n {purchaseDetails.paymentMethod === 'credits' ? (\n <>\n <Wallet className=\"size-4 text-status-warning-text\" />\n <span className=\"font-medium text-text-primary\">\n {tr('billing.checkout.credits', 'Credits')}\n </span>\n </>\n ) : (\n <>\n <CreditCard className=\"size-4 text-accent-blue\" />\n <span className=\"font-medium text-text-primary\">\n {tr('billing.checkout.card', 'Card')}\n </span>\n </>\n )}\n </div>\n </div>\n {/* Total */}\n <div className=\"text-right\">\n <p className=\"text-xs text-text-secondary mb-1\">\n {tr('billing.checkout.totalPaid', 'Total Paid')}\n </p>\n <p className=\"font-bold text-lg text-text-primary\">\n {formatAmount(purchaseDetails.total)}\n </p>\n </div>\n </div>\n </div>\n\n {/* Credit Balance (only for credit payments) */}\n {purchaseDetails.paymentMethod === 'credits' && (\n <div className=\"p-4 border-b border-border-subtle\">\n <h3 className=\"font-semibold text-text-primary mb-3 flex items-center gap-2\">\n <Wallet className=\"size-4\" />\n {tr('billing.checkout.creditBalance', 'Credit Balance')}\n </h3>\n <div className=\"flex items-center gap-4\">\n <div className=\"flex-1 text-center p-3 rounded-lg bg-bg-sunken\">\n <p className=\"text-xs text-text-secondary mb-1\">\n {tr('billing.checkout.previousBalance', 'Previous Balance')}\n </p>\n <p className=\"font-semibold text-text-primary\">\n {formatCredits(purchaseDetails.previousBalance)}\n </p>\n </div>\n <ArrowRight className=\"size-5 text-text-secondary shrink-0\" />\n <div className=\"flex-1 text-center p-3 rounded-lg bg-status-success-bg-subtle\">\n <p className=\"text-xs text-text-secondary mb-1\">\n {tr('billing.checkout.newBalance', 'New Balance')}\n </p>\n <p className=\"font-semibold text-status-success-text\">\n {formatCredits(purchaseDetails.newBalance)}\n </p>\n </div>\n </div>\n </div>\n )}\n\n {/* Billing Account Info */}\n <div className=\"p-4 bg-bg-sunken\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-xs text-text-secondary mb-1\">\n {tr('billing.checkout.billingAccount', 'Billing Account')}\n </p>\n <p className=\"font-medium text-text-primary\">\n {purchaseDetails.billingAccountName}\n </p>\n </div>\n {purchaseDetails.transactionId && (\n <div className=\"text-right\">\n <p className=\"text-xs text-text-secondary mb-1\">\n {tr('billing.checkout.transactionId', 'Transaction ID')}\n </p>\n <p className=\"font-mono text-xs text-text-secondary\">\n {purchaseDetails.transactionId.slice(0, 8)}...\n </p>\n </div>\n )}\n </div>\n </div>\n </div>\n )}\n\n {/* Action Buttons */}\n <div className=\"flex flex-col sm:flex-row gap-3 justify-center pt-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/overview')}\n className=\"px-6 py-3 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-lg hover:bg-action-primary-bgHover transition-colors\"\n >\n {tr('billing.checkout.backToOverview', 'Back to Overview')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/subscriptions')}\n className=\"px-6 py-3 text-sm font-medium border border-border-subtle text-text-primary rounded-lg hover:bg-bg-sunken transition-colors\"\n >\n {tr('billing.checkout.viewSubscriptions', 'View Subscriptions')}\n </button>\n </div>\n </div>\n\n {/* Workspace Quota Assignment Modal */}\n {showAssignModal &&\n pendingWorkspaceQuotas.length > 0 &&\n workspaceId &&\n pendingSubscriptionId && (\n <WorkspaceQuotaAssignmentModal\n billingAccountId={purchaseDetails?.billingAccountId ?? ''}\n subscriptionId={pendingSubscriptionId}\n quotas={pendingWorkspaceQuotas}\n workspaces={workspaceOptions}\n currentWorkspaceId={workspaceId}\n onAssigned={() => setShowAssignModal(false)}\n onSkip={() => setShowAssignModal(false)}\n />\n )}\n </>\n );\n }\n\n // Render error state\n if (mode === 'error') {\n return (\n <div className=\"max-w-lg mx-auto p-6\">\n <div className=\"text-center space-y-6\">\n <div className=\"inline-flex p-4 rounded-full bg-status-error-bg-subtle text-status-error-text\">\n <XCircle className=\"size-12\" />\n </div>\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {tr('billing.checkout.paymentFailed', 'Payment Failed')}\n </h1>\n <p className=\"text-text-secondary mt-2\">\n {paymentErrorMessage ||\n pollingError ||\n checkout.error ||\n tr('billing.checkout.genericError', 'Something went wrong. Please try again.')}\n </p>\n </div>\n <div className=\"flex gap-3 justify-center\">\n <button\n type=\"button\"\n onClick={() => navigateTo(-1)}\n className=\"px-4 py-2 rounded-lg border border-border-subtle hover:bg-bg-sunken\"\n >\n {tr('billing.checkout.goBack', 'Go Back')}\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setMode('select');\n setPaymentErrorMessage(null);\n checkout.reset();\n }}\n className=\"px-4 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bgHover\"\n >\n {tr('billing.checkout.tryAgain', 'Try Again')}\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n // Render processing state (waiting for payment confirmation)\n if (mode === 'processing') {\n const formatTime = (ms: number) => {\n const seconds = Math.floor(ms / 1000);\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = seconds % 60;\n return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;\n };\n\n return (\n <div className=\"max-w-lg mx-auto p-6\">\n <div className=\"text-center space-y-6\">\n <div className=\"inline-flex p-4 rounded-full bg-[var(--color-accent-soft)] text-text-link\">\n <Loader2 className=\"size-12 animate-spin\" />\n </div>\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {tr('billing.checkout.verifyingPayment', 'Verifying Payment')}\n </h1>\n <p className=\"text-text-secondary mt-2\">\n {tr(\n 'billing.checkout.verifyingPaymentDescription',\n 'Please wait while we confirm your payment...'\n )}\n </p>\n </div>\n\n {/* Elapsed time */}\n <div className=\"bg-bg-sunken rounded-lg p-4\">\n <p className=\"text-sm text-text-secondary\">\n {tr('billing.checkout.timeElapsed', 'Time elapsed:')}{' '}\n <span className=\"font-mono font-medium text-text-primary\">\n {formatTime(elapsedTime)}\n </span>\n </p>\n <p className=\"text-xs text-text-secondary mt-1\">\n {tr(\n 'billing.checkout.keepPageOpen',\n \"This may take a few moments. Please don't close this page.\"\n )}\n </p>\n </div>\n\n {/* Progress indicator */}\n <div className=\"w-full bg-bg-sunken rounded-full h-2 overflow-hidden\">\n <div\n className=\"bg-action-primary-bg h-2 rounded-full transition-all duration-1000\"\n style={{ width: `${Math.min((elapsedTime / 300000) * 100, 100)}%` }}\n />\n </div>\n\n {/* Cancel button - only show after 30 seconds */}\n {elapsedTime > 30000 && (\n <button\n type=\"button\"\n onClick={() => {\n setMode('error');\n setPaymentErrorMessage(\n 'Payment verification was cancelled. Please check your billing history.'\n );\n }}\n className=\"text-sm text-text-secondary hover:text-text-primary underline\"\n >\n {tr('billing.checkout.cancelAndCheckHistory', 'Cancel and check billing history')}\n </button>\n )}\n </div>\n </div>\n );\n }\n\n // Render payment form\n if (mode === 'payment' && paymentResponse) {\n // Format currency for display\n const formatDisplayCurrency = (amount: number, currencyCode: string) => {\n const symbols: Record<string, string> = { INR: '₹', USD: '$', EUR: '€', GBP: '£' };\n const symbol = symbols[currencyCode] || currencyCode + ' ';\n return `${symbol}${amount.toFixed(2)}`;\n };\n\n const displayCurrency = paymentResponse.currency || checkout.selectedCurrency;\n const displayTotal = paymentResponse.total ?? checkout.total;\n\n return (\n <div className=\"max-w-2xl mx-auto p-4 sm:p-6 space-y-6\">\n <button\n type=\"button\"\n onClick={() => setMode('select')}\n className=\"flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('billing.checkout.backToCheckout', 'Back to checkout')}\n </button>\n\n <PageHeader\n title={tr('billing.checkout.completePayment', 'Complete Payment')}\n description={tr(\n 'billing.checkout.completePaymentDescription',\n 'Enter your payment details to complete the purchase'\n )}\n />\n\n {/* Payment Summary with correct tax calculation */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 space-y-4\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <Receipt className=\"size-4\" />\n {tr('billing.checkout.paymentSummary', 'Payment Summary')}\n </h3>\n\n {/* Billing Account Info */}\n {checkout.billingAccount && (\n <div className=\"p-3 bg-bg-sunken rounded-lg space-y-1\">\n <div className=\"text-xs text-text-secondary uppercase tracking-wide\">\n {tr('billing.checkout.billingAccount', 'Billing Account')}\n </div>\n <div className=\"font-medium text-text-primary\">{checkout.billingAccount.name}</div>\n <div className=\"text-sm text-text-secondary\">{checkout.billingAccount.email}</div>\n </div>\n )}\n\n {/* Subscription Info (for addon purchases) */}\n {purchaseType === 'ADDON' && checkout.selectedSubscriptionId && selectedSubscription && (\n <div className=\"p-3 bg-bg-sunken rounded-lg space-y-1\">\n <div className=\"text-xs text-text-secondary uppercase tracking-wide\">\n {tr('billing.checkout.addingToSubscription', 'Adding to Subscription')}\n </div>\n <div className=\"font-medium text-text-primary\">\n {selectedSubscription.plan?.name ||\n tr('billing.checkout.subscription', 'Subscription')}\n </div>\n {selectedSubscription.plan?.product?.name && (\n <div className=\"text-sm text-text-secondary\">\n {selectedSubscription.plan.product.name}\n </div>\n )}\n </div>\n )}\n\n {/* Price Breakdown */}\n <div className=\"space-y-2 text-sm\">\n {/* Calculate display values correctly */}\n {(() => {\n const taxRate = checkout.taxRate;\n // For non-USD: calculate subtotal and tax from the total\n const calcSubtotal = taxRate > 0 ? displayTotal / (1 + taxRate) : displayTotal;\n const calcTax = taxRate > 0 ? displayTotal - calcSubtotal : 0;\n\n return (\n <>\n {/* Subtotal */}\n <div className=\"flex justify-between\">\n <span className=\"text-text-secondary\">\n {tr('billing.checkout.subtotal', 'Subtotal')}\n </span>\n <span className=\"text-text-primary\">\n {formatDisplayCurrency(calcSubtotal, displayCurrency)}\n </span>\n </div>\n {/* Discount */}\n {checkout.discountAmount > 0 && (\n <div className=\"flex justify-between text-status-success-text\">\n <span>{tr('billing.checkout.discount', 'Discount')}</span>\n <span>\n -{formatDisplayCurrency(checkout.discountAmount, displayCurrency)}\n </span>\n </div>\n )}\n {/* Tax */}\n {calcTax > 0 && (\n <div className=\"flex justify-between\">\n <span className=\"text-text-secondary\">\n {checkout.taxType || tr('billing.checkout.tax', 'Tax')} (\n {(taxRate * 100).toFixed(0)}%)\n </span>\n <span className=\"text-text-primary\">\n {formatDisplayCurrency(calcTax, displayCurrency)}\n </span>\n </div>\n )}\n </>\n );\n })()}\n {/* Divider */}\n <div className=\"border-t border-border-subtle my-2\" />\n {/* Total */}\n <div className=\"flex justify-between font-semibold\">\n <span className=\"text-text-primary\">{tr('billing.checkout.total', 'Total')}</span>\n <span className=\"text-lg text-text-primary\">\n {formatDisplayCurrency(displayTotal, displayCurrency)}\n </span>\n </div>\n </div>\n </div>\n\n {checkout.selectedProvider === 'STRIPE' &&\n paymentResponse.clientSecret &&\n (() => {\n // NOTE: Saved cards for Stripe are temporarily disabled on the backend\n // NOTE: Card saving disabled — re-enable when backend supports setup_future_usage on PaymentIntents\n // const stripeCards = savedPaymentMethods.filter(pm => pm.stripePaymentMethodId && pm.isVerified);\n\n return (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface overflow-hidden\">\n {/* Stripe Elements form for new card */}\n <div className=\"p-4 space-y-4\">\n {/* Save card checkbox - disabled until backend supports it */}\n {/* <label className=\"flex items-center gap-2 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={checkout.saveCard}\n onChange={(e) => checkout.setSaveCard(e.target.checked)}\n className=\"size-4 rounded border-border-subtle text-text-link focus:ring-[var(--color-focus-ring)]\"\n />\n <span className=\"text-sm text-text-secondary\">\n Save card for future purchases\n </span>\n </label> */}\n\n <StripePaymentForm\n clientSecret={paymentResponse.clientSecret}\n publishableKey={\n stripePublishableKey || providerConfig?.stripePublishableKey || ''\n }\n amount={Math.round((paymentResponse.total ?? checkout.total) * 100)}\n currency={paymentResponse.currency || checkout.selectedCurrency}\n onSuccess={handlePaymentSuccess}\n onError={handlePaymentError}\n onCancel={() => setMode('select')}\n isCompletingExistingPayment={!!checkout.selectedPaymentMethodId}\n />\n </div>\n </div>\n );\n })()}\n\n {checkout.selectedProvider === 'RAZORPAY' && paymentResponse.orderId && (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface overflow-hidden\">\n {/* Razorpay Payment Section */}\n <div className=\"p-4 space-y-4\">\n {/* Save card checkbox for new card payments */}\n <label className=\"flex items-center gap-2 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={checkout.saveCard}\n onChange={(e) => checkout.setSaveCard(e.target.checked)}\n className=\"size-4 rounded border-border-subtle text-text-link focus:ring-[var(--color-focus-ring)]\"\n />\n <span className=\"text-sm text-text-secondary\">Save card for future purchases</span>\n </label>\n\n <RazorpayPaymentButton\n orderId={paymentResponse.orderId}\n keyId={paymentResponse.razorpayKeyId || razorpayKey || ''}\n amount={Math.round((paymentResponse.total ?? checkout.total) * 100)}\n currency={paymentResponse.currency || 'INR'}\n customerId={paymentResponse.razorpayCustomerId}\n prefill={{\n name: paymentResponse.prefillName,\n email: paymentResponse.prefillEmail,\n contact: paymentResponse.prefillContact,\n }}\n onSuccess={(response) => handlePaymentSuccess(response.razorpay_payment_id)}\n onError={handlePaymentError}\n onCancel={() => setMode('select')}\n />\n </div>\n </div>\n )}\n </div>\n );\n }\n\n // Main checkout flow\n return (\n <div className=\"max-w-6xl mx-auto p-4 sm:p-6\">\n {/* Back button */}\n <button\n type=\"button\"\n onClick={() => navigateTo(-1)}\n className=\"flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary mb-4\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n\n {/* Header */}\n <div className=\"flex items-center gap-3 mb-6\">\n <div className=\"p-2 rounded-lg bg-[var(--color-accent-soft)] text-text-link\">\n {getPageIcon()}\n </div>\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">{getPageTitle()}</h1>\n <p className=\"text-sm text-text-secondary\">Complete your purchase securely</p>\n </div>\n </div>\n\n <PagePurpose className=\"mb-6\">\n {tr(\n 'billing.checkout.purpose',\n 'Review what you are buying — plan, add-ons or credits — pick the billing account and payment method, apply any discount code, and pay. Taxes and the final total are shown before you confirm.'\n )}\n </PagePurpose>\n\n {/* Upgrade Banner */}\n {locationState?.upgradeFromSubscriptionId && locationState?.currentPlanName && (\n <div className=\"mb-6 p-4 rounded-lg border border-border-subtle bg-status-info-bg-subtle\">\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 rounded-full bg-status-info-bg-subtle\">\n <RefreshCw className=\"size-4 text-accent-blue\" />\n </div>\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-status-info-text\">\n Upgrading from:{' '}\n <span className=\"font-semibold\">{locationState.currentPlanName}</span>\n </p>\n <p className=\"text-xs text-status-info-text mt-0.5\">\n Your current subscription will be marked as upgraded and quotas will transfer to the\n new plan.\n </p>\n </div>\n </div>\n </div>\n )}\n\n {/* Downgrade Banner */}\n {locationState?.downgradeFromSubscriptionId && locationState?.currentPlanName && (\n <div className=\"mb-6 p-4 rounded-lg border border-status-warning-border bg-status-warning-bg-subtle\">\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 rounded-full bg-status-warning-bg-subtle\">\n <RefreshCw className=\"size-4 text-status-warning-text\" />\n </div>\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-status-warning-text\">\n Downgrading from:{' '}\n <span className=\"font-semibold\">{locationState.currentPlanName}</span>\n </p>\n <p className=\"text-xs text-status-warning-text mt-0.5\">\n Your current plan stays active until it expires, you keep all its features until\n then. After expiry, only the new plan applies.\n </p>\n </div>\n </div>\n </div>\n )}\n\n {/* Main content - responsive grid. On mobile the order summary appears first\n so users see the total before filling in payment details. */}\n <div className=\"grid gap-6 lg:grid-cols-5\">\n {/* Left column - Configuration (renders second on mobile) */}\n <div className=\"lg:col-span-3 space-y-6 order-2 lg:order-1\">\n {/* Credits amount (for credits purchase) */}\n {purchaseType === 'CREDITS' && checkout.items[0] && (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 sm:p-6\">\n <CreditsAmountInput\n value={checkout.items[0].quantity}\n onChange={handleCreditsAmountChange}\n costPerCredit={costPerCredit}\n currency={checkout.selectedCurrency}\n disabled={creditCostLoading}\n />\n </div>\n )}\n\n {/* Billing Account Selection */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 sm:p-6 space-y-4\">\n <BillingAccountSelector\n accounts={billingAccounts}\n selectedAccountId={checkout.billingAccount?.id ?? null}\n onSelect={checkout.setBillingAccount}\n isLoading={accountsLoading}\n disabled={isBillingAccountLocked}\n disabledMessage={\n isBillingAccountLocked ? 'Billing account is locked for plan changes' : undefined\n }\n />\n\n {/* Subscription Selection - inline for addon purchases (only shows if multiple subs or no subs) */}\n {/* Skip subscription selection for store checkout - store items don't require an active subscription */}\n {purchaseType === 'ADDON' && !isStoreCheckout && checkout.billingAccount && (\n <SubscriptionSelector\n subscriptions={activeSubscriptions}\n selectedSubscriptionId={checkout.selectedSubscriptionId}\n onSelect={checkout.setSelectedSubscriptionId}\n isLoading={subscriptionsLoading}\n />\n )}\n </div>\n\n {/* Shipping — physical store orders only. Non-physical flows render\n nothing new here. */}\n {isPhysicalStoreOrder && (\n <StoreShippingSection\n storeOrderId={storeOrderId!}\n currency={checkout.selectedCurrency}\n onChange={setShipping}\n />\n )}\n\n {/* Unified Payment Method Selection */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 sm:p-6 space-y-4\">\n {/* canPayWithCredits below gates whether credit payment applies to\n this purchase at all (e.g. not already buying credits) —\n whether the balance is *sufficient* is the child's own\n concern: it already computes hasEnoughCredits internally and\n shows a disabled option with an \"Insufficient credits\"\n message rather than hiding the option outright. */}\n <UnifiedPaymentMethodSelector\n savedPaymentMethods={savedPaymentMethods}\n selectedPaymentMethodId={checkout.selectedPaymentMethodId}\n onSelectSavedCard={checkout.setSelectedPaymentMethodId}\n selectedProvider={checkout.selectedProvider}\n onSelectProvider={checkout.setPaymentProvider}\n stripeEnabled={stripeEnabled}\n razorpayEnabled={razorpayEnabled}\n canPayWithCredits={checkout.canPayWithCredits}\n availableCredits={checkout.availableCredits}\n creditsRequired={checkout.creditsRequired}\n billingCountry={billingCountry}\n saveCard={checkout.saveCard}\n onSaveCardChange={checkout.setSaveCard}\n autoRenewEnabled={purchaseType === 'PLAN' && checkout.autoRenew}\n isLoading={paymentMethodsLoading}\n />\n\n {/* Currency Info - Locked to Billing Account */}\n {checkout.selectedProvider !== 'CREDITS' && checkout.selectedProvider !== 'AUTO' && (\n <>\n <div className=\"border-t border-border-subtle\" />\n <div className=\"bg-bg-sunken p-4 rounded-button\">\n <div className=\"flex items-start gap-3\">\n <svg\n className=\"size-5 text-text-secondary mt-0.5\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n <div className=\"flex-1 text-sm\">\n <p className=\"font-medium text-text-primary mb-1\">\n Currency: {checkout.selectedCurrency}\n </p>\n <p className=\"text-text-secondary text-xs\">\n All prices are shown in{' '}\n <strong className=\"text-text-primary\">{checkout.selectedCurrency}</strong>{' '}\n based on your billing account settings.\n {checkout.selectedCurrency === 'INR' && ' GST will be applied at checkout.'}\n </p>\n </div>\n </div>\n </div>\n </>\n )}\n\n {/* Seat count selector — only for PER_SEAT plans */}\n {purchaseType === 'PLAN' && planData?.getPlan?.pricingModel === 'PER_SEAT' && (\n <div className=\"border-t border-border-subtle pt-4\">\n <div className=\"space-y-3\">\n <div>\n <label className=\"text-sm font-medium text-text-primary\">Number of seats</label>\n {planData.getPlan.minSeats && (\n <p className=\"text-xs text-text-secondary mt-0.5\">\n Minimum {planData.getPlan.minSeats} seat\n {planData.getPlan.minSeats !== 1 ? 's' : ''} required\n </p>\n )}\n </div>\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() =>\n setSeatCount((n) => Math.max(planData.getPlan?.minSeats ?? 1, n - 1))\n }\n disabled={seatCount <= (planData.getPlan.minSeats ?? 1)}\n className=\"size-9 rounded-button border border-border-subtle flex items-center justify-center hover:bg-action-ghost-bgHover disabled:opacity-40 disabled:cursor-not-allowed transition-colors text-text-primary\"\n >\n −\n </button>\n <input\n type=\"number\"\n value={seatCount}\n min={planData.getPlan.minSeats ?? 1}\n max={planData.getPlan.maxSeats ?? 999}\n onChange={(e) => {\n const v = parseInt(e.target.value, 10);\n if (!isNaN(v)) setSeatCount(Math.max(planData.getPlan?.minSeats ?? 1, v));\n }}\n className=\"w-20 h-9 text-center border border-border-subtle rounded-button bg-bg-surface text-text-primary text-sm focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n <button\n type=\"button\"\n onClick={() =>\n setSeatCount((n) => Math.min(planData.getPlan?.maxSeats ?? 999, n + 1))\n }\n disabled={seatCount >= (planData.getPlan.maxSeats ?? 999)}\n className=\"size-9 rounded-button border border-border-subtle flex items-center justify-center hover:bg-action-ghost-bgHover disabled:opacity-40 disabled:cursor-not-allowed transition-colors text-text-primary\"\n >\n +\n </button>\n {planData.getPlan.pricePerSeat && (\n <span className=\"text-sm text-text-secondary\">\n ×{' '}\n {planData.getPlan.pricePerSeat.toLocaleString('en', {\n style: 'currency',\n currency: planData.getPlan.currency ?? 'USD',\n })}{' '}\n ={' '}\n {(planData.getPlan.pricePerSeat * seatCount).toLocaleString('en', {\n style: 'currency',\n currency: planData.getPlan.currency ?? 'USD',\n })}\n /mo\n </span>\n )}\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n\n {/* Right column - Order Summary (sticky on desktop, shown first on mobile) */}\n <div className=\"lg:col-span-2 order-1 lg:order-2\">\n <div className=\"lg:sticky lg:top-6 space-y-4\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface\">\n <div className=\"p-4\">\n <OrderSummary\n items={checkout.items}\n subtotal={checkout.subtotal}\n discountAmount={checkout.discountAmount}\n taxAmount={checkout.taxAmount}\n taxRate={checkout.taxRate}\n taxType={checkout.taxType}\n total={checkout.total}\n currency={checkout.selectedCurrency}\n isCalculatingTax={checkout.isCalculatingTax}\n showInCredits={checkout.selectedProvider === 'CREDITS'}\n onUpdateQuantity={\n purchaseType !== 'PLAN' && purchaseType !== 'ORDER'\n ? checkout.updateItemQuantity\n : undefined\n }\n onRemoveItem={checkout.removeItem}\n isEditable={purchaseType !== 'PLAN' && purchaseType !== 'ORDER'}\n />\n </div>\n\n {/* Promo Code Input — not supported for credits purchases yet (BOFF-7192) */}\n {checkout.selectedProvider !== 'CREDITS' && (\n <div className=\"px-4 pb-4 border-t border-border-subtle pt-4\">\n <PromoCodeInput\n value={checkout.promoCode}\n onChange={checkout.setPromoCode}\n onValidate={async () => {\n const result = await checkout.validatePromoCode();\n return result;\n }}\n onClear={checkout.clearPromoCode}\n isApplied={checkout.promoDiscount > 0}\n discountValue={checkout.promoDiscount}\n discountType={checkout.promoDiscountType}\n isValidating={checkout.isProcessing}\n />\n </div>\n )}\n\n {/* Simple Auto-Pay Toggle - only for PLAN purchases */}\n {purchaseType === 'PLAN' &&\n checkout.selectedProvider !== 'CREDITS' &&\n checkout.selectedProvider !== 'AUTO' &&\n (() => {\n // Determine payment method display\n let paymentMethodDisplay = '';\n let hasPaymentMethod = false;\n let isNewCard = false;\n\n if (checkout.selectedPaymentMethodId) {\n // Saved card selected\n const selectedCard = savedPaymentMethods.find(\n (pm) => pm.id === checkout.selectedPaymentMethodId\n );\n if (selectedCard) {\n const type =\n selectedCard.type === 'CREDIT_CARD'\n ? 'Card'\n : selectedCard.type === 'DEBIT_CARD'\n ? 'Card'\n : selectedCard.type;\n const last4 = selectedCard.last4 ? ` •••• ${selectedCard.last4}` : '';\n paymentMethodDisplay = `${type}${last4}`;\n hasPaymentMethod = true;\n isNewCard = false;\n }\n } else if (\n checkout.selectedProvider === 'RAZORPAY' ||\n checkout.selectedProvider === 'STRIPE'\n ) {\n // New card selected\n paymentMethodDisplay =\n checkout.selectedProvider === 'RAZORPAY'\n ? 'new card (Razorpay)'\n : 'new card (Stripe)';\n hasPaymentMethod = true;\n isNewCard = true;\n }\n\n return (\n <div className=\"px-4 pb-4\">\n <SimpleAutoPayToggle\n enabled={checkout.autoRenew}\n onToggle={(enabled) => {\n checkout.setAutoRenew(enabled);\n // When enabling auto-pay with saved card, also set it as auto-renew method\n if (enabled && checkout.selectedPaymentMethodId) {\n checkout.setAutoRenewPaymentMethodId(checkout.selectedPaymentMethodId);\n }\n }}\n hasPaymentMethodSelected={hasPaymentMethod}\n selectedPaymentMethodDisplay={paymentMethodDisplay}\n isNewCard={isNewCard}\n />\n </div>\n );\n })()}\n </div>\n\n {/* Checkout Button */}\n <button\n type=\"button\"\n onClick={handleProceedToPayment}\n disabled={\n checkout.isProcessing ||\n !checkout.billingAccount ||\n checkout.items.length === 0 ||\n // Insufficient credits (BOFF-7192): the auto-select effect\n // avoids landing on CREDITS when short, but a stale/manual\n // selection must not be payable either.\n (checkout.selectedProvider === 'CREDITS' && !hasEnoughCredits) ||\n // For addon purchases: require active subscription to be selected (skip for store checkout)\n (purchaseType === 'ADDON' && !isStoreCheckout && subscriptionsLoading) ||\n (purchaseType === 'ADDON' &&\n !isStoreCheckout &&\n activeSubscriptions.length === 0) ||\n (purchaseType === 'ADDON' &&\n !isStoreCheckout &&\n !checkout.selectedSubscriptionId) ||\n // Physical store orders: require a delivery address + method\n // (with a resolved estimate) before payment.\n (isPhysicalStoreOrder &&\n (!shipping.shippingAddressId || !shipping.shippingMethodId))\n }\n className=\"w-full px-6 py-4 rounded-lg bg-action-primary-bg text-action-primary-text font-semibold\n hover:bg-action-primary-bgHover disabled:opacity-50 disabled:cursor-not-allowed\n transition-colors flex items-center justify-center gap-2\"\n >\n {checkout.isProcessing ? (\n <>\n <Loader2 className=\"size-5 animate-spin\" />\n Processing…\n </>\n ) : checkout.selectedProvider === 'CREDITS' ? (\n 'Pay with Credits'\n ) : (\n 'Proceed to Payment'\n )}\n </button>\n\n {/* Security badge */}\n <p className=\"text-center text-xs text-text-secondary\">\n 🔒 Secure checkout with 256-bit SSL encryption\n </p>\n </div>\n </div>\n </div>\n\n {/* Billing account completion gate */}\n {showCompleteAccountModal && checkout.billingAccount && (\n <CompleteBillingAccountModal\n billingAccountId={checkout.billingAccount.id}\n hasPhone={Boolean(checkout.billingAccount.contactNumber?.trim())}\n hasAddress={Boolean(\n checkout.billingAccount.billingAddresses?.[0]?.line1?.trim() &&\n checkout.billingAccount.billingAddresses?.[0]?.postalCode?.trim()\n )}\n onCompleted={() => {\n // Set bypass BEFORE closing modal so the re-triggered payment skips the stale check\n bypassBillingGate.current = true;\n setShowCompleteAccountModal(false);\n void handleProceedToPayment();\n // Refetch accounts in background so future gate checks use fresh contactNumber/address\n void refetchAccounts().then((result: { data?: GetBillingAccountsCheckoutQuery }) => {\n const freshAccount = (result.data?.myBillingAccounts ?? [])\n .map(normalizeCheckoutBillingAccount)\n .find((account) => account?.id === checkout.billingAccount?.id);\n if (freshAccount) checkout.setBillingAccount(freshAccount);\n });\n }}\n onCancel={() => setShowCompleteAccountModal(false)}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|