@blocklet/payment-react 1.25.10 → 1.26.1

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 +611 -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 +795 -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 +4 -3
  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 +642 -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 +677 -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 +205 -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,795 @@
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
+ return "";
148
+ }, [creditAmount, currencySymbol, hasSchedule, scheduleConfig, t]);
149
+ const validityText = (0, _react.useMemo)(() => {
150
+ if (!hasExpiry) return "";
151
+ return t("payment.checkout.creditTopup.validFor", {
152
+ duration: validDuration,
153
+ unit: t(`common.${validDurationUnit}`)
154
+ });
155
+ }, [hasExpiry, validDuration, validDurationUnit, t]);
156
+ const hasSubtitle = !!creditInfoText;
157
+ const currentQty = item?.quantity || 1;
158
+ const [localQty, setLocalQty] = (0, _react.useState)(currentQty);
159
+ const [desiredCredits, setDesiredCredits] = (0, _react.useState)(currentQty * step);
160
+ const [isEditing, setIsEditing] = (0, _react.useState)(false);
161
+ const [editValue, setEditValue] = (0, _react.useState)("");
162
+ const inputRef = (0, _react.useRef)(null);
163
+ (0, _react.useEffect)(() => {
164
+ if (configLocked || session?.status === "complete") return;
165
+ if (item?.quantity && item.quantity !== localQty) {
166
+ setLocalQty(item.quantity);
167
+ setDesiredCredits(item.quantity * step);
168
+ }
169
+ }, [item?.quantity]);
170
+ const pendingEnforcedRef = (0, _react.useRef)(false);
171
+ (0, _react.useEffect)(() => {
172
+ if (configLocked || session?.status === "complete") return;
173
+ if (pendingEnforcedRef.current) return;
174
+ if (minQtyForPending && minQtyForPending > localQty) {
175
+ pendingEnforcedRef.current = true;
176
+ const newQty = Math.min(Math.max(minQtyForPending, minQuantity), maxQuantity);
177
+ setLocalQty(newQty);
178
+ setDesiredCredits(newQty * step);
179
+ if (item) lineItems.updateQuantity(item.price_id, newQty);
180
+ }
181
+ }, [minQtyForPending]);
182
+ const maxCredits = maxQuantity * step;
183
+ const minCredits = minQuantity * step;
184
+ const commitCredits = (0, _react.useCallback)(credits => {
185
+ if (credits <= 0) return;
186
+ const clamped = Math.max(minCredits, Math.min(maxCredits, credits));
187
+ const packs = Math.ceil(clamped / step);
188
+ const clampedPacks = Math.max(minQuantity, Math.min(maxQuantity, packs));
189
+ setLocalQty(clampedPacks);
190
+ setDesiredCredits(clamped);
191
+ if (item) lineItems.updateQuantity(item.price_id, clampedPacks);
192
+ }, [step, minQuantity, maxQuantity, minCredits, maxCredits, item, lineItems]);
193
+ const handleStep = (0, _react.useCallback)(delta => {
194
+ const newCredits = desiredCredits + delta * step;
195
+ if (newCredits < step * minQuantity || Math.ceil(newCredits / step) > maxQuantity) return;
196
+ commitCredits(newCredits);
197
+ }, [desiredCredits, step, minQuantity, maxQuantity, commitCredits]);
198
+ const actualPacks = Math.max(minQuantity, Math.min(maxQuantity, Math.ceil(desiredCredits / step)));
199
+ const actualCredits = actualPacks * step;
200
+ const actualCreditsFormatted = (0, _util2.formatNumber)(actualCredits, 6, true, true);
201
+ const desiredCreditsFormatted = (0, _util2.formatNumber)(desiredCredits, 6, true, true);
202
+ const showReceiveSection = canAdjust && step > 1;
203
+ const pendingMessage = (0, _react.useMemo)(() => {
204
+ if (!hasPendingAmount) return "";
205
+ if (actualCredits >= minCreditsForPending) {
206
+ return t("payment.checkout.creditTopup.pendingEnough", {
207
+ pendingAmount: pendingDisplayAmount,
208
+ availableAmount: (0, _util2.formatCreditForCheckout)((0, _util2.formatNumber)(actualCredits - minCreditsForPending), creditCurrencySymbol, locale)
209
+ });
210
+ }
211
+ return t("payment.checkout.creditTopup.pendingWarning", {
212
+ pendingAmount: pendingDisplayAmount,
213
+ minCredits: minCreditsForPendingFormatted
214
+ });
215
+ }, [hasPendingAmount, actualCredits, minCreditsForPending, pendingDisplayAmount, creditCurrencySymbol, locale, minCreditsForPendingFormatted, t]);
216
+ const startEditing = (0, _react.useCallback)(() => {
217
+ if (!canAdjust) return;
218
+ setEditValue(String(desiredCredits));
219
+ setIsEditing(true);
220
+ setTimeout(() => inputRef.current?.select(), 0);
221
+ }, [canAdjust, desiredCredits]);
222
+ const commitEdit = (0, _react.useCallback)(() => {
223
+ setIsEditing(false);
224
+ const parsed = parseInt(editValue.replace(/,/g, ""), 10);
225
+ if (!Number.isFinite(parsed) || parsed <= 0) return;
226
+ commitCredits(parsed);
227
+ }, [editValue, commitCredits]);
228
+ const handleEditKeyDown = (0, _react.useCallback)(e => {
229
+ if (e.key === "Enter") commitEdit();else if (e.key === "Escape") setIsEditing(false);
230
+ }, [commitEdit]);
231
+ const numberHeight = {
232
+ xs: 48,
233
+ md: 72
234
+ };
235
+ const numberFontSx = {
236
+ fontSize: numberHeight,
237
+ fontWeight: 800,
238
+ lineHeight: 1,
239
+ letterSpacing: "-0.03em"
240
+ };
241
+ const circleBtnSx = {
242
+ width: {
243
+ xs: 40,
244
+ md: 56
245
+ },
246
+ height: {
247
+ xs: 40,
248
+ md: 56
249
+ },
250
+ borderRadius: "50%",
251
+ bgcolor: "background.paper",
252
+ border: "1px solid",
253
+ borderColor: theme => theme.palette.mode === "dark" ? "rgba(255,255,255,0.12)" : "divider",
254
+ color: "text.secondary",
255
+ transition: "all 0.2s ease",
256
+ "&:hover": {
257
+ borderColor: "primary.main",
258
+ color: "primary.main",
259
+ bgcolor: "background.paper"
260
+ },
261
+ "&.Mui-disabled": {
262
+ borderColor: theme => theme.palette.mode === "dark" ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.06)",
263
+ color: theme => theme.palette.mode === "dark" ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.12)"
264
+ }
265
+ };
266
+ return /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
267
+ sx: {
268
+ display: "flex",
269
+ flexDirection: "column",
270
+ height: "100%"
271
+ },
272
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
273
+ sx: {
274
+ flex: 1,
275
+ display: "flex",
276
+ flexDirection: "column",
277
+ justifyContent: "center"
278
+ },
279
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_scenarioBadge.default, {
280
+ livemode,
281
+ label: (0, _format.tSafe)(t, "payment.checkout.typeBadge.topup", "TOP-UP")
282
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
283
+ sx: {
284
+ fontWeight: 800,
285
+ fontSize: {
286
+ xs: 28,
287
+ md: 48
288
+ },
289
+ lineHeight: 1.1,
290
+ letterSpacing: "-0.03em",
291
+ color: "text.primary",
292
+ mb: {
293
+ xs: 0.5,
294
+ md: 1
295
+ }
296
+ },
297
+ children: t("payment.checkout.creditTopup.title", {
298
+ name: creditName
299
+ })
300
+ }), hasSubtitle && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
301
+ sx: {
302
+ display: "flex",
303
+ alignItems: "flex-start",
304
+ gap: 1,
305
+ mb: {
306
+ xs: 1.5,
307
+ md: 2.5
308
+ }
309
+ },
310
+ children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
311
+ sx: {
312
+ fontSize: {
313
+ xs: 13,
314
+ md: 15
315
+ },
316
+ fontWeight: 500,
317
+ color: "text.secondary",
318
+ lineHeight: 1.5
319
+ },
320
+ children: creditInfoText
321
+ })
322
+ }), hasPendingAmount && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
323
+ direction: "row",
324
+ alignItems: "flex-start",
325
+ spacing: 1.5,
326
+ sx: {
327
+ mb: {
328
+ xs: 1.5,
329
+ md: 2.5
330
+ },
331
+ p: {
332
+ xs: 1.5,
333
+ md: 2
334
+ },
335
+ borderRadius: "12px",
336
+ bgcolor: theme => theme.palette.mode === "dark" ? "rgba(255,152,0,0.12)" : "rgba(255,152,0,0.08)",
337
+ border: "1px solid",
338
+ borderColor: theme => theme.palette.mode === "dark" ? "rgba(255,152,0,0.25)" : "rgba(255,152,0,0.2)"
339
+ },
340
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_WarningAmber.default, {
341
+ sx: {
342
+ fontSize: 18,
343
+ color: "warning.main",
344
+ mt: 0.25,
345
+ flexShrink: 0
346
+ }
347
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
348
+ sx: {
349
+ fontSize: {
350
+ xs: 12,
351
+ md: 13
352
+ },
353
+ fontWeight: 600,
354
+ lineHeight: 1.5,
355
+ color: "text.primary"
356
+ },
357
+ children: pendingMessage
358
+ })]
359
+ }), canAdjust ? /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
360
+ sx: {
361
+ display: "flex",
362
+ flexDirection: "column",
363
+ maxWidth: 480
364
+ },
365
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
366
+ sx: {
367
+ fontSize: {
368
+ xs: 14,
369
+ md: 17
370
+ },
371
+ fontWeight: 700,
372
+ color: "text.secondary",
373
+ mt: {
374
+ xs: 3,
375
+ md: 5
376
+ },
377
+ mb: {
378
+ xs: 2,
379
+ md: 3
380
+ }
381
+ },
382
+ children: t("payment.checkout.creditTopup.question", {
383
+ symbol: currencySymbol
384
+ })
385
+ }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
386
+ sx: {
387
+ display: "flex",
388
+ alignItems: "center",
389
+ gap: {
390
+ xs: 1.5,
391
+ md: 2
392
+ }
393
+ },
394
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.IconButton, {
395
+ onClick: () => handleStep(-1),
396
+ disabled: actualPacks <= minQuantity,
397
+ sx: circleBtnSx,
398
+ children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
399
+ component: "span",
400
+ sx: {
401
+ fontSize: {
402
+ xs: 22,
403
+ md: 28
404
+ },
405
+ fontWeight: 300,
406
+ lineHeight: 1,
407
+ mt: "-1px"
408
+ },
409
+ children: "\u2212"
410
+ })
411
+ }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
412
+ sx: {
413
+ flex: 1,
414
+ minWidth: 0,
415
+ textAlign: "center",
416
+ cursor: canAdjust ? "text" : "default"
417
+ },
418
+ onClick: startEditing,
419
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
420
+ sx: {
421
+ height: numberHeight,
422
+ display: "flex",
423
+ alignItems: "center",
424
+ justifyContent: "center"
425
+ },
426
+ children: isEditing ? /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.InputBase, {
427
+ inputRef,
428
+ value: editValue,
429
+ onChange: e => setEditValue(e.target.value.replace(/\D/g, "")),
430
+ onBlur: commitEdit,
431
+ onKeyDown: handleEditKeyDown,
432
+ autoFocus: true,
433
+ inputProps: {
434
+ inputMode: "numeric",
435
+ pattern: "[0-9]*"
436
+ },
437
+ sx: {
438
+ width: "100%",
439
+ bgcolor: "transparent",
440
+ "&.Mui-focused": {
441
+ bgcolor: "transparent"
442
+ },
443
+ "& input": {
444
+ textAlign: "center",
445
+ ...numberFontSx,
446
+ p: 0,
447
+ fontFamily: "inherit",
448
+ bgcolor: "transparent",
449
+ "&:focus": {
450
+ bgcolor: "transparent"
451
+ }
452
+ }
453
+ }
454
+ }) : /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
455
+ sx: {
456
+ ...numberFontSx,
457
+ color: "text.primary",
458
+ userSelect: "none"
459
+ },
460
+ children: desiredCreditsFormatted
461
+ })
462
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
463
+ sx: {
464
+ display: "flex",
465
+ alignItems: "center",
466
+ justifyContent: "center",
467
+ gap: 0.75,
468
+ mt: 1
469
+ },
470
+ children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
471
+ sx: {
472
+ fontSize: {
473
+ xs: 12,
474
+ md: 14
475
+ },
476
+ fontWeight: 700,
477
+ color: "text.secondary",
478
+ textTransform: "uppercase",
479
+ letterSpacing: "0.05em"
480
+ },
481
+ children: currencySymbol
482
+ })
483
+ })]
484
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.IconButton, {
485
+ onClick: () => handleStep(1),
486
+ disabled: actualPacks >= maxQuantity,
487
+ sx: circleBtnSx,
488
+ children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
489
+ component: "span",
490
+ sx: {
491
+ fontSize: {
492
+ xs: 22,
493
+ md: 28
494
+ },
495
+ fontWeight: 300,
496
+ lineHeight: 1
497
+ },
498
+ children: "+"
499
+ })
500
+ })]
501
+ }), step > 1 && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
502
+ sx: {
503
+ fontSize: 11,
504
+ fontWeight: 600,
505
+ color: "grey.500",
506
+ textTransform: "uppercase",
507
+ letterSpacing: "0.08em",
508
+ textAlign: "center",
509
+ mt: {
510
+ xs: 1.5,
511
+ md: 2
512
+ }
513
+ },
514
+ children: t("payment.checkout.creditTopup.increment", {
515
+ step: (0, _util2.formatNumber)(step, 6, true, true),
516
+ symbol: currencySymbol
517
+ })
518
+ }), showReceiveSection && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
519
+ sx: {
520
+ display: "flex",
521
+ flexDirection: "column",
522
+ mt: {
523
+ xs: 3,
524
+ md: 4
525
+ },
526
+ borderRadius: "20px",
527
+ backdropFilter: "blur(12px)",
528
+ bgcolor: theme => theme.palette.mode === "dark" ? "rgba(59,130,246,0.08)" : "rgba(59,130,246,0.04)",
529
+ border: "1px solid",
530
+ borderColor: theme => theme.palette.mode === "dark" ? "rgba(59,130,246,0.18)" : "rgba(59,130,246,0.10)",
531
+ boxShadow: theme => theme.palette.mode === "dark" ? "0 8px 32px rgba(59,130,246,0.06)" : "0 8px 32px rgba(59,130,246,0.04)"
532
+ },
533
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
534
+ sx: {
535
+ p: {
536
+ xs: 2.5,
537
+ md: 3.5
538
+ }
539
+ },
540
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, {
541
+ sx: {
542
+ fontSize: {
543
+ xs: 16,
544
+ md: 20
545
+ },
546
+ fontWeight: 700,
547
+ color: "primary.main",
548
+ mb: 0.5
549
+ },
550
+ children: [t("payment.checkout.creditTopup.willReceive"), ": ", /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
551
+ component: "span",
552
+ sx: {
553
+ fontWeight: 800
554
+ },
555
+ children: (0, _util2.formatCreditForCheckout)(actualCreditsFormatted, currencySymbol, locale)
556
+ })]
557
+ }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
558
+ sx: {
559
+ display: "flex",
560
+ alignItems: "center",
561
+ gap: 0.75,
562
+ mb: 0.5
563
+ },
564
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_Inventory2Outlined.default, {
565
+ sx: {
566
+ fontSize: 16,
567
+ color: "text.secondary"
568
+ }
569
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
570
+ sx: {
571
+ fontSize: {
572
+ xs: 12,
573
+ md: 14
574
+ },
575
+ fontWeight: 600,
576
+ color: "text.secondary"
577
+ },
578
+ children: t("payment.checkout.creditTopup.packInfo", {
579
+ packs: actualPacks,
580
+ perPack: (0, _util2.formatNumber)(step, 6, true, true)
581
+ })
582
+ })]
583
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
584
+ sx: {
585
+ fontSize: {
586
+ xs: 12,
587
+ md: 13
588
+ },
589
+ fontWeight: 500,
590
+ color: "grey.500"
591
+ },
592
+ children: t("payment.checkout.creditTopup.autoMatch")
593
+ })]
594
+ }), validityText && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
595
+ sx: {
596
+ display: "flex",
597
+ alignItems: "center",
598
+ gap: 0.75,
599
+ px: {
600
+ xs: 2.5,
601
+ md: 3.5
602
+ },
603
+ py: {
604
+ xs: 1.5,
605
+ md: 2
606
+ },
607
+ borderTop: "1px solid",
608
+ borderColor: theme => theme.palette.mode === "dark" ? "rgba(59,130,246,0.15)" : "rgba(59,130,246,0.08)"
609
+ },
610
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_AccessTimeOutlined.default, {
611
+ sx: {
612
+ fontSize: 14,
613
+ color: "grey.500"
614
+ }
615
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
616
+ sx: {
617
+ fontSize: {
618
+ xs: 12,
619
+ md: 13
620
+ },
621
+ fontWeight: 500,
622
+ color: "grey.500"
623
+ },
624
+ children: validityText
625
+ })]
626
+ })]
627
+ }), !showReceiveSection && validityText && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
628
+ sx: {
629
+ display: "flex",
630
+ alignItems: "center",
631
+ gap: 0.75,
632
+ mt: {
633
+ xs: 2.5,
634
+ md: 3.5
635
+ }
636
+ },
637
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_InfoOutlined.default, {
638
+ sx: {
639
+ fontSize: 14,
640
+ color: "grey.500"
641
+ }
642
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
643
+ sx: {
644
+ fontSize: {
645
+ xs: 12,
646
+ md: 13
647
+ },
648
+ fontWeight: 500,
649
+ color: "grey.500"
650
+ },
651
+ children: validityText
652
+ })]
653
+ })]
654
+ }) :
655
+ // ── Non-adjustable: show fixed credit amount in a styled card ──
656
+ /* @__PURE__ */
657
+ (0, _jsxRuntime.jsxs)(_material.Box, {
658
+ sx: {
659
+ display: "flex",
660
+ flexDirection: "column",
661
+ mt: {
662
+ xs: 2,
663
+ md: 3
664
+ },
665
+ borderRadius: "20px",
666
+ backdropFilter: "blur(12px)",
667
+ bgcolor: theme => theme.palette.mode === "dark" ? "rgba(59,130,246,0.08)" : "rgba(59,130,246,0.04)",
668
+ border: "1px solid",
669
+ borderColor: theme => theme.palette.mode === "dark" ? "rgba(59,130,246,0.18)" : "rgba(59,130,246,0.10)",
670
+ boxShadow: theme => theme.palette.mode === "dark" ? "0 8px 32px rgba(59,130,246,0.06)" : "0 8px 32px rgba(59,130,246,0.04)"
671
+ },
672
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
673
+ sx: {
674
+ p: {
675
+ xs: 2.5,
676
+ md: 3.5
677
+ }
678
+ },
679
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
680
+ sx: {
681
+ fontSize: {
682
+ xs: 13,
683
+ md: 14
684
+ },
685
+ fontWeight: 600,
686
+ color: "text.secondary",
687
+ mb: 1
688
+ },
689
+ children: t("payment.checkout.creditTopup.willReceive")
690
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
691
+ sx: {
692
+ display: "flex",
693
+ alignItems: "center",
694
+ gap: 1.5
695
+ },
696
+ children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
697
+ sx: {
698
+ fontSize: {
699
+ xs: 32,
700
+ md: 48
701
+ },
702
+ fontWeight: 800,
703
+ lineHeight: 1,
704
+ letterSpacing: "-0.03em",
705
+ color: "primary.main"
706
+ },
707
+ children: (0, _util2.formatCreditForCheckout)(actualCreditsFormatted, currencySymbol, locale)
708
+ })
709
+ })]
710
+ }), validityText && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
711
+ sx: {
712
+ display: "flex",
713
+ alignItems: "center",
714
+ gap: 0.75,
715
+ px: {
716
+ xs: 2.5,
717
+ md: 3.5
718
+ },
719
+ py: {
720
+ xs: 1.5,
721
+ md: 2
722
+ },
723
+ borderTop: "1px solid",
724
+ borderColor: theme => theme.palette.mode === "dark" ? "rgba(59,130,246,0.15)" : "rgba(59,130,246,0.08)"
725
+ },
726
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_AccessTimeOutlined.default, {
727
+ sx: {
728
+ fontSize: 14,
729
+ color: "grey.500"
730
+ }
731
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
732
+ sx: {
733
+ fontSize: {
734
+ xs: 12,
735
+ md: 13
736
+ },
737
+ fontWeight: 500,
738
+ color: "grey.500"
739
+ },
740
+ children: validityText
741
+ })]
742
+ })]
743
+ })]
744
+ }), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
745
+ sx: {
746
+ flexShrink: 0
747
+ },
748
+ children: [rate.hasDynamicPricing && rate.status === "unavailable" && !isStripe && /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
749
+ direction: "row",
750
+ alignItems: "center",
751
+ spacing: 0.75,
752
+ sx: {
753
+ mb: 2
754
+ },
755
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
756
+ sx: {
757
+ fontSize: 13,
758
+ color: "text.secondary",
759
+ fontWeight: 500
760
+ },
761
+ children: t("payment.dynamicPricing.unavailable.title")
762
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
763
+ component: "span",
764
+ onClick: rate.refresh,
765
+ sx: {
766
+ fontSize: 13,
767
+ color: "primary.main",
768
+ fontWeight: 600,
769
+ cursor: "pointer",
770
+ "&:hover": {
771
+ textDecoration: "underline"
772
+ }
773
+ },
774
+ children: t("payment.dynamicPricing.unavailable.retry")
775
+ })]
776
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_exchangeRateFooter.default, {
777
+ hasDynamicPricing: rate.hasDynamicPricing,
778
+ rate: {
779
+ value: rate.value,
780
+ display: rate.display,
781
+ provider: rate.provider,
782
+ providerDisplay: rate.providerDisplay,
783
+ fetchedAt: rate.fetchedAt,
784
+ status: rate.status
785
+ },
786
+ slippage: {
787
+ percent: slippage.percent,
788
+ set: slippage.set
789
+ },
790
+ currencySymbol: currency?.symbol || "",
791
+ isSubscription: ["subscription", "setup"].includes(session?.mode || "payment")
792
+ })]
793
+ })]
794
+ });
795
+ }