@ecomiq/storefront 2.1.3 → 3.0.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.
- package/LICENSE +21 -0
- package/README.md +150 -123
- package/dist/account-Dx4e3syi.js +258 -0
- package/dist/account-forms-AVk71ko7.js +39 -0
- package/dist/account-forms.d.ts +13 -0
- package/dist/account.d.ts +240 -0
- package/dist/address-defaults-BqjRhS82.js +14 -0
- package/dist/address-defaults.d.ts +11 -0
- package/dist/analytics/analytics-CG1Qagwl.js +148 -0
- package/dist/analytics/analytics.d.ts +1 -0
- package/dist/analytics/config-DJr-GRhs.js +62 -0
- package/dist/analytics/config.d.ts +22 -0
- package/dist/analytics/consent-B13rBpBs.js +57 -0
- package/dist/analytics/consent-banner-9QvRRy8v.js +239 -0
- package/dist/analytics/consent-banner.d.ts +20 -0
- package/dist/analytics/cookies-pLozllyB.js +100 -0
- package/dist/analytics/cookies.d.ts +6 -0
- package/dist/analytics/destinations-iECrlLIr.js +144 -0
- package/dist/analytics/events-Dt81tIp1.js +102 -0
- package/dist/analytics/events.d.ts +51 -0
- package/dist/analytics/tracker-p4yaoOyk.js +51 -0
- package/dist/api-cache-DsuIn5hX.js +159 -0
- package/dist/bank-accounts-DlEczQQ5.js +81 -0
- package/dist/bank-accounts.d.ts +4 -0
- package/dist/captcha-DAqaIx8m.js +117 -0
- package/dist/captcha.d.ts +34 -0
- package/dist/cart-D7DxZ2lD.js +30 -0
- package/dist/cart-actions-Bsub_FFG.js +116 -0
- package/dist/cart-actions.d.ts +15 -0
- package/dist/cart-drawer-B7PPsR69.js +22 -0
- package/dist/cart-drawer.d.ts +9 -0
- package/dist/cart-form-y9P1r_EX.js +81 -0
- package/dist/cart-form.d.ts +24 -0
- package/dist/cart-summary-ur2VWKr-.js +89 -0
- package/dist/cart-summary.d.ts +31 -0
- package/dist/cart.d.ts +4 -0
- package/dist/catalog-7gJ-l5hW.js +67 -0
- package/dist/catalog-hooks-B7ZP4wYY.js +312 -0
- package/dist/catalog-hooks.d.ts +111 -0
- package/dist/catalog.d.ts +5 -0
- package/dist/checkout-DVWSXANi.js +474 -0
- package/dist/checkout-form-CbOIdENC.js +131 -0
- package/dist/checkout-form.d.ts +20 -0
- package/dist/checkout.d.ts +86 -0
- package/dist/complaint-book-BvwLvfx0.js +117 -0
- package/dist/complaint-book.d.ts +62 -0
- package/dist/culqi-authentication-BGr20vAJ.js +204 -0
- package/dist/culqi-authentication.d.ts +15 -0
- package/dist/culqi-checkout-CTXpKgp_.js +55 -0
- package/dist/culqi-three-ds-HP3mfrvV.js +33 -0
- package/dist/culqi-three-ds.d.ts +29 -0
- package/dist/customer-avatar-CJXSDroK.js +12 -0
- package/dist/customer-avatar.d.ts +5 -0
- package/dist/customer-cookies-DHOgFAMV.js +45 -0
- package/dist/customer-csrf-C1oDX-Py.js +17 -0
- package/dist/customer-csrf.d.ts +3 -0
- package/dist/customer-document-fields-C1K-_DAD.js +70 -0
- package/dist/customer-document-fields.d.ts +9 -0
- package/dist/customer-login-form-C7AC6vXd.js +144 -0
- package/dist/customer-login-form.d.ts +17 -0
- package/dist/customer-registration-form-BEm_NEfP.js +93 -0
- package/dist/customer-registration-form.d.ts +8 -0
- package/dist/document-url-CGsAl5Tf.js +9 -0
- package/dist/documents-T1mzc_j8.js +38 -0
- package/dist/documents.d.ts +16 -0
- package/dist/entries/account.d.ts +11 -0
- package/dist/entries/account.js +10 -0
- package/dist/entries/analytics.d.ts +6 -0
- package/dist/entries/analytics.js +5 -0
- package/dist/entries/cart.d.ts +8 -0
- package/dist/entries/cart.js +7 -0
- package/dist/entries/checkout.d.ts +12 -0
- package/dist/entries/checkout.js +11 -0
- package/dist/entries/order.d.ts +11 -0
- package/dist/entries/order.js +11 -0
- package/dist/favicon-YpyhVJ9H.js +28 -0
- package/dist/favicon.d.ts +4 -0
- package/dist/form-BtQcbXlJ.js +18 -0
- package/dist/form-contracts-BNpHaTGJ.js +166 -0
- package/dist/form-contracts.d.ts +42 -0
- package/dist/form.d.ts +3 -0
- package/dist/hidden-CCWtOWKR.js +3 -0
- package/dist/hidden.d.ts +4 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +50 -0
- package/dist/invoice-fields-BG0HjL6T.js +103 -0
- package/dist/invoice-fields.d.ts +39 -0
- package/dist/links-kFxBZfdb.js +96 -0
- package/dist/links.d.ts +21 -0
- package/dist/mercadopago-checkout-DC7RXa8k.js +27 -0
- package/dist/mercadopago-dialog-UTQev0MG.js +180 -0
- package/dist/mercadopago-dialog.d.ts +21 -0
- package/dist/money-DtGOtDri.js +32 -0
- package/dist/money.d.ts +1 -0
- package/dist/optimistic-cart-BveVOHuh.js +53 -0
- package/dist/optimistic-cart.d.ts +16 -0
- package/dist/order-display-DTt5WBbc.js +112 -0
- package/dist/order-display.d.ts +20 -0
- package/dist/order-draft-DxV1Uhm3.js +38 -0
- package/dist/order-draft.d.ts +16 -0
- package/dist/order-pages-Dm6l5m0m.js +38 -0
- package/dist/order-pages.d.ts +12 -0
- package/dist/order-payment-continuation-D_d5PGA4.js +212 -0
- package/dist/order-payment-continuation.d.ts +10 -0
- package/dist/order-payment-in-review-x4gdLFHZ.js +65 -0
- package/dist/order-payment-in-review.d.ts +5 -0
- package/dist/order-status-BJXLJwxq.js +162 -0
- package/dist/order-status.d.ts +89 -0
- package/dist/order-view-9IlBg5AF.js +62 -0
- package/dist/order-view.d.ts +12 -0
- package/dist/page-action-Ctkf4xC6.js +7 -0
- package/dist/page-seo-a5FJl8x5.js +262 -0
- package/dist/page-seo.d.ts +34 -0
- package/dist/pages-Ca_Gt6mq.js +150 -0
- package/dist/pages.d.ts +45 -0
- package/dist/payment-dialog-frame-B-Q_xQ94.js +57 -0
- package/dist/payment-logo-BoPaGJc_.js +46 -0
- package/dist/payment-logo-svgs-By9lDTLK.js +20 -0
- package/dist/payment-logo.d.ts +11 -0
- package/dist/payment-logos-BYvdwYyJ.js +23 -0
- package/dist/payment-methods-B8ocTgFZ.js +73 -0
- package/dist/payment-methods.d.ts +58 -0
- package/dist/payment-return-5cjypwIw.js +7 -0
- package/dist/place-CmW4hBWY.js +13 -0
- package/dist/place.d.ts +4 -0
- package/dist/preview-BiIBif3C.js +427 -0
- package/dist/preview-address-Cf0sJ-9p.js +21 -0
- package/dist/preview-cart-Dg2CVktS.js +376 -0
- package/dist/product-preview-BspMAecJ.js +51 -0
- package/dist/product-preview.d.ts +5 -0
- package/dist/product-variants-BNDHvs1y.js +46 -0
- package/dist/product-variants.d.ts +12 -0
- package/dist/public-pages-XtwlaLwt.js +194 -0
- package/dist/public-pages.d.ts +67 -0
- package/dist/registration-CpDcv8ql.js +91 -0
- package/dist/registration.d.ts +12 -0
- package/dist/request-security-CRsRMgwj.js +76 -0
- package/dist/rich-text-M-Ju8RL4.js +9 -0
- package/dist/rich-text.d.ts +5 -0
- package/dist/route-text-DcJw2SsK.js +9 -0
- package/dist/route-text.d.ts +4 -0
- package/dist/section-preview-CmsMl3yu.js +83 -0
- package/dist/section-preview.d.ts +12 -0
- package/dist/sections/account-addresses.d.ts +18 -0
- package/dist/sections/account-addresses.js +375 -0
- package/dist/sections/account-order.d.ts +12 -0
- package/dist/sections/account-order.js +592 -0
- package/dist/sections/account-orders.d.ts +8 -0
- package/dist/sections/account-orders.js +359 -0
- package/dist/sections/account-profile.d.ts +16 -0
- package/dist/sections/account-profile.js +123 -0
- package/dist/sections/checkout.d.ts +24 -0
- package/dist/sections/checkout.js +963 -0
- package/dist/sections/complaint-book.d.ts +8 -0
- package/dist/sections/complaint-book.js +538 -0
- package/dist/sections/design-system.d.ts +1 -0
- package/dist/sections/design-system.js +310 -0
- package/dist/sections/draft-invoice.d.ts +20 -0
- package/dist/sections/draft-invoice.js +158 -0
- package/dist/sections/order-tracking.d.ts +19 -0
- package/dist/sections/order-tracking.js +141 -0
- package/dist/sections-CNUMWTF_.js +77 -0
- package/dist/sections.d.ts +19 -0
- package/dist/seo-GpDRhAAA.js +78 -0
- package/dist/session-CKvmFJyg.js +379 -0
- package/dist/session.d.ts +24 -0
- package/dist/settings-B0NO2A6Y.js +78 -0
- package/dist/settings.d.ts +51 -0
- package/dist/source-marker-DhqSkW7n.js +4 -0
- package/dist/sources-B-3JFJfb.js +46 -0
- package/dist/sources.d.ts +7 -0
- package/dist/store-8Q-_LFrQ.js +152 -0
- package/dist/store-contact--hY6CoFb.js +19 -0
- package/dist/store-contact.d.ts +6 -0
- package/dist/store.d.ts +17 -0
- package/dist/storefront-language-DrMjVbmk.js +29 -0
- package/dist/storefront-language.d.ts +14 -0
- package/dist/storefront-text-D25ae1qf.js +11 -0
- package/dist/storefront-text-en-ljjjSS4q.js +1030 -0
- package/dist/storefront-text-pt-BtHaJLcp.js +1030 -0
- package/dist/storefront-text.d.ts +4 -0
- package/dist/stripe-dialog-Ev9s3Z62.js +396 -0
- package/dist/stripe-dialog.d.ts +30 -0
- package/dist/telemetry-HuZXWBPi.js +114 -0
- package/dist/telemetry.d.ts +10 -0
- package/dist/templates-CAqoNyTW.js +40 -0
- package/dist/templates.d.ts +15 -0
- package/dist/urls-CpGQPxvf.js +71 -0
- package/dist/urls.d.ts +18 -0
- package/dist/use-checkout-CZcxXJXi.js +42 -0
- package/dist/use-checkout-payment-9R-zacrJ.js +80 -0
- package/dist/use-checkout-payment.d.ts +18 -0
- package/dist/use-checkout.d.ts +36 -0
- package/dist/use-culqi-checkout-CrjLAipU.js +109 -0
- package/dist/use-order-payment-BKIz_IO0.js +45 -0
- package/dist/use-order-payment.d.ts +18 -0
- package/dist/use-payment-token-CErx-tN-.js +105 -0
- package/dist/vite-payment-logos-kziu7wKb.js +27 -0
- package/dist/vite-payment-logos.d.ts +24 -0
- package/dist/vite-public-assets-DW2fgtVb.js +209 -0
- package/dist/vite-public-assets.d.ts +29 -0
- package/dist/vite-sections-DTBr0-Kg.js +464 -0
- package/dist/vite-sections.d.ts +21 -0
- package/dist/vite-theme-ui-BMO-CDi3.js +41 -0
- package/dist/vite-theme-ui.d.ts +10 -0
- package/dist/vite.d.ts +36 -0
- package/dist/vite.js +127 -0
- package/dist/voucher-payment-CVFDZ1L8.js +66 -0
- package/dist/worker-CNi-gzCu.js +86 -0
- package/dist/worker.d.ts +27 -0
- package/package.json +100 -81
- package/src/sections/account-addresses.tsx +215 -0
- package/src/sections/account-order.tsx +424 -0
- package/src/sections/account-orders.tsx +259 -0
- package/src/sections/account-profile.tsx +70 -0
- package/src/sections/checkout.tsx +586 -0
- package/src/sections/complaint-book.tsx +325 -0
- package/src/sections/design-system.tsx +146 -0
- package/src/sections/draft-invoice.tsx +105 -0
- package/src/sections/order-tracking.tsx +81 -0
- package/src/vite-client.d.ts +4 -0
- package/bin/ecomiq.mjs +0 -179
- package/public/design-bridge.js +0 -318
- package/src/api-proxy.ts +0 -194
- package/src/auth/create-storefront-auth.ts +0 -94
- package/src/auth/ecomiq-oauth-password-client.ts +0 -26
- package/src/auth/ecomiq-oauth-password-constants.ts +0 -24
- package/src/auth/ecomiq-oauth-password-plugin.ts +0 -294
- package/src/auth/ecomiq-oauth-password-types.ts +0 -24
- package/src/auth/ecomiq-storefront-sign-in.ts +0 -44
- package/src/auth/env.ts +0 -7
- package/src/auth/secure-cookies.ts +0 -23
- package/src/auth/storefront-auth-base-url.ts +0 -117
- package/src/auth/storefront-auth.ts +0 -7
- package/src/cart-session.ts +0 -239
- package/src/client.ts +0 -48
- package/src/cloud-runtime-client.ts +0 -378
- package/src/codegen.mjs +0 -789
- package/src/components/account/customer-addresses-panel.tsx +0 -678
- package/src/components/account/customer-order-detail-panel.tsx +0 -285
- package/src/components/account/customer-orders-panel.tsx +0 -192
- package/src/components/account/customer-profile-panel.tsx +0 -379
- package/src/components/checkout/cart-sync.ts +0 -143
- package/src/components/checkout/checkout-contact-section.tsx +0 -76
- package/src/components/checkout/checkout-external-update-banner.tsx +0 -15
- package/src/components/checkout/checkout-form-controls.tsx +0 -139
- package/src/components/checkout/checkout-line-items.tsx +0 -43
- package/src/components/checkout/checkout-order-summary.tsx +0 -165
- package/src/components/checkout/checkout-payment-section.tsx +0 -212
- package/src/components/checkout/checkout-session-view.tsx +0 -564
- package/src/components/checkout/checkout-shipping-address-section.tsx +0 -400
- package/src/components/checkout/checkout-shipping-section.tsx +0 -98
- package/src/components/checkout/checkout-states.tsx +0 -73
- package/src/components/checkout/checkout-step-card.tsx +0 -101
- package/src/components/checkout/checkout-steps.ts +0 -111
- package/src/components/checkout/form-schema.ts +0 -281
- package/src/components/checkout/format.ts +0 -15
- package/src/components/checkout/index.ts +0 -5
- package/src/components/checkout/map-customer-to-checkout.ts +0 -188
- package/src/components/checkout/types.ts +0 -41
- package/src/components/checkout/use-checkout-customer-prefill.ts +0 -409
- package/src/components/checkout/use-checkout-geo.ts +0 -50
- package/src/components/checkout/use-checkout-session.ts +0 -640
- package/src/components/hero.tsx +0 -291
- package/src/config.d.ts +0 -94
- package/src/config.mjs +0 -146
- package/src/format-money.ts +0 -31
- package/src/health.ts +0 -90
- package/src/hooks/index.ts +0 -9
- package/src/hooks/recently-viewed.ts +0 -125
- package/src/index.ts +0 -354
- package/src/link.tsx +0 -2
- package/src/pages/account/account-page-skeleton.tsx +0 -69
- package/src/pages/account/account-page-view.tsx +0 -270
- package/src/pages/account/account-shell.tsx +0 -101
- package/src/pages/account/parse-account-route.ts +0 -36
- package/src/pages/account.tsx +0 -16
- package/src/pages/api.$.ts +0 -20
- package/src/pages/api.auth.$.ts +0 -16
- package/src/pages/checkout.$checkoutId.tsx +0 -60
- package/src/pages/checkout.tsx +0 -31
- package/src/pages/pages.$pageSlug.tsx +0 -111
- package/src/pages/thank-you.tsx +0 -53
- package/src/providers/auth.tsx +0 -96
- package/src/providers/cart.tsx +0 -275
- package/src/providers/ecomiq.ts +0 -137
- package/src/providers/index.ts +0 -26
- package/src/proxy-api.ts +0 -20
- package/src/public-config.ts +0 -24
- package/src/review-submission.ts +0 -89
- package/src/runtime/context.ts +0 -2
- package/src/runtime/default-layout.tsx +0 -64
- package/src/runtime/head.ts +0 -149
- package/src/runtime/page.tsx +0 -143
- package/src/runtime/postcss-browser-stub.mjs +0 -5
- package/src/server-config-store.ts +0 -35
- package/src/server.ts +0 -301
- package/src/storefront-definition/contract.ts +0 -1
- package/src/storefront-definition/data.ts +0 -188
- package/src/storefront-definition/index.ts +0 -2
- package/src/storefront-definition/renderer.tsx +0 -1481
- package/src/storefront-layout.ts +0 -59
- package/src/storefront-runtime.ts +0 -173
- package/src/styles.css +0 -97
- package/src/ui/badge.tsx +0 -32
- package/src/ui/button.tsx +0 -31
- package/src/ui/card.tsx +0 -31
- package/src/ui/carousel.tsx +0 -218
- package/src/ui/cn.ts +0 -6
- package/src/ui/index.ts +0 -8
- package/src/ui/separator.tsx +0 -6
- package/src/ui/skeleton.tsx +0 -6
- package/src/ui/storefront-error.tsx +0 -49
- package/src/utils.ts +0 -36
- package/src/vite-inline.mjs +0 -263
- package/templates/__root.tsx +0 -83
- package/templates/router.tsx +0 -19
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { GeoPlace } from "./public-pages.js";
|
|
2
|
+
import { SearchFilter } from "@ecomiq/core";
|
|
3
|
+
export declare const SORT_OPTIONS: readonly [{
|
|
4
|
+
readonly value: "relevance";
|
|
5
|
+
readonly label: "Relevancia";
|
|
6
|
+
}, {
|
|
7
|
+
readonly value: "newest";
|
|
8
|
+
readonly label: "Más nuevos";
|
|
9
|
+
}, {
|
|
10
|
+
readonly value: "price_asc";
|
|
11
|
+
readonly label: "Precio: menor a mayor";
|
|
12
|
+
}, {
|
|
13
|
+
readonly value: "price_desc";
|
|
14
|
+
readonly label: "Precio: mayor a menor";
|
|
15
|
+
}, {
|
|
16
|
+
readonly value: "name_asc";
|
|
17
|
+
readonly label: "Nombre: A–Z";
|
|
18
|
+
}];
|
|
19
|
+
export type PageItem = number | "gap";
|
|
20
|
+
export type SortValue = (typeof SORT_OPTIONS)[number]["value"];
|
|
21
|
+
export type SortLabels = Partial<Record<SortValue, string>>;
|
|
22
|
+
export type FilterLabels = {
|
|
23
|
+
filters?: Map<string, string>;
|
|
24
|
+
values?: Map<string, string>;
|
|
25
|
+
};
|
|
26
|
+
type FilterTranslate = (source: string, values?: Readonly<Record<string, string>>) => string;
|
|
27
|
+
export type FilterChipText = {
|
|
28
|
+
labels?: FilterLabels;
|
|
29
|
+
price?: (amount: string) => string;
|
|
30
|
+
translate?: FilterTranslate;
|
|
31
|
+
};
|
|
32
|
+
export declare function filterLabel(filter: SearchFilter, labels?: FilterLabels, translate?: FilterTranslate): string;
|
|
33
|
+
export declare function filterValueLabel(value: string, label: string | null | undefined, labels?: FilterLabels, translate?: FilterTranslate): string;
|
|
34
|
+
export declare function isSingleChoiceFilter(filter: SearchFilter): boolean;
|
|
35
|
+
export declare function selectedFacetCount(filters: SearchFilter[]): number;
|
|
36
|
+
export declare function activeFilterCount(filters: SearchFilter[], params: URLSearchParams): number;
|
|
37
|
+
export declare function clearedFiltersHref(params: URLSearchParams): string;
|
|
38
|
+
export type ActiveFilterChip = {
|
|
39
|
+
key: string;
|
|
40
|
+
value: string;
|
|
41
|
+
text: string;
|
|
42
|
+
href: string;
|
|
43
|
+
};
|
|
44
|
+
export declare function activeFilterChips(filters: SearchFilter[], params: URLSearchParams, text?: FilterChipText): ActiveFilterChip[];
|
|
45
|
+
export declare function pageItems(page: number, pages: number): PageItem[];
|
|
46
|
+
export declare function useFacets(): {
|
|
47
|
+
params: URLSearchParams;
|
|
48
|
+
navigate: (change: (next: URLSearchParams) => void) => void;
|
|
49
|
+
toggle: (filterId: string, value: string, checked: boolean) => void;
|
|
50
|
+
choose: (filterId: string, value: string) => void;
|
|
51
|
+
setPrice: (from: number | undefined, to: number | undefined, low: number, high: number) => void;
|
|
52
|
+
clearedHref: string;
|
|
53
|
+
};
|
|
54
|
+
export declare function useSort(labels?: SortLabels): {
|
|
55
|
+
value: string;
|
|
56
|
+
label: string;
|
|
57
|
+
options: {
|
|
58
|
+
value: "name_asc" | "newest" | "price_asc" | "price_desc" | "relevance";
|
|
59
|
+
label: string;
|
|
60
|
+
}[];
|
|
61
|
+
change: (next: string) => void;
|
|
62
|
+
};
|
|
63
|
+
export declare function usePagination(page: number, pages: number): {
|
|
64
|
+
items: PageItem[];
|
|
65
|
+
href: (target: number) => string;
|
|
66
|
+
hasPrevious: boolean;
|
|
67
|
+
hasNext: boolean;
|
|
68
|
+
};
|
|
69
|
+
export declare function useSearchSuggestions(initial?: string): {
|
|
70
|
+
q: string;
|
|
71
|
+
setQ: import("react").Dispatch<import("react").SetStateAction<string>>;
|
|
72
|
+
trimmed: string;
|
|
73
|
+
searching: boolean;
|
|
74
|
+
products: never[] | {
|
|
75
|
+
id: string;
|
|
76
|
+
name: string;
|
|
77
|
+
slug: string;
|
|
78
|
+
url: string;
|
|
79
|
+
imageUrl: string;
|
|
80
|
+
}[];
|
|
81
|
+
missing: boolean;
|
|
82
|
+
loading: boolean;
|
|
83
|
+
loaded: boolean;
|
|
84
|
+
current: boolean;
|
|
85
|
+
};
|
|
86
|
+
export declare function placeValue(place: GeoPlace | null): string;
|
|
87
|
+
export declare function useGeoCascade(defaults: {
|
|
88
|
+
country?: string | null;
|
|
89
|
+
department?: string | null;
|
|
90
|
+
province?: string | null;
|
|
91
|
+
district?: string | null;
|
|
92
|
+
}): {
|
|
93
|
+
country: string;
|
|
94
|
+
setCountry: (next: string) => void;
|
|
95
|
+
department: GeoPlace | null;
|
|
96
|
+
setDepartment: (next: GeoPlace | null) => void;
|
|
97
|
+
province: GeoPlace | null;
|
|
98
|
+
setProvince: (next: GeoPlace | null) => void;
|
|
99
|
+
district: GeoPlace | null;
|
|
100
|
+
setDistrict: import("react").Dispatch<import("react").SetStateAction<GeoPlace | null>>;
|
|
101
|
+
setDepartmentValue: (value: string) => void;
|
|
102
|
+
setProvinceValue: (value: string) => void;
|
|
103
|
+
setDistrictValue: (value: string) => void;
|
|
104
|
+
places: {
|
|
105
|
+
departments: GeoPlace[];
|
|
106
|
+
provinces: GeoPlace[];
|
|
107
|
+
districts: GeoPlace[];
|
|
108
|
+
};
|
|
109
|
+
structured: boolean;
|
|
110
|
+
};
|
|
111
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ShouldRevalidateFunctionArgs } from "react-router";
|
|
2
|
+
import { ProductsParamsInput } from "@ecomiq/core";
|
|
3
|
+
/** Menus, customer and cart data do not depend on filters in the current catalog. */
|
|
4
|
+
export declare function shouldRevalidateCatalogRoot(args: ShouldRevalidateFunctionArgs): boolean;
|
|
5
|
+
export declare function readCatalog(url: URL, base?: ProductsParamsInput): ProductsParamsInput;
|
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
import { addressDefaults } from "./address-defaults-BqjRhS82.js";
|
|
2
|
+
import { trackingConsented } from "./analytics/config-DJr-GRhs.js";
|
|
3
|
+
import { purchaseCookie } from "./analytics/cookies-pLozllyB.js";
|
|
4
|
+
import { checkoutAnalytics } from "./analytics/events-Dt81tIp1.js";
|
|
5
|
+
import { cartCookie, cartToken } from "./cart-D7DxZ2lD.js";
|
|
6
|
+
import { optional, text } from "./form-BtQcbXlJ.js";
|
|
7
|
+
import { identityDocumentError, isIdentityDocumentType } from "./documents-T1mzc_j8.js";
|
|
8
|
+
import { CHECKOUT_INTENTS, isIntent } from "./form-contracts-BNpHaTGJ.js";
|
|
9
|
+
import { readPlace } from "./place-CmW4hBWY.js";
|
|
10
|
+
import { customer, customerHeaders, requireCustomerRegistration } from "./session-CKvmFJyg.js";
|
|
11
|
+
import { readCountries, readDocumentCountries, store } from "./store-8Q-_LFrQ.js";
|
|
12
|
+
import { data, redirect, replace } from "react-router";
|
|
13
|
+
import { ApiError, OrderPaymentStartSchema, StorefrontInvoiceTypeSchema } from "@ecomiq/core";
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
// src/checkout.ts
|
|
16
|
+
const UNSTRUCTURED_POSTAL_CODE = "00000";
|
|
17
|
+
const CheckoutAuthenticationSchema = z.object({ authentication: z.object({
|
|
18
|
+
payment: OrderPaymentStartSchema,
|
|
19
|
+
orderStatusUrl: z.string()
|
|
20
|
+
}) });
|
|
21
|
+
function shouldRevalidateCheckout({ actionResult, defaultShouldRevalidate }) {
|
|
22
|
+
const result = CheckoutAuthenticationSchema.safeParse(actionResult);
|
|
23
|
+
if (!result.success) return defaultShouldRevalidate;
|
|
24
|
+
const payment = result.data.authentication.payment;
|
|
25
|
+
if (payment.actionKind === "threeDS") return false;
|
|
26
|
+
if (payment.actionKind === "embedded" && payment.providerId === "stripe") return false;
|
|
27
|
+
return defaultShouldRevalidate;
|
|
28
|
+
}
|
|
29
|
+
function needsShipping(cart) {
|
|
30
|
+
return cart.items.some((item) => item.requiresShipping);
|
|
31
|
+
}
|
|
32
|
+
function checkoutProgress(cart) {
|
|
33
|
+
const contact = [
|
|
34
|
+
cart.email,
|
|
35
|
+
cart.firstName,
|
|
36
|
+
cart.lastName,
|
|
37
|
+
cart.phone
|
|
38
|
+
].every((value) => value !== null && value !== "");
|
|
39
|
+
const delivery = contact && (!needsShipping(cart) || cart.shippingAddress !== null);
|
|
40
|
+
return {
|
|
41
|
+
contact,
|
|
42
|
+
delivery,
|
|
43
|
+
shipping: delivery && (!needsShipping(cart) || cart.shippingMethods.length > 0)
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function checkoutStep(cart, edit) {
|
|
47
|
+
const done = checkoutProgress(cart);
|
|
48
|
+
if (edit === "contact" || !done.contact) return "contact";
|
|
49
|
+
if (edit === "delivery" || !done.delivery) return "delivery";
|
|
50
|
+
if (edit === "shipping" || !done.shipping) return "shipping";
|
|
51
|
+
return "payment";
|
|
52
|
+
}
|
|
53
|
+
function checkoutAddressDefaults(cart, address, countryCode) {
|
|
54
|
+
const saved = cart.shippingAddress;
|
|
55
|
+
if (saved) return {
|
|
56
|
+
country: countryCode,
|
|
57
|
+
department: saved.department,
|
|
58
|
+
province: saved.province,
|
|
59
|
+
district: saved.district,
|
|
60
|
+
line: saved.address1,
|
|
61
|
+
reference: saved.address2,
|
|
62
|
+
phone: saved.phone
|
|
63
|
+
};
|
|
64
|
+
if (address) return {
|
|
65
|
+
...addressDefaults(address),
|
|
66
|
+
country: countryCode
|
|
67
|
+
};
|
|
68
|
+
return {
|
|
69
|
+
country: countryCode,
|
|
70
|
+
phone: cart.phone
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function defaultPaymentMethod(cart, methods) {
|
|
74
|
+
return (methods.find((method) => method.code === cart.paymentMethodCode) ?? methods.find((method) => method.isDefault) ?? methods[0])?.code;
|
|
75
|
+
}
|
|
76
|
+
function shippingField(group) {
|
|
77
|
+
return `rate-${group.sellerId ?? "store"}-${group.shippingProfileId ?? "default"}`;
|
|
78
|
+
}
|
|
79
|
+
function rateValue(rate) {
|
|
80
|
+
if (rate.rateId) return rate.rateId;
|
|
81
|
+
return [
|
|
82
|
+
rate.shippingMethodId,
|
|
83
|
+
rate.target,
|
|
84
|
+
rate.targetId ?? "",
|
|
85
|
+
rate.serviceCode ?? ""
|
|
86
|
+
].join(":");
|
|
87
|
+
}
|
|
88
|
+
function checkoutShipping(groups) {
|
|
89
|
+
return groups.map((group) => ({
|
|
90
|
+
...group,
|
|
91
|
+
field: shippingField(group),
|
|
92
|
+
rates: group.rates.map((rate) => ({
|
|
93
|
+
...rate,
|
|
94
|
+
value: rateValue(rate)
|
|
95
|
+
}))
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
async function shippingOptions(client, cart) {
|
|
99
|
+
const postalCode = cart.shippingAddress?.postalCode;
|
|
100
|
+
if (!needsShipping(cart) || !postalCode) return [];
|
|
101
|
+
const options = await client.getShippingOptions({
|
|
102
|
+
cartToken: cart.token,
|
|
103
|
+
postalCode
|
|
104
|
+
});
|
|
105
|
+
if (options.groups.some((group) => group.rates.length === 0)) return [];
|
|
106
|
+
return options.groups;
|
|
107
|
+
}
|
|
108
|
+
async function loadCheckout(request, context) {
|
|
109
|
+
const token = await cartToken(request);
|
|
110
|
+
if (token === null) throw redirect("/cart");
|
|
111
|
+
const client = store(context);
|
|
112
|
+
const checkout = await client.getCartCheckout({ cartToken: token }).catch(async (error) => {
|
|
113
|
+
if (error instanceof ApiError && (error.status === 404 || error.code === "Carts.NotActive")) throw redirect("/cart", { headers: { "Set-Cookie": await cartCookie.serialize("", { maxAge: 0 }) } });
|
|
114
|
+
throw error;
|
|
115
|
+
});
|
|
116
|
+
if (checkout.cart.items.length === 0) throw redirect("/cart");
|
|
117
|
+
const { store: settings } = await client.getSettings();
|
|
118
|
+
const current = await customer(request, context);
|
|
119
|
+
if (current) await requireCustomerRegistration(request, context, current);
|
|
120
|
+
const step = checkoutStep(checkout.cart, new URL(request.url).searchParams.get("edit"));
|
|
121
|
+
const delivery = needsShipping(checkout.cart);
|
|
122
|
+
const placed = delivery && checkout.cart.shippingAddress !== null;
|
|
123
|
+
const [me, addresses, countries, shipping, documentCountries] = await Promise.all([
|
|
124
|
+
current && step === "contact" ? client.getCustomerMe(customerHeaders(current)) : null,
|
|
125
|
+
current && delivery && !placed ? client.getCustomerAddresses(customerHeaders(current)) : [],
|
|
126
|
+
delivery ? readCountries(client) : [],
|
|
127
|
+
placed ? shippingOptions(client, checkout.cart) : [],
|
|
128
|
+
readDocumentCountries(client)
|
|
129
|
+
]);
|
|
130
|
+
const methods = checkout.paymentMethods.methods.filter((method) => method.isActive).sort((a, b) => a.sortOrder - b.sortOrder);
|
|
131
|
+
return {
|
|
132
|
+
countryCode: settings.countryCode,
|
|
133
|
+
documentCountries,
|
|
134
|
+
language: settings.language,
|
|
135
|
+
cart: checkout.cart,
|
|
136
|
+
methods,
|
|
137
|
+
bumps: checkout.orderBumps,
|
|
138
|
+
signedIn: current !== null,
|
|
139
|
+
me,
|
|
140
|
+
address: addresses.find((item) => item.isDefault) ?? addresses[0] ?? null,
|
|
141
|
+
countries,
|
|
142
|
+
shipping: checkoutShipping(shipping),
|
|
143
|
+
analytics: checkoutAnalytics(checkout.cart)
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function failure(message) {
|
|
147
|
+
return data({
|
|
148
|
+
error: message,
|
|
149
|
+
promo: false
|
|
150
|
+
}, { status: 400 });
|
|
151
|
+
}
|
|
152
|
+
async function saveContact(client, token, form) {
|
|
153
|
+
let invoiceType;
|
|
154
|
+
if (form.has("invoiceType")) {
|
|
155
|
+
const selected = StorefrontInvoiceTypeSchema.safeParse(text(form, "invoiceType"));
|
|
156
|
+
if (!selected.success) return failure("Elige el comprobante.");
|
|
157
|
+
invoiceType = selected.data;
|
|
158
|
+
}
|
|
159
|
+
const identificationNumber = optional(form, "identificationNumber");
|
|
160
|
+
const entityTaxId = optional(form, "entityTaxId");
|
|
161
|
+
const input = {
|
|
162
|
+
cartToken: token,
|
|
163
|
+
email: text(form, "email"),
|
|
164
|
+
firstName: text(form, "firstName"),
|
|
165
|
+
lastName: text(form, "lastName"),
|
|
166
|
+
phone: text(form, "phone"),
|
|
167
|
+
identificationType: identificationNumber === null ? null : optional(form, "identificationType"),
|
|
168
|
+
identificationNumber,
|
|
169
|
+
identificationCountryCode: identificationNumber !== null || entityTaxId !== null || invoiceType === "invoice" ? optional(form, "identificationCountryCode") : null,
|
|
170
|
+
entityTaxId,
|
|
171
|
+
entityName: optional(form, "entityName"),
|
|
172
|
+
invoiceType
|
|
173
|
+
};
|
|
174
|
+
if (!input.email || !input.firstName || !input.lastName || !input.phone) return failure("Completa tus datos de contacto.");
|
|
175
|
+
if (invoiceType === "invoice" && (input.entityTaxId === null || identityDocumentError("TaxId", input.identificationCountryCode ?? "", input.entityTaxId) !== null || input.entityName === null)) return failure("Ingresa la identificación fiscal, el país emisor y la razón social para la factura.");
|
|
176
|
+
if (entityTaxId !== null && identityDocumentError("TaxId", input.identificationCountryCode ?? "", entityTaxId) !== null) return failure("Revisa el documento del comprador.");
|
|
177
|
+
if (identificationNumber !== null && !(isIdentityDocumentType(input.identificationType) && identityDocumentError(input.identificationType, input.identificationCountryCode ?? "", identificationNumber) === null)) return failure("Revisa el documento del comprador.");
|
|
178
|
+
await client.patchCartBuyer(input);
|
|
179
|
+
return redirect("/checkout");
|
|
180
|
+
}
|
|
181
|
+
async function saveDelivery(client, token, form) {
|
|
182
|
+
const { store: settings } = await client.getSettings();
|
|
183
|
+
const requestedCountry = text(form, "country");
|
|
184
|
+
if (requestedCountry !== "" && requestedCountry !== settings.countryCode) return failure("La entrega debe ser dentro del país de la tienda.");
|
|
185
|
+
const cart = await client.getCart({ cartToken: token });
|
|
186
|
+
const department = readPlace(text(form, "department"));
|
|
187
|
+
const province = readPlace(text(form, "province"));
|
|
188
|
+
const district = readPlace(text(form, "district"));
|
|
189
|
+
const line = text(form, "line");
|
|
190
|
+
if (!line || !district.name) return failure("Completa la dirección de entrega.");
|
|
191
|
+
const structured = department.code !== "" && province.code !== "";
|
|
192
|
+
let city = district.name;
|
|
193
|
+
let postalCode = optional(form, "postalCode") ?? UNSTRUCTURED_POSTAL_CODE;
|
|
194
|
+
if (structured) {
|
|
195
|
+
city = province.name;
|
|
196
|
+
postalCode = optional(form, "postalCode") ?? district.code;
|
|
197
|
+
}
|
|
198
|
+
const address = {
|
|
199
|
+
name: `${cart.firstName ?? ""} ${cart.lastName ?? ""}`.trim() || line,
|
|
200
|
+
address1: line,
|
|
201
|
+
address2: optional(form, "reference"),
|
|
202
|
+
city,
|
|
203
|
+
countryCode: settings.countryCode,
|
|
204
|
+
department: department.name || null,
|
|
205
|
+
province: structured ? province.name : null,
|
|
206
|
+
district: district.name,
|
|
207
|
+
postalCode,
|
|
208
|
+
phone: optional(form, "phone") ?? cart.phone,
|
|
209
|
+
latitude: null,
|
|
210
|
+
longitude: null
|
|
211
|
+
};
|
|
212
|
+
await client.patchCartAddresses({
|
|
213
|
+
cartToken: token,
|
|
214
|
+
shippingAddress: {
|
|
215
|
+
...address,
|
|
216
|
+
departmentCode: structured ? department.code : null,
|
|
217
|
+
provinceCode: structured ? province.code : null,
|
|
218
|
+
districtCode: structured ? district.code : null
|
|
219
|
+
},
|
|
220
|
+
billingAddress: address
|
|
221
|
+
});
|
|
222
|
+
return redirect("/checkout");
|
|
223
|
+
}
|
|
224
|
+
async function saveShipping(client, token, form) {
|
|
225
|
+
const postalCode = (await client.getCart({ cartToken: token })).shippingAddress?.postalCode;
|
|
226
|
+
if (!postalCode) return failure("Primero indica la dirección de entrega.");
|
|
227
|
+
const { groups } = await client.getShippingOptions({
|
|
228
|
+
cartToken: token,
|
|
229
|
+
postalCode
|
|
230
|
+
});
|
|
231
|
+
if (groups.length === 0 || groups.some((group) => group.rates.length === 0)) return failure("No hay métodos de envío para esta dirección. Prueba con otra dirección.");
|
|
232
|
+
const choices = groups.map((group) => ({
|
|
233
|
+
group,
|
|
234
|
+
rate: group.rates.find((candidate) => rateValue(candidate) === text(form, shippingField(group)))
|
|
235
|
+
}));
|
|
236
|
+
if (choices.some((choice) => choice.rate === void 0)) return failure("Las opciones de envío cambiaron. Elige un método para cada paquete.");
|
|
237
|
+
for (const { group, rate } of choices) {
|
|
238
|
+
if (rate === void 0) continue;
|
|
239
|
+
await client.putCartShippingMethod({
|
|
240
|
+
cartToken: token,
|
|
241
|
+
rateId: rate.rateId ?? null,
|
|
242
|
+
shippingMethodId: rate.shippingMethodId,
|
|
243
|
+
target: rate.target,
|
|
244
|
+
targetId: rate.targetId ?? null,
|
|
245
|
+
serviceCode: rate.serviceCode ?? null,
|
|
246
|
+
sellerId: group.sellerId ?? null,
|
|
247
|
+
shippingProfileId: group.shippingProfileId ?? null,
|
|
248
|
+
scheduledDate: null,
|
|
249
|
+
scheduledTime: null
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
return redirect("/checkout");
|
|
253
|
+
}
|
|
254
|
+
async function savePaymentMethod(client, token, form) {
|
|
255
|
+
const method = text(form, "method");
|
|
256
|
+
if (!method) return failure("Elige un medio de pago.");
|
|
257
|
+
await client.putCartPaymentMethod({
|
|
258
|
+
cartToken: token,
|
|
259
|
+
paymentMethodCode: method
|
|
260
|
+
});
|
|
261
|
+
return replace("/checkout");
|
|
262
|
+
}
|
|
263
|
+
async function placeOrder(client, input, headers) {
|
|
264
|
+
try {
|
|
265
|
+
return await client.checkoutCart(input, headers);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
if (error instanceof ApiError && error.status >= 400 && error.status < 500) throw error;
|
|
268
|
+
if ((await client.getCart({ cartToken: input.cartToken }).catch(() => null))?.status !== "completed") throw error;
|
|
269
|
+
return client.checkoutCart(input, headers);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
async function pay(client, token, form, headers, tracked) {
|
|
273
|
+
const method = text(form, "method");
|
|
274
|
+
if (!method) return failure("Elige un medio de pago.");
|
|
275
|
+
if (method === "powerpay") {
|
|
276
|
+
const identificationNumber = text(form, "payerIdentificationNumber");
|
|
277
|
+
if (!/^\d{8}$/.test(identificationNumber)) return failure("Ingresa tu DNI de 8 dígitos para pagar con PowerPay.");
|
|
278
|
+
const cart = await client.getCart({ cartToken: token });
|
|
279
|
+
const matchingDocument = cart.identificationType === "NationalId" && cart.identificationCountryCode === "PE" && cart.identificationNumber === identificationNumber;
|
|
280
|
+
if ((cart.identificationType != null || cart.identificationNumber != null) && !matchingDocument || (cart.identificationCountryCode != null || cart.entityTaxId != null) && cart.identificationCountryCode !== "PE") return failure("PowerPay requiere un comprador con DNI peruano. Revisa tus datos o elige otro medio de pago.");
|
|
281
|
+
if (!matchingDocument) {
|
|
282
|
+
if (!cart.email || !cart.firstName || !cart.lastName || !cart.phone) return failure("Completa tus datos de contacto.");
|
|
283
|
+
await client.patchCartBuyer({
|
|
284
|
+
cartToken: token,
|
|
285
|
+
email: cart.email,
|
|
286
|
+
firstName: cart.firstName,
|
|
287
|
+
lastName: cart.lastName,
|
|
288
|
+
phone: cart.phone,
|
|
289
|
+
identificationType: "NationalId",
|
|
290
|
+
identificationCountryCode: "PE",
|
|
291
|
+
identificationNumber,
|
|
292
|
+
invoiceType: cart.invoiceType,
|
|
293
|
+
entityTaxId: cart.entityTaxId,
|
|
294
|
+
entityName: cart.entityName
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const receipt = form.get("receipt");
|
|
299
|
+
let receiptUrl;
|
|
300
|
+
if (receipt instanceof File && receipt.size > 0) receiptUrl = (await client.uploadPaymentReceipt({
|
|
301
|
+
cartToken: token,
|
|
302
|
+
file: receipt,
|
|
303
|
+
fileName: receipt.name
|
|
304
|
+
})).url;
|
|
305
|
+
const paymentToken = text(form, "token") || void 0;
|
|
306
|
+
const providerPaymentMethodId = text(form, "providerPaymentMethodId") || void 0;
|
|
307
|
+
const deviceSessionId = text(form, "deviceSessionId") || void 0;
|
|
308
|
+
const result = await placeOrder(client, {
|
|
309
|
+
cartToken: token,
|
|
310
|
+
paymentMethodCode: method,
|
|
311
|
+
externalRef: `checkout-${token}`,
|
|
312
|
+
receiptUrl,
|
|
313
|
+
token: paymentToken,
|
|
314
|
+
providerPaymentMethodId,
|
|
315
|
+
deviceSessionId
|
|
316
|
+
}, headers);
|
|
317
|
+
const cookies = new Headers({ "Set-Cookie": await cartCookie.serialize("", { maxAge: 0 }) });
|
|
318
|
+
if (tracked) cookies.append("Set-Cookie", purchaseCookie(result));
|
|
319
|
+
if (result.payment?.actionKind === "threeDS") {
|
|
320
|
+
const payment = result.payment;
|
|
321
|
+
return data({ authentication: {
|
|
322
|
+
orderStatusUrl: result.orderStatusUrl,
|
|
323
|
+
payment: {
|
|
324
|
+
transactionId: payment.transactionId,
|
|
325
|
+
externalId: payment.externalId,
|
|
326
|
+
providerId: payment.providerId ?? null,
|
|
327
|
+
paymentMethodCode: payment.paymentMethodCode,
|
|
328
|
+
isCaptured: false,
|
|
329
|
+
actionKind: "threeDS",
|
|
330
|
+
amount: payment.amount,
|
|
331
|
+
currencyCode: result.cart.currencyCode,
|
|
332
|
+
expiresAt: payment.action?.expiresAt ?? null,
|
|
333
|
+
publicConfiguration: payment.action?.publicConfiguration ?? {},
|
|
334
|
+
actionUrl: null,
|
|
335
|
+
clientSecret: null,
|
|
336
|
+
qrCode: null,
|
|
337
|
+
receiptUrl: null,
|
|
338
|
+
voucherCode: null,
|
|
339
|
+
qrImageUrl: null
|
|
340
|
+
}
|
|
341
|
+
} }, { headers: cookies });
|
|
342
|
+
}
|
|
343
|
+
if (result.payment?.actionKind === "embedded" && result.payment.providerId === "stripe" && providerPaymentMethodId !== void 0) {
|
|
344
|
+
const payment = result.payment;
|
|
345
|
+
return data({ authentication: {
|
|
346
|
+
orderStatusUrl: result.orderStatusUrl,
|
|
347
|
+
payment: {
|
|
348
|
+
transactionId: payment.transactionId,
|
|
349
|
+
externalId: payment.externalId,
|
|
350
|
+
providerId: payment.providerId ?? null,
|
|
351
|
+
paymentMethodCode: payment.paymentMethodCode,
|
|
352
|
+
isCaptured: false,
|
|
353
|
+
actionKind: "embedded",
|
|
354
|
+
amount: payment.amount,
|
|
355
|
+
currencyCode: result.cart.currencyCode,
|
|
356
|
+
expiresAt: payment.action?.expiresAt ?? null,
|
|
357
|
+
publicConfiguration: payment.action?.publicConfiguration ?? {},
|
|
358
|
+
actionUrl: null,
|
|
359
|
+
clientSecret: payment.action?.clientSecret ?? null,
|
|
360
|
+
qrCode: null,
|
|
361
|
+
receiptUrl: null,
|
|
362
|
+
voucherCode: null,
|
|
363
|
+
qrImageUrl: null
|
|
364
|
+
}
|
|
365
|
+
} }, { headers: cookies });
|
|
366
|
+
}
|
|
367
|
+
const url = result.payment?.action?.url;
|
|
368
|
+
if (result.payment?.actionKind === "redirect" && url) return redirect(url, { headers: cookies });
|
|
369
|
+
if (result.payment && (result.payment.actionKind !== "none" || paysOutsideCheckout(result.payment))) return redirect(result.orderStatusUrl, { headers: cookies });
|
|
370
|
+
return redirect(`/checkout/done?order=${encodeURIComponent(result.orderId)}`, { headers: cookies });
|
|
371
|
+
}
|
|
372
|
+
function paysOutsideCheckout(payment) {
|
|
373
|
+
return payment.providerId === "manual" && payment.paymentMethodCode !== "cash_on_delivery";
|
|
374
|
+
}
|
|
375
|
+
async function saveDetails(client, token, form) {
|
|
376
|
+
const contact = await saveContact(client, token, form);
|
|
377
|
+
if (!(contact instanceof Response)) return contact;
|
|
378
|
+
if (needsShipping(await client.getCart({ cartToken: token }))) {
|
|
379
|
+
const delivery = await saveDelivery(client, token, form);
|
|
380
|
+
if (!(delivery instanceof Response)) return delivery;
|
|
381
|
+
const shipping = await saveShipping(client, token, form);
|
|
382
|
+
if (!(shipping instanceof Response)) return shipping;
|
|
383
|
+
}
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
async function checkoutAll(client, token, form, headers, tracked) {
|
|
387
|
+
const unsaved = await saveDetails(client, token, form);
|
|
388
|
+
if (unsaved !== null) return unsaved;
|
|
389
|
+
return pay(client, token, form, headers, tracked);
|
|
390
|
+
}
|
|
391
|
+
const CHECKOUT_ERRORS = {
|
|
392
|
+
"Catalog.InsufficientStock": "Ya no hay stock suficiente de un producto. Revisa tu carrito.",
|
|
393
|
+
"Inventory.InsufficientStock": "Ya no hay stock suficiente de un producto. Revisa tu carrito.",
|
|
394
|
+
"Checkout.InProgress": "Tu pago se está procesando. Espera un momento antes de volver a intentar.",
|
|
395
|
+
"Checkout.PaymentMethodChanged": "Este pedido se calculó con otro medio de pago. Paga con ese medio o empieza otra compra.",
|
|
396
|
+
"Checkout.OrderNotActive": "Este pedido ya no está activo. Empieza una compra nueva.",
|
|
397
|
+
"Payments.MethodNotAvailable": "Ese medio de pago ya no está disponible. Elige otro.",
|
|
398
|
+
"Payments.Rejected": "No pudimos cobrar tu pago. Prueba con otra tarjeta o elige otro medio de pago.",
|
|
399
|
+
"Payments.InvalidPayerEmail": "La pasarela de pago no pudo validar tu correo. Revisa que esté bien escrito o usa otro.",
|
|
400
|
+
"Carts.NotActive": "Esta compra ya terminó. Vuelve a la tienda para empezar otra.",
|
|
401
|
+
"Order.DomesticShippingRequired": "La entrega debe ser dentro del país de la tienda."
|
|
402
|
+
};
|
|
403
|
+
async function checkoutAction(request, context) {
|
|
404
|
+
const current = await customer(request, context);
|
|
405
|
+
if (current) await requireCustomerRegistration(request, context, current);
|
|
406
|
+
const token = await cartToken(request);
|
|
407
|
+
if (token === null) throw redirect("/cart");
|
|
408
|
+
const client = store(context);
|
|
409
|
+
const form = await request.formData();
|
|
410
|
+
const intent = text(form, "intent");
|
|
411
|
+
if (!isIntent(CHECKOUT_INTENTS, intent)) return failure("Acción no válida.");
|
|
412
|
+
try {
|
|
413
|
+
if (intent === "contact") return await saveContact(client, token, form);
|
|
414
|
+
if (intent === "delivery") return await saveDelivery(client, token, form);
|
|
415
|
+
if (intent === "shipping") return await saveShipping(client, token, form);
|
|
416
|
+
if (intent === "payment-method") return await savePaymentMethod(client, token, form);
|
|
417
|
+
if (intent === "promo") {
|
|
418
|
+
const code = text(form, "code");
|
|
419
|
+
if (code) await client.addCartPromotionCode({
|
|
420
|
+
cartToken: token,
|
|
421
|
+
code
|
|
422
|
+
});
|
|
423
|
+
return redirect("/checkout");
|
|
424
|
+
}
|
|
425
|
+
if (intent === "promo-remove") {
|
|
426
|
+
await client.removeCartPromotionCode({
|
|
427
|
+
cartToken: token,
|
|
428
|
+
code: text(form, "code")
|
|
429
|
+
});
|
|
430
|
+
return redirect("/checkout");
|
|
431
|
+
}
|
|
432
|
+
if (intent === "bump") {
|
|
433
|
+
await client.addCartItem({
|
|
434
|
+
cartToken: token,
|
|
435
|
+
variantId: text(form, "variantId"),
|
|
436
|
+
quantity: 1,
|
|
437
|
+
orderBumpId: text(form, "bumpId")
|
|
438
|
+
});
|
|
439
|
+
return redirect("/checkout");
|
|
440
|
+
}
|
|
441
|
+
if (intent === "pay" || intent === "checkout") {
|
|
442
|
+
const tracked = await client.getSettings().then((settings) => trackingConsented(request, settings), () => false);
|
|
443
|
+
const headers = current ? customerHeaders(current) : void 0;
|
|
444
|
+
if (intent === "checkout") return await checkoutAll(client, token, form, headers, tracked);
|
|
445
|
+
return await pay(client, token, form, headers, tracked);
|
|
446
|
+
}
|
|
447
|
+
return failure("Acción no válida.");
|
|
448
|
+
} catch (error) {
|
|
449
|
+
const apiError = error instanceof ApiError ? error : void 0;
|
|
450
|
+
const functional = apiError !== void 0 && apiError.status >= 400 && apiError.status < 500;
|
|
451
|
+
const promo = intent === "promo" && functional && [
|
|
452
|
+
400,
|
|
453
|
+
409,
|
|
454
|
+
422
|
|
455
|
+
].includes(apiError.status);
|
|
456
|
+
let message = "No pudimos continuar con la compra. Intenta de nuevo.";
|
|
457
|
+
if (functional) {
|
|
458
|
+
message = promo ? "Ese código no es válido para tu carrito." : "Revisa los datos e intenta de nuevo.";
|
|
459
|
+
if (apiError.status === 401) message = "Tu sesión venció. Vuelve a ingresar.";
|
|
460
|
+
if (apiError.status === 403) message = "No tienes acceso a esta compra.";
|
|
461
|
+
if (apiError.status === 404) message = "El carrito o la opción elegida ya no está disponible.";
|
|
462
|
+
if (apiError.status === 429) message = "Espera un momento antes de volver a intentar.";
|
|
463
|
+
if (apiError.code !== void 0) message = CHECKOUT_ERRORS[apiError.code] ?? message;
|
|
464
|
+
}
|
|
465
|
+
const status = functional ? apiError.status : apiError?.status === 0 ? 503 : 502;
|
|
466
|
+
return data({
|
|
467
|
+
error: message,
|
|
468
|
+
promo,
|
|
469
|
+
code: apiError?.code,
|
|
470
|
+
traceId: apiError?.traceId
|
|
471
|
+
}, { status });
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
export { checkoutAction, checkoutAddressDefaults, checkoutProgress, checkoutStep, defaultPaymentMethod, loadCheckout, needsShipping, shouldRevalidateCheckout };
|