@blocklet/payment-react-headless 1.26.0 → 1.26.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/es/checkout/context/CheckoutProvider.js +16 -4
  2. package/es/checkout/context/SessionContext.d.ts +2 -0
  3. package/es/checkout/core/crossSell.d.ts +3 -2
  4. package/es/checkout/core/crossSell.js +24 -8
  5. package/es/checkout/core/lineItems.d.ts +6 -5
  6. package/es/checkout/core/lineItems.js +48 -17
  7. package/es/checkout/core/promotion.d.ts +1 -1
  8. package/es/checkout/core/promotion.js +2 -1
  9. package/es/checkout/hooks/useBillingInterval.js +8 -5
  10. package/es/checkout/hooks/useCheckout.js +14 -9
  11. package/es/checkout/hooks/useCrossSell.js +11 -3
  12. package/es/checkout/hooks/useLineItems.js +36 -9
  13. package/es/checkout/hooks/usePaymentMethod.d.ts +1 -1
  14. package/es/checkout/hooks/usePaymentMethod.js +14 -4
  15. package/es/checkout/hooks/useUpsell.js +3 -3
  16. package/lib/checkout/context/CheckoutProvider.js +13 -4
  17. package/lib/checkout/context/SessionContext.d.ts +2 -0
  18. package/lib/checkout/core/crossSell.d.ts +3 -2
  19. package/lib/checkout/core/crossSell.js +36 -8
  20. package/lib/checkout/core/lineItems.d.ts +6 -5
  21. package/lib/checkout/core/lineItems.js +72 -18
  22. package/lib/checkout/core/promotion.d.ts +1 -1
  23. package/lib/checkout/core/promotion.js +4 -1
  24. package/lib/checkout/hooks/useBillingInterval.js +10 -5
  25. package/lib/checkout/hooks/useCheckout.js +18 -9
  26. package/lib/checkout/hooks/useCrossSell.js +5 -3
  27. package/lib/checkout/hooks/useLineItems.js +10 -8
  28. package/lib/checkout/hooks/usePaymentMethod.d.ts +1 -1
  29. package/lib/checkout/hooks/usePaymentMethod.js +20 -4
  30. package/lib/checkout/hooks/useUpsell.js +5 -3
  31. package/package.json +3 -3
  32. package/src/checkout/context/CheckoutProvider.tsx +18 -5
  33. package/src/checkout/context/SessionContext.ts +2 -0
  34. package/src/checkout/core/crossSell.ts +29 -8
  35. package/src/checkout/core/lineItems.ts +62 -18
  36. package/src/checkout/core/promotion.ts +6 -2
  37. package/src/checkout/hooks/useBillingInterval.ts +8 -5
  38. package/src/checkout/hooks/useCheckout.ts +15 -10
  39. package/src/checkout/hooks/useCrossSell.ts +11 -3
  40. package/src/checkout/hooks/useLineItems.ts +42 -9
  41. package/src/checkout/hooks/usePaymentMethod.ts +17 -5
  42. package/src/checkout/hooks/useUpsell.ts +3 -3
@@ -1,4 +1,5 @@
1
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>;
2
+ import type { SessionData } from '../hooks/useCheckoutSession';
3
+ export declare function addCrossSellItem(sessionId: string, crossSellItemId: string, session: TCheckoutSessionExpanded | undefined | null, currencyId: string | null | undefined, refresh: (force?: boolean) => Promise<void>, sessionData?: SessionData | null, setSessionData?: (data: SessionData) => void): Promise<void>;
4
+ export declare function removeCrossSellItem(sessionId: string, session: TCheckoutSessionExpanded | undefined | null, currencyId: string | null | undefined, refresh: (force?: boolean) => Promise<void>, sessionData?: SessionData | null, setSessionData?: (data: SessionData) => void): Promise<void>;
4
5
  export declare function fetchCrossSellItem(sessionId: string): Promise<TPrice | null>;
@@ -10,17 +10,45 @@ var _api = _interopRequireWildcard(require("../../shared/api"));
10
10
  var _lineItems = require("./lineItems");
11
11
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
12
12
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
13
- async function addCrossSellItem(sessionId, crossSellItemId, session, currencyId, refresh) {
14
- await _api.default.put(_api.API.CROSS_SELL(sessionId), {
13
+ async function addCrossSellItem(sessionId, crossSellItemId, session, currencyId, refresh, sessionData, setSessionData) {
14
+ const {
15
+ data
16
+ } = await _api.default.put(_api.API.CROSS_SELL(sessionId), {
15
17
  to: crossSellItemId
16
18
  });
17
- await (0, _lineItems.recalculatePromotionIfNeeded)(session, sessionId, currencyId);
18
- await refresh(true);
19
+ let finalSession = data;
20
+ if (data.discounts?.length) {
21
+ const recalculated = await (0, _lineItems.recalculatePromotionIfNeeded)(session, sessionId, currencyId);
22
+ if (recalculated) finalSession = recalculated;
23
+ }
24
+ if (sessionData && setSessionData) {
25
+ setSessionData({
26
+ ...sessionData,
27
+ checkoutSession: finalSession,
28
+ quotes: void 0
29
+ });
30
+ } else {
31
+ await refresh(true);
32
+ }
19
33
  }
20
- async function removeCrossSellItem(sessionId, session, currencyId, refresh) {
21
- await _api.default.delete(_api.API.CROSS_SELL(sessionId));
22
- await (0, _lineItems.recalculatePromotionIfNeeded)(session, sessionId, currencyId);
23
- await refresh(true);
34
+ async function removeCrossSellItem(sessionId, session, currencyId, refresh, sessionData, setSessionData) {
35
+ const {
36
+ data
37
+ } = await _api.default.delete(_api.API.CROSS_SELL(sessionId));
38
+ let finalSession = data;
39
+ if (data.discounts?.length) {
40
+ const recalculated = await (0, _lineItems.recalculatePromotionIfNeeded)(session, sessionId, currencyId);
41
+ if (recalculated) finalSession = recalculated;
42
+ }
43
+ if (sessionData && setSessionData) {
44
+ setSessionData({
45
+ ...sessionData,
46
+ checkoutSession: finalSession,
47
+ quotes: void 0
48
+ });
49
+ } else {
50
+ await refresh(true);
51
+ }
24
52
  }
25
53
  const pendingFetches = /* @__PURE__ */new Map();
26
54
  function fetchCrossSellItem(sessionId) {
@@ -1,7 +1,8 @@
1
1
  import type { TCheckoutSessionExpanded, TLineItemExpanded, TPrice } from '@blocklet/payment-types';
2
- export declare function recalculatePromotionIfNeeded(session: TCheckoutSessionExpanded | undefined | null, sessionId: string, currencyId: string | null | undefined): Promise<void>;
3
- export declare function adjustQuantity(sessionId: string, itemId: string, qty: number, currencyId: string | null | undefined, session: TCheckoutSessionExpanded | undefined | null, refresh: (force?: boolean) => Promise<void>): Promise<void>;
4
- export declare function performUpsell(sessionId: string, fromId: string, toId: string, session: TCheckoutSessionExpanded | undefined | null, currencyId: string | null | undefined, refresh: (force?: boolean) => Promise<void>): Promise<void>;
5
- export declare function performDownsell(sessionId: string, priceId: string, session: TCheckoutSessionExpanded | undefined | null, currencyId: string | null | undefined, refresh: (force?: boolean) => Promise<void>): Promise<void>;
6
- export declare function changeDonationAmount(sessionId: string, priceId: string, amount: string, session: TCheckoutSessionExpanded | undefined | null, currencyId: string | null | undefined, refresh: (force?: boolean) => Promise<void>): Promise<void>;
2
+ import type { SessionData } from '../hooks/useCheckoutSession';
3
+ export declare function recalculatePromotionIfNeeded(session: TCheckoutSessionExpanded | undefined | null, sessionId: string, currencyId: string | null | undefined): Promise<TCheckoutSessionExpanded | null>;
4
+ export declare function adjustQuantity(sessionId: string, itemId: string, qty: number, currencyId: string | null | undefined, session: TCheckoutSessionExpanded | undefined | null, refresh: (force?: boolean) => Promise<void>, sessionData?: SessionData | null, setSessionData?: (data: SessionData) => void): Promise<void>;
5
+ export declare function performUpsell(sessionId: string, fromId: string, toId: string, session: TCheckoutSessionExpanded | undefined | null, currencyId: string | null | undefined, refresh: (force?: boolean) => Promise<void>, sessionData?: SessionData | null, setSessionData?: (data: SessionData) => void): Promise<void>;
6
+ export declare function performDownsell(sessionId: string, priceId: string, session: TCheckoutSessionExpanded | undefined | null, currencyId: string | null | undefined, refresh: (force?: boolean) => Promise<void>, sessionData?: SessionData | null, setSessionData?: (data: SessionData) => void): Promise<void>;
7
+ export declare function changeDonationAmount(sessionId: string, priceId: string, amount: string, session: TCheckoutSessionExpanded | undefined | null, currencyId: string | null | undefined, refresh: (force?: boolean) => Promise<void>, sessionData?: SessionData | null, setSessionData?: (data: SessionData) => void): Promise<void>;
7
8
  export declare function getCrossSellItem(items: TLineItemExpanded[]): TPrice | null;
@@ -14,56 +14,110 @@ var _promotion = require("./promotion");
14
14
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
15
15
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
16
16
  async function recalculatePromotionIfNeeded(session, sessionId, currencyId) {
17
- if (!(0, _promotion.hasAppliedDiscounts)(session)) return;
17
+ if (!(0, _promotion.hasAppliedDiscounts)(session)) return null;
18
18
  try {
19
- await (0, _promotion.recalculatePromotion)(sessionId, currencyId);
20
- } catch {}
19
+ return await (0, _promotion.recalculatePromotion)(sessionId, currencyId);
20
+ } catch {
21
+ return null;
22
+ }
21
23
  }
22
- async function adjustQuantity(sessionId, itemId, qty, currencyId, session, refresh) {
23
- await _api.default.put(_api.API.ADJUST_QUANTITY(sessionId), {
24
+ async function adjustQuantity(sessionId, itemId, qty, currencyId, session, refresh, sessionData, setSessionData) {
25
+ const {
26
+ data
27
+ } = await _api.default.put(_api.API.ADJUST_QUANTITY(sessionId), {
24
28
  itemId,
25
29
  quantity: qty,
26
30
  currency_id: currencyId
27
31
  });
28
- await recalculatePromotionIfNeeded(session, sessionId, currencyId);
29
- await refresh(true);
32
+ let finalSession = data;
33
+ if (data.discounts?.length || (0, _promotion.hasAppliedDiscounts)(session)) {
34
+ const recalculated = await recalculatePromotionIfNeeded(session, sessionId, currencyId);
35
+ if (recalculated) finalSession = recalculated;
36
+ }
37
+ if (sessionData && setSessionData) {
38
+ setSessionData({
39
+ ...sessionData,
40
+ checkoutSession: finalSession,
41
+ quotes: void 0
42
+ });
43
+ } else {
44
+ await refresh(true);
45
+ }
30
46
  }
31
- async function performUpsell(sessionId, fromId, toId, session, currencyId, refresh) {
47
+ async function performUpsell(sessionId, fromId, toId, session, currencyId, refresh, sessionData, setSessionData) {
32
48
  if ((session?.line_items?.length || 0) > 1) {
33
49
  try {
34
50
  await _api.default.delete(_api.API.CROSS_SELL(sessionId));
35
51
  } catch {}
36
52
  }
37
- await _api.default.put(_api.API.UPSELL(sessionId), {
53
+ const {
54
+ data
55
+ } = await _api.default.put(_api.API.UPSELL(sessionId), {
38
56
  from: fromId,
39
57
  to: toId
40
58
  });
41
- await recalculatePromotionIfNeeded(session, sessionId, currencyId);
42
- await refresh(true);
59
+ let finalSession = data;
60
+ if (data.discounts?.length || (0, _promotion.hasAppliedDiscounts)(session)) {
61
+ const recalculated = await recalculatePromotionIfNeeded(session, sessionId, currencyId);
62
+ if (recalculated) finalSession = recalculated;
63
+ }
64
+ if (sessionData && setSessionData) {
65
+ setSessionData({
66
+ ...sessionData,
67
+ checkoutSession: finalSession,
68
+ quotes: void 0
69
+ });
70
+ } else {
71
+ await refresh(true);
72
+ }
43
73
  }
44
- async function performDownsell(sessionId, priceId, session, currencyId, refresh) {
74
+ async function performDownsell(sessionId, priceId, session, currencyId, refresh, sessionData, setSessionData) {
45
75
  if ((session?.line_items?.length || 0) > 1) {
46
76
  try {
47
77
  await _api.default.delete(_api.API.CROSS_SELL(sessionId));
48
78
  } catch {}
49
79
  }
50
- await _api.default.put(_api.API.DOWNSELL(sessionId), {
80
+ const {
81
+ data
82
+ } = await _api.default.put(_api.API.DOWNSELL(sessionId), {
51
83
  from: priceId
52
84
  });
53
- await recalculatePromotionIfNeeded(session, sessionId, currencyId);
54
- await refresh(true);
85
+ let finalSession = data;
86
+ if (data.discounts?.length || (0, _promotion.hasAppliedDiscounts)(session)) {
87
+ const recalculated = await recalculatePromotionIfNeeded(session, sessionId, currencyId);
88
+ if (recalculated) finalSession = recalculated;
89
+ }
90
+ if (sessionData && setSessionData) {
91
+ setSessionData({
92
+ ...sessionData,
93
+ checkoutSession: finalSession,
94
+ quotes: void 0
95
+ });
96
+ } else {
97
+ await refresh(true);
98
+ }
55
99
  }
56
- async function changeDonationAmount(sessionId, priceId, amount, session, currencyId, refresh) {
100
+ async function changeDonationAmount(sessionId, priceId, amount, session, currencyId, refresh, sessionData, setSessionData) {
57
101
  const {
58
102
  data
59
103
  } = await _api.default.put(_api.API.CHANGE_AMOUNT(sessionId), {
60
104
  priceId,
61
105
  amount
62
106
  });
107
+ let finalSession = data;
63
108
  if (data?.discounts?.length) {
64
- await recalculatePromotionIfNeeded(session, sessionId, currencyId);
109
+ const recalculated = await recalculatePromotionIfNeeded(session, sessionId, currencyId);
110
+ if (recalculated) finalSession = recalculated;
111
+ }
112
+ if (sessionData && setSessionData) {
113
+ setSessionData({
114
+ ...sessionData,
115
+ checkoutSession: finalSession,
116
+ quotes: void 0
117
+ });
118
+ } else {
119
+ await refresh(true);
65
120
  }
66
- await refresh(true);
67
121
  }
68
122
  function getCrossSellItem(items) {
69
123
  for (const item of items) {
@@ -5,6 +5,6 @@ export declare function applyPromotionCode(sessionId: string, code: string, curr
5
5
  error?: string;
6
6
  }>;
7
7
  export declare function removePromotionCode(sessionId: string): Promise<void>;
8
- export declare function recalculatePromotion(sessionId: string, currencyId: string | null | undefined): Promise<void>;
8
+ export declare function recalculatePromotion(sessionId: string, currencyId: string | null | undefined): Promise<TCheckoutSessionExpanded>;
9
9
  export declare function isPromotionActive(session: TCheckoutSessionExpanded | undefined | null): boolean;
10
10
  export declare function hasAppliedDiscounts(session: TCheckoutSessionExpanded | undefined | null): boolean;
@@ -49,9 +49,12 @@ async function removePromotionCode(sessionId) {
49
49
  await _api.default.delete(_api.API.REMOVE_PROMOTION(sessionId));
50
50
  }
51
51
  async function recalculatePromotion(sessionId, currencyId) {
52
- await _api.default.post(_api.API.RECALCULATE_PROMOTION_SESSION(sessionId), {
52
+ const {
53
+ data
54
+ } = await _api.default.post(_api.API.RECALCULATE_PROMOTION_SESSION(sessionId), {
53
55
  currency_id: currencyId
54
56
  });
57
+ return data;
55
58
  }
56
59
  function isPromotionActive(session) {
57
60
  return session?.allow_promotion_codes !== false;
@@ -16,23 +16,26 @@ function useBillingInterval() {
16
16
  items,
17
17
  session,
18
18
  effectiveSessionId,
19
- refresh
19
+ refresh,
20
+ sessionData,
21
+ setSessionData
20
22
  } = (0, _SessionContext.useSessionContext)();
21
23
  const {
22
24
  currency
23
25
  } = (0, _PaymentMethodContext.usePaymentMethodContext)();
24
26
  const currencyId = currency?.id || null;
25
27
  const [switching, setSwitching] = (0, _react.useState)(false);
28
+ const [pendingInterval, setPendingInterval] = (0, _react.useState)(null);
26
29
  const upsell = (0, _ahooks.useMemoizedFn)(async (fromId, toId) => {
27
30
  try {
28
- await (0, _lineItems.performUpsell)(effectiveSessionId, fromId, toId, session, currencyId, refresh);
31
+ await (0, _lineItems.performUpsell)(effectiveSessionId, fromId, toId, session, currencyId, refresh, sessionData, setSessionData);
29
32
  } catch (err) {
30
33
  console.error("Failed to upsell:", (0, _checkoutAugmented.getErrorMessage)(err));
31
34
  }
32
35
  });
33
36
  const downsell = (0, _ahooks.useMemoizedFn)(async priceId => {
34
37
  try {
35
- await (0, _lineItems.performDownsell)(effectiveSessionId, priceId, session, currencyId, refresh);
38
+ await (0, _lineItems.performDownsell)(effectiveSessionId, priceId, session, currencyId, refresh, sessionData, setSessionData);
36
39
  } catch (err) {
37
40
  console.error("Failed to downsell:", (0, _checkoutAugmented.getErrorMessage)(err));
38
41
  }
@@ -41,12 +44,13 @@ function useBillingInterval() {
41
44
  const parsed = (0, _billingInterval.parseBillingInterval)(items);
42
45
  if (!parsed) return null;
43
46
  return {
44
- current: parsed.current,
47
+ current: pendingInterval || parsed.current,
45
48
  available: parsed.available,
46
49
  switching,
47
50
  switch: async interval => {
48
51
  const target = parsed.available.find(a => a.interval === interval);
49
52
  if (!target || switching) return;
53
+ setPendingInterval(interval);
50
54
  setSwitching(true);
51
55
  try {
52
56
  if (!parsed.firstItem.upsell_price_id && target.priceId) {
@@ -56,8 +60,9 @@ function useBillingInterval() {
56
60
  }
57
61
  } finally {
58
62
  setSwitching(false);
63
+ setPendingInterval(null);
59
64
  }
60
65
  }
61
66
  };
62
- }, [items, effectiveSessionId, switching]);
67
+ }, [items, effectiveSessionId, switching, pendingInterval]);
63
68
  }
@@ -21,6 +21,7 @@ function useCheckout(sessionId) {
21
21
  error,
22
22
  errorCode,
23
23
  refresh,
24
+ setSessionData,
24
25
  sessionData,
25
26
  resolvedSessionId,
26
27
  vendorCount,
@@ -30,12 +31,12 @@ function useCheckout(sessionId) {
30
31
  } = (0, _useCheckoutSession.useCheckoutSession)(sessionId);
31
32
  const session = sessionData?.checkoutSession;
32
33
  const effectiveSessionId = resolvedSessionId || sessionId;
33
- const paymentMethodHook = (0, _usePaymentMethod.usePaymentMethod)(sessionData, effectiveSessionId, refresh);
34
+ const paymentMethodHook = (0, _usePaymentMethod.usePaymentMethod)(sessionData, effectiveSessionId, refresh, setSessionData);
34
35
  const pricingHook = (0, _usePricing.usePricing)(sessionData, effectiveSessionId, paymentMethodHook.currency, paymentMethodHook.isStripe, refresh, paymentMethodHook.current?.type || null);
35
36
  const formHook = (0, _useCustomerForm.useCustomerForm)(sessionData, paymentMethodHook.currency?.id || null, paymentMethodHook.current?.id || null);
36
37
  const isDonation = session?.submit_type === "donate";
37
38
  const submitHook = (0, _useSubmit.useSubmit)(sessionData, effectiveSessionId, paymentMethodHook.currency?.id || null, paymentMethodHook.isStripe, paymentMethodHook.isCredit, isDonation, formHook.values, formHook.validate, refresh);
38
- const items = session?.line_items || [];
39
+ const items = (0, _react.useMemo)(() => session?.line_items || [], [session?.line_items]);
39
40
  const currencyId = paymentMethodHook.currency?.id || null;
40
41
  const prevCurrencyRef = (0, _react.useRef)(null);
41
42
  (0, _react.useEffect)(() => {
@@ -43,26 +44,34 @@ function useCheckout(sessionId) {
43
44
  if (!currId || !session) return;
44
45
  if (prevCurrencyRef.current === null || currId !== prevCurrencyRef.current) {
45
46
  prevCurrencyRef.current = currId;
46
- (0, _lineItems.recalculatePromotionIfNeeded)(session, effectiveSessionId, currId).then(() => refresh(true));
47
+ (0, _lineItems.recalculatePromotionIfNeeded)(session, effectiveSessionId, currId).then(recalculated => {
48
+ if (recalculated && sessionData) {
49
+ setSessionData({
50
+ ...sessionData,
51
+ checkoutSession: recalculated,
52
+ quotes: void 0
53
+ });
54
+ }
55
+ });
47
56
  }
48
57
  }, [paymentMethodHook.currency?.id, session?.id]);
49
58
  const updateQuantity = (0, _ahooks.useMemoizedFn)(async (itemId, qty) => {
50
59
  try {
51
- await (0, _lineItems.adjustQuantity)(effectiveSessionId, itemId, qty, currencyId, session, refresh);
60
+ await (0, _lineItems.adjustQuantity)(effectiveSessionId, itemId, qty, currencyId, session, refresh, sessionData, setSessionData);
52
61
  } catch (err) {
53
62
  console.error("Failed to update quantity:", (0, _checkoutAugmented.getErrorMessage)(err));
54
63
  }
55
64
  });
56
65
  const upsell = (0, _ahooks.useMemoizedFn)(async (fromId, toId) => {
57
66
  try {
58
- await (0, _lineItems.performUpsell)(effectiveSessionId, fromId, toId, session, currencyId, refresh);
67
+ await (0, _lineItems.performUpsell)(effectiveSessionId, fromId, toId, session, currencyId, refresh, sessionData, setSessionData);
59
68
  } catch (err) {
60
69
  console.error("Failed to upsell:", (0, _checkoutAugmented.getErrorMessage)(err));
61
70
  }
62
71
  });
63
72
  const downsell = (0, _ahooks.useMemoizedFn)(async priceId => {
64
73
  try {
65
- await (0, _lineItems.performDownsell)(effectiveSessionId, priceId, session, currencyId, refresh);
74
+ await (0, _lineItems.performDownsell)(effectiveSessionId, priceId, session, currencyId, refresh, sessionData, setSessionData);
66
75
  } catch (err) {
67
76
  console.error("Failed to downsell:", (0, _checkoutAugmented.getErrorMessage)(err));
68
77
  }
@@ -87,14 +96,14 @@ function useCheckout(sessionId) {
87
96
  const addCrossSell = (0, _ahooks.useMemoizedFn)(async () => {
88
97
  if (!crossSellItem) return;
89
98
  try {
90
- await (0, _crossSell.addCrossSellItem)(effectiveSessionId, crossSellItem.id, session, currencyId, refresh);
99
+ await (0, _crossSell.addCrossSellItem)(effectiveSessionId, crossSellItem.id, session, currencyId, refresh, sessionData, setSessionData);
91
100
  } catch (err) {
92
101
  console.error("Failed to add cross-sell:", (0, _checkoutAugmented.getErrorMessage)(err));
93
102
  }
94
103
  });
95
104
  const removeCrossSell = (0, _ahooks.useMemoizedFn)(async () => {
96
105
  try {
97
- await (0, _crossSell.removeCrossSellItem)(effectiveSessionId, session, currencyId, refresh);
106
+ await (0, _crossSell.removeCrossSellItem)(effectiveSessionId, session, currencyId, refresh, sessionData, setSessionData);
98
107
  } catch (err) {
99
108
  console.error("Failed to remove cross-sell:", (0, _checkoutAugmented.getErrorMessage)(err));
100
109
  }
@@ -119,7 +128,7 @@ function useCheckout(sessionId) {
119
128
  const setDonationAmount = (0, _ahooks.useMemoizedFn)(async (priceId, amount) => {
120
129
  if (!isDonation) return;
121
130
  try {
122
- await (0, _lineItems.changeDonationAmount)(effectiveSessionId, priceId, amount, session, currencyId, refresh);
131
+ await (0, _lineItems.changeDonationAmount)(effectiveSessionId, priceId, amount, session, currencyId, refresh, sessionData, setSessionData);
123
132
  } catch (err) {
124
133
  console.error("Failed to change amount:", (0, _checkoutAugmented.getErrorMessage)(err));
125
134
  }
@@ -16,7 +16,9 @@ function useCrossSell() {
16
16
  items,
17
17
  session,
18
18
  effectiveSessionId,
19
- refresh
19
+ refresh,
20
+ sessionData,
21
+ setSessionData
20
22
  } = (0, _SessionContext.useSessionContext)();
21
23
  const {
22
24
  currency
@@ -53,7 +55,7 @@ function useCrossSell() {
53
55
  const crossSellItemPrice = (0, _lineItems.getCrossSellItem)(items);
54
56
  if (!crossSellItemPrice) return;
55
57
  try {
56
- await (0, _crossSell.addCrossSellItem)(effectiveSessionId, crossSellItemPrice.id, session, currencyId, refresh);
58
+ await (0, _crossSell.addCrossSellItem)(effectiveSessionId, crossSellItemPrice.id, session, currencyId, refresh, sessionData, setSessionData);
57
59
  } catch (err) {
58
60
  console.error("Failed to add cross-sell:", (0, _checkoutAugmented.getErrorMessage)(err));
59
61
  }
@@ -61,7 +63,7 @@ function useCrossSell() {
61
63
  const remove = (0, _ahooks.useMemoizedFn)(async () => {
62
64
  if (session?.status === "complete") return;
63
65
  try {
64
- await (0, _crossSell.removeCrossSellItem)(effectiveSessionId, session, currencyId, refresh);
66
+ await (0, _crossSell.removeCrossSellItem)(effectiveSessionId, session, currencyId, refresh, sessionData, setSessionData);
65
67
  } catch (err) {
66
68
  console.error("Failed to remove cross-sell:", (0, _checkoutAugmented.getErrorMessage)(err));
67
69
  }
@@ -17,7 +17,9 @@ function useLineItems() {
17
17
  session,
18
18
  effectiveSessionId,
19
19
  isDonation,
20
- refresh
20
+ refresh,
21
+ sessionData,
22
+ setSessionData
21
23
  } = (0, _SessionContext.useSessionContext)();
22
24
  const {
23
25
  currency
@@ -34,7 +36,7 @@ function useLineItems() {
34
36
  const qty = Math.max(1, parseInt(qtyStr, 10));
35
37
  if (Number.isFinite(qty) && qty !== item.quantity) {
36
38
  defaultQtyApplied.current = true;
37
- (0, _lineItems.adjustQuantity)(effectiveSessionId, item.price_id, qty, currencyId, session, refresh);
39
+ (0, _lineItems.adjustQuantity)(effectiveSessionId, item.price_id, qty, currencyId, session, refresh, sessionData, setSessionData);
38
40
  break;
39
41
  }
40
42
  }
@@ -44,18 +46,18 @@ function useLineItems() {
44
46
  const updateQuantity = (0, _ahooks.useMemoizedFn)(async (itemId, qty) => {
45
47
  if (session?.status === "complete") return;
46
48
  try {
47
- await (0, _lineItems.adjustQuantity)(effectiveSessionId, itemId, qty, currencyId, session, refresh);
49
+ await (0, _lineItems.adjustQuantity)(effectiveSessionId, itemId, qty, currencyId, session, refresh, sessionData, setSessionData);
48
50
  } catch (err) {
49
51
  console.error("Failed to update quantity:", (0, _checkoutAugmented.getErrorMessage)(err));
50
52
  }
51
53
  });
52
54
  const upsell = (0, _ahooks.useMemoizedFn)(async (fromId, toId) => {
53
55
  if (session?.status === "complete") return;
54
- await (0, _lineItems.performUpsell)(effectiveSessionId, fromId, toId, session, currencyId, refresh);
56
+ await (0, _lineItems.performUpsell)(effectiveSessionId, fromId, toId, session, currencyId, refresh, sessionData, setSessionData);
55
57
  });
56
58
  const downsell = (0, _ahooks.useMemoizedFn)(async priceId => {
57
59
  if (session?.status === "complete") return;
58
- await (0, _lineItems.performDownsell)(effectiveSessionId, priceId, session, currencyId, refresh);
60
+ await (0, _lineItems.performDownsell)(effectiveSessionId, priceId, session, currencyId, refresh, sessionData, setSessionData);
59
61
  });
60
62
  const embeddedCrossSellItem = (0, _react.useMemo)(() => (0, _lineItems.getCrossSellItem)(items), [items]);
61
63
  const [fetchedCrossSellItem, setFetchedCrossSellItem] = (0, _react.useState)(null);
@@ -87,7 +89,7 @@ function useLineItems() {
87
89
  if (session?.status === "complete") return;
88
90
  if (!crossSellItem) return;
89
91
  try {
90
- await (0, _crossSell.addCrossSellItem)(effectiveSessionId, crossSellItem.id, session, currencyId, refresh);
92
+ await (0, _crossSell.addCrossSellItem)(effectiveSessionId, crossSellItem.id, session, currencyId, refresh, sessionData, setSessionData);
91
93
  } catch (err) {
92
94
  console.error("Failed to add cross-sell:", (0, _checkoutAugmented.getErrorMessage)(err));
93
95
  }
@@ -95,7 +97,7 @@ function useLineItems() {
95
97
  const removeCrossSell = (0, _ahooks.useMemoizedFn)(async () => {
96
98
  if (session?.status === "complete") return;
97
99
  try {
98
- await (0, _crossSell.removeCrossSellItem)(effectiveSessionId, session, currencyId, refresh);
100
+ await (0, _crossSell.removeCrossSellItem)(effectiveSessionId, session, currencyId, refresh, sessionData, setSessionData);
99
101
  } catch (err) {
100
102
  console.error("Failed to remove cross-sell:", (0, _checkoutAugmented.getErrorMessage)(err));
101
103
  }
@@ -104,7 +106,7 @@ function useLineItems() {
104
106
  if (session?.status === "complete") return;
105
107
  if (!isDonation) return;
106
108
  try {
107
- await (0, _lineItems.changeDonationAmount)(effectiveSessionId, priceId, amount, session, currencyId, refresh);
109
+ await (0, _lineItems.changeDonationAmount)(effectiveSessionId, priceId, amount, session, currencyId, refresh, sessionData, setSessionData);
108
110
  } catch (err) {
109
111
  console.error("Failed to change amount:", (0, _checkoutAugmented.getErrorMessage)(err));
110
112
  }
@@ -23,4 +23,4 @@ export interface UsePaymentMethodReturn {
23
23
  status: 'idle' | 'ready' | 'processing' | 'succeeded' | 'failed';
24
24
  } | null;
25
25
  }
26
- export declare function usePaymentMethod(sessionData: SessionData | null, sessionId: string, refreshSession: (force?: boolean) => Promise<void>): UsePaymentMethodReturn;
26
+ export declare function usePaymentMethod(sessionData: SessionData | null, sessionId: string, refreshSession: (force?: boolean) => Promise<void>, setSessionData?: (data: SessionData) => void): UsePaymentMethodReturn;
@@ -9,10 +9,11 @@ var _ahooks = require("ahooks");
9
9
  var _checkoutAugmented = require("../../types/checkout-augmented");
10
10
  var _api = _interopRequireWildcard(require("../../shared/api"));
11
11
  var _paymentMethod = require("../core/paymentMethod");
12
+ var _lineItems = require("../core/lineItems");
12
13
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
13
14
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
14
- function usePaymentMethod(sessionData, sessionId, refreshSession) {
15
- const methods = sessionData?.paymentMethods || [];
15
+ function usePaymentMethod(sessionData, sessionId, refreshSession, setSessionData) {
16
+ const methods = (0, _react.useMemo)(() => sessionData?.paymentMethods || [], [sessionData?.paymentMethods]);
16
17
  const session = sessionData?.checkoutSession;
17
18
  const [currencyId, setCurrencyId] = (0, _react.useState)(() => (0, _paymentMethod.getInitialCurrencyId)(session, methods));
18
19
  const [switching, setSwitching] = (0, _react.useState)(false);
@@ -47,7 +48,9 @@ function usePaymentMethod(sessionData, sessionId, refreshSession) {
47
48
  if (!method || !sessionId) return;
48
49
  setSwitching(true);
49
50
  try {
50
- await _api.default.put(_api.API.SWITCH_CURRENCY(sessionId), {
51
+ const {
52
+ data
53
+ } = await _api.default.put(_api.API.SWITCH_CURRENCY(sessionId), {
51
54
  currency_id: newCurrencyId,
52
55
  payment_method_id: method.id
53
56
  });
@@ -55,7 +58,20 @@ function usePaymentMethod(sessionData, sessionId, refreshSession) {
55
58
  try {
56
59
  localStorage.setItem((0, _paymentMethod.getCurrencyStorageKey)(session?.user?.did), newCurrencyId);
57
60
  } catch {}
58
- await refreshSession(true);
61
+ let finalSession = data;
62
+ if (data.discounts?.length) {
63
+ const recalculated = await (0, _lineItems.recalculatePromotionIfNeeded)(session, sessionId, newCurrencyId);
64
+ if (recalculated) finalSession = recalculated;
65
+ }
66
+ if (sessionData && setSessionData) {
67
+ setSessionData({
68
+ ...sessionData,
69
+ checkoutSession: finalSession,
70
+ quotes: void 0
71
+ });
72
+ } else {
73
+ await refreshSession(true);
74
+ }
59
75
  } catch (err) {
60
76
  console.error("Failed to switch currency:", (0, _checkoutAugmented.getErrorMessage)(err));
61
77
  if (session?.currency_id) {
@@ -13,7 +13,9 @@ function useUpsell() {
13
13
  const {
14
14
  session,
15
15
  effectiveSessionId,
16
- refresh
16
+ refresh,
17
+ sessionData,
18
+ setSessionData
17
19
  } = (0, _SessionContext.useSessionContext)();
18
20
  const {
19
21
  currency
@@ -21,14 +23,14 @@ function useUpsell() {
21
23
  const currencyId = currency?.id || null;
22
24
  const upsell = (0, _ahooks.useMemoizedFn)(async (fromId, toId) => {
23
25
  try {
24
- await (0, _lineItems.performUpsell)(effectiveSessionId, fromId, toId, session, currencyId, refresh);
26
+ await (0, _lineItems.performUpsell)(effectiveSessionId, fromId, toId, session, currencyId, refresh, sessionData, setSessionData);
25
27
  } catch (err) {
26
28
  console.error("Failed to upsell:", (0, _checkoutAugmented.getErrorMessage)(err));
27
29
  }
28
30
  });
29
31
  const downsell = (0, _ahooks.useMemoizedFn)(async priceId => {
30
32
  try {
31
- await (0, _lineItems.performDownsell)(effectiveSessionId, priceId, session, currencyId, refresh);
33
+ await (0, _lineItems.performDownsell)(effectiveSessionId, priceId, session, currencyId, refresh, sessionData, setSessionData);
32
34
  } catch (err) {
33
35
  console.error("Failed to downsell:", (0, _checkoutAugmented.getErrorMessage)(err));
34
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/payment-react-headless",
3
- "version": "1.26.0",
3
+ "version": "1.26.2",
4
4
  "description": "Headless React hooks for payment-kit checkout",
5
5
  "keywords": [
6
6
  "react",
@@ -35,7 +35,7 @@
35
35
  "dependencies": {
36
36
  "@arcblock/ws": "^1.28.5",
37
37
  "@blocklet/js-sdk": "workspace:*",
38
- "@blocklet/payment-types": "1.26.0",
38
+ "@blocklet/payment-types": "1.26.2",
39
39
  "@ocap/util": "^1.28.5",
40
40
  "ahooks": "^3.8.5",
41
41
  "google-libphonenumber": "^3.2.42",
@@ -60,5 +60,5 @@
60
60
  "publishConfig": {
61
61
  "access": "public"
62
62
  },
63
- "gitHead": "9585ec8bc077fc5f8a8c5946d05436b10576e145"
63
+ "gitHead": "71242a68d27d56666487176425153dc08071960f"
64
64
  }
@@ -38,12 +38,13 @@ export function CheckoutProvider({ sessionId, children }: CheckoutProviderProps)
38
38
 
39
39
  const session = sessionData?.checkoutSession;
40
40
  const effectiveSessionId = resolvedSessionId || sessionId;
41
- const items = (session?.line_items || []) as TLineItemExpanded[];
41
+ const items = useMemo(() => (session?.line_items || []) as TLineItemExpanded[], [session?.line_items]);
42
42
  const isDonation = session?.submit_type === 'donate';
43
43
 
44
44
  const sessionValue = useMemo<SessionContextValue>(
45
45
  () => ({
46
46
  sessionData,
47
+ setSessionData,
47
48
  sessionId,
48
49
  effectiveSessionId,
49
50
  isLoading,
@@ -60,6 +61,7 @@ export function CheckoutProvider({ sessionId, children }: CheckoutProviderProps)
60
61
  }),
61
62
  [
62
63
  sessionData,
64
+ setSessionData,
63
65
  sessionId,
64
66
  effectiveSessionId,
65
67
  isLoading,
@@ -77,16 +79,20 @@ export function CheckoutProvider({ sessionId, children }: CheckoutProviderProps)
77
79
  );
78
80
 
79
81
  // 2. Payment method layer
80
- const paymentMethodHook = usePaymentMethodHook(sessionData, effectiveSessionId, refresh);
82
+ const paymentMethodHook = usePaymentMethodHook(sessionData, effectiveSessionId, refresh, setSessionData);
81
83
 
82
- // Recalculate promotion when currency changes
84
+ // Recalculate promotion when currency changes — use response directly (no extra GET)
83
85
  const prevCurrencyRef = useRef<string | null>(null);
84
86
  useEffect(() => {
85
87
  const currId = paymentMethodHook.currency?.id || null;
86
88
  if (!currId || !session || session.status === 'complete') return;
87
89
  if (prevCurrencyRef.current === null || currId !== prevCurrencyRef.current) {
88
90
  prevCurrencyRef.current = currId;
89
- recalculatePromotionIfNeeded(session, effectiveSessionId, currId).then(() => refresh(true));
91
+ recalculatePromotionIfNeeded(session, effectiveSessionId, currId).then((recalculated) => {
92
+ if (recalculated && sessionData) {
93
+ setSessionData({ ...sessionData, checkoutSession: recalculated, quotes: undefined });
94
+ }
95
+ });
90
96
  }
91
97
  }, [paymentMethodHook.currency?.id, session?.id]); // eslint-disable-line react-hooks/exhaustive-deps
92
98
 
@@ -219,7 +225,14 @@ export function CheckoutProvider({ sessionId, children }: CheckoutProviderProps)
219
225
  if (intervalRef.current) clearInterval(intervalRef.current);
220
226
  document.removeEventListener('visibilitychange', handleVisibility);
221
227
  };
222
- }, [hasDynamicPricing, paymentMethodHook.isStripe, effectiveSessionId, paymentMethodHook.currency?.id, session?.status]); // eslint-disable-line react-hooks/exhaustive-deps
228
+ // eslint-disable-next-line react-hooks/exhaustive-deps
229
+ }, [
230
+ hasDynamicPricing,
231
+ paymentMethodHook.isStripe,
232
+ effectiveSessionId,
233
+ paymentMethodHook.currency?.id,
234
+ session?.status,
235
+ ]);
223
236
 
224
237
  const exchangeRateValue = useMemo<ExchangeRateContextValue>(
225
238
  () => ({