@ecomiq/storefront 2.1.4 → 3.0.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 (310) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +377 -76
  3. package/dist/account-Dx4e3syi.js +258 -0
  4. package/dist/account-forms-AVk71ko7.js +39 -0
  5. package/dist/account-forms.d.ts +13 -0
  6. package/dist/account.d.ts +240 -0
  7. package/dist/address-defaults-BqjRhS82.js +14 -0
  8. package/dist/address-defaults.d.ts +11 -0
  9. package/dist/analytics/analytics-CG1Qagwl.js +148 -0
  10. package/dist/analytics/analytics.d.ts +1 -0
  11. package/dist/analytics/config-DJr-GRhs.js +62 -0
  12. package/dist/analytics/config.d.ts +22 -0
  13. package/dist/analytics/consent-B13rBpBs.js +57 -0
  14. package/dist/analytics/consent-banner-9QvRRy8v.js +239 -0
  15. package/dist/analytics/consent-banner.d.ts +20 -0
  16. package/dist/analytics/cookies-pLozllyB.js +100 -0
  17. package/dist/analytics/cookies.d.ts +6 -0
  18. package/dist/analytics/destinations-iECrlLIr.js +144 -0
  19. package/dist/analytics/events-Dt81tIp1.js +102 -0
  20. package/dist/analytics/events.d.ts +51 -0
  21. package/dist/analytics/tracker-p4yaoOyk.js +51 -0
  22. package/dist/api-cache-DsuIn5hX.js +159 -0
  23. package/dist/bank-accounts-DlEczQQ5.js +81 -0
  24. package/dist/bank-accounts.d.ts +4 -0
  25. package/dist/captcha-DAqaIx8m.js +117 -0
  26. package/dist/captcha.d.ts +34 -0
  27. package/dist/cart-D7DxZ2lD.js +30 -0
  28. package/dist/cart-actions-Bsub_FFG.js +116 -0
  29. package/dist/cart-actions.d.ts +15 -0
  30. package/dist/cart-drawer-B7PPsR69.js +22 -0
  31. package/dist/cart-drawer.d.ts +9 -0
  32. package/dist/cart-form-y9P1r_EX.js +81 -0
  33. package/dist/cart-form.d.ts +24 -0
  34. package/dist/cart-summary-ur2VWKr-.js +89 -0
  35. package/dist/cart-summary.d.ts +31 -0
  36. package/dist/cart.d.ts +4 -0
  37. package/dist/catalog-7gJ-l5hW.js +67 -0
  38. package/dist/catalog-hooks-B7ZP4wYY.js +312 -0
  39. package/dist/catalog-hooks.d.ts +111 -0
  40. package/dist/catalog.d.ts +5 -0
  41. package/dist/checkout-DVWSXANi.js +474 -0
  42. package/dist/checkout-form-CbOIdENC.js +131 -0
  43. package/dist/checkout-form.d.ts +20 -0
  44. package/dist/checkout.d.ts +86 -0
  45. package/dist/complaint-book-BvwLvfx0.js +117 -0
  46. package/dist/complaint-book.d.ts +62 -0
  47. package/dist/culqi-authentication-BGr20vAJ.js +204 -0
  48. package/dist/culqi-authentication.d.ts +15 -0
  49. package/dist/culqi-checkout-CTXpKgp_.js +55 -0
  50. package/dist/culqi-three-ds-HP3mfrvV.js +33 -0
  51. package/dist/culqi-three-ds.d.ts +29 -0
  52. package/dist/customer-avatar-CJXSDroK.js +12 -0
  53. package/dist/customer-avatar.d.ts +5 -0
  54. package/dist/customer-cookies-DHOgFAMV.js +45 -0
  55. package/dist/customer-csrf-C1oDX-Py.js +17 -0
  56. package/dist/customer-csrf.d.ts +3 -0
  57. package/dist/customer-document-fields-C1K-_DAD.js +70 -0
  58. package/dist/customer-document-fields.d.ts +9 -0
  59. package/dist/customer-login-form-C7AC6vXd.js +144 -0
  60. package/dist/customer-login-form.d.ts +17 -0
  61. package/dist/customer-registration-form-BEm_NEfP.js +93 -0
  62. package/dist/customer-registration-form.d.ts +8 -0
  63. package/dist/document-url-CGsAl5Tf.js +9 -0
  64. package/dist/documents-T1mzc_j8.js +38 -0
  65. package/dist/documents.d.ts +16 -0
  66. package/dist/entries/account.d.ts +11 -0
  67. package/dist/entries/account.js +10 -0
  68. package/dist/entries/analytics.d.ts +6 -0
  69. package/dist/entries/analytics.js +5 -0
  70. package/dist/entries/cart.d.ts +8 -0
  71. package/dist/entries/cart.js +7 -0
  72. package/dist/entries/checkout.d.ts +12 -0
  73. package/dist/entries/checkout.js +11 -0
  74. package/dist/entries/order.d.ts +11 -0
  75. package/dist/entries/order.js +11 -0
  76. package/dist/favicon-YpyhVJ9H.js +28 -0
  77. package/dist/favicon.d.ts +4 -0
  78. package/dist/form-BtQcbXlJ.js +18 -0
  79. package/dist/form-contracts-BNpHaTGJ.js +166 -0
  80. package/dist/form-contracts.d.ts +42 -0
  81. package/dist/form.d.ts +3 -0
  82. package/dist/hidden-CCWtOWKR.js +3 -0
  83. package/dist/hidden.d.ts +4 -0
  84. package/dist/index.d.ts +50 -0
  85. package/dist/index.js +50 -0
  86. package/dist/invoice-fields-BG0HjL6T.js +103 -0
  87. package/dist/invoice-fields.d.ts +39 -0
  88. package/dist/links-kFxBZfdb.js +96 -0
  89. package/dist/links.d.ts +21 -0
  90. package/dist/mercadopago-checkout-DC7RXa8k.js +27 -0
  91. package/dist/mercadopago-dialog-UTQev0MG.js +180 -0
  92. package/dist/mercadopago-dialog.d.ts +21 -0
  93. package/dist/money-DtGOtDri.js +32 -0
  94. package/dist/money.d.ts +1 -0
  95. package/dist/optimistic-cart-BveVOHuh.js +53 -0
  96. package/dist/optimistic-cart.d.ts +16 -0
  97. package/dist/order-display-DTt5WBbc.js +112 -0
  98. package/dist/order-display.d.ts +20 -0
  99. package/dist/order-draft-DxV1Uhm3.js +38 -0
  100. package/dist/order-draft.d.ts +16 -0
  101. package/dist/order-pages-Dm6l5m0m.js +38 -0
  102. package/dist/order-pages.d.ts +12 -0
  103. package/dist/order-payment-continuation-D_d5PGA4.js +212 -0
  104. package/dist/order-payment-continuation.d.ts +10 -0
  105. package/dist/order-payment-in-review-x4gdLFHZ.js +65 -0
  106. package/dist/order-payment-in-review.d.ts +5 -0
  107. package/dist/order-status-BJXLJwxq.js +162 -0
  108. package/dist/order-status.d.ts +89 -0
  109. package/dist/order-view-9IlBg5AF.js +62 -0
  110. package/dist/order-view.d.ts +12 -0
  111. package/dist/page-action-Ctkf4xC6.js +7 -0
  112. package/dist/page-seo-a5FJl8x5.js +262 -0
  113. package/dist/page-seo.d.ts +34 -0
  114. package/dist/pages-Ca_Gt6mq.js +150 -0
  115. package/dist/pages.d.ts +45 -0
  116. package/dist/payment-dialog-frame-B-Q_xQ94.js +57 -0
  117. package/dist/payment-logo-BoPaGJc_.js +46 -0
  118. package/dist/payment-logo-svgs-By9lDTLK.js +20 -0
  119. package/dist/payment-logo.d.ts +11 -0
  120. package/dist/payment-logos-BYvdwYyJ.js +23 -0
  121. package/dist/payment-methods-B8ocTgFZ.js +73 -0
  122. package/dist/payment-methods.d.ts +58 -0
  123. package/dist/payment-return-5cjypwIw.js +7 -0
  124. package/dist/place-CmW4hBWY.js +13 -0
  125. package/dist/place.d.ts +4 -0
  126. package/dist/preview-BiIBif3C.js +427 -0
  127. package/dist/preview-address-Cf0sJ-9p.js +21 -0
  128. package/dist/preview-cart-Dg2CVktS.js +376 -0
  129. package/dist/product-preview-BspMAecJ.js +51 -0
  130. package/dist/product-preview.d.ts +5 -0
  131. package/dist/product-variants-BNDHvs1y.js +46 -0
  132. package/dist/product-variants.d.ts +12 -0
  133. package/dist/public-pages-XtwlaLwt.js +194 -0
  134. package/dist/public-pages.d.ts +67 -0
  135. package/dist/registration-CpDcv8ql.js +91 -0
  136. package/dist/registration.d.ts +12 -0
  137. package/dist/request-security-CRsRMgwj.js +76 -0
  138. package/dist/rich-text-M-Ju8RL4.js +9 -0
  139. package/dist/rich-text.d.ts +5 -0
  140. package/dist/route-text-DcJw2SsK.js +9 -0
  141. package/dist/route-text.d.ts +4 -0
  142. package/dist/section-preview-CmsMl3yu.js +83 -0
  143. package/dist/section-preview.d.ts +12 -0
  144. package/dist/sections/account-addresses.d.ts +18 -0
  145. package/dist/sections/account-addresses.js +375 -0
  146. package/dist/sections/account-order.d.ts +12 -0
  147. package/dist/sections/account-order.js +592 -0
  148. package/dist/sections/account-orders.d.ts +8 -0
  149. package/dist/sections/account-orders.js +359 -0
  150. package/dist/sections/account-profile.d.ts +16 -0
  151. package/dist/sections/account-profile.js +123 -0
  152. package/dist/sections/checkout.d.ts +24 -0
  153. package/dist/sections/checkout.js +963 -0
  154. package/dist/sections/complaint-book.d.ts +8 -0
  155. package/dist/sections/complaint-book.js +538 -0
  156. package/dist/sections/design-system.d.ts +1 -0
  157. package/dist/sections/design-system.js +310 -0
  158. package/dist/sections/draft-invoice.d.ts +20 -0
  159. package/dist/sections/draft-invoice.js +158 -0
  160. package/dist/sections/order-tracking.d.ts +19 -0
  161. package/dist/sections/order-tracking.js +141 -0
  162. package/dist/sections-CNUMWTF_.js +77 -0
  163. package/dist/sections.d.ts +19 -0
  164. package/dist/seo-GpDRhAAA.js +78 -0
  165. package/dist/session-CKvmFJyg.js +379 -0
  166. package/dist/session.d.ts +24 -0
  167. package/dist/settings-B0NO2A6Y.js +78 -0
  168. package/dist/settings.d.ts +51 -0
  169. package/dist/source-marker-DhqSkW7n.js +4 -0
  170. package/dist/sources-B-3JFJfb.js +46 -0
  171. package/dist/sources.d.ts +7 -0
  172. package/dist/store-8Q-_LFrQ.js +152 -0
  173. package/dist/store-contact--hY6CoFb.js +19 -0
  174. package/dist/store-contact.d.ts +6 -0
  175. package/dist/store.d.ts +17 -0
  176. package/dist/storefront-language-DrMjVbmk.js +29 -0
  177. package/dist/storefront-language.d.ts +14 -0
  178. package/dist/storefront-text-D25ae1qf.js +11 -0
  179. package/dist/storefront-text-en-ljjjSS4q.js +1030 -0
  180. package/dist/storefront-text-pt-BtHaJLcp.js +1030 -0
  181. package/dist/storefront-text.d.ts +4 -0
  182. package/dist/stripe-dialog-Ev9s3Z62.js +396 -0
  183. package/dist/stripe-dialog.d.ts +30 -0
  184. package/dist/telemetry-HuZXWBPi.js +114 -0
  185. package/dist/telemetry.d.ts +10 -0
  186. package/dist/templates-CAqoNyTW.js +40 -0
  187. package/dist/templates.d.ts +15 -0
  188. package/dist/urls-CpGQPxvf.js +71 -0
  189. package/dist/urls.d.ts +18 -0
  190. package/dist/use-checkout-CZcxXJXi.js +42 -0
  191. package/dist/use-checkout-payment-9R-zacrJ.js +80 -0
  192. package/dist/use-checkout-payment.d.ts +18 -0
  193. package/dist/use-checkout.d.ts +36 -0
  194. package/dist/use-culqi-checkout-CrjLAipU.js +109 -0
  195. package/dist/use-order-payment-BKIz_IO0.js +45 -0
  196. package/dist/use-order-payment.d.ts +18 -0
  197. package/dist/use-payment-token-CErx-tN-.js +105 -0
  198. package/dist/vite-payment-logos-kziu7wKb.js +27 -0
  199. package/dist/vite-payment-logos.d.ts +24 -0
  200. package/dist/vite-public-assets-DW2fgtVb.js +209 -0
  201. package/dist/vite-public-assets.d.ts +29 -0
  202. package/dist/vite-sections-DTBr0-Kg.js +464 -0
  203. package/dist/vite-sections.d.ts +21 -0
  204. package/dist/vite-theme-ui-BMO-CDi3.js +41 -0
  205. package/dist/vite-theme-ui.d.ts +10 -0
  206. package/dist/vite.d.ts +36 -0
  207. package/dist/vite.js +127 -0
  208. package/dist/voucher-payment-CVFDZ1L8.js +66 -0
  209. package/dist/worker-CNi-gzCu.js +86 -0
  210. package/dist/worker.d.ts +27 -0
  211. package/package.json +100 -76
  212. package/src/sections/account-addresses.tsx +215 -0
  213. package/src/sections/account-order.tsx +424 -0
  214. package/src/sections/account-orders.tsx +259 -0
  215. package/src/sections/account-profile.tsx +70 -0
  216. package/src/sections/checkout.tsx +586 -0
  217. package/src/sections/complaint-book.tsx +325 -0
  218. package/src/sections/design-system.tsx +146 -0
  219. package/src/sections/draft-invoice.tsx +105 -0
  220. package/src/sections/order-tracking.tsx +81 -0
  221. package/src/vite-client.d.ts +4 -0
  222. package/bin/ecomiq.mjs +0 -179
  223. package/public/design-bridge.js +0 -318
  224. package/src/api-proxy.ts +0 -194
  225. package/src/auth/create-storefront-auth.ts +0 -94
  226. package/src/auth/ecomiq-oauth-password-client.ts +0 -26
  227. package/src/auth/ecomiq-oauth-password-constants.ts +0 -24
  228. package/src/auth/ecomiq-oauth-password-plugin.ts +0 -294
  229. package/src/auth/ecomiq-oauth-password-types.ts +0 -24
  230. package/src/auth/ecomiq-storefront-sign-in.ts +0 -44
  231. package/src/auth/env.ts +0 -7
  232. package/src/auth/secure-cookies.ts +0 -23
  233. package/src/auth/storefront-auth-base-url.ts +0 -117
  234. package/src/auth/storefront-auth.ts +0 -7
  235. package/src/cart-session.ts +0 -239
  236. package/src/client.ts +0 -48
  237. package/src/codegen.mjs +0 -801
  238. package/src/components/account/customer-addresses-panel.tsx +0 -678
  239. package/src/components/account/customer-order-detail-panel.tsx +0 -285
  240. package/src/components/account/customer-orders-panel.tsx +0 -192
  241. package/src/components/account/customer-profile-panel.tsx +0 -379
  242. package/src/components/checkout/cart-sync.ts +0 -143
  243. package/src/components/checkout/checkout-contact-section.tsx +0 -76
  244. package/src/components/checkout/checkout-external-update-banner.tsx +0 -15
  245. package/src/components/checkout/checkout-form-controls.tsx +0 -139
  246. package/src/components/checkout/checkout-line-items.tsx +0 -43
  247. package/src/components/checkout/checkout-order-summary.tsx +0 -165
  248. package/src/components/checkout/checkout-payment-section.tsx +0 -212
  249. package/src/components/checkout/checkout-session-view.tsx +0 -564
  250. package/src/components/checkout/checkout-shipping-address-section.tsx +0 -400
  251. package/src/components/checkout/checkout-shipping-section.tsx +0 -98
  252. package/src/components/checkout/checkout-states.tsx +0 -73
  253. package/src/components/checkout/checkout-step-card.tsx +0 -101
  254. package/src/components/checkout/checkout-steps.ts +0 -111
  255. package/src/components/checkout/form-schema.ts +0 -281
  256. package/src/components/checkout/format.ts +0 -15
  257. package/src/components/checkout/index.ts +0 -5
  258. package/src/components/checkout/map-customer-to-checkout.ts +0 -188
  259. package/src/components/checkout/types.ts +0 -41
  260. package/src/components/checkout/use-checkout-customer-prefill.ts +0 -409
  261. package/src/components/checkout/use-checkout-geo.ts +0 -50
  262. package/src/components/checkout/use-checkout-session.ts +0 -640
  263. package/src/components/hero.tsx +0 -291
  264. package/src/config.d.ts +0 -94
  265. package/src/config.mjs +0 -146
  266. package/src/format-money.ts +0 -31
  267. package/src/health.ts +0 -90
  268. package/src/hooks/index.ts +0 -9
  269. package/src/hooks/recently-viewed.ts +0 -125
  270. package/src/index.ts +0 -349
  271. package/src/link.tsx +0 -2
  272. package/src/pages/account/account-page-skeleton.tsx +0 -69
  273. package/src/pages/account/account-page-view.tsx +0 -270
  274. package/src/pages/account/account-shell.tsx +0 -101
  275. package/src/pages/account/parse-account-route.ts +0 -36
  276. package/src/pages/account.tsx +0 -16
  277. package/src/pages/api.$.ts +0 -20
  278. package/src/pages/api.auth.$.ts +0 -16
  279. package/src/pages/checkout.$checkoutId.tsx +0 -60
  280. package/src/pages/checkout.tsx +0 -31
  281. package/src/pages/pages.$pageSlug.tsx +0 -74
  282. package/src/pages/thank-you.tsx +0 -53
  283. package/src/providers/auth.tsx +0 -96
  284. package/src/providers/cart.tsx +0 -275
  285. package/src/providers/ecomiq.ts +0 -137
  286. package/src/providers/index.ts +0 -26
  287. package/src/proxy-api.ts +0 -20
  288. package/src/public-config.ts +0 -24
  289. package/src/review-submission.ts +0 -89
  290. package/src/runtime/context.ts +0 -2
  291. package/src/runtime/default-layout.tsx +0 -64
  292. package/src/runtime/head.ts +0 -149
  293. package/src/runtime/page.tsx +0 -143
  294. package/src/runtime/postcss-browser-stub.mjs +0 -5
  295. package/src/server-config-store.ts +0 -35
  296. package/src/server.ts +0 -286
  297. package/src/styles.css +0 -76
  298. package/src/ui/badge.tsx +0 -32
  299. package/src/ui/button.tsx +0 -31
  300. package/src/ui/card.tsx +0 -31
  301. package/src/ui/carousel.tsx +0 -218
  302. package/src/ui/cn.ts +0 -6
  303. package/src/ui/index.ts +0 -8
  304. package/src/ui/separator.tsx +0 -6
  305. package/src/ui/skeleton.tsx +0 -6
  306. package/src/ui/storefront-error.tsx +0 -49
  307. package/src/utils.ts +0 -36
  308. package/src/vite-inline.mjs +0 -263
  309. package/templates/__root.tsx +0 -83
  310. package/templates/router.tsx +0 -19
@@ -1,640 +0,0 @@
1
- import {
2
- useCallback,
3
- useEffect,
4
- useMemo,
5
- useRef,
6
- useState,
7
- } from 'react'
8
- import type {
9
- CartCheckoutResponse,
10
- CartPaymentMethod,
11
- ShippingOptionGroup,
12
- ShippingRate,
13
- StorefrontCart,
14
- StorefrontOrderBump,
15
- } from '@ecomiq/core'
16
- import { useCart } from '../../providers'
17
- import { serverApi } from '../../server'
18
- import {
19
- areCartSignaturesEqual,
20
- cartItemsSignature,
21
- cartSessionSignature,
22
- findCartReconciliationActions,
23
- isCheckoutCartAlreadyPurchased,
24
- methodUsesProviderCheckoutUi,
25
- toSignatureItems,
26
- } from './cart-sync'
27
- import { emptyCheckoutContact, type CheckoutContactForm } from './types'
28
-
29
- type ShippingFlat = {
30
- key: string
31
- group: ShippingOptionGroup
32
- rate: ShippingRate
33
- }
34
-
35
- export function useCheckoutSession(cartToken: string) {
36
- const { cart: providerCart, clearCart, reload: reloadProviderCart } = useCart()
37
-
38
- const [checkoutCart, setCheckoutCart] = useState<StorefrontCart | null>(null)
39
- const [paymentMethods, setPaymentMethods] = useState<CartPaymentMethod[]>([])
40
- const [orderBumps, setOrderBumps] = useState<StorefrontOrderBump[]>([])
41
- const [loadStatus, setLoadStatus] = useState<'loading' | 'ready' | 'error'>('loading')
42
- const [loadError, setLoadError] = useState<string>()
43
- const [checkoutAlreadyPurchased, setCheckoutAlreadyPurchased] = useState(false)
44
- const [isReconciling, setIsReconciling] = useState(false)
45
- const [cartChangedExternally, setCartChangedExternally] = useState(false)
46
-
47
- const [contact, setContact] = useState<CheckoutContactForm>(() => emptyCheckoutContact())
48
- const [selectedShippingKey, setSelectedShippingKey] = useState<string | null>(null)
49
- const [selectedPaymentCode, setSelectedPaymentCode] = useState<string | null>(null)
50
- const [providerPaymentCompleted, setProviderPaymentCompleted] = useState(false)
51
- const [couponCode, setCouponCode] = useState('')
52
- const [couponError, setCouponError] = useState<string | null>(null)
53
- const [formError, setFormError] = useState<string | null>(null)
54
- const [isSaving, setIsSaving] = useState(false)
55
-
56
- const [shippingGroups, setShippingGroups] = useState<ShippingOptionGroup[]>([])
57
- const [shippingLoading, setShippingLoading] = useState(false)
58
- const [shippingError, setShippingError] = useState<string | null>(null)
59
-
60
- const [checkoutPaymentResponse, setCheckoutPaymentResponse] =
61
- useState<CartCheckoutResponse | null>(null)
62
- const [checkoutPaymentMethodCode, setCheckoutPaymentMethodCode] = useState<string | null>(
63
- null,
64
- )
65
- const [checkoutPaymentLoading, setCheckoutPaymentLoading] = useState(false)
66
- const [checkoutPaymentError, setCheckoutPaymentError] = useState<string | null>(null)
67
- const [deliveryPersisted, setDeliveryPersisted] = useState(false)
68
-
69
- const hydratedRef = useRef(false)
70
- const reconcileAttemptRef = useRef<string | null>(null)
71
- const isReconcilingRef = useRef(false)
72
- const checkoutPaymentInitKeyRef = useRef<string | null>(null)
73
- const baselineSigRef = useRef<string | null>(null)
74
-
75
- const localItems = useMemo(
76
- () => toSignatureItems(providerCart?.items),
77
- [providerCart?.items],
78
- )
79
- const apiItems = useMemo(
80
- () => toSignatureItems(checkoutCart?.items),
81
- [checkoutCart?.items],
82
- )
83
- const cartsReconciled = useMemo(
84
- () => areCartSignaturesEqual(localItems, apiItems),
85
- [localItems, apiItems],
86
- )
87
-
88
- const loadCheckout = useCallback(async () => {
89
- setLoadStatus('loading')
90
- setLoadError(undefined)
91
- try {
92
- const data = await serverApi.getCartCheckout({ cartToken })
93
- if (isCheckoutCartAlreadyPurchased(data.cart)) {
94
- setCheckoutAlreadyPurchased(true)
95
- clearCart()
96
- setCheckoutCart(data.cart)
97
- setLoadStatus('ready')
98
- return
99
- }
100
- setCheckoutCart(data.cart)
101
- setPaymentMethods(data.paymentMethods.methods.filter((m) => m.isActive))
102
- setOrderBumps(data.orderBumps ?? [])
103
- setCheckoutAlreadyPurchased(false)
104
- setLoadStatus('ready')
105
- } catch (error) {
106
- setLoadError(
107
- error instanceof Error ? error.message : 'No pudimos cargar el checkout.',
108
- )
109
- setLoadStatus('error')
110
- }
111
- }, [cartToken, clearCart])
112
-
113
- useEffect(() => {
114
- hydratedRef.current = false
115
- reconcileAttemptRef.current = null
116
- checkoutPaymentInitKeyRef.current = null
117
- baselineSigRef.current = null
118
- setCartChangedExternally(false)
119
- setSelectedShippingKey(null)
120
- setSelectedPaymentCode(null)
121
- setProviderPaymentCompleted(false)
122
- setCheckoutPaymentResponse(null)
123
- setCheckoutPaymentMethodCode(null)
124
- setDeliveryPersisted(false)
125
- void loadCheckout()
126
- }, [cartToken, loadCheckout])
127
-
128
- useEffect(() => {
129
- if (!checkoutCart || hydratedRef.current) return
130
- hydratedRef.current = true
131
- const ship = checkoutCart.shippingAddress
132
- setContact((c) => ({
133
- ...c,
134
- email: checkoutCart.email ?? c.email,
135
- firstName: checkoutCart.firstName ?? c.firstName,
136
- lastName: checkoutCart.lastName ?? c.lastName,
137
- phone: checkoutCart.phone ?? c.phone,
138
- address: ship?.address1 ?? c.address,
139
- address2: ship?.address2 ?? c.address2,
140
- postalCode: ship?.postalCode ?? c.postalCode,
141
- province: ship?.province ?? c.province,
142
- city: ship?.city ?? c.city,
143
- countryCode: ship?.countryCode ?? c.countryCode,
144
- company: ship?.company ?? c.company,
145
- districtName: ship?.district ?? c.districtName,
146
- }))
147
- }, [checkoutCart])
148
-
149
- // Cross-tab: sesión de carrito o ítems del provider cambian tras baseline.
150
- useEffect(() => {
151
- if (loadStatus !== 'ready' || isReconciling || checkoutAlreadyPurchased) return
152
- const sig = cartSessionSignature(cartToken, localItems)
153
- if (baselineSigRef.current === null) {
154
- baselineSigRef.current = sig
155
- return
156
- }
157
- if (sig !== baselineSigRef.current) {
158
- setCartChangedExternally(true)
159
- }
160
- }, [cartToken, checkoutAlreadyPurchased, isReconciling, loadStatus, localItems])
161
-
162
- useEffect(() => {
163
- const key = `ecomiq:cart:v1:`
164
- const onStorage = (event: StorageEvent) => {
165
- if (!event.key?.startsWith(key)) return
166
- setCartChangedExternally(true)
167
- }
168
- window.addEventListener('storage', onStorage)
169
- return () => window.removeEventListener('storage', onStorage)
170
- }, [])
171
-
172
- // Reconciliación local → API
173
- useEffect(() => {
174
- if (loadStatus !== 'ready' || !checkoutCart || checkoutAlreadyPurchased) return
175
- if (isReconcilingRef.current) return
176
- if (providerCart && providerCart.token !== cartToken) return
177
- if (localItems.length === 0 && apiItems.length === 0) return
178
- if (areCartSignaturesEqual(localItems, apiItems)) return
179
-
180
- const actions = findCartReconciliationActions(localItems, apiItems)
181
- if (actions.length === 0) return
182
-
183
- const attemptKey = `${cartToken}::${cartItemsSignature(localItems)}::${cartItemsSignature(apiItems)}`
184
- if (reconcileAttemptRef.current === attemptKey) return
185
-
186
- reconcileAttemptRef.current = attemptKey
187
- isReconcilingRef.current = true
188
- setIsReconciling(true)
189
-
190
- void (async () => {
191
- try {
192
- for (const action of actions) {
193
- if (action.action === 'remove') {
194
- try {
195
- const next = await serverApi.removeCartItem({
196
- cartToken,
197
- cartItemId: action.itemId,
198
- })
199
- if (next.ok) setCheckoutCart(next.value)
200
- } catch {
201
- /* stock / línea inválida */
202
- }
203
- } else if (action.action === 'update') {
204
- try {
205
- const next = await serverApi.setCartItemQuantity({
206
- cartToken,
207
- cartItemId: action.itemId,
208
- quantity: action.quantity,
209
- })
210
- if (next.ok) setCheckoutCart(next.value)
211
- } catch {
212
- /* */
213
- }
214
- }
215
- }
216
- for (const action of actions) {
217
- if (action.action !== 'add') continue
218
- try {
219
- const next = await serverApi.addCartItem({
220
- cartToken,
221
- variantId: action.variantId,
222
- quantity: action.quantity,
223
- })
224
- setCheckoutCart(next)
225
- } catch {
226
- /* */
227
- }
228
- }
229
- await loadCheckout()
230
- await reloadProviderCart()
231
- baselineSigRef.current = null
232
- } finally {
233
- isReconcilingRef.current = false
234
- setIsReconciling(false)
235
- }
236
- })()
237
- }, [
238
- apiItems,
239
- cartToken,
240
- checkoutAlreadyPurchased,
241
- checkoutCart,
242
- loadCheckout,
243
- loadStatus,
244
- localItems,
245
- providerCart,
246
- reloadProviderCart,
247
- ])
248
-
249
- const postalCode = contact.postalCode.trim()
250
- useEffect(() => {
251
- if (!postalCode || checkoutAlreadyPurchased) {
252
- setShippingGroups([])
253
- return
254
- }
255
- let cancelled = false
256
- setShippingLoading(true)
257
- setShippingError(null)
258
- void serverApi
259
- .getShippingOptions({ cartToken, postalCode })
260
- .then((res) => {
261
- if (!cancelled) setShippingGroups(res.groups)
262
- })
263
- .catch((error) => {
264
- if (!cancelled) {
265
- setShippingError(
266
- error instanceof Error ? error.message : 'No pudimos cargar envíos.',
267
- )
268
- setShippingGroups([])
269
- }
270
- })
271
- .finally(() => {
272
- if (!cancelled) setShippingLoading(false)
273
- })
274
- return () => {
275
- cancelled = true
276
- }
277
- }, [cartToken, checkoutAlreadyPurchased, postalCode])
278
-
279
- const shippingFlat = useMemo(() => {
280
- const out: ShippingFlat[] = []
281
- for (const group of shippingGroups) {
282
- for (const rate of group.rates ?? []) {
283
- out.push({
284
- key: `${group.sellerId ?? ''}::${rate.rateId}`,
285
- group,
286
- rate,
287
- })
288
- }
289
- }
290
- return out
291
- }, [shippingGroups])
292
-
293
- useEffect(() => {
294
- if (!shippingFlat.length || selectedShippingKey) return
295
- setSelectedShippingKey(shippingFlat[0]!.key)
296
- }, [shippingFlat, selectedShippingKey])
297
-
298
- const externalRef = `checkout-${cartToken}`
299
-
300
- const initProviderCheckout = useCallback(
301
- (paymentMethodCode: string) => {
302
- const method = paymentMethods.find((m) => m.code === paymentMethodCode)
303
- if (!method || !methodUsesProviderCheckoutUi(method)) return
304
-
305
- const dedupeKey = `${cartToken}:${paymentMethodCode}`
306
- if (checkoutPaymentInitKeyRef.current === dedupeKey) return
307
- checkoutPaymentInitKeyRef.current = dedupeKey
308
-
309
- setCheckoutPaymentLoading(true)
310
- setCheckoutPaymentError(null)
311
- setCheckoutPaymentMethodCode(paymentMethodCode)
312
- void serverApi
313
- .checkoutCart({ cartToken, paymentMethodCode, externalRef })
314
- .then((result) => {
315
- setCheckoutPaymentResponse(result)
316
- })
317
- .catch((error) => {
318
- setCheckoutPaymentError(
319
- error instanceof Error ? error.message : 'No pudimos iniciar el pago.',
320
- )
321
- setCheckoutPaymentResponse(null)
322
- })
323
- .finally(() => setCheckoutPaymentLoading(false))
324
- },
325
- [cartToken, externalRef, paymentMethods],
326
- )
327
-
328
- const selectPaymentMethod = useCallback(
329
- (paymentMethodCode: string) => {
330
- setSelectedPaymentCode(paymentMethodCode)
331
- setProviderPaymentCompleted(false)
332
- initProviderCheckout(paymentMethodCode)
333
- },
334
- [initProviderCheckout],
335
- )
336
-
337
- const clearPaymentSession = useCallback(() => {
338
- checkoutPaymentInitKeyRef.current = null
339
- setSelectedPaymentCode(null)
340
- setProviderPaymentCompleted(false)
341
- setCheckoutPaymentResponse(null)
342
- setCheckoutPaymentMethodCode(null)
343
- setCheckoutPaymentError(null)
344
- setCheckoutPaymentLoading(false)
345
- }, [])
346
-
347
- const ensureDefaultPaymentMethod = useCallback(() => {
348
- if (selectedPaymentCode || !paymentMethods.length) return
349
- const first = paymentMethods.find((m) => m.isDefault) ?? paymentMethods[0]
350
- if (first) selectPaymentMethod(first.code)
351
- }, [paymentMethods, selectPaymentMethod, selectedPaymentCode])
352
-
353
- const saveBuyer = useCallback(async () => {
354
- const buyerCart = await serverApi.patchCartBuyer({
355
- cartToken,
356
- email: contact.email.trim(),
357
- firstName: contact.firstName.trim(),
358
- lastName: contact.lastName.trim(),
359
- phone: contact.phone.trim(),
360
- })
361
- setCheckoutCart(buyerCart)
362
- return buyerCart
363
- }, [cartToken, contact.email, contact.firstName, contact.lastName, contact.phone])
364
-
365
- const saveAddresses = useCallback(async () => {
366
- const countryCode = (contact.countryCode || 'PE').toUpperCase()
367
- const districtName = contact.districtName || contact.city
368
- const provinceName = contact.provinceName || contact.province
369
- const baseAddress = {
370
- name: `${contact.firstName} ${contact.lastName}`.trim() || contact.address,
371
- address1: contact.address,
372
- city: districtName,
373
- countryCode,
374
- company: contact.company || null,
375
- address2: contact.address2 || null,
376
- province: provinceName,
377
- postalCode: contact.postalCode,
378
- district: districtName,
379
- phone: contact.phone || null,
380
- latitude: null,
381
- longitude: null,
382
- geo: {
383
- departmentCode: contact.departmentCode || '',
384
- provinceCode: contact.provinceCode || '',
385
- districtCode: contact.districtCode || '',
386
- },
387
- }
388
-
389
- const updated = await serverApi.patchCartAddresses({
390
- cartToken,
391
- shippingAddress: baseAddress,
392
- billingAddress: baseAddress,
393
- })
394
- setCheckoutCart(updated)
395
- return updated
396
- }, [cartToken, contact])
397
-
398
- const saveContactAndDelivery = useCallback(async () => {
399
- await saveBuyer()
400
- await saveAddresses()
401
- }, [saveAddresses, saveBuyer])
402
-
403
- const confirmShipping = useCallback(
404
- async (picked: ShippingFlat) => {
405
- const updated = await serverApi.putCartShippingMethod({
406
- cartToken,
407
- rateId: picked.rate.rateId,
408
- shippingMethodId: picked.rate.shippingMethodId,
409
- serviceCode: picked.rate.serviceCode ?? null,
410
- sellerId: picked.group.sellerId ?? null,
411
- shippingProfileId: picked.group.shippingProfileId ?? null,
412
- locationId: null,
413
- pickupPointId: null,
414
- scheduledDate: null,
415
- scheduledTime: null,
416
- })
417
- setCheckoutCart(updated)
418
- return updated
419
- },
420
- [cartToken],
421
- )
422
-
423
- /** Persiste buyer + dirección + envío al completar el paso Dirección. */
424
- const persistDeliveryStep = useCallback(async () => {
425
- setFormError(null)
426
- setIsSaving(true)
427
- try {
428
- await saveContactAndDelivery()
429
- if (selectedShippingKey) {
430
- const picked = shippingFlat.find((o) => o.key === selectedShippingKey)
431
- if (picked) await confirmShipping(picked)
432
- }
433
- setDeliveryPersisted(true)
434
- clearPaymentSession()
435
- } catch (error) {
436
- setFormError(
437
- error instanceof Error ? error.message : 'No se pudo guardar la dirección.',
438
- )
439
- throw error
440
- } finally {
441
- setIsSaving(false)
442
- }
443
- }, [
444
- clearPaymentSession,
445
- confirmShipping,
446
- saveContactAndDelivery,
447
- selectedShippingKey,
448
- shippingFlat,
449
- ])
450
-
451
- const invalidateDeliveryPersistence = useCallback(() => {
452
- setDeliveryPersisted(false)
453
- clearPaymentSession()
454
- }, [clearPaymentSession])
455
-
456
- const handleApplyCoupon = useCallback(async () => {
457
- const code = couponCode.trim()
458
- if (!code) return
459
- setCouponError(null)
460
- try {
461
- const next = await serverApi.addCartPromotionCode({ cartToken, code })
462
- setCheckoutCart(next)
463
- setCouponCode('')
464
- } catch (error) {
465
- setCouponError(error instanceof Error ? error.message : 'No se pudo aplicar el cupón.')
466
- }
467
- }, [cartToken, couponCode])
468
-
469
- const handleRemoveCoupon = useCallback(
470
- async (code: string) => {
471
- setCouponError(null)
472
- try {
473
- const next = await serverApi.removeCartPromotionCode({ cartToken, code })
474
- setCheckoutCart(next)
475
- } catch (error) {
476
- setCouponError(error instanceof Error ? error.message : 'No se pudo remover el cupón.')
477
- }
478
- },
479
- [cartToken],
480
- )
481
-
482
- const handleAddOrderBump = useCallback(
483
- async (orderBump: StorefrontOrderBump) => {
484
- const next = await serverApi.addCartItem({
485
- cartToken,
486
- variantId: orderBump.variantId,
487
- quantity: 1,
488
- })
489
- setCheckoutCart(next)
490
- await loadCheckout()
491
- await reloadProviderCart()
492
- },
493
- [cartToken, loadCheckout, reloadProviderCart],
494
- )
495
-
496
- const uploadPaymentReceipt = useCallback(
497
- async (file: File) => {
498
- return serverApi.uploadPaymentReceipt({ cartToken, file, fileName: file.name })
499
- },
500
- [cartToken],
501
- )
502
-
503
- const payNow = useCallback(
504
- async (options?: { receiptUrl?: string }): Promise<CartCheckoutResponse | undefined> => {
505
- setFormError(null)
506
- setIsSaving(true)
507
- try {
508
- if (!deliveryPersisted) {
509
- await saveContactAndDelivery()
510
- if (selectedShippingKey) {
511
- const picked = shippingFlat.find((o) => o.key === selectedShippingKey)
512
- if (picked) await confirmShipping(picked)
513
- }
514
- setDeliveryPersisted(true)
515
- }
516
- const code =
517
- selectedPaymentCode ?? paymentMethods[0]?.code
518
- if (!code) {
519
- setFormError('Selecciona un método de pago.')
520
- return
521
- }
522
- const method = paymentMethods.find((m) => m.code === code)
523
- const usesProviderUi = method ? methodUsesProviderCheckoutUi(method) : false
524
-
525
- if (usesProviderUi) {
526
- if (!providerPaymentCompleted) {
527
- setFormError(
528
- 'Completa el pago con Stripe o Mercado Pago antes de continuar.',
529
- )
530
- return
531
- }
532
- if (
533
- checkoutPaymentResponse &&
534
- checkoutPaymentMethodCode === code
535
- ) {
536
- return checkoutPaymentResponse
537
- }
538
- }
539
-
540
- const result = await serverApi.checkoutCart({
541
- cartToken,
542
- paymentMethodCode: code,
543
- externalRef,
544
- ...(options?.receiptUrl ? { receiptUrl: options.receiptUrl } : {}),
545
- })
546
- setCheckoutPaymentResponse(result)
547
- setCheckoutPaymentMethodCode(code)
548
- return result
549
- } catch (error) {
550
- setFormError(
551
- error instanceof Error ? error.message : 'No se pudo completar el pago.',
552
- )
553
- throw error
554
- } finally {
555
- setIsSaving(false)
556
- }
557
- },
558
- [
559
- cartToken,
560
- checkoutPaymentMethodCode,
561
- checkoutPaymentResponse,
562
- confirmShipping,
563
- deliveryPersisted,
564
- externalRef,
565
- paymentMethods,
566
- providerPaymentCompleted,
567
- saveContactAndDelivery,
568
- selectedPaymentCode,
569
- selectedShippingKey,
570
- shippingFlat,
571
- ],
572
- )
573
-
574
- const reloadCheckout = useCallback(() => {
575
- if (typeof window !== 'undefined') window.location.reload()
576
- }, [])
577
-
578
- const uiCart = cartsReconciled || !providerCart ? checkoutCart : providerCart
579
- const totals = uiCart?.totals
580
- const subtotal = cartsReconciled
581
- ? (totals?.subtotal ?? 0)
582
- : (providerCart?.totals.subtotal ?? 0)
583
- const shipping = cartsReconciled ? (totals?.shippingTotal ?? 0) : 0
584
- const discount = cartsReconciled ? (totals?.discountTotal ?? 0) : 0
585
- const tax = cartsReconciled ? (totals?.taxTotal ?? 0) : 0
586
- const total = cartsReconciled
587
- ? (totals?.total ?? subtotal + shipping + tax - discount)
588
- : subtotal
589
-
590
- return {
591
- cartToken,
592
- loadStatus,
593
- loadError,
594
- cartData: checkoutCart,
595
- uiCart,
596
- paymentMethods,
597
- orderBumps,
598
- isReconciling,
599
- cartChangedExternally,
600
- checkoutAlreadyPurchased,
601
- reloadCheckout,
602
- clearCart,
603
- contact,
604
- setContact,
605
- selectedShippingKey,
606
- setSelectedShippingKey,
607
- selectedPaymentCode,
608
- selectPaymentMethod,
609
- ensureDefaultPaymentMethod,
610
- clearPaymentSession,
611
- persistDeliveryStep,
612
- invalidateDeliveryPersistence,
613
- saveBuyer,
614
- providerPaymentCompleted,
615
- setProviderPaymentCompleted,
616
- payNow,
617
- couponCode,
618
- setCouponCode,
619
- couponError,
620
- formError,
621
- handleApplyCoupon,
622
- handleRemoveCoupon,
623
- handleAddOrderBump,
624
- shippingFlat,
625
- shippingLoading,
626
- shippingError,
627
- uploadPaymentReceipt,
628
- checkoutPaymentResponse,
629
- checkoutPaymentMethodCode,
630
- checkoutPaymentLoading,
631
- checkoutPaymentError,
632
- isSaving: isSaving || cartChangedExternally,
633
- cartsReconciled,
634
- subtotal,
635
- shipping,
636
- discount,
637
- tax,
638
- total,
639
- }
640
- }