@asteby/storefront-react 0.8.0 → 0.10.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.
@@ -0,0 +1,243 @@
1
+ import { brandName, BrandMark } from './chunk-DSZVJ2PW.js';
2
+ export { BrandMark, brandKey, brandName, findBrand, listBrands } from './chunk-DSZVJ2PW.js';
3
+ import { useResolvedLocale } from './chunk-T52H5HL3.js';
4
+ import './chunk-DQBL2DFJ.js';
5
+ import { useMemo, useSyncExternalStore } from 'react';
6
+ import { formatMoney } from '@asteby/storefront';
7
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
8
+
9
+ // src/checkout/delivery.ts
10
+ var ES = {
11
+ etaOne: "Llega en 1 d\xEDa h\xE1bil",
12
+ etaMany: "Llega en {n} d\xEDas h\xE1biles",
13
+ etaRange: "Llega en {min} a {max} d\xEDas h\xE1biles",
14
+ etaUpTo: "Llega en m\xE1ximo {max} d\xEDas h\xE1biles",
15
+ etaFrom: "Llega en {min} d\xEDas h\xE1biles o m\xE1s",
16
+ cheapest: "M\xE1s econ\xF3mico",
17
+ fastest: "M\xE1s r\xE1pido",
18
+ free: "Gratis",
19
+ pickupTitle: "Recoger en {name}",
20
+ quoteToday: "Precios de env\xEDo vigentes hasta las {time}.",
21
+ quoteTomorrow: "Precios de env\xEDo vigentes hasta ma\xF1ana a las {time}.",
22
+ quoteLater: "Precios de env\xEDo vigentes hasta el {date}.",
23
+ quoteSoon: "Los precios de env\xEDo vencen pronto ({time}).",
24
+ partial: "No pudimos consultar todas las paqueter\xEDas. Estas son las opciones disponibles ahora.",
25
+ loading: "Buscando opciones de env\xEDo\u2026",
26
+ requoted: "Las tarifas de env\xEDo cambiaron y ya est\xE1n actualizadas. Revisa tu env\xEDo antes de pagar."
27
+ };
28
+ var EN = {
29
+ etaOne: "Arrives in 1 business day",
30
+ etaMany: "Arrives in {n} business days",
31
+ etaRange: "Arrives in {min} to {max} business days",
32
+ etaUpTo: "Arrives in up to {max} business days",
33
+ etaFrom: "Arrives in {min}+ business days",
34
+ cheapest: "Cheapest",
35
+ fastest: "Fastest",
36
+ free: "Free",
37
+ pickupTitle: "Pick up at {name}",
38
+ quoteToday: "Shipping prices valid until {time}.",
39
+ quoteTomorrow: "Shipping prices valid until tomorrow at {time}.",
40
+ quoteLater: "Shipping prices valid until {date}.",
41
+ quoteSoon: "Shipping prices expire soon ({time}).",
42
+ partial: "We couldn't reach every carrier. These are the options available now.",
43
+ loading: "Looking for delivery options\u2026",
44
+ requoted: "Shipping rates changed and are now up to date. Check your delivery before paying."
45
+ };
46
+ function deliveryMessages(locale, overrides) {
47
+ const base = (locale ?? "es-MX").toLowerCase().startsWith("en") ? EN : ES;
48
+ return overrides ? { ...base, ...overrides } : base;
49
+ }
50
+ var fill = (tpl, vars) => tpl.replace(/\{(\w+)\}/g, (_, k) => String(vars[k] ?? "")).replace(/\.\.$/, ".");
51
+ function rateCarrier(rate) {
52
+ return brandName(rate.carrier);
53
+ }
54
+ function rateService(rate) {
55
+ const title = rate.title.trim();
56
+ const carrier = rateCarrier(rate);
57
+ if (!carrier) return title;
58
+ for (const prefix of [carrier, rate.carrier ?? ""]) {
59
+ if (!prefix) continue;
60
+ if (title.toLowerCase().startsWith(prefix.toLowerCase())) {
61
+ const rest = title.slice(prefix.length).replace(/^[\s·:|-]+/, "");
62
+ if (rest) return rest;
63
+ }
64
+ }
65
+ return title;
66
+ }
67
+ function rateEta(rate, messages = ES) {
68
+ const { eta_days_min: min, eta_days_max: max } = rate;
69
+ if (typeof min === "number" && typeof max === "number") {
70
+ if (min === max) return min === 1 ? messages.etaOne : fill(messages.etaMany, { n: min });
71
+ return fill(messages.etaRange, { min, max });
72
+ }
73
+ if (typeof max === "number") return max === 1 ? messages.etaOne : fill(messages.etaUpTo, { max });
74
+ if (typeof min === "number") return min === 1 ? messages.etaOne : fill(messages.etaFrom, { min });
75
+ return rate.eta ?? null;
76
+ }
77
+ var costOf = (r) => {
78
+ const n = Number(r.cost.amount);
79
+ return Number.isFinite(n) ? n : null;
80
+ };
81
+ var daysOf = (r) => r.eta_days_max ?? r.eta_days_min ?? null;
82
+ function rateBadges(rates) {
83
+ const out = /* @__PURE__ */ new Map();
84
+ if (rates.length < 2) return out;
85
+ const add = (id, b) => out.set(id, [...out.get(id) ?? [], b]);
86
+ const unique = (values) => {
87
+ const known = values.filter((v) => v[1] !== null);
88
+ if (known.length < 2) return null;
89
+ const min = Math.min(...known.map((v) => v[1]));
90
+ const winners = known.filter((v) => v[1] === min);
91
+ return winners.length === 1 ? winners[0][0] : null;
92
+ };
93
+ const currencies = new Set(rates.map((r) => r.cost.currency_code));
94
+ if (currencies.size === 1) {
95
+ const id = unique(rates.map((r) => [r.id, costOf(r)]));
96
+ if (id) add(id, "cheapest");
97
+ }
98
+ const fast = unique(rates.map((r) => [r.id, daysOf(r)]));
99
+ if (fast) add(fast, "fastest");
100
+ return out;
101
+ }
102
+ function quoteDeadline(rates) {
103
+ let best = null;
104
+ for (const r of rates) {
105
+ if (!r.expires_at) continue;
106
+ const d = new Date(r.expires_at);
107
+ if (Number.isNaN(d.getTime())) continue;
108
+ if (!best || d < best) best = d;
109
+ }
110
+ return best;
111
+ }
112
+ function quoteNote(deadline, opts = {}) {
113
+ if (!deadline) return null;
114
+ const now = opts.now ?? /* @__PURE__ */ new Date();
115
+ const left = deadline.getTime() - now.getTime();
116
+ if (left <= 0) return null;
117
+ const m = opts.messages ?? ES;
118
+ const locale = opts.locale ?? "es-MX";
119
+ const tz = opts.timeZone ? { timeZone: opts.timeZone } : {};
120
+ const time = new Intl.DateTimeFormat(locale, { hour: "numeric", minute: "2-digit", ...tz }).format(deadline);
121
+ if (left <= (opts.soonMinutes ?? 15) * 6e4) return { text: fill(m.quoteSoon, { time }), soon: true };
122
+ const day = (d) => new Intl.DateTimeFormat("en-CA", { year: "numeric", month: "2-digit", day: "2-digit", ...tz }).format(d);
123
+ if (day(deadline) === day(now)) return { text: fill(m.quoteToday, { time }), soon: false };
124
+ if (day(deadline) === day(new Date(now.getTime() + 864e5))) return { text: fill(m.quoteTomorrow, { time }), soon: false };
125
+ const date = new Intl.DateTimeFormat(locale, { day: "numeric", month: "short", hour: "numeric", minute: "2-digit", ...tz }).format(deadline);
126
+ return { text: fill(m.quoteLater, { date }), soon: false };
127
+ }
128
+ var QUOTE_CODES = /* @__PURE__ */ new Set(["quote_expired", "rate_expired", "rate_changed", "rate_unavailable", "rate_not_found", "stale_rate", "invalid_rate"]);
129
+ function isQuoteChange(err) {
130
+ if (!err) return false;
131
+ if (err.code && QUOTE_CODES.has(err.code)) return true;
132
+ if (err.status === 409) return true;
133
+ return err.status === 422 && (err.field === "rate_id" || err.field === "delivery" || err.field === "delivery.rate_id" || !err.field);
134
+ }
135
+ function matchRate(previous, rates) {
136
+ if (!previous) return null;
137
+ const norm = (s) => (s ?? "").trim().toLowerCase();
138
+ const sameCarrier = (r) => norm(r.carrier) === norm(previous.carrier);
139
+ if (previous.service_code) {
140
+ const hit = rates.find((r) => sameCarrier(r) && norm(r.service_code) === norm(previous.service_code));
141
+ if (hit) return hit;
142
+ }
143
+ return rates.find((r) => sameCarrier(r) && norm(r.title) === norm(previous.title)) ?? null;
144
+ }
145
+ var SR_ONLY = { position: "absolute", width: 1, height: 1, overflow: "hidden", clip: "rect(0 0 0 0)", whiteSpace: "nowrap" };
146
+ var useHydrated = () => useSyncExternalStore(
147
+ () => () => {
148
+ },
149
+ () => true,
150
+ () => false
151
+ );
152
+ function PickupGlyph({ size = 24 }) {
153
+ return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 48 32", width: Math.round(size * 1.5), height: size, "aria-hidden": "true", focusable: "false", style: { flex: "none" }, "data-sf-brand-mark": "pickup", children: [
154
+ /* @__PURE__ */ jsx("rect", { x: "0.5", y: "0.5", width: "47", height: "31", rx: "5", fill: "var(--sf-brand-mark-bg, #E7E9EE)", stroke: "var(--sf-brand-mark-ring, rgb(127 127 127 / 0.35))" }),
155
+ /* @__PURE__ */ jsx("path", { d: "M15 13.5 17 9h14l2 4.5M15 13.5h18M15 13.5V23h18v-9.5M21 23v-5h6v5", fill: "none", stroke: "var(--sf-brand-mark-fg, #2B2F38)", strokeWidth: "1.8", strokeLinejoin: "round" })
156
+ ] });
157
+ }
158
+ function DeliveryOptions(props) {
159
+ const {
160
+ rates,
161
+ pickup = [],
162
+ name = "delivery_choice",
163
+ rateValue = (r) => `rate:${r.id}`,
164
+ pickupValue = (b) => `pickup:${b.branch_id}`,
165
+ value,
166
+ onValueChange,
167
+ partial = false,
168
+ requoted = false,
169
+ loading = false,
170
+ timeZone,
171
+ marks = true,
172
+ badges: showBadges = true,
173
+ required = true,
174
+ renderAmount,
175
+ classNames: cx = {},
176
+ testId,
177
+ children
178
+ } = props;
179
+ const locale = useResolvedLocale(props.locale);
180
+ const m = useMemo(() => deliveryMessages(locale, props.messages), [locale, props.messages]);
181
+ const hydrated = useHydrated();
182
+ const badges = useMemo(() => showBadges ? rateBadges(rates) : /* @__PURE__ */ new Map(), [rates, showBadges]);
183
+ const firstValue = rates[0] ? rateValue(rates[0]) : null;
184
+ const defaultValue = props.defaultValue === void 0 ? firstValue : props.defaultValue;
185
+ const amount = (money) => Number(money.amount) === 0 ? /* @__PURE__ */ jsx("span", { className: cx.free, "data-sf-delivery-free": true, children: m.free }) : renderAmount ? renderAmount(money) : formatMoney(money, locale);
186
+ const mode = props.quoteNote ?? "always";
187
+ const deadline = mode === "never" ? null : quoteDeadline(rates);
188
+ const note = hydrated && deadline ? quoteNote(deadline, { locale, messages: m, ...timeZone ? { timeZone } : {} }) : null;
189
+ const shownNote = note && (mode === "always" || note.soon) ? note : null;
190
+ const radio = (v) => /* @__PURE__ */ jsx(
191
+ "input",
192
+ {
193
+ type: "radio",
194
+ name,
195
+ value: v,
196
+ required,
197
+ className: cx.radio,
198
+ ...value !== void 0 ? { checked: value === v, onChange: () => onValueChange?.(v) } : { defaultChecked: v === defaultValue, ...onValueChange ? { onChange: () => onValueChange(v) } : {} },
199
+ ...testId ? { "data-testid": testId } : {}
200
+ }
201
+ );
202
+ return /* @__PURE__ */ jsxs("div", { className: cx.root, "data-sf-delivery": true, "aria-busy": loading || void 0, children: [
203
+ partial ? /* @__PURE__ */ jsx("p", { role: "status", className: cx.partial, "data-sf-delivery-partial": true, children: m.partial }) : null,
204
+ requoted ? /* @__PURE__ */ jsx("p", { role: "status", className: cx.partial, "data-sf-delivery-requoted": true, children: m.requoted }) : null,
205
+ loading ? /* @__PURE__ */ jsx("span", { style: SR_ONLY, role: "status", "data-sf-delivery-loading": true, children: m.loading }) : null,
206
+ children,
207
+ loading && rates.length === 0 ? [0, 1, 2].map((i) => /* @__PURE__ */ jsx("div", { className: cx.skeleton, "data-sf-delivery-skeleton": true, "aria-hidden": "true" }, i)) : null,
208
+ rates.map((rate) => {
209
+ const carrier = rateCarrier(rate);
210
+ const service = rateService(rate);
211
+ const eta = rateEta(rate, m);
212
+ const own = badges.get(rate.id) ?? [];
213
+ return /* @__PURE__ */ jsxs("label", { className: cx.option, "data-sf-delivery-option": true, "data-loading": loading || void 0, children: [
214
+ radio(rateValue(rate)),
215
+ marks ? /* @__PURE__ */ jsx(BrandMark, { brand: rate.carrier ?? rate.provider ?? rate.title, size: 28, ...cx.mark ? { className: cx.mark } : {} }) : null,
216
+ /* @__PURE__ */ jsxs("span", { className: cx.body, "data-sf-delivery-body": true, children: [
217
+ /* @__PURE__ */ jsx("span", { className: cx.title, "data-sf-delivery-title": true, children: carrier ? /* @__PURE__ */ jsxs(Fragment, { children: [
218
+ /* @__PURE__ */ jsx("span", { className: cx.carrier, "data-sf-delivery-carrier": true, children: carrier }),
219
+ " ",
220
+ /* @__PURE__ */ jsx("span", { className: cx.service, "data-sf-delivery-service": true, children: service })
221
+ ] }) : /* @__PURE__ */ jsx("span", { className: cx.carrier, "data-sf-delivery-carrier": true, children: service }) }),
222
+ eta ? /* @__PURE__ */ jsx("span", { className: cx.eta, "data-sf-delivery-eta": true, children: eta }) : null,
223
+ own.length ? /* @__PURE__ */ jsx("span", { className: cx.badges, "data-sf-delivery-badges": true, children: own.map((b) => /* @__PURE__ */ jsx("span", { className: cx.badge, "data-sf-delivery-badge": b, children: b === "cheapest" ? m.cheapest : m.fastest }, b)) }) : null
224
+ ] }),
225
+ /* @__PURE__ */ jsx("span", { className: cx.price, "data-sf-delivery-price": true, children: amount(rate.cost) })
226
+ ] }, rate.id);
227
+ }),
228
+ pickup.map((b) => /* @__PURE__ */ jsxs("label", { className: cx.option, "data-sf-delivery-option": true, "data-sf-delivery-pickup": true, children: [
229
+ radio(pickupValue(b)),
230
+ marks ? /* @__PURE__ */ jsx(PickupGlyph, { size: 28 }) : null,
231
+ /* @__PURE__ */ jsxs("span", { className: cx.body, "data-sf-delivery-body": true, children: [
232
+ /* @__PURE__ */ jsx("span", { className: cx.title, "data-sf-delivery-title": true, children: /* @__PURE__ */ jsx("span", { className: cx.carrier, "data-sf-delivery-carrier": true, children: m.pickupTitle.replace("{name}", b.name) }) }),
233
+ b.address || b.city ? /* @__PURE__ */ jsx("span", { className: cx.eta, "data-sf-delivery-eta": true, children: [b.address, b.city].filter(Boolean).join(", ") }) : null
234
+ ] }),
235
+ /* @__PURE__ */ jsx("span", { className: cx.price, "data-sf-delivery-price": true, children: /* @__PURE__ */ jsx("span", { className: cx.free, "data-sf-delivery-free": true, children: m.free }) })
236
+ ] }, b.branch_id)),
237
+ deadline ? /* @__PURE__ */ jsx("p", { className: cx.note, "data-sf-delivery-note": true, "data-soon": shownNote?.soon || void 0, "aria-live": "polite", children: shownNote?.text ?? "" }) : null
238
+ ] });
239
+ }
240
+
241
+ export { DeliveryOptions, deliveryMessages, isQuoteChange, matchRate, quoteDeadline, quoteNote, rateBadges, rateCarrier, rateEta, rateService };
242
+ //# sourceMappingURL=checkout.js.map
243
+ //# sourceMappingURL=checkout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/checkout/delivery.ts","../src/checkout/delivery-options.tsx"],"names":[],"mappings":"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","file":"checkout.js","sourcesContent":["/**\n * Pure helpers for the \"Delivery\" step: how to name a carrier rate, its arrival time, which rate is the cheapest or the\n * fastest, and one human note for the quote deadline. No amounts are computed: `cost` is shown as the server sends it;\n * comparing two costs to flag the cheapest is the only arithmetic.\n */\nimport type { ShippingRate } from '@asteby/storefront'\nimport { brandName } from './brands.js'\n\nexport type DeliveryMessages = {\n /** `{n}` = business days. */\n etaOne: string\n etaMany: string\n /** `{min}` / `{max}` = business days. */\n etaRange: string\n etaUpTo: string\n etaFrom: string\n cheapest: string\n fastest: string\n free: string\n pickupTitle: string\n /** Quote deadline, `{time}` = local time. */\n quoteToday: string\n quoteTomorrow: string\n /** `{date}` = short date + time. */\n quoteLater: string\n quoteSoon: string\n partial: string\n loading: string\n /** The quote expired or changed and the rates were reloaded. */\n requoted: string\n}\n\nconst ES: DeliveryMessages = {\n etaOne: 'Llega en 1 día hábil',\n etaMany: 'Llega en {n} días hábiles',\n etaRange: 'Llega en {min} a {max} días hábiles',\n etaUpTo: 'Llega en máximo {max} días hábiles',\n etaFrom: 'Llega en {min} días hábiles o más',\n cheapest: 'Más económico',\n fastest: 'Más rápido',\n free: 'Gratis',\n pickupTitle: 'Recoger en {name}',\n quoteToday: 'Precios de envío vigentes hasta las {time}.',\n quoteTomorrow: 'Precios de envío vigentes hasta mañana a las {time}.',\n quoteLater: 'Precios de envío vigentes hasta el {date}.',\n quoteSoon: 'Los precios de envío vencen pronto ({time}).',\n partial: 'No pudimos consultar todas las paqueterías. Estas son las opciones disponibles ahora.',\n loading: 'Buscando opciones de envío…',\n requoted: 'Las tarifas de envío cambiaron y ya están actualizadas. Revisa tu envío antes de pagar.',\n}\n\nconst EN: DeliveryMessages = {\n etaOne: 'Arrives in 1 business day',\n etaMany: 'Arrives in {n} business days',\n etaRange: 'Arrives in {min} to {max} business days',\n etaUpTo: 'Arrives in up to {max} business days',\n etaFrom: 'Arrives in {min}+ business days',\n cheapest: 'Cheapest',\n fastest: 'Fastest',\n free: 'Free',\n pickupTitle: 'Pick up at {name}',\n quoteToday: 'Shipping prices valid until {time}.',\n quoteTomorrow: 'Shipping prices valid until tomorrow at {time}.',\n quoteLater: 'Shipping prices valid until {date}.',\n quoteSoon: 'Shipping prices expire soon ({time}).',\n partial: \"We couldn't reach every carrier. These are the options available now.\",\n loading: 'Looking for delivery options…',\n requoted: 'Shipping rates changed and are now up to date. Check your delivery before paying.',\n}\n\n/** Messages for a locale (`es*` default, `en*`), with optional overrides. */\nexport function deliveryMessages(locale?: string | null, overrides?: Partial<DeliveryMessages> | null): DeliveryMessages {\n const base = (locale ?? 'es-MX').toLowerCase().startsWith('en') ? EN : ES\n return overrides ? { ...base, ...overrides } : base\n}\n\n// A value that already ends in a period (\"9:39 p.m.\") does not get a second one from the template.\nconst fill = (tpl: string, vars: Record<string, string | number>) => tpl.replace(/\\{(\\w+)\\}/g, (_, k: string) => String(vars[k] ?? '')).replace(/\\.\\.$/, '.')\n\n/** Commercial carrier name of a rate (`fedex` → \"FedEx\"); `null` without carrier. */\nexport function rateCarrier(rate: Pick<ShippingRate, 'carrier'>): string | null {\n return brandName(rate.carrier)\n}\n\n/**\n * Service name without the carrier repeated: \"FedEx Express Saver\" with carrier `fedex` → \"Express Saver\". Falls back\n * to the title when stripping would leave nothing.\n */\nexport function rateService(rate: Pick<ShippingRate, 'title' | 'carrier'>): string {\n const title = rate.title.trim()\n const carrier = rateCarrier(rate)\n if (!carrier) return title\n for (const prefix of [carrier, rate.carrier ?? '']) {\n if (!prefix) continue\n if (title.toLowerCase().startsWith(prefix.toLowerCase())) {\n const rest = title.slice(prefix.length).replace(/^[\\s·:|-]+/, '')\n if (rest) return rest\n }\n }\n return title\n}\n\n/** Arrival time of a rate: business days from the provider, else the server `eta` text as is. */\nexport function rateEta(\n rate: Pick<ShippingRate, 'eta' | 'eta_days_min' | 'eta_days_max'>,\n messages: DeliveryMessages = ES,\n): string | null {\n const { eta_days_min: min, eta_days_max: max } = rate\n if (typeof min === 'number' && typeof max === 'number') {\n if (min === max) return min === 1 ? messages.etaOne : fill(messages.etaMany, { n: min })\n return fill(messages.etaRange, { min, max })\n }\n if (typeof max === 'number') return max === 1 ? messages.etaOne : fill(messages.etaUpTo, { max })\n if (typeof min === 'number') return min === 1 ? messages.etaOne : fill(messages.etaFrom, { min })\n return rate.eta ?? null\n}\n\nexport type RateBadge = 'cheapest' | 'fastest'\n\nconst costOf = (r: ShippingRate) => {\n const n = Number(r.cost.amount)\n return Number.isFinite(n) ? n : null\n}\nconst daysOf = (r: ShippingRate) => r.eta_days_max ?? r.eta_days_min ?? null\n\n/**\n * Badges per rate id. Only with two or more rates, only when one rate is strictly the cheapest (same currency) or\n * strictly the fastest: a tie gets no badge, so the badge always tells the buyer something.\n */\nexport function rateBadges(rates: readonly ShippingRate[]): Map<string, RateBadge[]> {\n const out = new Map<string, RateBadge[]>()\n if (rates.length < 2) return out\n const add = (id: string, b: RateBadge) => out.set(id, [...(out.get(id) ?? []), b])\n const unique = (values: Array<[string, number | null]>) => {\n const known = values.filter((v): v is [string, number] => v[1] !== null)\n if (known.length < 2) return null\n const min = Math.min(...known.map((v) => v[1]))\n const winners = known.filter((v) => v[1] === min)\n return winners.length === 1 ? winners[0]![0] : null\n }\n const currencies = new Set(rates.map((r) => r.cost.currency_code))\n if (currencies.size === 1) {\n const id = unique(rates.map((r) => [r.id, costOf(r)]))\n if (id) add(id, 'cheapest')\n }\n const fast = unique(rates.map((r) => [r.id, daysOf(r)]))\n if (fast) add(fast, 'fastest')\n return out\n}\n\n/** Earliest `expires_at` of the rates (the deadline of the whole list), or `null`. */\nexport function quoteDeadline(rates: readonly Pick<ShippingRate, 'expires_at'>[]): Date | null {\n let best: Date | null = null\n for (const r of rates) {\n if (!r.expires_at) continue\n const d = new Date(r.expires_at)\n if (Number.isNaN(d.getTime())) continue\n if (!best || d < best) best = d\n }\n return best\n}\n\nexport type QuoteNote = { text: string; soon: boolean }\n\n/**\n * One human note for the quote deadline, in the buyer's time zone (or `timeZone`): \"valid until 9:39 p.m.\", \"until\n * tomorrow at…\", \"until Oct 24, …\", or \"expire soon\" within `soonMinutes` (default 15). `null` when already past.\n */\nexport function quoteNote(\n deadline: Date | null,\n opts: { locale?: string; now?: Date; timeZone?: string; soonMinutes?: number; messages?: DeliveryMessages } = {},\n): QuoteNote | null {\n if (!deadline) return null\n const now = opts.now ?? new Date()\n const left = deadline.getTime() - now.getTime()\n if (left <= 0) return null\n const m = opts.messages ?? ES\n const locale = opts.locale ?? 'es-MX'\n const tz = opts.timeZone ? { timeZone: opts.timeZone } : {}\n const time = new Intl.DateTimeFormat(locale, { hour: 'numeric', minute: '2-digit', ...tz }).format(deadline)\n if (left <= (opts.soonMinutes ?? 15) * 60_000) return { text: fill(m.quoteSoon, { time }), soon: true }\n const day = (d: Date) => new Intl.DateTimeFormat('en-CA', { year: 'numeric', month: '2-digit', day: '2-digit', ...tz }).format(d)\n if (day(deadline) === day(now)) return { text: fill(m.quoteToday, { time }), soon: false }\n if (day(deadline) === day(new Date(now.getTime() + 86_400_000))) return { text: fill(m.quoteTomorrow, { time }), soon: false }\n const date = new Intl.DateTimeFormat(locale, { day: 'numeric', month: 'short', hour: 'numeric', minute: '2-digit', ...tz }).format(deadline)\n return { text: fill(m.quoteLater, { date }), soon: false }\n}\n\n/** Error codes a server uses (or will use) when the chosen rate is no longer quotable. */\nconst QUOTE_CODES = new Set(['quote_expired', 'rate_expired', 'rate_changed', 'rate_unavailable', 'rate_not_found', 'stale_rate', 'invalid_rate'])\n\n/**\n * `true` when saving a shipping rate failed because the quote expired or the rates changed, so the shop should reload\n * the rates (and keep the address) instead of showing a generic failure. Recognizes the explicit codes\n * (`quote_expired`, `rate_expired`…, any status), a `409`, and the generic `422` on the rate field that older servers\n * answer when the provider re-quoted with new ids. 5xx and other 4xx stay real failures.\n */\nexport function isQuoteChange(err: { status?: number; code?: string | null; field?: string | null } | null | undefined): boolean {\n if (!err) return false\n if (err.code && QUOTE_CODES.has(err.code)) return true\n if (err.status === 409) return true\n return err.status === 422 && (err.field === 'rate_id' || err.field === 'delivery' || err.field === 'delivery.rate_id' || !err.field)\n}\n\n/**\n * The rate of a fresh quote that matches the one the buyer had chosen (same carrier and service), so a re-quote keeps\n * the choice even though the provider issued new ids. `null` when that service is no longer offered.\n */\nexport function matchRate<R extends Pick<ShippingRate, 'id' | 'carrier' | 'service_code' | 'title'>>(previous: Pick<ShippingRate, 'carrier' | 'service_code' | 'title'> | null | undefined, rates: readonly R[]): R | null {\n if (!previous) return null\n const norm = (s: string | null | undefined) => (s ?? '').trim().toLowerCase()\n const sameCarrier = (r: R) => norm(r.carrier) === norm(previous.carrier)\n if (previous.service_code) {\n const hit = rates.find((r) => sameCarrier(r) && norm(r.service_code) === norm(previous.service_code))\n if (hit) return hit\n }\n return rates.find((r) => sameCarrier(r) && norm(r.title) === norm(previous.title)) ?? null\n}\n","import { useMemo, useSyncExternalStore, type CSSProperties, type ReactNode } from 'react'\nimport { formatMoney, type Money, type PickupOption, type ShippingRate } from '@asteby/storefront'\nimport { useResolvedLocale } from '../locale.js'\nimport { BrandMark } from './brand-mark.js'\nimport {\n deliveryMessages,\n quoteDeadline,\n quoteNote,\n rateBadges,\n rateCarrier,\n rateEta,\n rateService,\n type DeliveryMessages,\n type RateBadge,\n} from './delivery.js'\n\nexport type DeliveryOptionsClassNames = Partial<\n Record<\n | 'root'\n | 'option'\n | 'radio'\n | 'mark'\n | 'body'\n | 'title'\n | 'carrier'\n | 'service'\n | 'eta'\n | 'badges'\n | 'badge'\n | 'price'\n | 'free'\n | 'note'\n | 'partial'\n | 'skeleton',\n string\n >\n>\n\nexport type DeliveryOptionsProps = {\n rates: readonly ShippingRate[]\n pickup?: readonly PickupOption[]\n /** Radio group name. Default `delivery_choice`. */\n name?: string\n /** Form value of a rate. Default `rate:<id>`. */\n rateValue?: (rate: ShippingRate) => string\n /** Form value of a pickup branch. Default `pickup:<branch_id>`. */\n pickupValue?: (branch: PickupOption) => string\n /** Uncontrolled preselection. Default: the first rate. `null`: nothing preselected. */\n defaultValue?: string | null\n /** Controlled value (use with `onValueChange`). */\n value?: string\n onValueChange?: (value: string) => void\n /** Some provider did not answer (`DeliveryOptions.partial`): one neutral note above the list. */\n partial?: boolean\n /** The previous quote expired or changed and these rates are fresh: one status note above the list. */\n requoted?: boolean\n /** Rates are being quoted again: skeleton rows (no rates yet) or the current rows dimmed, `aria-busy`. */\n loading?: boolean\n /** Quote deadline note: `always` (default), only when it is `soon`, or `never`. */\n quoteNote?: 'always' | 'soon' | 'never'\n /** Time zone of the note. Default: the buyer's browser. */\n timeZone?: string\n /** Show the carrier marks. Default true. */\n marks?: boolean\n /** Cheapest / fastest badges. Default true. */\n badges?: boolean\n required?: boolean\n locale?: string\n messages?: Partial<DeliveryMessages>\n renderAmount?: (amount: Money) => ReactNode\n classNames?: DeliveryOptionsClassNames\n /** `data-testid` of every radio. */\n testId?: string\n /** Rows the shop renders before the rates (e.g. \"home delivery, rates after the address\"). */\n children?: ReactNode\n}\n\nconst SR_ONLY: CSSProperties = { position: 'absolute', width: 1, height: 1, overflow: 'hidden', clip: 'rect(0 0 0 0)', whiteSpace: 'nowrap' }\n\nconst useHydrated = () =>\n useSyncExternalStore(\n () => () => {},\n () => true,\n () => false,\n )\n\nfunction PickupGlyph({ size = 24 }: { size?: number }) {\n return (\n <svg viewBox=\"0 0 48 32\" width={Math.round(size * 1.5)} height={size} aria-hidden=\"true\" focusable=\"false\" style={{ flex: 'none' }} data-sf-brand-mark=\"pickup\">\n <rect x=\"0.5\" y=\"0.5\" width=\"47\" height=\"31\" rx=\"5\" fill=\"var(--sf-brand-mark-bg, #E7E9EE)\" stroke=\"var(--sf-brand-mark-ring, rgb(127 127 127 / 0.35))\" />\n <path d=\"M15 13.5 17 9h14l2 4.5M15 13.5h18M15 13.5V23h18v-9.5M21 23v-5h6v5\" fill=\"none\" stroke=\"var(--sf-brand-mark-fg, #2B2F38)\" strokeWidth=\"1.8\" strokeLinejoin=\"round\" />\n </svg>\n )\n}\n\n/**\n * The rates and pickup branches of \"Delivery\" as native radios: carrier mark and commercial name, readable service,\n * arrival time, cheapest / fastest badges, price as the server sends it, and ONE note for the quote deadline in the\n * buyer's time zone. Works without JavaScript (plain radios in the page form); the note appears after hydration in a\n * reserved line, so it never shifts the layout.\n *\n * Wrap it in the shop's own `<fieldset><legend>`. Style it with `classNames` and/or the default sheet\n * `@asteby/storefront-react/checkout.css`.\n */\nexport function DeliveryOptions(props: DeliveryOptionsProps) {\n const {\n rates,\n pickup = [],\n name = 'delivery_choice',\n rateValue = (r) => `rate:${r.id}`,\n pickupValue = (b) => `pickup:${b.branch_id}`,\n value,\n onValueChange,\n partial = false,\n requoted = false,\n loading = false,\n timeZone,\n marks = true,\n badges: showBadges = true,\n required = true,\n renderAmount,\n classNames: cx = {},\n testId,\n children,\n } = props\n const locale = useResolvedLocale(props.locale)\n const m = useMemo(() => deliveryMessages(locale, props.messages), [locale, props.messages])\n const hydrated = useHydrated()\n const badges = useMemo(() => (showBadges ? rateBadges(rates) : new Map<string, RateBadge[]>()), [rates, showBadges])\n const firstValue = rates[0] ? rateValue(rates[0]) : null\n const defaultValue = props.defaultValue === undefined ? firstValue : props.defaultValue\n const amount = (money: Money) => (Number(money.amount) === 0 ? <span className={cx.free} data-sf-delivery-free>{m.free}</span> : renderAmount ? renderAmount(money) : formatMoney(money, locale))\n const mode = props.quoteNote ?? 'always'\n const deadline = mode === 'never' ? null : quoteDeadline(rates)\n const note = hydrated && deadline ? quoteNote(deadline, { locale, messages: m, ...(timeZone ? { timeZone } : {}) }) : null\n const shownNote = note && (mode === 'always' || note.soon) ? note : null\n\n const radio = (v: string) => (\n <input\n type=\"radio\"\n name={name}\n value={v}\n required={required}\n className={cx.radio}\n {...(value !== undefined ? { checked: value === v, onChange: () => onValueChange?.(v) } : { defaultChecked: v === defaultValue, ...(onValueChange ? { onChange: () => onValueChange(v) } : {}) })}\n {...(testId ? { 'data-testid': testId } : {})}\n />\n )\n\n return (\n <div className={cx.root} data-sf-delivery aria-busy={loading || undefined}>\n {partial ? (\n <p role=\"status\" className={cx.partial} data-sf-delivery-partial>\n {m.partial}\n </p>\n ) : null}\n {requoted ? (\n <p role=\"status\" className={cx.partial} data-sf-delivery-requoted>\n {m.requoted}\n </p>\n ) : null}\n {loading ? (\n <span style={SR_ONLY} role=\"status\" data-sf-delivery-loading>\n {m.loading}\n </span>\n ) : null}\n {children}\n {loading && rates.length === 0\n ? [0, 1, 2].map((i) => <div key={i} className={cx.skeleton} data-sf-delivery-skeleton aria-hidden=\"true\" />)\n : null}\n {rates.map((rate) => {\n const carrier = rateCarrier(rate)\n const service = rateService(rate)\n const eta = rateEta(rate, m)\n const own = badges.get(rate.id) ?? []\n return (\n <label key={rate.id} className={cx.option} data-sf-delivery-option data-loading={loading || undefined}>\n {radio(rateValue(rate))}\n {marks ? <BrandMark brand={rate.carrier ?? rate.provider ?? rate.title} size={28} {...(cx.mark ? { className: cx.mark } : {})} /> : null}\n <span className={cx.body} data-sf-delivery-body>\n <span className={cx.title} data-sf-delivery-title>\n {carrier ? (\n <>\n <span className={cx.carrier} data-sf-delivery-carrier>\n {carrier}\n </span>{' '}\n <span className={cx.service} data-sf-delivery-service>\n {service}\n </span>\n </>\n ) : (\n <span className={cx.carrier} data-sf-delivery-carrier>\n {service}\n </span>\n )}\n </span>\n {eta ? (\n <span className={cx.eta} data-sf-delivery-eta>\n {eta}\n </span>\n ) : null}\n {own.length ? (\n <span className={cx.badges} data-sf-delivery-badges>\n {own.map((b) => (\n <span key={b} className={cx.badge} data-sf-delivery-badge={b}>\n {b === 'cheapest' ? m.cheapest : m.fastest}\n </span>\n ))}\n </span>\n ) : null}\n </span>\n <span className={cx.price} data-sf-delivery-price>\n {amount(rate.cost)}\n </span>\n </label>\n )\n })}\n {pickup.map((b) => (\n <label key={b.branch_id} className={cx.option} data-sf-delivery-option data-sf-delivery-pickup>\n {radio(pickupValue(b))}\n {marks ? <PickupGlyph size={28} /> : null}\n <span className={cx.body} data-sf-delivery-body>\n <span className={cx.title} data-sf-delivery-title>\n <span className={cx.carrier} data-sf-delivery-carrier>\n {m.pickupTitle.replace('{name}', b.name)}\n </span>\n </span>\n {b.address || b.city ? (\n <span className={cx.eta} data-sf-delivery-eta>\n {[b.address, b.city].filter(Boolean).join(', ')}\n </span>\n ) : null}\n </span>\n <span className={cx.price} data-sf-delivery-price>\n <span className={cx.free} data-sf-delivery-free>\n {m.free}\n </span>\n </span>\n </label>\n ))}\n {deadline ? (\n <p className={cx.note} data-sf-delivery-note data-soon={shownNote?.soon || undefined} aria-live=\"polite\">\n {shownNote?.text ?? ''}\n </p>\n ) : null}\n </div>\n )\n}\n"]}
@@ -0,0 +1,130 @@
1
+ import { PaymentClient, PaymentOptionKey, PaymentPoint, PaymentMethod, CheckoutResult, PaymentAttemptResult } from '@asteby/storefront';
2
+
3
+ type PaymentCspOptions = {
4
+ /** Per-request nonce; also set it on the inserted `<script>` and on `<Scripts nonce>`. */
5
+ nonce?: string;
6
+ /** Extra origins by directive (e.g. your image CDN). */
7
+ extra?: {
8
+ script?: string[];
9
+ frame?: string[];
10
+ connect?: string[];
11
+ img?: string[];
12
+ form?: string[];
13
+ };
14
+ /** `report-uri` / `report-to` target for the report-only rollout. */
15
+ reportUri?: string;
16
+ /** Inline scripts allowed by hash (`'sha256-…'`), e.g. a theme bootstrap that runs before paint. */
17
+ scriptHashes?: string[];
18
+ /** Allow any https image source (product photos from a CDN). */
19
+ imgHttps?: boolean;
20
+ };
21
+ /**
22
+ * Builds the Content-Security-Policy for the payment routes from what each provider declares
23
+ * (`PaymentClient.csp`), so the shop never hard-codes provider hosts. Meant for `/pagar` only,
24
+ * never for the whole site. Roll out as `Content-Security-Policy-Report-Only` first.
25
+ *
26
+ * No SRI on purpose: provider SDK URLs are unversioned and a hash would break payments on any update.
27
+ */
28
+ declare function paymentCsp(clients: readonly (PaymentClient | null | undefined)[], options?: PaymentCspOptions): string;
29
+ /** `Content-Security-Policy` or, during the rollout week, `Content-Security-Policy-Report-Only`. */
30
+ declare function paymentCspHeaderName(reportOnly: boolean): string;
31
+ type PaymentCspMode = 'off' | 'report-only' | 'enforce';
32
+ /** `PAYMENT_CSP`-style value: `off`, `report-only` or `enforce`. Unset: report-only in production, off elsewhere (dev servers inject scripts). */
33
+ declare function paymentCspMode(value: string | null | undefined, production: boolean): PaymentCspMode;
34
+ /** Fresh per-request nonce (128 bits, base64). Works in Node, edge and browsers. */
35
+ declare function paymentNonce(): string;
36
+ /** Header ready for the response (`{}` when `off`): policy from the providers' `client.csp`, never the whole site. */
37
+ declare function paymentCspHeaders(clients: readonly (PaymentClient | null | undefined)[], options: PaymentCspOptions & {
38
+ mode: PaymentCspMode;
39
+ }): Record<string, string>;
40
+ /** The nonce the route left in the CSP header, to hand to `ServerRouter` / `<Scripts>`. */
41
+ declare function paymentNonceFromHeaders(headers: Headers): string | undefined;
42
+
43
+ /**
44
+ * "Payment method" rows for a hybrid checkout: pure functions, no DOM and no server, so the loader, the action and the
45
+ * browser share them.
46
+ *
47
+ * Rules (embedded-payment contract 1.2):
48
+ * - Rows come ONLY from what the server publishes (`payment-methods[].options` and `.client`). No provider names and no
49
+ * amounts live here.
50
+ * - Card and cash are paid in the page; `wallet` and `credits` redirect to the provider.
51
+ * - Without `client`, or with the card flagged `redirect: true`, the server does not offer `embedded`: the shop falls
52
+ * back to the usual redirect flow.
53
+ */
54
+
55
+ /** One row of "Payment method". `value` is what travels in the form (`payment_choice`). */
56
+ type PayChoice = {
57
+ value: string;
58
+ /** Server method key (`payment_method` of the checkout). */
59
+ methodKey: string;
60
+ /** Provider option; `null` for a method without options (the plain flow). */
61
+ option: PaymentOptionKey | null;
62
+ /** `card` (form in the page), `cash` (voucher), `redirect` (leaves to the gateway) or `plain`. */
63
+ mode: 'card' | 'cash' | 'redirect' | 'plain';
64
+ /** The provider's own text (`option.label`); the shop supplies its own when missing. */
65
+ label: string | null;
66
+ points: PaymentPoint[];
67
+ maxInstallments: number | null;
68
+ };
69
+ type PayChoices = {
70
+ choices: PayChoice[];
71
+ /** Public data to mount the card form; `null` when the server does not offer `embedded`. */
72
+ cardClient: PaymentClient | null;
73
+ defaultValue: string | null;
74
+ };
75
+ type BuildPayChoicesOptions = {
76
+ /** Method keys this shop never offers even when the server publishes them (e.g. a manual transfer). */
77
+ hide?: Iterable<string>;
78
+ };
79
+ declare function choiceValue(methodKey: string, option: PaymentOptionKey | null): string;
80
+ /** `"mercadopago|card"` becomes method and option. A value without `|` is a method without options. */
81
+ declare function parseChoice(value: string | null | undefined): {
82
+ methodKey: string;
83
+ option: PaymentOptionKey | null;
84
+ } | null;
85
+ /**
86
+ * Rows from `GET payment-methods`. A method with `options` unfolds into one row per option (server order); one without
87
+ * is a single plain row. `cardClient` only exists when the card is paid in the page.
88
+ */
89
+ declare function buildPayChoices(methods: readonly PaymentMethod[], options?: BuildPayChoicesOptions): PayChoices;
90
+ /** Chosen cash point becomes the cash `provider_data`, built on the server from the provider's list. */
91
+ declare function cashProviderData(points: readonly PaymentPoint[], pointId: string | null | undefined): Record<string, string> | null;
92
+ /**
93
+ * A checkout (`GET checkouts/:id`) as an attempt result, for polling a 3DS challenge or a payment under review.
94
+ * It only translates server states: it never decides a payment.
95
+ */
96
+ declare function checkoutToAttempt(result: CheckoutResult): PaymentAttemptResult;
97
+ /** Body `{ error: { code, message, details } }` that `createFetchTransport` understands. */
98
+ type PayErrorBody = {
99
+ error: {
100
+ code: string;
101
+ message: string;
102
+ details?: unknown;
103
+ };
104
+ };
105
+ type ErrorLike = {
106
+ status?: number;
107
+ code?: string;
108
+ field?: string;
109
+ message?: string;
110
+ details?: unknown;
111
+ };
112
+ /**
113
+ * Server error becomes a JSON response for the browser. Keeps the codes the SDK knows how to read (`price_mismatch`,
114
+ * `payment_in_progress`, `expired`, `too_many_attempts`); the mock sends "in progress" in `field`, so that is checked too.
115
+ */
116
+ declare function payErrorResponse(err: ErrorLike): {
117
+ status: number;
118
+ body: PayErrorBody;
119
+ };
120
+ type VoucherRowLabels = {
121
+ point: string;
122
+ reference: string;
123
+ clabe: string;
124
+ barcode: string;
125
+ expires: string;
126
+ };
127
+ /** Cookie receipt rows for a payment under review or with a voucher (shown on the thank-you page). */
128
+ declare function voucherRows(voucher: NonNullable<NonNullable<PaymentAttemptResult['payment']['action']>['voucher']> | null | undefined, labels: VoucherRowLabels, locale: string): [label: string, value: string][];
129
+
130
+ export { type BuildPayChoicesOptions as B, type PayChoice as P, type VoucherRowLabels as V, type PayChoices as a, type PayErrorBody as b, type PaymentCspMode as c, type PaymentCspOptions as d, buildPayChoices as e, cashProviderData as f, checkoutToAttempt as g, choiceValue as h, payErrorResponse as i, paymentCsp as j, paymentCspHeaderName as k, paymentCspHeaders as l, paymentCspMode as m, paymentNonce as n, paymentNonceFromHeaders as o, parseChoice as p, voucherRows as v };
@@ -0,0 +1,130 @@
1
+ import { PaymentClient, PaymentOptionKey, PaymentPoint, PaymentMethod, CheckoutResult, PaymentAttemptResult } from '@asteby/storefront';
2
+
3
+ type PaymentCspOptions = {
4
+ /** Per-request nonce; also set it on the inserted `<script>` and on `<Scripts nonce>`. */
5
+ nonce?: string;
6
+ /** Extra origins by directive (e.g. your image CDN). */
7
+ extra?: {
8
+ script?: string[];
9
+ frame?: string[];
10
+ connect?: string[];
11
+ img?: string[];
12
+ form?: string[];
13
+ };
14
+ /** `report-uri` / `report-to` target for the report-only rollout. */
15
+ reportUri?: string;
16
+ /** Inline scripts allowed by hash (`'sha256-…'`), e.g. a theme bootstrap that runs before paint. */
17
+ scriptHashes?: string[];
18
+ /** Allow any https image source (product photos from a CDN). */
19
+ imgHttps?: boolean;
20
+ };
21
+ /**
22
+ * Builds the Content-Security-Policy for the payment routes from what each provider declares
23
+ * (`PaymentClient.csp`), so the shop never hard-codes provider hosts. Meant for `/pagar` only,
24
+ * never for the whole site. Roll out as `Content-Security-Policy-Report-Only` first.
25
+ *
26
+ * No SRI on purpose: provider SDK URLs are unversioned and a hash would break payments on any update.
27
+ */
28
+ declare function paymentCsp(clients: readonly (PaymentClient | null | undefined)[], options?: PaymentCspOptions): string;
29
+ /** `Content-Security-Policy` or, during the rollout week, `Content-Security-Policy-Report-Only`. */
30
+ declare function paymentCspHeaderName(reportOnly: boolean): string;
31
+ type PaymentCspMode = 'off' | 'report-only' | 'enforce';
32
+ /** `PAYMENT_CSP`-style value: `off`, `report-only` or `enforce`. Unset: report-only in production, off elsewhere (dev servers inject scripts). */
33
+ declare function paymentCspMode(value: string | null | undefined, production: boolean): PaymentCspMode;
34
+ /** Fresh per-request nonce (128 bits, base64). Works in Node, edge and browsers. */
35
+ declare function paymentNonce(): string;
36
+ /** Header ready for the response (`{}` when `off`): policy from the providers' `client.csp`, never the whole site. */
37
+ declare function paymentCspHeaders(clients: readonly (PaymentClient | null | undefined)[], options: PaymentCspOptions & {
38
+ mode: PaymentCspMode;
39
+ }): Record<string, string>;
40
+ /** The nonce the route left in the CSP header, to hand to `ServerRouter` / `<Scripts>`. */
41
+ declare function paymentNonceFromHeaders(headers: Headers): string | undefined;
42
+
43
+ /**
44
+ * "Payment method" rows for a hybrid checkout: pure functions, no DOM and no server, so the loader, the action and the
45
+ * browser share them.
46
+ *
47
+ * Rules (embedded-payment contract 1.2):
48
+ * - Rows come ONLY from what the server publishes (`payment-methods[].options` and `.client`). No provider names and no
49
+ * amounts live here.
50
+ * - Card and cash are paid in the page; `wallet` and `credits` redirect to the provider.
51
+ * - Without `client`, or with the card flagged `redirect: true`, the server does not offer `embedded`: the shop falls
52
+ * back to the usual redirect flow.
53
+ */
54
+
55
+ /** One row of "Payment method". `value` is what travels in the form (`payment_choice`). */
56
+ type PayChoice = {
57
+ value: string;
58
+ /** Server method key (`payment_method` of the checkout). */
59
+ methodKey: string;
60
+ /** Provider option; `null` for a method without options (the plain flow). */
61
+ option: PaymentOptionKey | null;
62
+ /** `card` (form in the page), `cash` (voucher), `redirect` (leaves to the gateway) or `plain`. */
63
+ mode: 'card' | 'cash' | 'redirect' | 'plain';
64
+ /** The provider's own text (`option.label`); the shop supplies its own when missing. */
65
+ label: string | null;
66
+ points: PaymentPoint[];
67
+ maxInstallments: number | null;
68
+ };
69
+ type PayChoices = {
70
+ choices: PayChoice[];
71
+ /** Public data to mount the card form; `null` when the server does not offer `embedded`. */
72
+ cardClient: PaymentClient | null;
73
+ defaultValue: string | null;
74
+ };
75
+ type BuildPayChoicesOptions = {
76
+ /** Method keys this shop never offers even when the server publishes them (e.g. a manual transfer). */
77
+ hide?: Iterable<string>;
78
+ };
79
+ declare function choiceValue(methodKey: string, option: PaymentOptionKey | null): string;
80
+ /** `"mercadopago|card"` becomes method and option. A value without `|` is a method without options. */
81
+ declare function parseChoice(value: string | null | undefined): {
82
+ methodKey: string;
83
+ option: PaymentOptionKey | null;
84
+ } | null;
85
+ /**
86
+ * Rows from `GET payment-methods`. A method with `options` unfolds into one row per option (server order); one without
87
+ * is a single plain row. `cardClient` only exists when the card is paid in the page.
88
+ */
89
+ declare function buildPayChoices(methods: readonly PaymentMethod[], options?: BuildPayChoicesOptions): PayChoices;
90
+ /** Chosen cash point becomes the cash `provider_data`, built on the server from the provider's list. */
91
+ declare function cashProviderData(points: readonly PaymentPoint[], pointId: string | null | undefined): Record<string, string> | null;
92
+ /**
93
+ * A checkout (`GET checkouts/:id`) as an attempt result, for polling a 3DS challenge or a payment under review.
94
+ * It only translates server states: it never decides a payment.
95
+ */
96
+ declare function checkoutToAttempt(result: CheckoutResult): PaymentAttemptResult;
97
+ /** Body `{ error: { code, message, details } }` that `createFetchTransport` understands. */
98
+ type PayErrorBody = {
99
+ error: {
100
+ code: string;
101
+ message: string;
102
+ details?: unknown;
103
+ };
104
+ };
105
+ type ErrorLike = {
106
+ status?: number;
107
+ code?: string;
108
+ field?: string;
109
+ message?: string;
110
+ details?: unknown;
111
+ };
112
+ /**
113
+ * Server error becomes a JSON response for the browser. Keeps the codes the SDK knows how to read (`price_mismatch`,
114
+ * `payment_in_progress`, `expired`, `too_many_attempts`); the mock sends "in progress" in `field`, so that is checked too.
115
+ */
116
+ declare function payErrorResponse(err: ErrorLike): {
117
+ status: number;
118
+ body: PayErrorBody;
119
+ };
120
+ type VoucherRowLabels = {
121
+ point: string;
122
+ reference: string;
123
+ clabe: string;
124
+ barcode: string;
125
+ expires: string;
126
+ };
127
+ /** Cookie receipt rows for a payment under review or with a voucher (shown on the thank-you page). */
128
+ declare function voucherRows(voucher: NonNullable<NonNullable<PaymentAttemptResult['payment']['action']>['voucher']> | null | undefined, labels: VoucherRowLabels, locale: string): [label: string, value: string][];
129
+
130
+ export { type BuildPayChoicesOptions as B, type PayChoice as P, type VoucherRowLabels as V, type PayChoices as a, type PayErrorBody as b, type PaymentCspMode as c, type PaymentCspOptions as d, buildPayChoices as e, cashProviderData as f, checkoutToAttempt as g, choiceValue as h, payErrorResponse as i, paymentCsp as j, paymentCspHeaderName as k, paymentCspHeaders as l, paymentCspMode as m, paymentNonce as n, paymentNonceFromHeaders as o, parseChoice as p, voucherRows as v };