@blocklet/payment-react 1.25.9 → 1.26.0

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.
Files changed (160) hide show
  1. package/es/checkout-v2/checkout-v2.d.ts +2 -0
  2. package/es/checkout-v2/checkout-v2.js +121 -0
  3. package/es/checkout-v2/components/dialogs/checkout-dialogs.d.ts +1 -0
  4. package/es/checkout-v2/components/dialogs/checkout-dialogs.js +106 -0
  5. package/es/checkout-v2/components/left/billing-toggle.d.ts +6 -0
  6. package/es/checkout-v2/components/left/billing-toggle.js +118 -0
  7. package/es/checkout-v2/components/left/cross-sell-card.d.ts +10 -0
  8. package/es/checkout-v2/components/left/cross-sell-card.js +167 -0
  9. package/es/checkout-v2/components/left/product-item-card.d.ts +26 -0
  10. package/es/checkout-v2/components/left/product-item-card.js +571 -0
  11. package/es/checkout-v2/components/left/promotion-input.d.ts +19 -0
  12. package/es/checkout-v2/components/left/promotion-input.js +178 -0
  13. package/es/checkout-v2/components/left/staking-breakdown.d.ts +9 -0
  14. package/es/checkout-v2/components/left/staking-breakdown.js +48 -0
  15. package/es/checkout-v2/components/left/trial-info.d.ts +13 -0
  16. package/es/checkout-v2/components/left/trial-info.js +48 -0
  17. package/es/checkout-v2/components/right/currency-grid.d.ts +8 -0
  18. package/es/checkout-v2/components/right/currency-grid.js +48 -0
  19. package/es/checkout-v2/components/right/customer-info-card.d.ts +17 -0
  20. package/es/checkout-v2/components/right/customer-info-card.js +156 -0
  21. package/es/checkout-v2/components/right/status-feedback.d.ts +7 -0
  22. package/es/checkout-v2/components/right/status-feedback.js +17 -0
  23. package/es/checkout-v2/components/right/submit-button.d.ts +10 -0
  24. package/es/checkout-v2/components/right/submit-button.js +29 -0
  25. package/es/checkout-v2/components/right/subscription-disclaimer.d.ts +11 -0
  26. package/es/checkout-v2/components/right/subscription-disclaimer.js +8 -0
  27. package/es/checkout-v2/components/shared/exchange-rate-footer.d.ts +23 -0
  28. package/es/checkout-v2/components/shared/exchange-rate-footer.js +182 -0
  29. package/es/checkout-v2/components/shared/scenario-badge.d.ts +6 -0
  30. package/es/checkout-v2/components/shared/scenario-badge.js +47 -0
  31. package/es/checkout-v2/components/shared/total-display.d.ts +7 -0
  32. package/es/checkout-v2/components/shared/total-display.js +84 -0
  33. package/es/checkout-v2/index.d.ts +2 -0
  34. package/es/checkout-v2/index.js +1 -0
  35. package/es/checkout-v2/layouts/checkout-layout.d.ts +7 -0
  36. package/es/checkout-v2/layouts/checkout-layout.js +226 -0
  37. package/es/checkout-v2/panels/left/composite-panel.d.ts +1 -0
  38. package/es/checkout-v2/panels/left/composite-panel.js +423 -0
  39. package/es/checkout-v2/panels/left/credit-topup-panel.d.ts +1 -0
  40. package/es/checkout-v2/panels/left/credit-topup-panel.js +615 -0
  41. package/es/checkout-v2/panels/left/scenario-router.d.ts +1 -0
  42. package/es/checkout-v2/panels/left/scenario-router.js +19 -0
  43. package/es/checkout-v2/panels/right/payment-panel.d.ts +1 -0
  44. package/es/checkout-v2/panels/right/payment-panel.js +644 -0
  45. package/es/checkout-v2/types.d.ts +15 -0
  46. package/es/checkout-v2/types.js +0 -0
  47. package/es/checkout-v2/utils/format.d.ts +59 -0
  48. package/es/checkout-v2/utils/format.js +125 -0
  49. package/es/checkout-v2/utils/scenario-detector.d.ts +3 -0
  50. package/es/checkout-v2/utils/scenario-detector.js +17 -0
  51. package/es/checkout-v2/views/error-view.d.ts +7 -0
  52. package/es/checkout-v2/views/error-view.js +269 -0
  53. package/es/checkout-v2/views/loading-view.d.ts +5 -0
  54. package/es/checkout-v2/views/loading-view.js +158 -0
  55. package/es/checkout-v2/views/success-view.d.ts +29 -0
  56. package/es/checkout-v2/views/success-view.js +614 -0
  57. package/es/components/phone-field.d.ts +14 -0
  58. package/es/components/phone-field.js +96 -0
  59. package/es/index.d.ts +3 -1
  60. package/es/index.js +3 -1
  61. package/es/locales/en.js +45 -6
  62. package/es/locales/zh.js +45 -6
  63. package/es/payment/form/index.js +10 -1
  64. package/lib/checkout-v2/checkout-v2.d.ts +2 -0
  65. package/lib/checkout-v2/checkout-v2.js +151 -0
  66. package/lib/checkout-v2/components/dialogs/checkout-dialogs.d.ts +1 -0
  67. package/lib/checkout-v2/components/dialogs/checkout-dialogs.js +131 -0
  68. package/lib/checkout-v2/components/left/billing-toggle.d.ts +6 -0
  69. package/lib/checkout-v2/components/left/billing-toggle.js +126 -0
  70. package/lib/checkout-v2/components/left/cross-sell-card.d.ts +10 -0
  71. package/lib/checkout-v2/components/left/cross-sell-card.js +257 -0
  72. package/lib/checkout-v2/components/left/product-item-card.d.ts +26 -0
  73. package/lib/checkout-v2/components/left/product-item-card.js +738 -0
  74. package/lib/checkout-v2/components/left/promotion-input.d.ts +19 -0
  75. package/lib/checkout-v2/components/left/promotion-input.js +220 -0
  76. package/lib/checkout-v2/components/left/staking-breakdown.d.ts +9 -0
  77. package/lib/checkout-v2/components/left/staking-breakdown.js +96 -0
  78. package/lib/checkout-v2/components/left/trial-info.d.ts +13 -0
  79. package/lib/checkout-v2/components/left/trial-info.js +82 -0
  80. package/lib/checkout-v2/components/right/currency-grid.d.ts +8 -0
  81. package/lib/checkout-v2/components/right/currency-grid.js +96 -0
  82. package/lib/checkout-v2/components/right/customer-info-card.d.ts +17 -0
  83. package/lib/checkout-v2/components/right/customer-info-card.js +246 -0
  84. package/lib/checkout-v2/components/right/status-feedback.d.ts +7 -0
  85. package/lib/checkout-v2/components/right/status-feedback.js +30 -0
  86. package/lib/checkout-v2/components/right/submit-button.d.ts +10 -0
  87. package/lib/checkout-v2/components/right/submit-button.js +35 -0
  88. package/lib/checkout-v2/components/right/subscription-disclaimer.d.ts +11 -0
  89. package/lib/checkout-v2/components/right/subscription-disclaimer.js +33 -0
  90. package/lib/checkout-v2/components/shared/exchange-rate-footer.d.ts +23 -0
  91. package/lib/checkout-v2/components/shared/exchange-rate-footer.js +282 -0
  92. package/lib/checkout-v2/components/shared/scenario-badge.d.ts +6 -0
  93. package/lib/checkout-v2/components/shared/scenario-badge.js +57 -0
  94. package/lib/checkout-v2/components/shared/total-display.d.ts +7 -0
  95. package/lib/checkout-v2/components/shared/total-display.js +154 -0
  96. package/lib/checkout-v2/index.d.ts +2 -0
  97. package/lib/checkout-v2/index.js +13 -0
  98. package/lib/checkout-v2/layouts/checkout-layout.d.ts +7 -0
  99. package/lib/checkout-v2/layouts/checkout-layout.js +308 -0
  100. package/lib/checkout-v2/panels/left/composite-panel.d.ts +1 -0
  101. package/lib/checkout-v2/panels/left/composite-panel.js +515 -0
  102. package/lib/checkout-v2/panels/left/credit-topup-panel.d.ts +1 -0
  103. package/lib/checkout-v2/panels/left/credit-topup-panel.js +799 -0
  104. package/lib/checkout-v2/panels/left/scenario-router.d.ts +1 -0
  105. package/lib/checkout-v2/panels/left/scenario-router.js +29 -0
  106. package/lib/checkout-v2/panels/right/payment-panel.d.ts +1 -0
  107. package/lib/checkout-v2/panels/right/payment-panel.js +906 -0
  108. package/lib/checkout-v2/types.d.ts +15 -0
  109. package/lib/checkout-v2/types.js +1 -0
  110. package/lib/checkout-v2/utils/format.d.ts +59 -0
  111. package/lib/checkout-v2/utils/format.js +158 -0
  112. package/lib/checkout-v2/utils/scenario-detector.d.ts +3 -0
  113. package/lib/checkout-v2/utils/scenario-detector.js +23 -0
  114. package/lib/checkout-v2/views/error-view.d.ts +7 -0
  115. package/lib/checkout-v2/views/error-view.js +321 -0
  116. package/lib/checkout-v2/views/loading-view.d.ts +5 -0
  117. package/lib/checkout-v2/views/loading-view.js +168 -0
  118. package/lib/checkout-v2/views/success-view.d.ts +29 -0
  119. package/lib/checkout-v2/views/success-view.js +735 -0
  120. package/lib/components/phone-field.d.ts +14 -0
  121. package/lib/components/phone-field.js +130 -0
  122. package/lib/index.d.ts +3 -1
  123. package/lib/index.js +8 -0
  124. package/lib/locales/en.js +45 -6
  125. package/lib/locales/zh.js +45 -6
  126. package/lib/payment/form/index.js +10 -1
  127. package/package.json +10 -9
  128. package/src/checkout-v2/checkout-v2.tsx +155 -0
  129. package/src/checkout-v2/components/dialogs/checkout-dialogs.tsx +134 -0
  130. package/src/checkout-v2/components/left/billing-toggle.tsx +122 -0
  131. package/src/checkout-v2/components/left/cross-sell-card.tsx +170 -0
  132. package/src/checkout-v2/components/left/product-item-card.tsx +634 -0
  133. package/src/checkout-v2/components/left/promotion-input.tsx +207 -0
  134. package/src/checkout-v2/components/left/staking-breakdown.tsx +57 -0
  135. package/src/checkout-v2/components/left/trial-info.tsx +63 -0
  136. package/src/checkout-v2/components/right/currency-grid.tsx +59 -0
  137. package/src/checkout-v2/components/right/customer-info-card.tsx +214 -0
  138. package/src/checkout-v2/components/right/status-feedback.tsx +35 -0
  139. package/src/checkout-v2/components/right/submit-button.tsx +37 -0
  140. package/src/checkout-v2/components/right/subscription-disclaimer.tsx +27 -0
  141. package/src/checkout-v2/components/shared/exchange-rate-footer.tsx +221 -0
  142. package/src/checkout-v2/components/shared/scenario-badge.tsx +51 -0
  143. package/src/checkout-v2/components/shared/total-display.tsx +112 -0
  144. package/src/checkout-v2/index.ts +2 -0
  145. package/src/checkout-v2/layouts/checkout-layout.tsx +232 -0
  146. package/src/checkout-v2/panels/left/composite-panel.tsx +465 -0
  147. package/src/checkout-v2/panels/left/credit-topup-panel.tsx +681 -0
  148. package/src/checkout-v2/panels/left/scenario-router.tsx +22 -0
  149. package/src/checkout-v2/panels/right/payment-panel.tsx +703 -0
  150. package/src/checkout-v2/types.ts +18 -0
  151. package/src/checkout-v2/utils/format.ts +204 -0
  152. package/src/checkout-v2/utils/scenario-detector.ts +30 -0
  153. package/src/checkout-v2/views/error-view.tsx +293 -0
  154. package/src/checkout-v2/views/loading-view.tsx +162 -0
  155. package/src/checkout-v2/views/success-view.tsx +770 -0
  156. package/src/components/phone-field.tsx +119 -0
  157. package/src/index.ts +3 -0
  158. package/src/locales/en.tsx +45 -4
  159. package/src/locales/zh.tsx +43 -4
  160. package/src/payment/form/index.tsx +16 -1
@@ -0,0 +1,799 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ module.exports = CreditTopupPanel;
7
+ var _jsxRuntime = require("react/jsx-runtime");
8
+ var _react = require("react");
9
+ var _material = require("@mui/material");
10
+ var _InfoOutlined = _interopRequireDefault(require("@mui/icons-material/InfoOutlined"));
11
+ var _AccessTimeOutlined = _interopRequireDefault(require("@mui/icons-material/AccessTimeOutlined"));
12
+ var _Inventory2Outlined = _interopRequireDefault(require("@mui/icons-material/Inventory2Outlined"));
13
+ var _ahooks = require("ahooks");
14
+ var _util = require("@ocap/util");
15
+ var _context = require("@arcblock/ux/lib/Locale/context");
16
+ var _WarningAmber = _interopRequireDefault(require("@mui/icons-material/WarningAmber"));
17
+ var _paymentReactHeadless = require("@blocklet/payment-react-headless");
18
+ var _payment = require("../../../contexts/payment");
19
+ var _api = _interopRequireDefault(require("../../../libs/api"));
20
+ var _util2 = require("../../../libs/util");
21
+ var _format = require("../../utils/format");
22
+ var _scenarioBadge = _interopRequireDefault(require("../../components/shared/scenario-badge"));
23
+ var _exchangeRateFooter = _interopRequireDefault(require("../../components/shared/exchange-rate-footer"));
24
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
25
+ function CreditTopupPanel() {
26
+ const {
27
+ t,
28
+ locale
29
+ } = (0, _context.useLocaleContext)();
30
+ const {
31
+ session,
32
+ sessionData
33
+ } = (0, _paymentReactHeadless.useSessionContext)();
34
+ const {
35
+ livemode
36
+ } = (0, _paymentReactHeadless.useCheckoutStatus)();
37
+ const {
38
+ product
39
+ } = (0, _paymentReactHeadless.useProduct)();
40
+ const {
41
+ currency,
42
+ isStripe
43
+ } = (0, _paymentReactHeadless.usePaymentMethodContext)();
44
+ const lineItems = (0, _paymentReactHeadless.useLineItems)();
45
+ const rate = (0, _paymentReactHeadless.useExchangeRate)();
46
+ const slippage = (0, _paymentReactHeadless.useSlippage)();
47
+ const {
48
+ locked: configLocked
49
+ } = (0, _paymentReactHeadless.useSubmitFeature)();
50
+ const item = lineItems.items[0];
51
+ const activePrice = item ? item.upsell_price || item.price : null;
52
+ const creditConfig = activePrice?.metadata?.credit_config;
53
+ const creditAmount = creditConfig?.credit_amount ? Number(creditConfig.credit_amount) : 0;
54
+ const creditCurrencyId = creditConfig?.currency_id;
55
+ const {
56
+ session: didSession,
57
+ getCurrency
58
+ } = (0, _payment.usePaymentContext)();
59
+ const creditCurrency = creditCurrencyId ? getCurrency(creditCurrencyId) : null;
60
+ const userDid = didSession?.user?.did || sessionData?.customer?.did;
61
+ const creditCurrencyDecimal = creditCurrency?.decimal;
62
+ const creditCurrencySymbol = creditCurrency?.symbol || "Credits";
63
+ const currencySymbol = "Credits";
64
+ const meterId = activePrice?.metadata?.meter_id;
65
+ const {
66
+ data: meterData
67
+ } = (0, _ahooks.useRequest)(async () => {
68
+ if (!meterId) return null;
69
+ try {
70
+ const {
71
+ data
72
+ } = await _api.default.get(`/api/meters/public/${meterId}`);
73
+ return data;
74
+ } catch {
75
+ return null;
76
+ }
77
+ }, {
78
+ refreshDeps: [meterId]
79
+ });
80
+ const creditName = meterData?.name || product?.name || "Credits";
81
+ const validDuration = creditConfig?.valid_duration_value;
82
+ const validDurationUnit = creditConfig?.valid_duration_unit || "days";
83
+ const scheduleConfig = creditConfig?.schedule;
84
+ const hasSchedule = scheduleConfig?.enabled && scheduleConfig?.delivery_mode && scheduleConfig.delivery_mode !== "invoice";
85
+ const hasExpiry = validDuration && validDuration > 0;
86
+ const step = Math.max(creditAmount, 1);
87
+ const adjustableQty = item?.adjustable_quantity;
88
+ const canAdjust = adjustableQty?.enabled !== false;
89
+ const minQuantity = Math.max(adjustableQty?.minimum || 1, 1);
90
+ const quantityAvailable = Math.min(activePrice?.quantity_limit_per_checkout ?? Infinity, activePrice?.quantity_available ?? Infinity);
91
+ const maxQuantity = quantityAvailable ? Math.min(adjustableQty?.maximum || Infinity, quantityAvailable) : adjustableQty?.maximum || Infinity;
92
+ const {
93
+ data: pendingAmount
94
+ } = (0, _ahooks.useRequest)(async () => {
95
+ if (!creditConfig || !userDid || !creditCurrencyId || creditCurrencyDecimal == null) return null;
96
+ try {
97
+ const {
98
+ data
99
+ } = await _api.default.get("/api/meter-events/pending-amount", {
100
+ params: {
101
+ customer_id: userDid,
102
+ currency_id: creditCurrencyId
103
+ }
104
+ });
105
+ return data?.[creditCurrencyId];
106
+ } catch {
107
+ return null;
108
+ }
109
+ }, {
110
+ refreshDeps: [creditConfig, userDid, creditCurrencyId, creditCurrencyDecimal]
111
+ });
112
+ const minQtyForPending = (0, _react.useMemo)(() => {
113
+ if (!pendingAmount || !creditAmount || creditAmount <= 0) return null;
114
+ const pendingBN = new _util.BN(pendingAmount || "0");
115
+ if (!pendingBN.gt(new _util.BN(0))) return null;
116
+ const creditBN = (0, _util.fromTokenToUnit)(creditAmount, creditCurrencyDecimal);
117
+ if (!creditBN || creditBN.isZero()) return null;
118
+ return Math.ceil(pendingBN.mul(new _util.BN(100)).div(creditBN).toNumber() / 100);
119
+ }, [pendingAmount, creditAmount, creditCurrencyDecimal]);
120
+ const hasPendingAmount = pendingAmount && new _util.BN(pendingAmount || "0").gt(new _util.BN(0));
121
+ const pendingDisplayAmount = (0, _react.useMemo)(() => {
122
+ if (!hasPendingAmount) return "";
123
+ return (0, _util2.formatCreditForCheckout)((0, _util2.formatBNStr)(pendingAmount, creditCurrencyDecimal), creditCurrencySymbol, locale);
124
+ }, [hasPendingAmount, pendingAmount, creditCurrencyDecimal, creditCurrencySymbol, locale]);
125
+ const minCreditsForPending = minQtyForPending ? minQtyForPending * step : 0;
126
+ const minCreditsForPendingFormatted = minCreditsForPending ? (0, _util2.formatCreditForCheckout)((0, _util2.formatNumber)(minCreditsForPending), creditCurrencySymbol, locale) : "";
127
+ const creditInfoText = (0, _react.useMemo)(() => {
128
+ if (hasSchedule && scheduleConfig) {
129
+ const intervalUnit = scheduleConfig.interval_unit;
130
+ const intervalValue = scheduleConfig.interval_value;
131
+ let amountPerGrant;
132
+ if (scheduleConfig.amount_per_grant) {
133
+ amountPerGrant = Number(scheduleConfig.amount_per_grant);
134
+ } else {
135
+ amountPerGrant = creditAmount;
136
+ }
137
+ const formattedAmount = (0, _util2.formatCreditAmount)((0, _util2.formatNumber)(amountPerGrant), currencySymbol);
138
+ const intervalDisplay = intervalValue === 1 ? t(`common.${intervalUnit}`) : `${intervalValue} ${t(`common.${intervalUnit}s`)}`;
139
+ return scheduleConfig.expire_with_next_grant ? t("payment.checkout.credit.schedule.withRefresh", {
140
+ amount: formattedAmount,
141
+ interval: intervalDisplay
142
+ }) : t("payment.checkout.credit.schedule.periodic", {
143
+ amount: formattedAmount,
144
+ interval: intervalDisplay
145
+ });
146
+ }
147
+ const productDesc = product?.description || "";
148
+ if (productDesc && productDesc.length > 10 && productDesc !== creditName) {
149
+ return productDesc;
150
+ }
151
+ return "";
152
+ }, [creditAmount, currencySymbol, hasSchedule, scheduleConfig, creditName, product, t]);
153
+ const validityText = (0, _react.useMemo)(() => {
154
+ if (!hasExpiry) return "";
155
+ return t("payment.checkout.creditTopup.validFor", {
156
+ duration: validDuration,
157
+ unit: t(`common.${validDurationUnit}`)
158
+ });
159
+ }, [hasExpiry, validDuration, validDurationUnit, t]);
160
+ const hasSubtitle = !!creditInfoText;
161
+ const currentQty = item?.quantity || 1;
162
+ const [localQty, setLocalQty] = (0, _react.useState)(currentQty);
163
+ const [desiredCredits, setDesiredCredits] = (0, _react.useState)(currentQty * step);
164
+ const [isEditing, setIsEditing] = (0, _react.useState)(false);
165
+ const [editValue, setEditValue] = (0, _react.useState)("");
166
+ const inputRef = (0, _react.useRef)(null);
167
+ (0, _react.useEffect)(() => {
168
+ if (configLocked || session?.status === "complete") return;
169
+ if (item?.quantity && item.quantity !== localQty) {
170
+ setLocalQty(item.quantity);
171
+ setDesiredCredits(item.quantity * step);
172
+ }
173
+ }, [item?.quantity]);
174
+ const pendingEnforcedRef = (0, _react.useRef)(false);
175
+ (0, _react.useEffect)(() => {
176
+ if (configLocked || session?.status === "complete") return;
177
+ if (pendingEnforcedRef.current) return;
178
+ if (minQtyForPending && minQtyForPending > localQty) {
179
+ pendingEnforcedRef.current = true;
180
+ const newQty = Math.min(Math.max(minQtyForPending, minQuantity), maxQuantity);
181
+ setLocalQty(newQty);
182
+ setDesiredCredits(newQty * step);
183
+ if (item) lineItems.updateQuantity(item.price_id, newQty);
184
+ }
185
+ }, [minQtyForPending]);
186
+ const maxCredits = maxQuantity * step;
187
+ const minCredits = minQuantity * step;
188
+ const commitCredits = (0, _react.useCallback)(credits => {
189
+ if (credits <= 0) return;
190
+ const clamped = Math.max(minCredits, Math.min(maxCredits, credits));
191
+ const packs = Math.ceil(clamped / step);
192
+ const clampedPacks = Math.max(minQuantity, Math.min(maxQuantity, packs));
193
+ setLocalQty(clampedPacks);
194
+ setDesiredCredits(clamped);
195
+ if (item) lineItems.updateQuantity(item.price_id, clampedPacks);
196
+ }, [step, minQuantity, maxQuantity, minCredits, maxCredits, item, lineItems]);
197
+ const handleStep = (0, _react.useCallback)(delta => {
198
+ const newCredits = desiredCredits + delta * step;
199
+ if (newCredits < step * minQuantity || Math.ceil(newCredits / step) > maxQuantity) return;
200
+ commitCredits(newCredits);
201
+ }, [desiredCredits, step, minQuantity, maxQuantity, commitCredits]);
202
+ const actualPacks = Math.max(minQuantity, Math.min(maxQuantity, Math.ceil(desiredCredits / step)));
203
+ const actualCredits = actualPacks * step;
204
+ const actualCreditsFormatted = (0, _util2.formatNumber)(actualCredits, 6, true, true);
205
+ const desiredCreditsFormatted = (0, _util2.formatNumber)(desiredCredits, 6, true, true);
206
+ const showReceiveSection = canAdjust && step > 1;
207
+ const pendingMessage = (0, _react.useMemo)(() => {
208
+ if (!hasPendingAmount) return "";
209
+ if (actualCredits >= minCreditsForPending) {
210
+ return t("payment.checkout.creditTopup.pendingEnough", {
211
+ pendingAmount: pendingDisplayAmount,
212
+ availableAmount: (0, _util2.formatCreditForCheckout)((0, _util2.formatNumber)(actualCredits - minCreditsForPending), creditCurrencySymbol, locale)
213
+ });
214
+ }
215
+ return t("payment.checkout.creditTopup.pendingWarning", {
216
+ pendingAmount: pendingDisplayAmount,
217
+ minCredits: minCreditsForPendingFormatted
218
+ });
219
+ }, [hasPendingAmount, actualCredits, minCreditsForPending, pendingDisplayAmount, creditCurrencySymbol, locale, minCreditsForPendingFormatted, t]);
220
+ const startEditing = (0, _react.useCallback)(() => {
221
+ if (!canAdjust) return;
222
+ setEditValue(String(desiredCredits));
223
+ setIsEditing(true);
224
+ setTimeout(() => inputRef.current?.select(), 0);
225
+ }, [canAdjust, desiredCredits]);
226
+ const commitEdit = (0, _react.useCallback)(() => {
227
+ setIsEditing(false);
228
+ const parsed = parseInt(editValue.replace(/,/g, ""), 10);
229
+ if (!Number.isFinite(parsed) || parsed <= 0) return;
230
+ commitCredits(parsed);
231
+ }, [editValue, commitCredits]);
232
+ const handleEditKeyDown = (0, _react.useCallback)(e => {
233
+ if (e.key === "Enter") commitEdit();else if (e.key === "Escape") setIsEditing(false);
234
+ }, [commitEdit]);
235
+ const numberHeight = {
236
+ xs: 48,
237
+ md: 72
238
+ };
239
+ const numberFontSx = {
240
+ fontSize: numberHeight,
241
+ fontWeight: 800,
242
+ lineHeight: 1,
243
+ letterSpacing: "-0.03em"
244
+ };
245
+ const circleBtnSx = {
246
+ width: {
247
+ xs: 40,
248
+ md: 56
249
+ },
250
+ height: {
251
+ xs: 40,
252
+ md: 56
253
+ },
254
+ borderRadius: "50%",
255
+ bgcolor: "background.paper",
256
+ border: "1px solid",
257
+ borderColor: theme => theme.palette.mode === "dark" ? "rgba(255,255,255,0.12)" : "divider",
258
+ color: "text.secondary",
259
+ transition: "all 0.2s ease",
260
+ "&:hover": {
261
+ borderColor: "primary.main",
262
+ color: "primary.main",
263
+ bgcolor: "background.paper"
264
+ },
265
+ "&.Mui-disabled": {
266
+ borderColor: theme => theme.palette.mode === "dark" ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.06)",
267
+ color: theme => theme.palette.mode === "dark" ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.12)"
268
+ }
269
+ };
270
+ return /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
271
+ sx: {
272
+ display: "flex",
273
+ flexDirection: "column",
274
+ height: "100%"
275
+ },
276
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
277
+ sx: {
278
+ flex: 1,
279
+ display: "flex",
280
+ flexDirection: "column",
281
+ justifyContent: "center"
282
+ },
283
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_scenarioBadge.default, {
284
+ livemode,
285
+ label: (0, _format.tSafe)(t, "payment.checkout.typeBadge.topup", "TOP-UP")
286
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
287
+ sx: {
288
+ fontWeight: 800,
289
+ fontSize: {
290
+ xs: 28,
291
+ md: 48
292
+ },
293
+ lineHeight: 1.1,
294
+ letterSpacing: "-0.03em",
295
+ color: "text.primary",
296
+ mb: {
297
+ xs: 0.5,
298
+ md: 1
299
+ }
300
+ },
301
+ children: t("payment.checkout.creditTopup.title", {
302
+ name: creditName
303
+ })
304
+ }), hasSubtitle && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
305
+ sx: {
306
+ display: "flex",
307
+ alignItems: "flex-start",
308
+ gap: 1,
309
+ mb: {
310
+ xs: 1.5,
311
+ md: 2.5
312
+ }
313
+ },
314
+ children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
315
+ sx: {
316
+ fontSize: {
317
+ xs: 13,
318
+ md: 15
319
+ },
320
+ fontWeight: 500,
321
+ color: "text.secondary",
322
+ lineHeight: 1.5
323
+ },
324
+ children: creditInfoText
325
+ })
326
+ }), hasPendingAmount && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
327
+ direction: "row",
328
+ alignItems: "flex-start",
329
+ spacing: 1.5,
330
+ sx: {
331
+ mb: {
332
+ xs: 1.5,
333
+ md: 2.5
334
+ },
335
+ p: {
336
+ xs: 1.5,
337
+ md: 2
338
+ },
339
+ borderRadius: "12px",
340
+ bgcolor: theme => theme.palette.mode === "dark" ? "rgba(255,152,0,0.12)" : "rgba(255,152,0,0.08)",
341
+ border: "1px solid",
342
+ borderColor: theme => theme.palette.mode === "dark" ? "rgba(255,152,0,0.25)" : "rgba(255,152,0,0.2)"
343
+ },
344
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_WarningAmber.default, {
345
+ sx: {
346
+ fontSize: 18,
347
+ color: "warning.main",
348
+ mt: 0.25,
349
+ flexShrink: 0
350
+ }
351
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
352
+ sx: {
353
+ fontSize: {
354
+ xs: 12,
355
+ md: 13
356
+ },
357
+ fontWeight: 600,
358
+ lineHeight: 1.5,
359
+ color: "text.primary"
360
+ },
361
+ children: pendingMessage
362
+ })]
363
+ }), canAdjust ? /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
364
+ sx: {
365
+ display: "flex",
366
+ flexDirection: "column",
367
+ maxWidth: 480
368
+ },
369
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
370
+ sx: {
371
+ fontSize: {
372
+ xs: 14,
373
+ md: 17
374
+ },
375
+ fontWeight: 700,
376
+ color: "text.secondary",
377
+ mt: {
378
+ xs: 3,
379
+ md: 5
380
+ },
381
+ mb: {
382
+ xs: 2,
383
+ md: 3
384
+ }
385
+ },
386
+ children: t("payment.checkout.creditTopup.question", {
387
+ symbol: currencySymbol
388
+ })
389
+ }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
390
+ sx: {
391
+ display: "flex",
392
+ alignItems: "center",
393
+ gap: {
394
+ xs: 1.5,
395
+ md: 2
396
+ }
397
+ },
398
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.IconButton, {
399
+ onClick: () => handleStep(-1),
400
+ disabled: actualPacks <= minQuantity,
401
+ sx: circleBtnSx,
402
+ children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
403
+ component: "span",
404
+ sx: {
405
+ fontSize: {
406
+ xs: 22,
407
+ md: 28
408
+ },
409
+ fontWeight: 300,
410
+ lineHeight: 1,
411
+ mt: "-1px"
412
+ },
413
+ children: "\u2212"
414
+ })
415
+ }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
416
+ sx: {
417
+ flex: 1,
418
+ minWidth: 0,
419
+ textAlign: "center",
420
+ cursor: canAdjust ? "text" : "default"
421
+ },
422
+ onClick: startEditing,
423
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
424
+ sx: {
425
+ height: numberHeight,
426
+ display: "flex",
427
+ alignItems: "center",
428
+ justifyContent: "center"
429
+ },
430
+ children: isEditing ? /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.InputBase, {
431
+ inputRef,
432
+ value: editValue,
433
+ onChange: e => setEditValue(e.target.value.replace(/\D/g, "")),
434
+ onBlur: commitEdit,
435
+ onKeyDown: handleEditKeyDown,
436
+ autoFocus: true,
437
+ inputProps: {
438
+ inputMode: "numeric",
439
+ pattern: "[0-9]*"
440
+ },
441
+ sx: {
442
+ width: "100%",
443
+ bgcolor: "transparent",
444
+ "&.Mui-focused": {
445
+ bgcolor: "transparent"
446
+ },
447
+ "& input": {
448
+ textAlign: "center",
449
+ ...numberFontSx,
450
+ p: 0,
451
+ fontFamily: "inherit",
452
+ bgcolor: "transparent",
453
+ "&:focus": {
454
+ bgcolor: "transparent"
455
+ }
456
+ }
457
+ }
458
+ }) : /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
459
+ sx: {
460
+ ...numberFontSx,
461
+ color: "text.primary",
462
+ userSelect: "none"
463
+ },
464
+ children: desiredCreditsFormatted
465
+ })
466
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
467
+ sx: {
468
+ display: "flex",
469
+ alignItems: "center",
470
+ justifyContent: "center",
471
+ gap: 0.75,
472
+ mt: 1
473
+ },
474
+ children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
475
+ sx: {
476
+ fontSize: {
477
+ xs: 12,
478
+ md: 14
479
+ },
480
+ fontWeight: 700,
481
+ color: "text.secondary",
482
+ textTransform: "uppercase",
483
+ letterSpacing: "0.05em"
484
+ },
485
+ children: currencySymbol
486
+ })
487
+ })]
488
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.IconButton, {
489
+ onClick: () => handleStep(1),
490
+ disabled: actualPacks >= maxQuantity,
491
+ sx: circleBtnSx,
492
+ children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
493
+ component: "span",
494
+ sx: {
495
+ fontSize: {
496
+ xs: 22,
497
+ md: 28
498
+ },
499
+ fontWeight: 300,
500
+ lineHeight: 1
501
+ },
502
+ children: "+"
503
+ })
504
+ })]
505
+ }), step > 1 && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
506
+ sx: {
507
+ fontSize: 11,
508
+ fontWeight: 600,
509
+ color: "grey.500",
510
+ textTransform: "uppercase",
511
+ letterSpacing: "0.08em",
512
+ textAlign: "center",
513
+ mt: {
514
+ xs: 1.5,
515
+ md: 2
516
+ }
517
+ },
518
+ children: t("payment.checkout.creditTopup.increment", {
519
+ step: (0, _util2.formatNumber)(step, 6, true, true),
520
+ symbol: currencySymbol
521
+ })
522
+ }), showReceiveSection && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
523
+ sx: {
524
+ display: "flex",
525
+ flexDirection: "column",
526
+ mt: {
527
+ xs: 3,
528
+ md: 4
529
+ },
530
+ borderRadius: "20px",
531
+ backdropFilter: "blur(12px)",
532
+ bgcolor: theme => theme.palette.mode === "dark" ? "rgba(59,130,246,0.08)" : "rgba(59,130,246,0.04)",
533
+ border: "1px solid",
534
+ borderColor: theme => theme.palette.mode === "dark" ? "rgba(59,130,246,0.18)" : "rgba(59,130,246,0.10)",
535
+ boxShadow: theme => theme.palette.mode === "dark" ? "0 8px 32px rgba(59,130,246,0.06)" : "0 8px 32px rgba(59,130,246,0.04)"
536
+ },
537
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
538
+ sx: {
539
+ p: {
540
+ xs: 2.5,
541
+ md: 3.5
542
+ }
543
+ },
544
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, {
545
+ sx: {
546
+ fontSize: {
547
+ xs: 16,
548
+ md: 20
549
+ },
550
+ fontWeight: 700,
551
+ color: "primary.main",
552
+ mb: 0.5
553
+ },
554
+ children: [t("payment.checkout.creditTopup.willReceive"), ": ", /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
555
+ component: "span",
556
+ sx: {
557
+ fontWeight: 800
558
+ },
559
+ children: (0, _util2.formatCreditForCheckout)(actualCreditsFormatted, currencySymbol, locale)
560
+ })]
561
+ }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
562
+ sx: {
563
+ display: "flex",
564
+ alignItems: "center",
565
+ gap: 0.75,
566
+ mb: 0.5
567
+ },
568
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_Inventory2Outlined.default, {
569
+ sx: {
570
+ fontSize: 16,
571
+ color: "text.secondary"
572
+ }
573
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
574
+ sx: {
575
+ fontSize: {
576
+ xs: 12,
577
+ md: 14
578
+ },
579
+ fontWeight: 600,
580
+ color: "text.secondary"
581
+ },
582
+ children: t("payment.checkout.creditTopup.packInfo", {
583
+ packs: actualPacks,
584
+ perPack: (0, _util2.formatNumber)(step, 6, true, true)
585
+ })
586
+ })]
587
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
588
+ sx: {
589
+ fontSize: {
590
+ xs: 12,
591
+ md: 13
592
+ },
593
+ fontWeight: 500,
594
+ color: "grey.500"
595
+ },
596
+ children: t("payment.checkout.creditTopup.autoMatch")
597
+ })]
598
+ }), validityText && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
599
+ sx: {
600
+ display: "flex",
601
+ alignItems: "center",
602
+ gap: 0.75,
603
+ px: {
604
+ xs: 2.5,
605
+ md: 3.5
606
+ },
607
+ py: {
608
+ xs: 1.5,
609
+ md: 2
610
+ },
611
+ borderTop: "1px solid",
612
+ borderColor: theme => theme.palette.mode === "dark" ? "rgba(59,130,246,0.15)" : "rgba(59,130,246,0.08)"
613
+ },
614
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_AccessTimeOutlined.default, {
615
+ sx: {
616
+ fontSize: 14,
617
+ color: "grey.500"
618
+ }
619
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
620
+ sx: {
621
+ fontSize: {
622
+ xs: 12,
623
+ md: 13
624
+ },
625
+ fontWeight: 500,
626
+ color: "grey.500"
627
+ },
628
+ children: validityText
629
+ })]
630
+ })]
631
+ }), !showReceiveSection && validityText && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
632
+ sx: {
633
+ display: "flex",
634
+ alignItems: "center",
635
+ gap: 0.75,
636
+ mt: {
637
+ xs: 2.5,
638
+ md: 3.5
639
+ }
640
+ },
641
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_InfoOutlined.default, {
642
+ sx: {
643
+ fontSize: 14,
644
+ color: "grey.500"
645
+ }
646
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
647
+ sx: {
648
+ fontSize: {
649
+ xs: 12,
650
+ md: 13
651
+ },
652
+ fontWeight: 500,
653
+ color: "grey.500"
654
+ },
655
+ children: validityText
656
+ })]
657
+ })]
658
+ }) :
659
+ // ── Non-adjustable: show fixed credit amount in a styled card ──
660
+ /* @__PURE__ */
661
+ (0, _jsxRuntime.jsxs)(_material.Box, {
662
+ sx: {
663
+ display: "flex",
664
+ flexDirection: "column",
665
+ mt: {
666
+ xs: 2,
667
+ md: 3
668
+ },
669
+ borderRadius: "20px",
670
+ backdropFilter: "blur(12px)",
671
+ bgcolor: theme => theme.palette.mode === "dark" ? "rgba(59,130,246,0.08)" : "rgba(59,130,246,0.04)",
672
+ border: "1px solid",
673
+ borderColor: theme => theme.palette.mode === "dark" ? "rgba(59,130,246,0.18)" : "rgba(59,130,246,0.10)",
674
+ boxShadow: theme => theme.palette.mode === "dark" ? "0 8px 32px rgba(59,130,246,0.06)" : "0 8px 32px rgba(59,130,246,0.04)"
675
+ },
676
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
677
+ sx: {
678
+ p: {
679
+ xs: 2.5,
680
+ md: 3.5
681
+ }
682
+ },
683
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
684
+ sx: {
685
+ fontSize: {
686
+ xs: 13,
687
+ md: 14
688
+ },
689
+ fontWeight: 600,
690
+ color: "text.secondary",
691
+ mb: 1
692
+ },
693
+ children: t("payment.checkout.creditTopup.willReceive")
694
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
695
+ sx: {
696
+ display: "flex",
697
+ alignItems: "center",
698
+ gap: 1.5
699
+ },
700
+ children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
701
+ sx: {
702
+ fontSize: {
703
+ xs: 32,
704
+ md: 48
705
+ },
706
+ fontWeight: 800,
707
+ lineHeight: 1,
708
+ letterSpacing: "-0.03em",
709
+ color: "primary.main"
710
+ },
711
+ children: (0, _util2.formatCreditForCheckout)(actualCreditsFormatted, currencySymbol, locale)
712
+ })
713
+ })]
714
+ }), validityText && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
715
+ sx: {
716
+ display: "flex",
717
+ alignItems: "center",
718
+ gap: 0.75,
719
+ px: {
720
+ xs: 2.5,
721
+ md: 3.5
722
+ },
723
+ py: {
724
+ xs: 1.5,
725
+ md: 2
726
+ },
727
+ borderTop: "1px solid",
728
+ borderColor: theme => theme.palette.mode === "dark" ? "rgba(59,130,246,0.15)" : "rgba(59,130,246,0.08)"
729
+ },
730
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_AccessTimeOutlined.default, {
731
+ sx: {
732
+ fontSize: 14,
733
+ color: "grey.500"
734
+ }
735
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
736
+ sx: {
737
+ fontSize: {
738
+ xs: 12,
739
+ md: 13
740
+ },
741
+ fontWeight: 500,
742
+ color: "grey.500"
743
+ },
744
+ children: validityText
745
+ })]
746
+ })]
747
+ })]
748
+ }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
749
+ sx: {
750
+ flexShrink: 0
751
+ },
752
+ children: [rate.hasDynamicPricing && rate.status === "unavailable" && !isStripe && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
753
+ direction: "row",
754
+ alignItems: "center",
755
+ spacing: 0.75,
756
+ sx: {
757
+ mb: 2
758
+ },
759
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
760
+ sx: {
761
+ fontSize: 13,
762
+ color: "text.secondary",
763
+ fontWeight: 500
764
+ },
765
+ children: t("payment.dynamicPricing.unavailable.title")
766
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
767
+ component: "span",
768
+ onClick: rate.refresh,
769
+ sx: {
770
+ fontSize: 13,
771
+ color: "primary.main",
772
+ fontWeight: 600,
773
+ cursor: "pointer",
774
+ "&:hover": {
775
+ textDecoration: "underline"
776
+ }
777
+ },
778
+ children: t("payment.dynamicPricing.unavailable.retry")
779
+ })]
780
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_exchangeRateFooter.default, {
781
+ hasDynamicPricing: rate.hasDynamicPricing,
782
+ rate: {
783
+ value: rate.value,
784
+ display: rate.display,
785
+ provider: rate.provider,
786
+ providerDisplay: rate.providerDisplay,
787
+ fetchedAt: rate.fetchedAt,
788
+ status: rate.status
789
+ },
790
+ slippage: {
791
+ percent: slippage.percent,
792
+ set: slippage.set
793
+ },
794
+ currencySymbol: currency?.symbol || "",
795
+ isSubscription: ["subscription", "setup"].includes(session?.mode || "payment")
796
+ })]
797
+ })]
798
+ });
799
+ }