@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,14 @@
1
+ export interface PhoneFieldProps {
2
+ value: string;
3
+ country: string;
4
+ onChange: (phone: string) => void;
5
+ onCountryChange: (country: string) => void;
6
+ label: string;
7
+ error?: string;
8
+ onBlur?: () => void;
9
+ }
10
+ /**
11
+ * Phone input with country flag + dial code selector.
12
+ * Standalone version of V1 PhoneInput — no react-hook-form dependency.
13
+ */
14
+ export default function PhoneField({ value, country: externalCountry, onChange, onCountryChange, label, error, onBlur, }: PhoneFieldProps): import("react").JSX.Element;
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ module.exports = PhoneField;
7
+ var _jsxRuntime = require("react/jsx-runtime");
8
+ var _react = require("react");
9
+ var _material = require("@mui/material");
10
+ var _reactInternationalPhone = require("react-international-phone");
11
+ var _ahooks = require("ahooks");
12
+ var _countrySelect = _interopRequireDefault(require("./country-select"));
13
+ var _util = require("../libs/util");
14
+ var _phoneValidator = require("../libs/phone-validator");
15
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
16
+ function PhoneField({
17
+ value,
18
+ country: externalCountry,
19
+ onChange,
20
+ onCountryChange,
21
+ label,
22
+ error = void 0,
23
+ onBlur = void 0
24
+ }) {
25
+ const isUpdatingRef = (0, _react.useRef)(false);
26
+ const safeUpdate = (0, _react.useCallback)(callback => {
27
+ if (isUpdatingRef.current) return;
28
+ try {
29
+ isUpdatingRef.current = true;
30
+ callback();
31
+ } finally {
32
+ requestAnimationFrame(() => {
33
+ isUpdatingRef.current = false;
34
+ });
35
+ }
36
+ }, []);
37
+ const {
38
+ phone,
39
+ handlePhoneValueChange,
40
+ inputRef,
41
+ country,
42
+ setCountry
43
+ } = (0, _reactInternationalPhone.usePhoneInput)({
44
+ defaultCountry: (0, _util.isValidCountry)(externalCountry) ? externalCountry : "us",
45
+ value: value || "",
46
+ countries: _reactInternationalPhone.defaultCountries,
47
+ onChange: data => {
48
+ safeUpdate(() => {
49
+ onChange(data.phone);
50
+ onCountryChange(data.country);
51
+ });
52
+ }
53
+ });
54
+ (0, _ahooks.useMount)(() => {
55
+ (0, _phoneValidator.getPhoneUtil)().catch(err => {
56
+ console.error("Failed to preload phone validator:", err);
57
+ });
58
+ });
59
+ (0, _react.useEffect)(() => {
60
+ if (!externalCountry || externalCountry === country) return;
61
+ safeUpdate(() => {
62
+ setCountry(externalCountry);
63
+ });
64
+ }, [externalCountry, country, setCountry, safeUpdate]);
65
+ const handleCountryChange = (0, _react.useCallback)(v => {
66
+ safeUpdate(() => {
67
+ setCountry(v);
68
+ onCountryChange(v);
69
+ });
70
+ }, [setCountry, safeUpdate, onCountryChange]);
71
+ return /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
72
+ sx: {
73
+ mb: 1.5
74
+ },
75
+ children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
76
+ sx: {
77
+ fontSize: 13,
78
+ fontWeight: 600,
79
+ color: "text.primary",
80
+ mb: 0.5
81
+ },
82
+ children: label
83
+ }), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.InputBase, {
84
+ fullWidth: true,
85
+ value: phone,
86
+ onChange: handlePhoneValueChange,
87
+ onBlur,
88
+ type: "tel",
89
+ inputRef,
90
+ startAdornment: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.InputAdornment, {
91
+ position: "start",
92
+ sx: {
93
+ mr: 0.25,
94
+ ml: -0.5
95
+ },
96
+ children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_countrySelect.default, {
97
+ value: country,
98
+ onChange: handleCountryChange,
99
+ sx: {
100
+ ".MuiOutlinedInput-notchedOutline": {
101
+ borderColor: "transparent !important"
102
+ },
103
+ "& .MuiSelect-select": {
104
+ py: 0,
105
+ pr: "20px !important"
106
+ }
107
+ },
108
+ showDialCode: true
109
+ })
110
+ }),
111
+ sx: {
112
+ bgcolor: theme => theme.palette.mode === "dark" ? "rgba(255,255,255,0.06)" : "grey.50",
113
+ borderRadius: "8px",
114
+ px: 1.5,
115
+ py: 0.75,
116
+ fontSize: 14,
117
+ "& .MuiInputBase-input": {
118
+ p: 0
119
+ }
120
+ }
121
+ }), error && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
122
+ sx: {
123
+ fontSize: 12,
124
+ color: "error.main",
125
+ mt: 0.25
126
+ },
127
+ children: error
128
+ })]
129
+ });
130
+ }
package/lib/index.d.ts CHANGED
@@ -46,6 +46,7 @@ import PromotionCode from './components/promotion-code';
46
46
  import SourceDataViewer from './components/source-data-viewer';
47
47
  import SlippageConfig from './components/slippage-config';
48
48
  import DynamicPricingUnavailable from './components/dynamic-pricing-unavailable';
49
+ import { CheckoutV2 } from './checkout-v2';
49
50
  export { PaymentThemeProvider } from './theme';
50
51
  export * from './libs/util';
51
52
  export * from './libs/connect';
@@ -60,7 +61,8 @@ export * from './hooks/scroll';
60
61
  export * from './hooks/keyboard';
61
62
  export * from './libs/validator';
62
63
  export { translations, createTranslator } from './locales';
63
- export { createLazyComponent, api, dayjs, FormInput, FormLabel, PhoneInput, AddressForm, StripeForm, Status, Livemode, Switch, ConfirmDialog, CheckoutForm, CheckoutTable, CheckoutDonate, CurrencySelector, Payment, PaymentSummary, PricingTable, ProductSkeleton, Amount, CustomerInvoiceList, CustomerPaymentList, TxLink, TxGas, SafeGuard, PricingItem, CountrySelect, Table, TruncatedText, Link, OverdueInvoicePayment, StripePaymentAction, PaymentBeneficiaries, LoadingButton, DonateDetails, ResumeSubscription, CreditGrantsList, CreditTransactionsList, DateRangePicker, CreditStatusChip, AutoTopupModal, AutoTopup, Collapse, PromotionCode, SourceDataViewer, SlippageConfig, DynamicPricingUnavailable, PromotionSection, TotalSection, };
64
+ export { createLazyComponent, api, dayjs, FormInput, FormLabel, PhoneInput, AddressForm, StripeForm, Status, Livemode, Switch, ConfirmDialog, CheckoutForm, CheckoutTable, CheckoutDonate, CurrencySelector, Payment, PaymentSummary, PricingTable, ProductSkeleton, Amount, CustomerInvoiceList, CustomerPaymentList, TxLink, TxGas, SafeGuard, PricingItem, CountrySelect, Table, TruncatedText, Link, OverdueInvoicePayment, StripePaymentAction, PaymentBeneficiaries, LoadingButton, DonateDetails, ResumeSubscription, CreditGrantsList, CreditTransactionsList, DateRangePicker, CreditStatusChip, AutoTopupModal, AutoTopup, Collapse, PromotionCode, SourceDataViewer, SlippageConfig, DynamicPricingUnavailable, PromotionSection, TotalSection, CheckoutV2, };
64
65
  export type { CountrySelectProps } from './components/country-select';
65
66
  export type { StripePaymentActionProps } from './components/stripe-payment-action';
66
67
  export type { SlippageConfigValue, SlippageConfigProps } from './components/slippage-config';
68
+ export type { CheckoutV2Props } from './checkout-v2';
package/lib/index.js CHANGED
@@ -54,6 +54,7 @@ var _exportNames = {
54
54
  SourceDataViewer: true,
55
55
  SlippageConfig: true,
56
56
  DynamicPricingUnavailable: true,
57
+ CheckoutV2: true,
57
58
  PaymentThemeProvider: true,
58
59
  translations: true,
59
60
  createTranslator: true
@@ -100,6 +101,12 @@ Object.defineProperty(exports, "CheckoutTable", {
100
101
  return _table.default;
101
102
  }
102
103
  });
104
+ Object.defineProperty(exports, "CheckoutV2", {
105
+ enumerable: true,
106
+ get: function () {
107
+ return _checkoutV.CheckoutV2;
108
+ }
109
+ });
103
110
  Object.defineProperty(exports, "Collapse", {
104
111
  enumerable: true,
105
112
  get: function () {
@@ -424,6 +431,7 @@ var _promotionCode = _interopRequireDefault(require("./components/promotion-code
424
431
  var _sourceDataViewer = _interopRequireDefault(require("./components/source-data-viewer"));
425
432
  var _slippageConfig = _interopRequireDefault(require("./components/slippage-config"));
426
433
  var _dynamicPricingUnavailable = _interopRequireDefault(require("./components/dynamic-pricing-unavailable"));
434
+ var _checkoutV = require("./checkout-v2");
427
435
  var _theme = require("./theme");
428
436
  var _util = require("./libs/util");
429
437
  Object.keys(_util).forEach(function (key) {
package/lib/locales/en.js CHANGED
@@ -26,6 +26,7 @@ module.exports = (0, _flat.default)({
26
26
  setup: "Setup",
27
27
  amount: "Amount",
28
28
  total: "Total",
29
+ totalDue: "Total Due",
29
30
  subtotal: "Subtotal",
30
31
  status: "Status",
31
32
  livemode: "Test mode",
@@ -92,6 +93,7 @@ module.exports = (0, _flat.default)({
92
93
  viewConsumptionDetail: "Consumption Detail",
93
94
  customer: "Customer",
94
95
  currency: "Currency",
96
+ network: "Network",
95
97
  custom: "Custom",
96
98
  description: "Description",
97
99
  statementDescriptor: "Statement descriptor",
@@ -283,11 +285,14 @@ module.exports = (0, _flat.default)({
283
285
  free: "{count} {interval}{count > 1 ? 's' : ''} free",
284
286
  least: "continue with at least",
285
287
  completed: {
286
- payment: "Thanks for your purchase",
287
- subscription: "Thanks for subscribing",
288
- setup: "Thanks for subscribing",
289
- donate: "Thanks for your tip",
290
- tip: "A payment to {payee} has been completed. You can view the details of this payment in your account."
288
+ payment: "Purchase successful",
289
+ subscription: "Subscription successful",
290
+ setup: "Subscription successful",
291
+ donate: "Donation successful",
292
+ tip: "A payment to {payee} has been completed. You can view the details of this payment in your account.",
293
+ summary: {
294
+ paid: "You've paid {amount}"
295
+ }
291
296
  },
292
297
  vendor: {
293
298
  accountRequired: "This action requires a unified account. Please switch accounts and try again.",
@@ -367,7 +372,8 @@ module.exports = (0, _flat.default)({
367
372
  schedule: {
368
373
  periodic: "Grant {amount} every {interval}.",
369
374
  withRefresh: "Grant {amount} every {interval}; unused credits expire with the next grant."
370
- }
375
+ },
376
+ topupDescription: "Purchase {creditName} at {unitPrice} per credit."
371
377
  },
372
378
  expired: {
373
379
  title: "Expired Link",
@@ -381,9 +387,42 @@ module.exports = (0, _flat.default)({
381
387
  title: "Nothing to show here",
382
388
  description: "It seems this checkout session is not configured properly"
383
389
  },
390
+ error: {
391
+ title: "Something went wrong"
392
+ },
384
393
  orderSummary: "Order Summary",
394
+ orderSummarySubtitle: "Items included in this purchase",
385
395
  paymentDetails: "Payment Details",
386
396
  productListTotal: "Includes {total} items",
397
+ headerTitle: {
398
+ subscribe: "Subscribe to {name}",
399
+ purchase: "Purchase {name}"
400
+ },
401
+ planFeatures: "Plan Features",
402
+ typeBadge: {
403
+ subscription: "SUBSCRIPTION",
404
+ topup: "TOP-UP",
405
+ oneTime: "ONE-TIME"
406
+ },
407
+ subtitle: {
408
+ subscriptionInterval: "{interval} subscription",
409
+ creditsTopup: "Credits top-up",
410
+ addonFor: "Add-on for {product}",
411
+ oneTime: "One-time purchase"
412
+ },
413
+ creditTopup: {
414
+ title: "Get {name}",
415
+ question: "How many {symbol} do you want?",
416
+ credits: "Credits",
417
+ increment: "Increments of {step} {symbol}",
418
+ validFor: "Credits are valid for {duration} {unit} after purchase.",
419
+ willReceive: "You'll receive",
420
+ packInfo: "Includes {packs} credit packs ({perPack} per pack)",
421
+ autoMatch: "We'll automatically match the closest credit pack.",
422
+ autoMatchTooltip: "{symbol} are sold in packs of {step}",
423
+ pendingWarning: "You have a usage overage of {pendingAmount}. You need at least {minCredits} to restore access.",
424
+ pendingEnough: "You have a usage overage of {pendingAmount}. After covering the overage, your available balance will be {availableAmount}."
425
+ },
387
426
  promotion: {
388
427
  add_code: "Add promotion code",
389
428
  enter_code: "Enter promotion code",
package/lib/locales/zh.js CHANGED
@@ -26,6 +26,7 @@ module.exports = (0, _flat.default)({
26
26
  accessDenied: "\u60A8\u65E0\u6743\u8BBF\u95EE\u5176\u4ED6\u5BA2\u6237\u7684\u6570\u636E",
27
27
  amount: "\u91D1\u989D",
28
28
  total: "\u603B\u8BA1",
29
+ totalDue: "\u5E94\u4ED8\u603B\u989D",
29
30
  subtotal: "\u5C0F\u8BA1",
30
31
  status: "\u72B6\u6001",
31
32
  livemode: "\u6D4B\u8BD5\u6A21\u5F0F",
@@ -92,6 +93,7 @@ module.exports = (0, _flat.default)({
92
93
  viewConsumptionDetail: "\u6D88\u8D39\u8BE6\u60C5",
93
94
  customer: "\u5BA2\u6237",
94
95
  currency: "\u5E01\u79CD",
96
+ network: "\u7F51\u7EDC",
95
97
  custom: "\u81EA\u5B9A\u4E49",
96
98
  description: "\u63CF\u8FF0",
97
99
  statementDescriptor: "\u58F0\u660E\u63CF\u8FF0",
@@ -283,11 +285,14 @@ module.exports = (0, _flat.default)({
283
285
  free: "\u514D\u8D39\u8BD5\u7528 {count} {interval}",
284
286
  least: "\u81F3\u5C11",
285
287
  completed: {
286
- payment: "\u611F\u8C22\u60A8\u7684\u8D2D\u4E70",
287
- subscription: "\u611F\u8C22\u60A8\u7684\u8BA2\u9605",
288
- setup: "\u611F\u8C22\u60A8\u7684\u8BA2\u9605",
289
- donate: "\u611F\u8C22\u60A8\u7684\u652F\u6301",
290
- tip: "\u5411 {payee} \u7684\u4ED8\u6B3E\u5DF2\u5B8C\u6210\u3002\u60A8\u53EF\u4EE5\u5728\u60A8\u7684\u8D26\u6237\u4E2D\u67E5\u770B\u6B64\u4ED8\u6B3E\u7684\u8BE6\u7EC6\u4FE1\u606F\u3002"
288
+ payment: "\u8D2D\u4E70\u6210\u529F",
289
+ subscription: "\u8BA2\u9605\u6210\u529F",
290
+ setup: "\u8BA2\u9605\u6210\u529F",
291
+ donate: "\u6350\u8D60\u6210\u529F",
292
+ tip: "\u5411 {payee} \u7684\u4ED8\u6B3E\u5DF2\u5B8C\u6210\u3002\u60A8\u53EF\u4EE5\u5728\u60A8\u7684\u8D26\u6237\u4E2D\u67E5\u770B\u6B64\u4ED8\u6B3E\u7684\u8BE6\u7EC6\u4FE1\u606F\u3002",
293
+ summary: {
294
+ paid: "\u672C\u6B21\u652F\u4ED8 {amount}"
295
+ }
291
296
  },
292
297
  vendor: {
293
298
  accountRequired: "\u60A8\u5F53\u524D\u4F7F\u7528\u7684\u662F\u975E\u7EDF\u4E00\u8D26\u6237\u767B\u5F55\uFF0C\u6B64\u670D\u52A1\u9700\u8981\u60A8\u4F7F\u7528\u7EDF\u4E00\u8D26\u6237\u3002\u8BF7\u5207\u6362\u5230\u7EDF\u4E00\u8D26\u6237\u767B\u5F55\u540E\u91CD\u8BD5\u3002",
@@ -404,15 +409,49 @@ module.exports = (0, _flat.default)({
404
409
  schedule: {
405
410
  periodic: "\u6BCF{interval}\u53D1\u653E {amount}\u3002",
406
411
  withRefresh: "\u6BCF{interval}\u53D1\u653E {amount}\uFF0C\u672A\u4F7F\u7528\u989D\u5EA6\u5C06\u5728\u4E0B\u6B21\u53D1\u653E\u65F6\u8FC7\u671F\u3002"
407
- }
412
+ },
413
+ topupDescription: "\u8D2D\u4E70 {creditName}\uFF0C\u5355\u4EF7 {unitPrice}/\u989D\u5EA6\u3002"
408
414
  },
409
415
  emptyItems: {
410
416
  title: "\u6CA1\u6709\u4EFB\u4F55\u8D2D\u4E70\u9879\u76EE",
411
417
  description: "\u53EF\u80FD\u8FD9\u4E2A\u4ED8\u6B3E\u94FE\u63A5\u6CA1\u6709\u6B63\u786E\u914D\u7F6E"
412
418
  },
419
+ error: {
420
+ title: "\u51FA\u4E86\u70B9\u95EE\u9898"
421
+ },
413
422
  orderSummary: "\u8BA2\u5355\u6982\u89C8",
423
+ orderSummarySubtitle: "\u672C\u6B21\u4EA4\u6613\u5305\u542B\u4EE5\u4E0B\u9879\u76EE",
414
424
  paymentDetails: "\u652F\u4ED8\u4FE1\u606F",
415
425
  productListTotal: "\u5305\u62EC {total} \u9879",
426
+ headerTitle: {
427
+ subscribe: "\u8BA2\u9605 {name}",
428
+ purchase: "\u8D2D\u4E70 {name}"
429
+ },
430
+ planFeatures: "\u529F\u80FD\u7279\u6027",
431
+ typeBadge: {
432
+ subscription: "\u8BA2\u9605",
433
+ topup: "\u5145\u503C",
434
+ oneTime: "\u4E00\u6B21\u6027\u8D2D\u4E70"
435
+ },
436
+ subtitle: {
437
+ subscriptionInterval: "{interval}\u8BA2\u9605",
438
+ creditsTopup: "\u4F59\u989D\u5145\u503C",
439
+ addonFor: "{product} \u7684\u9644\u52A0\u9879",
440
+ oneTime: "\u4E00\u6B21\u6027\u8D2D\u4E70"
441
+ },
442
+ creditTopup: {
443
+ title: "\u8D2D\u4E70 {name}",
444
+ question: "\u4F60\u9700\u8981\u591A\u5C11 {symbol}\uFF1F",
445
+ credits: "\u989D\u5EA6",
446
+ increment: "\u6BCF\u6B21\u589E\u51CF {step} {symbol}",
447
+ validFor: "\u989D\u5EA6\u5728\u8D2D\u4E70\u540E {duration} {unit}\u5185\u6709\u6548\u3002",
448
+ willReceive: "\u4F60\u5C06\u83B7\u5F97",
449
+ packInfo: "\u5305\u542B {packs} \u4E2A\u989D\u5EA6\u5305\uFF08\u6BCF\u5305 {perPack}\uFF09",
450
+ autoMatch: "\u6211\u4EEC\u5C06\u81EA\u52A8\u5339\u914D\u6700\u63A5\u8FD1\u7684\u989D\u5EA6\u5305\u3002",
451
+ autoMatchTooltip: "{symbol} \u6309\u6BCF\u5305 {step} \u4E2A\u51FA\u552E",
452
+ pendingWarning: "\u60A8\u6709 {pendingAmount} \u7684\u4F7F\u7528\u8D85\u989D\uFF0C\u81F3\u5C11\u9700\u8981\u8D2D\u4E70 {minCredits} \u624D\u80FD\u6062\u590D\u8BBF\u95EE\u3002",
453
+ pendingEnough: "\u60A8\u6709 {pendingAmount} \u7684\u4F7F\u7528\u8D85\u989D\uFF0C\u6263\u9664\u540E\u60A8\u7684\u53EF\u7528\u4F59\u989D\u5C06\u4E3A {availableAmount}\u3002"
454
+ },
416
455
  connectModal: {
417
456
  title: "{action}",
418
457
  scan: "\u4F7F\u7528\u4EE5\u4E0B\u65B9\u5F0F\u5B8C\u6210\u672C\u6B21\u652F\u4ED8",
@@ -161,6 +161,8 @@ function PaymentForm({
161
161
  } = (0, _reactHookForm.useFormContext)();
162
162
  const errorRef = (0, _react.useRef)(null);
163
163
  const processingRef = (0, _react.useRef)(false);
164
+ const idempotencyKeyRef = (0, _react.useRef)("");
165
+ const sessionFingerprintRef = (0, _react.useRef)("");
164
166
  const quantityInventoryStatus = (0, _react.useMemo)(() => {
165
167
  let status = true;
166
168
  for (const item of checkoutSession.line_items) {
@@ -858,10 +860,17 @@ function PaymentForm({
858
860
  try {
859
861
  let result;
860
862
  const previewRate = checkoutSession.line_items?.find(item => item?.exchange_rate)?.exchange_rate || void 0;
863
+ const items = checkoutSession.line_items || [];
864
+ const itemsSig = items.map(i => `${i.upsell_price_id || i.price_id}:${i.quantity}`).join("|");
865
+ const fingerprint = `${checkoutSession.id}-${paymentCurrency?.id}-${itemsSig}`;
866
+ if (fingerprint !== sessionFingerprintRef.current || !idempotencyKeyRef.current) {
867
+ sessionFingerprintRef.current = fingerprint;
868
+ idempotencyKeyRef.current = generateIdempotencyKey(checkoutSession.id, paymentCurrency?.id || "");
869
+ }
861
870
  const payload = {
862
871
  ...data,
863
872
  // Final Freeze: Include these for new quote creation at submit
864
- idempotency_key: generateIdempotencyKey(checkoutSession.id, paymentCurrency?.id || ""),
873
+ idempotency_key: idempotencyKeyRef.current,
865
874
  preview_rate: previewRate || void 0,
866
875
  price_confirmed: state.priceChangeConfirm?.formData ? true : void 0
867
876
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/payment-react",
3
- "version": "1.25.10",
3
+ "version": "1.26.1",
4
4
  "description": "Reusable react components for payment kit v2",
5
5
  "keywords": [
6
6
  "react",
@@ -59,6 +59,7 @@
59
59
  "@arcblock/react-hooks": "^3.5.1",
60
60
  "@arcblock/ux": "^3.5.1",
61
61
  "@arcblock/ws": "^1.28.5",
62
+ "@blocklet/payment-react-headless": "1.26.1",
62
63
  "@blocklet/theme": "^3.5.1",
63
64
  "@blocklet/ui-react": "^3.5.1",
64
65
  "@mui/icons-material": "^7.1.2",
@@ -96,7 +97,7 @@
96
97
  "@babel/core": "^7.27.4",
97
98
  "@babel/preset-env": "^7.27.2",
98
99
  "@babel/preset-react": "^7.27.1",
99
- "@blocklet/payment-types": "1.25.10",
100
+ "@blocklet/payment-types": "1.26.1",
100
101
  "@storybook/addon-essentials": "^7.6.20",
101
102
  "@storybook/addon-interactions": "^7.6.20",
102
103
  "@storybook/addon-links": "^7.6.20",
@@ -127,5 +128,5 @@
127
128
  "vite-plugin-babel": "^1.3.1",
128
129
  "vite-plugin-node-polyfills": "^0.23.0"
129
130
  },
130
- "gitHead": "36417122ec9a0cfd1e289b4ca21d33f1b5d87a9a"
131
+ "gitHead": "1ba42f376f040b1214d992420cda37053fc14288"
131
132
  }
@@ -0,0 +1,155 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { useRequest } from 'ahooks';
3
+ import { joinURL } from 'ufo';
4
+ import { Box } from '@mui/material';
5
+ import { CheckoutProvider, useSessionContext, useSubmitFeature } from '@blocklet/payment-react-headless';
6
+
7
+ import api from '../libs/api';
8
+ import { getPrefix, mergeExtraParams } from '../libs/util';
9
+ import { PaymentThemeProvider } from '../theme';
10
+ import { useMobile } from '../hooks/mobile';
11
+ import type { CheckoutV2Props } from './types';
12
+ import CheckoutLayout from './layouts/checkout-layout';
13
+ import ScenarioRouter from './panels/left/scenario-router';
14
+ import PaymentPanel from './panels/right/payment-panel';
15
+ import CheckoutDialogs from './components/dialogs/checkout-dialogs';
16
+ import LoadingView from './views/loading-view';
17
+ import ErrorView from './views/error-view';
18
+ import SuccessView from './views/success-view';
19
+
20
+ // Deduplicated plink_ → cs_ resolution
21
+ const plinkPromises: Record<string, Promise<{ checkoutSession: { id: string } }>> = {};
22
+ function startFromPaymentLink(
23
+ id: string,
24
+ params?: Record<string, string>
25
+ ): Promise<{ checkoutSession: { id: string } }> {
26
+ if (!plinkPromises[id]) {
27
+ plinkPromises[id] = api
28
+ .post(`/api/checkout-sessions/start/${id}?${mergeExtraParams(params)}`)
29
+ .then((res: any) => res?.data)
30
+ .finally(() => {
31
+ setTimeout(() => {
32
+ delete plinkPromises[id];
33
+ }, 3000);
34
+ });
35
+ }
36
+ return plinkPromises[id];
37
+ }
38
+
39
+ // Inner router: must be rendered inside CheckoutProvider
40
+ function CheckoutRouter({
41
+ onPaid = undefined,
42
+ onError = undefined,
43
+ mode = 'inline',
44
+ }: {
45
+ onPaid?: (result: any) => void;
46
+ onError?: (err: Error) => void;
47
+ mode?: string;
48
+ }) {
49
+ const { isLoading, error, errorCode, session } = useSessionContext();
50
+ const submit = useSubmitFeature();
51
+ const { isMobile } = useMobile();
52
+
53
+ useEffect(() => {
54
+ if (error && onError) {
55
+ onError(new Error(error));
56
+ }
57
+ }, [error, onError]);
58
+
59
+ useEffect(() => {
60
+ if (submit.status === 'completed' && submit.result && onPaid) {
61
+ onPaid(submit.result);
62
+ }
63
+ }, [submit.status, submit.result, onPaid]);
64
+
65
+ if (isLoading) {
66
+ return <LoadingView mode={mode} />;
67
+ }
68
+
69
+ if (error) {
70
+ return <ErrorView error={error} errorCode={errorCode} mode={mode} />;
71
+ }
72
+
73
+ const isCompleted = submit.status === 'completed';
74
+ const mobileCompleted = isCompleted && isMobile;
75
+
76
+ return (
77
+ <>
78
+ <CheckoutLayout
79
+ left={
80
+ mobileCompleted ? null : (
81
+ <Box
82
+ sx={{
83
+ flex: 1,
84
+ display: 'flex',
85
+ flexDirection: 'column',
86
+ ...(isCompleted
87
+ ? { opacity: 0.45, pointerEvents: 'none', filter: 'grayscale(0.3)', transition: 'all 0.5s ease' }
88
+ : {}),
89
+ }}>
90
+ <ScenarioRouter />
91
+ </Box>
92
+ )
93
+ }
94
+ right={isCompleted ? <SuccessView submit={submit} session={session} /> : <PaymentPanel />}
95
+ mode={mode}
96
+ />
97
+ {!isCompleted && <CheckoutDialogs />}
98
+ </>
99
+ );
100
+ }
101
+
102
+ export default function CheckoutV2({
103
+ id,
104
+ onPaid,
105
+ onError,
106
+ theme = 'default',
107
+ mode = 'inline',
108
+ extraParams = {},
109
+ }: CheckoutV2Props) {
110
+ if (!id.startsWith('plink_') && !id.startsWith('cs_')) {
111
+ throw new Error('Either a checkoutSession or a paymentLink id is required.');
112
+ }
113
+
114
+ const isPaymentLink = id.startsWith('plink_');
115
+ const [resolvedSessionId, setResolvedSessionId] = useState<string | null>(isPaymentLink ? null : id);
116
+
117
+ // Resolve plink_ to cs_
118
+ useRequest(
119
+ async () => {
120
+ if (!isPaymentLink) return null;
121
+ const data = await startFromPaymentLink(id, extraParams);
122
+ const csId = data?.checkoutSession?.id;
123
+ if (csId) {
124
+ setResolvedSessionId(csId);
125
+ if (mode === 'standalone') {
126
+ window.history.replaceState(
127
+ null,
128
+ '',
129
+ joinURL(getPrefix(), `/checkout/pay/${csId}?${mergeExtraParams(extraParams)}`)
130
+ );
131
+ }
132
+ }
133
+ return data;
134
+ },
135
+ { ready: isPaymentLink }
136
+ );
137
+
138
+ if (!resolvedSessionId) {
139
+ return <LoadingView mode={mode} />;
140
+ }
141
+
142
+ const content = (
143
+ <CheckoutProvider sessionId={resolvedSessionId}>
144
+ <CheckoutRouter onPaid={onPaid} onError={onError} mode={mode} />
145
+ </CheckoutProvider>
146
+ );
147
+
148
+ if (theme === 'inherit') {
149
+ return content;
150
+ }
151
+ if (theme && typeof theme === 'object') {
152
+ return <PaymentThemeProvider theme={theme}>{content}</PaymentThemeProvider>;
153
+ }
154
+ return <PaymentThemeProvider>{content}</PaymentThemeProvider>;
155
+ }