@blocklet/payment-react-headless 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 (250) hide show
  1. package/.eslintrc.js +18 -0
  2. package/build.config.ts +30 -0
  3. package/es/checkout/context/CheckoutProvider.d.ts +6 -0
  4. package/es/checkout/context/CheckoutProvider.js +209 -0
  5. package/es/checkout/context/CustomerFormContext.d.ts +4 -0
  6. package/es/checkout/context/CustomerFormContext.js +9 -0
  7. package/es/checkout/context/ExchangeRateContext.d.ts +11 -0
  8. package/es/checkout/context/ExchangeRateContext.js +9 -0
  9. package/es/checkout/context/PaymentMethodContext.d.ts +26 -0
  10. package/es/checkout/context/PaymentMethodContext.js +9 -0
  11. package/es/checkout/context/SessionContext.d.ts +45 -0
  12. package/es/checkout/context/SessionContext.js +9 -0
  13. package/es/checkout/context/SubmitContext.d.ts +4 -0
  14. package/es/checkout/context/SubmitContext.js +9 -0
  15. package/es/checkout/context/index.d.ts +6 -0
  16. package/es/checkout/context/index.js +6 -0
  17. package/es/checkout/core/billingInterval.d.ts +15 -0
  18. package/es/checkout/core/billingInterval.js +36 -0
  19. package/es/checkout/core/crossSell.d.ts +4 -0
  20. package/es/checkout/core/crossSell.js +30 -0
  21. package/es/checkout/core/customerForm.d.ts +5 -0
  22. package/es/checkout/core/customerForm.js +105 -0
  23. package/es/checkout/core/exchangeRate.d.ts +11 -0
  24. package/es/checkout/core/exchangeRate.js +25 -0
  25. package/es/checkout/core/index.d.ts +10 -0
  26. package/es/checkout/core/index.js +55 -0
  27. package/es/checkout/core/lineItems.d.ts +7 -0
  28. package/es/checkout/core/lineItems.js +59 -0
  29. package/es/checkout/core/paymentMethod.d.ts +23 -0
  30. package/es/checkout/core/paymentMethod.js +85 -0
  31. package/es/checkout/core/pricing.d.ts +32 -0
  32. package/es/checkout/core/pricing.js +221 -0
  33. package/es/checkout/core/promotion.d.ts +10 -0
  34. package/es/checkout/core/promotion.js +39 -0
  35. package/es/checkout/core/session.d.ts +26 -0
  36. package/es/checkout/core/session.js +50 -0
  37. package/es/checkout/core/submit.d.ts +40 -0
  38. package/es/checkout/core/submit.js +66 -0
  39. package/es/checkout/hooks/index.d.ts +34 -0
  40. package/es/checkout/hooks/index.js +19 -0
  41. package/es/checkout/hooks/useBillingInterval.d.ts +14 -0
  42. package/es/checkout/hooks/useBillingInterval.js +50 -0
  43. package/es/checkout/hooks/useCheckout.d.ts +2 -0
  44. package/es/checkout/hooks/useCheckout.js +212 -0
  45. package/es/checkout/hooks/useCheckoutSession.d.ts +58 -0
  46. package/es/checkout/hooks/useCheckoutSession.js +107 -0
  47. package/es/checkout/hooks/useCheckoutStatus.d.ts +10 -0
  48. package/es/checkout/hooks/useCheckoutStatus.js +16 -0
  49. package/es/checkout/hooks/useCrossSell.d.ts +8 -0
  50. package/es/checkout/hooks/useCrossSell.js +57 -0
  51. package/es/checkout/hooks/useCustomerForm.d.ts +14 -0
  52. package/es/checkout/hooks/useCustomerForm.js +116 -0
  53. package/es/checkout/hooks/useCustomerFormFeature.d.ts +2 -0
  54. package/es/checkout/hooks/useCustomerFormFeature.js +4 -0
  55. package/es/checkout/hooks/useExchangeRate.d.ts +11 -0
  56. package/es/checkout/hooks/useExchangeRate.js +15 -0
  57. package/es/checkout/hooks/useLineItems.d.ts +22 -0
  58. package/es/checkout/hooks/useLineItems.js +139 -0
  59. package/es/checkout/hooks/usePaymentMethod.d.ts +26 -0
  60. package/es/checkout/hooks/usePaymentMethod.js +101 -0
  61. package/es/checkout/hooks/usePaymentMethodFeature.d.ts +2 -0
  62. package/es/checkout/hooks/usePaymentMethodFeature.js +4 -0
  63. package/es/checkout/hooks/usePricing.d.ts +57 -0
  64. package/es/checkout/hooks/usePricing.js +174 -0
  65. package/es/checkout/hooks/usePricingFeature.d.ts +28 -0
  66. package/es/checkout/hooks/usePricingFeature.js +36 -0
  67. package/es/checkout/hooks/useProduct.d.ts +32 -0
  68. package/es/checkout/hooks/useProduct.js +5 -0
  69. package/es/checkout/hooks/usePromotion.d.ts +12 -0
  70. package/es/checkout/hooks/usePromotion.js +48 -0
  71. package/es/checkout/hooks/useSlippage.d.ts +8 -0
  72. package/es/checkout/hooks/useSlippage.js +29 -0
  73. package/es/checkout/hooks/useSubmit.d.ts +38 -0
  74. package/es/checkout/hooks/useSubmit.js +493 -0
  75. package/es/checkout/hooks/useSubmitFeature.d.ts +2 -0
  76. package/es/checkout/hooks/useSubmitFeature.js +4 -0
  77. package/es/checkout/hooks/useUpsell.d.ts +5 -0
  78. package/es/checkout/hooks/useUpsell.js +25 -0
  79. package/es/checkout/index.d.ts +37 -0
  80. package/es/checkout/index.js +28 -0
  81. package/es/checkout/types.d.ts +262 -0
  82. package/es/checkout/types.js +0 -0
  83. package/es/index.d.ts +1 -0
  84. package/es/index.js +28 -0
  85. package/es/shared/api.d.ts +41 -0
  86. package/es/shared/api.js +81 -0
  87. package/es/shared/format.d.ts +38 -0
  88. package/es/shared/format.js +229 -0
  89. package/es/shared/polling.d.ts +15 -0
  90. package/es/shared/polling.js +20 -0
  91. package/es/shared/types.d.ts +10 -0
  92. package/es/shared/types.js +0 -0
  93. package/es/shared/validation.d.ts +38 -0
  94. package/es/shared/validation.js +190 -0
  95. package/es/types/checkout-augmented.d.ts +42 -0
  96. package/es/types/checkout-augmented.js +17 -0
  97. package/es/types/external.d.ts +18 -0
  98. package/examples/01-basic-checkout.tsx +159 -0
  99. package/examples/01-credit-recharge.tsx +19 -0
  100. package/examples/02-subscription.tsx +40 -0
  101. package/examples/03-upsell.tsx +60 -0
  102. package/examples/04-cross-sell.tsx +54 -0
  103. package/examples/05-full-checkout.tsx +126 -0
  104. package/jest.config.js +15 -0
  105. package/lib/checkout/context/CheckoutProvider.d.ts +6 -0
  106. package/lib/checkout/context/CheckoutProvider.js +181 -0
  107. package/lib/checkout/context/CustomerFormContext.d.ts +4 -0
  108. package/lib/checkout/context/CustomerFormContext.js +16 -0
  109. package/lib/checkout/context/ExchangeRateContext.d.ts +11 -0
  110. package/lib/checkout/context/ExchangeRateContext.js +16 -0
  111. package/lib/checkout/context/PaymentMethodContext.d.ts +26 -0
  112. package/lib/checkout/context/PaymentMethodContext.js +16 -0
  113. package/lib/checkout/context/SessionContext.d.ts +45 -0
  114. package/lib/checkout/context/SessionContext.js +16 -0
  115. package/lib/checkout/context/SubmitContext.d.ts +4 -0
  116. package/lib/checkout/context/SubmitContext.js +16 -0
  117. package/lib/checkout/context/index.d.ts +6 -0
  118. package/lib/checkout/context/index.js +77 -0
  119. package/lib/checkout/core/billingInterval.d.ts +15 -0
  120. package/lib/checkout/core/billingInterval.js +42 -0
  121. package/lib/checkout/core/crossSell.d.ts +4 -0
  122. package/lib/checkout/core/crossSell.js +43 -0
  123. package/lib/checkout/core/customerForm.d.ts +5 -0
  124. package/lib/checkout/core/customerForm.js +106 -0
  125. package/lib/checkout/core/exchangeRate.d.ts +11 -0
  126. package/lib/checkout/core/exchangeRate.js +45 -0
  127. package/lib/checkout/core/index.d.ts +10 -0
  128. package/lib/checkout/core/index.js +297 -0
  129. package/lib/checkout/core/lineItems.d.ts +7 -0
  130. package/lib/checkout/core/lineItems.js +76 -0
  131. package/lib/checkout/core/paymentMethod.d.ts +23 -0
  132. package/lib/checkout/core/paymentMethod.js +114 -0
  133. package/lib/checkout/core/pricing.d.ts +32 -0
  134. package/lib/checkout/core/pricing.js +216 -0
  135. package/lib/checkout/core/promotion.d.ts +10 -0
  136. package/lib/checkout/core/promotion.js +62 -0
  137. package/lib/checkout/core/session.d.ts +26 -0
  138. package/lib/checkout/core/session.js +58 -0
  139. package/lib/checkout/core/submit.d.ts +40 -0
  140. package/lib/checkout/core/submit.js +84 -0
  141. package/lib/checkout/hooks/index.d.ts +34 -0
  142. package/lib/checkout/hooks/index.js +138 -0
  143. package/lib/checkout/hooks/useBillingInterval.d.ts +14 -0
  144. package/lib/checkout/hooks/useBillingInterval.js +63 -0
  145. package/lib/checkout/hooks/useCheckout.d.ts +2 -0
  146. package/lib/checkout/hooks/useCheckout.js +190 -0
  147. package/lib/checkout/hooks/useCheckoutSession.d.ts +58 -0
  148. package/lib/checkout/hooks/useCheckoutSession.js +119 -0
  149. package/lib/checkout/hooks/useCheckoutStatus.d.ts +10 -0
  150. package/lib/checkout/hooks/useCheckoutStatus.js +28 -0
  151. package/lib/checkout/hooks/useCrossSell.d.ts +8 -0
  152. package/lib/checkout/hooks/useCrossSell.js +75 -0
  153. package/lib/checkout/hooks/useCustomerForm.d.ts +14 -0
  154. package/lib/checkout/hooks/useCustomerForm.js +135 -0
  155. package/lib/checkout/hooks/useCustomerFormFeature.d.ts +2 -0
  156. package/lib/checkout/hooks/useCustomerFormFeature.js +10 -0
  157. package/lib/checkout/hooks/useExchangeRate.d.ts +11 -0
  158. package/lib/checkout/hooks/useExchangeRate.js +29 -0
  159. package/lib/checkout/hooks/useLineItems.d.ts +22 -0
  160. package/lib/checkout/hooks/useLineItems.js +142 -0
  161. package/lib/checkout/hooks/usePaymentMethod.d.ts +26 -0
  162. package/lib/checkout/hooks/usePaymentMethod.js +101 -0
  163. package/lib/checkout/hooks/usePaymentMethodFeature.d.ts +2 -0
  164. package/lib/checkout/hooks/usePaymentMethodFeature.js +10 -0
  165. package/lib/checkout/hooks/usePricing.d.ts +57 -0
  166. package/lib/checkout/hooks/usePricing.js +168 -0
  167. package/lib/checkout/hooks/usePricingFeature.d.ts +28 -0
  168. package/lib/checkout/hooks/usePricingFeature.js +48 -0
  169. package/lib/checkout/hooks/useProduct.d.ts +32 -0
  170. package/lib/checkout/hooks/useProduct.js +21 -0
  171. package/lib/checkout/hooks/usePromotion.d.ts +12 -0
  172. package/lib/checkout/hooks/usePromotion.js +57 -0
  173. package/lib/checkout/hooks/useSlippage.d.ts +8 -0
  174. package/lib/checkout/hooks/useSlippage.js +39 -0
  175. package/lib/checkout/hooks/useSubmit.d.ts +38 -0
  176. package/lib/checkout/hooks/useSubmit.js +504 -0
  177. package/lib/checkout/hooks/useSubmitFeature.d.ts +2 -0
  178. package/lib/checkout/hooks/useSubmitFeature.js +10 -0
  179. package/lib/checkout/hooks/useUpsell.d.ts +5 -0
  180. package/lib/checkout/hooks/useUpsell.js +40 -0
  181. package/lib/checkout/index.d.ts +37 -0
  182. package/lib/checkout/index.js +182 -0
  183. package/lib/checkout/types.d.ts +262 -0
  184. package/lib/checkout/types.js +1 -0
  185. package/lib/index.d.ts +1 -0
  186. package/lib/index.js +162 -0
  187. package/lib/shared/api.d.ts +41 -0
  188. package/lib/shared/api.js +88 -0
  189. package/lib/shared/format.d.ts +38 -0
  190. package/lib/shared/format.js +262 -0
  191. package/lib/shared/polling.d.ts +15 -0
  192. package/lib/shared/polling.js +32 -0
  193. package/lib/shared/types.d.ts +10 -0
  194. package/lib/shared/types.js +1 -0
  195. package/lib/shared/validation.d.ts +38 -0
  196. package/lib/shared/validation.js +212 -0
  197. package/lib/types/checkout-augmented.d.ts +42 -0
  198. package/lib/types/checkout-augmented.js +24 -0
  199. package/lib/types/external.d.ts +18 -0
  200. package/package.json +64 -0
  201. package/src/checkout/context/CheckoutProvider.tsx +269 -0
  202. package/src/checkout/context/CustomerFormContext.ts +14 -0
  203. package/src/checkout/context/ExchangeRateContext.ts +21 -0
  204. package/src/checkout/context/PaymentMethodContext.ts +36 -0
  205. package/src/checkout/context/SessionContext.ts +49 -0
  206. package/src/checkout/context/SubmitContext.ts +14 -0
  207. package/src/checkout/context/index.ts +6 -0
  208. package/src/checkout/core/billingInterval.ts +62 -0
  209. package/src/checkout/core/crossSell.ts +52 -0
  210. package/src/checkout/core/customerForm.ts +122 -0
  211. package/src/checkout/core/exchangeRate.ts +38 -0
  212. package/src/checkout/core/index.ts +60 -0
  213. package/src/checkout/core/lineItems.ts +106 -0
  214. package/src/checkout/core/paymentMethod.ts +113 -0
  215. package/src/checkout/core/pricing.ts +347 -0
  216. package/src/checkout/core/promotion.ts +59 -0
  217. package/src/checkout/core/session.ts +62 -0
  218. package/src/checkout/core/submit.ts +109 -0
  219. package/src/checkout/hooks/index.ts +41 -0
  220. package/src/checkout/hooks/useBillingInterval.ts +71 -0
  221. package/src/checkout/hooks/useCheckout.ts +267 -0
  222. package/src/checkout/hooks/useCheckoutSession.ts +217 -0
  223. package/src/checkout/hooks/useCheckoutStatus.ts +31 -0
  224. package/src/checkout/hooks/useCrossSell.ts +80 -0
  225. package/src/checkout/hooks/useCustomerForm.ts +156 -0
  226. package/src/checkout/hooks/useCustomerFormFeature.ts +7 -0
  227. package/src/checkout/hooks/useExchangeRate.ts +28 -0
  228. package/src/checkout/hooks/useLineItems.ts +191 -0
  229. package/src/checkout/hooks/usePaymentMethod.ts +165 -0
  230. package/src/checkout/hooks/usePaymentMethodFeature.ts +8 -0
  231. package/src/checkout/hooks/usePricing.ts +274 -0
  232. package/src/checkout/hooks/usePricingFeature.ts +73 -0
  233. package/src/checkout/hooks/useProduct.ts +32 -0
  234. package/src/checkout/hooks/usePromotion.ts +67 -0
  235. package/src/checkout/hooks/useSlippage.ts +39 -0
  236. package/src/checkout/hooks/useSubmit.ts +684 -0
  237. package/src/checkout/hooks/useSubmitFeature.ts +7 -0
  238. package/src/checkout/hooks/useUpsell.ts +35 -0
  239. package/src/checkout/index.ts +65 -0
  240. package/src/checkout/types.ts +292 -0
  241. package/src/index.ts +64 -0
  242. package/src/shared/api.ts +118 -0
  243. package/src/shared/format.ts +318 -0
  244. package/src/shared/polling.ts +49 -0
  245. package/src/shared/types.ts +13 -0
  246. package/src/shared/validation.ts +254 -0
  247. package/src/types/checkout-augmented.ts +77 -0
  248. package/src/types/external.d.ts +18 -0
  249. package/tools/jest.js +1 -0
  250. package/tsconfig.json +18 -0
package/.eslintrc.js ADDED
@@ -0,0 +1,18 @@
1
+ const { join } = require('path');
2
+
3
+ module.exports = {
4
+ root: true,
5
+ extends: '@arcblock/eslint-config-ts',
6
+ parserOptions: {
7
+ project: [join(__dirname, 'tsconfig.json')],
8
+ },
9
+ rules: {
10
+ '@typescript-eslint/comma-dangle': 'off',
11
+ '@typescript-eslint/no-use-before-define': 'off',
12
+ '@typescript-eslint/indent': 'off',
13
+ 'import/prefer-default-export': 'off',
14
+ 'unicorn/filename-case': 'off',
15
+ // Downgrade to warn: logical expressions like `data?.items || []` trigger false positives
16
+ 'react-hooks/exhaustive-deps': 'warn',
17
+ },
18
+ };
@@ -0,0 +1,30 @@
1
+ import { defineBuildConfig, BuildEntry } from 'unbuild';
2
+
3
+ const pattern = ['**/*.ts', '**/*.tsx', '!**/*.spec.ts', '!**/*.test.ts'];
4
+
5
+ const shared: BuildEntry = {
6
+ builder: 'mkdist',
7
+ input: './src',
8
+ pattern,
9
+ ext: 'js',
10
+ esbuild: {
11
+ jsx: 'automatic',
12
+ },
13
+ declaration: true,
14
+ };
15
+
16
+ export default defineBuildConfig({
17
+ failOnWarn: false,
18
+ entries: [
19
+ {
20
+ ...shared,
21
+ outDir: './es',
22
+ format: 'esm',
23
+ },
24
+ {
25
+ ...shared,
26
+ outDir: './lib',
27
+ format: 'cjs',
28
+ },
29
+ ],
30
+ });
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ export interface CheckoutProviderProps {
3
+ sessionId: string;
4
+ children: React.ReactNode;
5
+ }
6
+ export declare function CheckoutProvider({ sessionId, children }: CheckoutProviderProps): JSX.Element;
@@ -0,0 +1,209 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useMemo, useState, useRef, useEffect } from "react";
3
+ import { useMemoizedFn } from "ahooks";
4
+ import { useCheckoutSession } from "../hooks/useCheckoutSession.js";
5
+ import { usePaymentMethod as usePaymentMethodHook } from "../hooks/usePaymentMethod.js";
6
+ import { useCustomerForm as useCustomerFormHook } from "../hooks/useCustomerForm.js";
7
+ import { useSubmit as useSubmitHook } from "../hooks/useSubmit.js";
8
+ import { checkHasDynamicPricing, fetchExchangeRate, BASE_INTERVAL } from "../core/exchangeRate.js";
9
+ import { recalculatePromotionIfNeeded } from "../core/lineItems.js";
10
+ import { SessionContext } from "./SessionContext.js";
11
+ import { PaymentMethodContext } from "./PaymentMethodContext.js";
12
+ import { ExchangeRateContext } from "./ExchangeRateContext.js";
13
+ import { CustomerFormContext } from "./CustomerFormContext.js";
14
+ import { SubmitReactContext } from "./SubmitContext.js";
15
+ export function CheckoutProvider({ sessionId, children }) {
16
+ const {
17
+ isLoading,
18
+ error,
19
+ errorCode,
20
+ refresh,
21
+ setSessionData,
22
+ sessionData,
23
+ resolvedSessionId,
24
+ vendorCount,
25
+ product,
26
+ subscription,
27
+ pageInfo
28
+ } = useCheckoutSession(sessionId);
29
+ const session = sessionData?.checkoutSession;
30
+ const effectiveSessionId = resolvedSessionId || sessionId;
31
+ const items = session?.line_items || [];
32
+ const isDonation = session?.submit_type === "donate";
33
+ const sessionValue = useMemo(
34
+ () => ({
35
+ sessionData,
36
+ sessionId,
37
+ effectiveSessionId,
38
+ isLoading,
39
+ error,
40
+ errorCode,
41
+ refresh,
42
+ items,
43
+ session: session || null,
44
+ isDonation,
45
+ vendorCount,
46
+ product,
47
+ subscription,
48
+ pageInfo
49
+ }),
50
+ [
51
+ sessionData,
52
+ sessionId,
53
+ effectiveSessionId,
54
+ isLoading,
55
+ error,
56
+ errorCode,
57
+ refresh,
58
+ items,
59
+ session,
60
+ isDonation,
61
+ vendorCount,
62
+ product,
63
+ subscription,
64
+ pageInfo
65
+ ]
66
+ );
67
+ const paymentMethodHook = usePaymentMethodHook(sessionData, effectiveSessionId, refresh);
68
+ const prevCurrencyRef = useRef(null);
69
+ useEffect(() => {
70
+ const currId = paymentMethodHook.currency?.id || null;
71
+ if (!currId || !session || session.status === "complete") return;
72
+ if (prevCurrencyRef.current === null || currId !== prevCurrencyRef.current) {
73
+ prevCurrencyRef.current = currId;
74
+ recalculatePromotionIfNeeded(session, effectiveSessionId, currId).then(() => refresh(true));
75
+ }
76
+ }, [paymentMethodHook.currency?.id, session?.id]);
77
+ const paymentMethodValue = useMemo(
78
+ () => ({
79
+ current: paymentMethodHook.current,
80
+ currency: paymentMethodHook.currency,
81
+ available: paymentMethodHook.available,
82
+ currencies: paymentMethodHook.currencies,
83
+ isStripe: paymentMethodHook.isStripe,
84
+ isCrypto: paymentMethodHook.isCrypto,
85
+ isCredit: paymentMethodHook.isCredit,
86
+ switching: paymentMethodHook.switching,
87
+ setType: paymentMethodHook.setType,
88
+ types: paymentMethodHook.types,
89
+ setCurrency: paymentMethodHook.setCurrency,
90
+ stripe: paymentMethodHook.stripe
91
+ }),
92
+ [
93
+ paymentMethodHook.current,
94
+ paymentMethodHook.currency,
95
+ paymentMethodHook.available,
96
+ paymentMethodHook.currencies,
97
+ paymentMethodHook.isStripe,
98
+ paymentMethodHook.isCrypto,
99
+ paymentMethodHook.isCredit,
100
+ paymentMethodHook.switching,
101
+ paymentMethodHook.setType,
102
+ paymentMethodHook.types,
103
+ paymentMethodHook.setCurrency,
104
+ paymentMethodHook.stripe
105
+ ]
106
+ );
107
+ const [exchangeRate, setExchangeRate] = useState(null);
108
+ const [rateProvider, setRateProvider] = useState(null);
109
+ const [rateProviderDisplay, setRateProviderDisplay] = useState(null);
110
+ const [rateFetchedAt, setRateFetchedAt] = useState(null);
111
+ const [rateStatus, setRateStatus] = useState("loading");
112
+ const intervalRef = useRef(null);
113
+ const fetchingRef = useRef(false);
114
+ const mountedRef = useRef(true);
115
+ const hasDynamicPricing = useMemo(() => checkHasDynamicPricing(items), [items]);
116
+ const fetchRate = useMemoizedFn(async () => {
117
+ if (!effectiveSessionId || !hasDynamicPricing || paymentMethodHook.isStripe) {
118
+ setRateStatus(hasDynamicPricing ? "unavailable" : "available");
119
+ if (paymentMethodHook.isStripe) {
120
+ setExchangeRate(null);
121
+ setRateProvider(null);
122
+ setRateProviderDisplay(null);
123
+ setRateFetchedAt(null);
124
+ }
125
+ return;
126
+ }
127
+ if (fetchingRef.current) return;
128
+ fetchingRef.current = true;
129
+ try {
130
+ setRateStatus("loading");
131
+ const result = await fetchExchangeRate(effectiveSessionId, paymentMethodHook.currency?.id);
132
+ if (!mountedRef.current) return;
133
+ if (result.rate) {
134
+ setExchangeRate(result.rate);
135
+ setRateProvider(result.provider);
136
+ setRateProviderDisplay(result.providerDisplay);
137
+ setRateFetchedAt(result.fetchedAt);
138
+ setRateStatus("available");
139
+ } else {
140
+ setRateStatus("unavailable");
141
+ }
142
+ } catch {
143
+ if (!mountedRef.current) return;
144
+ setRateStatus("unavailable");
145
+ } finally {
146
+ fetchingRef.current = false;
147
+ }
148
+ });
149
+ useEffect(() => {
150
+ mountedRef.current = true;
151
+ if (!hasDynamicPricing || paymentMethodHook.isStripe || !effectiveSessionId || session?.status === "complete") {
152
+ if (paymentMethodHook.isStripe) {
153
+ setExchangeRate(null);
154
+ setRateProvider(null);
155
+ setRateProviderDisplay(null);
156
+ setRateFetchedAt(null);
157
+ }
158
+ if (session?.status === "complete") {
159
+ setRateStatus("available");
160
+ } else {
161
+ setRateStatus(hasDynamicPricing ? "unavailable" : "available");
162
+ }
163
+ return void 0;
164
+ }
165
+ fetchRate();
166
+ intervalRef.current = setInterval(fetchRate, BASE_INTERVAL);
167
+ const handleVisibility = () => {
168
+ if (!document.hidden) {
169
+ fetchRate();
170
+ }
171
+ };
172
+ document.addEventListener("visibilitychange", handleVisibility);
173
+ return () => {
174
+ mountedRef.current = false;
175
+ if (intervalRef.current) clearInterval(intervalRef.current);
176
+ document.removeEventListener("visibilitychange", handleVisibility);
177
+ };
178
+ }, [hasDynamicPricing, paymentMethodHook.isStripe, effectiveSessionId, paymentMethodHook.currency?.id, session?.status]);
179
+ const exchangeRateValue = useMemo(
180
+ () => ({
181
+ rate: exchangeRate,
182
+ provider: rateProvider,
183
+ providerDisplay: rateProviderDisplay,
184
+ fetchedAt: rateFetchedAt,
185
+ status: rateStatus,
186
+ hasDynamicPricing,
187
+ refresh: fetchRate
188
+ }),
189
+ [exchangeRate, rateProvider, rateProviderDisplay, rateFetchedAt, rateStatus, hasDynamicPricing, fetchRate]
190
+ );
191
+ const customerForm = useCustomerFormHook(
192
+ sessionData,
193
+ paymentMethodHook.currency?.id || null,
194
+ paymentMethodHook.current?.id || null
195
+ );
196
+ const submit = useSubmitHook(
197
+ sessionData,
198
+ effectiveSessionId,
199
+ paymentMethodHook.currency?.id || null,
200
+ paymentMethodHook.isStripe,
201
+ paymentMethodHook.isCredit,
202
+ isDonation,
203
+ customerForm.values,
204
+ customerForm.validate,
205
+ refresh,
206
+ setSessionData
207
+ );
208
+ return /* @__PURE__ */ jsx(SessionContext.Provider, { value: sessionValue, children: /* @__PURE__ */ jsx(PaymentMethodContext.Provider, { value: paymentMethodValue, children: /* @__PURE__ */ jsx(ExchangeRateContext.Provider, { value: exchangeRateValue, children: /* @__PURE__ */ jsx(CustomerFormContext.Provider, { value: customerForm, children: /* @__PURE__ */ jsx(SubmitReactContext.Provider, { value: submit, children }) }) }) }) });
209
+ }
@@ -0,0 +1,4 @@
1
+ import type { UseCustomerFormReturn } from '../hooks/useCustomerForm';
2
+ export type CustomerFormContextValue = UseCustomerFormReturn;
3
+ export declare const CustomerFormContext: import("react").Context<UseCustomerFormReturn | null>;
4
+ export declare function useCustomerFormContext(): CustomerFormContextValue;
@@ -0,0 +1,9 @@
1
+ import { createContext, useContext } from "react";
2
+ export const CustomerFormContext = createContext(null);
3
+ export function useCustomerFormContext() {
4
+ const ctx = useContext(CustomerFormContext);
5
+ if (!ctx) {
6
+ throw new Error("useCustomerFormContext must be used within <CheckoutProvider>");
7
+ }
8
+ return ctx;
9
+ }
@@ -0,0 +1,11 @@
1
+ export interface ExchangeRateContextValue {
2
+ rate: string | null;
3
+ provider: string | null;
4
+ providerDisplay: string | null;
5
+ fetchedAt: number | null;
6
+ status: 'loading' | 'available' | 'unavailable';
7
+ hasDynamicPricing: boolean;
8
+ refresh: () => Promise<void>;
9
+ }
10
+ export declare const ExchangeRateContext: import("react").Context<ExchangeRateContextValue | null>;
11
+ export declare function useExchangeRateContext(): ExchangeRateContextValue;
@@ -0,0 +1,9 @@
1
+ import { createContext, useContext } from "react";
2
+ export const ExchangeRateContext = createContext(null);
3
+ export function useExchangeRateContext() {
4
+ const ctx = useContext(ExchangeRateContext);
5
+ if (!ctx) {
6
+ throw new Error("useExchangeRateContext must be used within <CheckoutProvider>");
7
+ }
8
+ return ctx;
9
+ }
@@ -0,0 +1,26 @@
1
+ import type { TPaymentMethodExpanded, TPaymentCurrency } from '@blocklet/payment-types';
2
+ export interface PaymentMethodContextValue {
3
+ current: TPaymentMethodExpanded;
4
+ currency: TPaymentCurrency;
5
+ available: TPaymentMethodExpanded[];
6
+ currencies: TPaymentCurrency[];
7
+ isStripe: boolean;
8
+ isCrypto: boolean;
9
+ isCredit: boolean;
10
+ switching: boolean;
11
+ setType: (type: 'stripe' | 'crypto') => Promise<void>;
12
+ types: Array<{
13
+ type: 'stripe' | 'crypto';
14
+ label: string;
15
+ currencies: TPaymentCurrency[];
16
+ active: boolean;
17
+ }>;
18
+ setCurrency: (currencyId: string) => Promise<void>;
19
+ stripe: {
20
+ publishableKey: string | null;
21
+ clientSecret: string | null;
22
+ status: 'idle' | 'ready' | 'processing' | 'succeeded' | 'failed';
23
+ } | null;
24
+ }
25
+ export declare const PaymentMethodContext: import("react").Context<PaymentMethodContextValue | null>;
26
+ export declare function usePaymentMethodContext(): PaymentMethodContextValue;
@@ -0,0 +1,9 @@
1
+ import { createContext, useContext } from "react";
2
+ export const PaymentMethodContext = createContext(null);
3
+ export function usePaymentMethodContext() {
4
+ const ctx = useContext(PaymentMethodContext);
5
+ if (!ctx) {
6
+ throw new Error("usePaymentMethodContext must be used within <CheckoutProvider>");
7
+ }
8
+ return ctx;
9
+ }
@@ -0,0 +1,45 @@
1
+ import type { TLineItemExpanded, TCheckoutSessionExpanded } from '@blocklet/payment-types';
2
+ import type { SessionData } from '../hooks/useCheckoutSession';
3
+ export interface SessionContextValue {
4
+ sessionData: SessionData | null;
5
+ sessionId: string;
6
+ effectiveSessionId: string;
7
+ isLoading: boolean;
8
+ error: string | null;
9
+ errorCode: 'SESSION_EXPIRED' | 'EMPTY_LINE_ITEMS' | null;
10
+ refresh: (forceRefresh?: boolean) => Promise<void>;
11
+ items: TLineItemExpanded[];
12
+ session: TCheckoutSessionExpanded | null | undefined;
13
+ isDonation: boolean;
14
+ vendorCount: number;
15
+ product: {
16
+ name: string;
17
+ description: string;
18
+ images: string[];
19
+ features: Array<{
20
+ name: string;
21
+ icon?: string;
22
+ }>;
23
+ billing: {
24
+ mode: 'payment' | 'subscription';
25
+ interval: 'month' | 'year' | 'week' | 'day' | null;
26
+ intervalCount: number;
27
+ displayInterval: string;
28
+ };
29
+ metadata: Record<string, string>;
30
+ } | null;
31
+ subscription: {
32
+ mode: 'payment' | 'subscription' | 'setup';
33
+ showStake: boolean;
34
+ confirmMessage: string;
35
+ } | null;
36
+ pageInfo: {
37
+ formPurposeDescription?: {
38
+ en: string;
39
+ zh: string;
40
+ };
41
+ showProductFeatures: boolean;
42
+ } | null;
43
+ }
44
+ export declare const SessionContext: import("react").Context<SessionContextValue | null>;
45
+ export declare function useSessionContext(): SessionContextValue;
@@ -0,0 +1,9 @@
1
+ import { createContext, useContext } from "react";
2
+ export const SessionContext = createContext(null);
3
+ export function useSessionContext() {
4
+ const ctx = useContext(SessionContext);
5
+ if (!ctx) {
6
+ throw new Error("useSessionContext must be used within <CheckoutProvider>");
7
+ }
8
+ return ctx;
9
+ }
@@ -0,0 +1,4 @@
1
+ import type { UseSubmitReturn } from '../hooks/useSubmit';
2
+ export type SubmitContextValue = UseSubmitReturn;
3
+ export declare const SubmitReactContext: import("react").Context<UseSubmitReturn | null>;
4
+ export declare function useSubmitContext(): SubmitContextValue;
@@ -0,0 +1,9 @@
1
+ import { createContext, useContext } from "react";
2
+ export const SubmitReactContext = createContext(null);
3
+ export function useSubmitContext() {
4
+ const ctx = useContext(SubmitReactContext);
5
+ if (!ctx) {
6
+ throw new Error("useSubmitContext must be used within <CheckoutProvider>");
7
+ }
8
+ return ctx;
9
+ }
@@ -0,0 +1,6 @@
1
+ export { CheckoutProvider, type CheckoutProviderProps } from './CheckoutProvider';
2
+ export { SessionContext, useSessionContext, type SessionContextValue } from './SessionContext';
3
+ export { PaymentMethodContext, usePaymentMethodContext, type PaymentMethodContextValue } from './PaymentMethodContext';
4
+ export { ExchangeRateContext, useExchangeRateContext, type ExchangeRateContextValue } from './ExchangeRateContext';
5
+ export { CustomerFormContext, useCustomerFormContext, type CustomerFormContextValue } from './CustomerFormContext';
6
+ export { SubmitReactContext, useSubmitContext, type SubmitContextValue } from './SubmitContext';
@@ -0,0 +1,6 @@
1
+ export { CheckoutProvider } from "./CheckoutProvider.js";
2
+ export { SessionContext, useSessionContext } from "./SessionContext.js";
3
+ export { PaymentMethodContext, usePaymentMethodContext } from "./PaymentMethodContext.js";
4
+ export { ExchangeRateContext, useExchangeRateContext } from "./ExchangeRateContext.js";
5
+ export { CustomerFormContext, useCustomerFormContext } from "./CustomerFormContext.js";
6
+ export { SubmitReactContext, useSubmitContext } from "./SubmitContext.js";
@@ -0,0 +1,15 @@
1
+ import type { TLineItemExpanded } from '@blocklet/payment-types';
2
+ export type BillingIntervalType = 'month' | 'year' | 'week' | 'day';
3
+ export interface BillingIntervalOption {
4
+ interval: BillingIntervalType;
5
+ priceId: string;
6
+ amount: string;
7
+ savings: string | null;
8
+ }
9
+ export interface BillingIntervalInfo {
10
+ current: BillingIntervalType | null;
11
+ available: BillingIntervalOption[];
12
+ /** The first item used to derive billing info */
13
+ firstItem: TLineItemExpanded;
14
+ }
15
+ export declare function parseBillingInterval(items: TLineItemExpanded[]): BillingIntervalInfo | null;
@@ -0,0 +1,36 @@
1
+ export function parseBillingInterval(items) {
2
+ if (!items.length) return null;
3
+ const firstItem = items[0];
4
+ const price = firstItem.upsell_price || firstItem.price;
5
+ const recurring = price?.recurring;
6
+ if (!recurring) return null;
7
+ const currentInterval = recurring.interval;
8
+ const upsell = price?.upsell;
9
+ const upsellTo = upsell?.upsells_to;
10
+ const available = [];
11
+ if (upsellTo?.recurring?.interval && upsellTo.recurring.interval !== currentInterval) {
12
+ available.push({
13
+ interval: upsellTo.recurring.interval,
14
+ priceId: upsell?.upsells_to_id || "",
15
+ amount: upsellTo.unit_amount || "0",
16
+ savings: null
17
+ });
18
+ }
19
+ if (firstItem.upsell_price_id && firstItem.price?.recurring) {
20
+ const origInterval = firstItem.price.recurring.interval;
21
+ if (origInterval && origInterval !== currentInterval) {
22
+ available.push({
23
+ interval: origInterval,
24
+ priceId: firstItem.price_id,
25
+ amount: firstItem.price.unit_amount || "0",
26
+ savings: null
27
+ });
28
+ }
29
+ }
30
+ if (available.length === 0) return null;
31
+ return {
32
+ current: currentInterval,
33
+ available,
34
+ firstItem
35
+ };
36
+ }
@@ -0,0 +1,4 @@
1
+ import type { TCheckoutSessionExpanded, TPrice } from '@blocklet/payment-types';
2
+ export declare function addCrossSellItem(sessionId: string, crossSellItemId: string, session: TCheckoutSessionExpanded | undefined | null, currencyId: string | null | undefined, refresh: (force?: boolean) => Promise<void>): Promise<void>;
3
+ export declare function removeCrossSellItem(sessionId: string, session: TCheckoutSessionExpanded | undefined | null, currencyId: string | null | undefined, refresh: (force?: boolean) => Promise<void>): Promise<void>;
4
+ export declare function fetchCrossSellItem(sessionId: string): Promise<TPrice | null>;
@@ -0,0 +1,30 @@
1
+ import api, { API } from "../../shared/api.js";
2
+ import { recalculatePromotionIfNeeded } from "./lineItems.js";
3
+ export async function addCrossSellItem(sessionId, crossSellItemId, session, currencyId, refresh) {
4
+ await api.put(API.CROSS_SELL(sessionId), { to: crossSellItemId });
5
+ await recalculatePromotionIfNeeded(session, sessionId, currencyId);
6
+ await refresh(true);
7
+ }
8
+ export async function removeCrossSellItem(sessionId, session, currencyId, refresh) {
9
+ await api.delete(API.CROSS_SELL(sessionId));
10
+ await recalculatePromotionIfNeeded(session, sessionId, currencyId);
11
+ await refresh(true);
12
+ }
13
+ const pendingFetches = /* @__PURE__ */ new Map();
14
+ export function fetchCrossSellItem(sessionId) {
15
+ const existing = pendingFetches.get(sessionId);
16
+ if (existing) return existing;
17
+ const promise = (async () => {
18
+ try {
19
+ const { data } = await api.get(`${API.CROSS_SELL(sessionId)}?skipError=true`);
20
+ if (data && !data.error) {
21
+ return data;
22
+ }
23
+ } catch {
24
+ }
25
+ return null;
26
+ })();
27
+ pendingFetches.set(sessionId, promise);
28
+ promise.finally(() => pendingFetches.delete(sessionId));
29
+ return promise;
30
+ }
@@ -0,0 +1,5 @@
1
+ import type { TCheckoutSessionExpanded, TCustomer } from '@blocklet/payment-types';
2
+ import type { FieldConfig, CheckoutFormData } from '../types';
3
+ export declare function buildFields(session: TCheckoutSessionExpanded | undefined | null): FieldConfig[];
4
+ export declare function createInitialValues(session: TCheckoutSessionExpanded | undefined | null, customer: TCustomer | undefined | null): CheckoutFormData;
5
+ export declare function setNestedValue<T extends Record<string, unknown>>(obj: T, path: string, value: unknown): T;
@@ -0,0 +1,105 @@
1
+ export function buildFields(session) {
2
+ const fields = [];
3
+ fields.push({
4
+ name: "customer_name",
5
+ type: "text",
6
+ required: true
7
+ });
8
+ fields.push({
9
+ name: "customer_email",
10
+ type: "email",
11
+ required: true
12
+ });
13
+ if (session?.phone_number_collection?.enabled) {
14
+ fields.push({
15
+ name: "customer_phone",
16
+ type: "phone",
17
+ required: true
18
+ });
19
+ }
20
+ const addressMode = session?.billing_address_collection;
21
+ if (addressMode === "required") {
22
+ fields.push(
23
+ {
24
+ name: "billing_address.country",
25
+ type: "country",
26
+ required: true,
27
+ group: "address"
28
+ },
29
+ {
30
+ name: "billing_address.line1",
31
+ type: "text",
32
+ required: true,
33
+ group: "address"
34
+ },
35
+ {
36
+ name: "billing_address.line2",
37
+ type: "text",
38
+ required: false,
39
+ group: "address"
40
+ },
41
+ {
42
+ name: "billing_address.city",
43
+ type: "text",
44
+ required: true,
45
+ group: "address"
46
+ },
47
+ {
48
+ name: "billing_address.state",
49
+ type: "text",
50
+ required: true,
51
+ group: "address"
52
+ },
53
+ {
54
+ name: "billing_address.postal_code",
55
+ type: "text",
56
+ required: true,
57
+ group: "address"
58
+ }
59
+ );
60
+ } else if (addressMode === "auto") {
61
+ fields.push(
62
+ {
63
+ name: "billing_address.state",
64
+ type: "text",
65
+ required: false,
66
+ group: "address"
67
+ },
68
+ {
69
+ name: "billing_address.postal_code",
70
+ type: "text",
71
+ required: false,
72
+ group: "address"
73
+ }
74
+ );
75
+ }
76
+ return fields;
77
+ }
78
+ export function createInitialValues(session, customer) {
79
+ return {
80
+ customer_name: customer?.name || "",
81
+ customer_email: session?.customer_details?.email || customer?.email || "",
82
+ customer_phone: customer?.phone || "",
83
+ payment_method: "",
84
+ payment_currency: session?.currency_id || "",
85
+ billing_address: {
86
+ country: customer?.address?.country || "",
87
+ state: customer?.address?.state || "",
88
+ city: customer?.address?.city || "",
89
+ line1: customer?.address?.line1 || "",
90
+ line2: customer?.address?.line2 || "",
91
+ postal_code: customer?.address?.postal_code || ""
92
+ }
93
+ };
94
+ }
95
+ export function setNestedValue(obj, path, value) {
96
+ const parts = path.split(".");
97
+ if (parts.length === 1) {
98
+ return { ...obj, [path]: value };
99
+ }
100
+ const [head, ...rest] = parts;
101
+ return {
102
+ ...obj,
103
+ [head]: setNestedValue(obj[head] || {}, rest.join("."), value)
104
+ };
105
+ }
@@ -0,0 +1,11 @@
1
+ import type { TLineItemExpanded } from '@blocklet/payment-types';
2
+ export declare const BASE_INTERVAL = 30000;
3
+ export declare const MAX_INTERVAL = 120000;
4
+ export declare function checkHasDynamicPricing(items: TLineItemExpanded[]): boolean;
5
+ export declare function fetchExchangeRate(sessionId: string, currencyId: string | null | undefined): Promise<{
6
+ rate: string | null;
7
+ provider: string | null;
8
+ providerDisplay: string | null;
9
+ fetchedAt: number | null;
10
+ }>;
11
+ export declare function getBackoffInterval(failCount: number): number;