@asteby/storefront-react 0.26.1 → 0.28.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.
@@ -13,6 +13,52 @@ var __export = (target, all) => {
13
13
  };
14
14
 
15
15
  // src/account/voucher.ts
16
+ function formatOf(v) {
17
+ const f = typeof v === "string" ? v.toLowerCase().replace(/[^a-z0-9]/g, "") : "";
18
+ if (f === "code128" || f === "c128" || f === "code128b" || f === "code128c") return "code128";
19
+ if (f === "code39" || f === "c39") return "code39";
20
+ if (f === "itf" || f === "itf14" || f === "i25" || f === "interleaved2of5") return "itf";
21
+ if (f === "ean13") return "ean13";
22
+ return null;
23
+ }
24
+ function barcodeOf(ext, raw) {
25
+ if ("barcode_data" in ext) {
26
+ const d = ext.barcode_data;
27
+ if (d && typeof d === "object") {
28
+ const o = d;
29
+ const value2 = text(o.value);
30
+ const format = formatOf(o.format);
31
+ if (value2 && format && barcodeModules(value2, format)) return { draw: { value: value2, format }, text: null };
32
+ return { draw: null, text: value2 ?? text(raw) };
33
+ }
34
+ return { draw: null, text: text(raw) };
35
+ }
36
+ if (raw && typeof raw === "object") {
37
+ const o = raw;
38
+ const value2 = text(o.value);
39
+ const format = formatOf(o.format ?? "code128");
40
+ return { draw: value2 && format ? { value: value2, format } : null, text: null };
41
+ }
42
+ const value = text(raw);
43
+ return { draw: value ? { value, format: "code128" } : null, text: null };
44
+ }
45
+ function instructionsKeyOf(raw) {
46
+ const k = raw?.replace(/^payment\.voucher\.instructions\./, "").toLowerCase() ?? "";
47
+ return !k ? null : k === "other" ? "generic" : k;
48
+ }
49
+ function httpsUrl(v) {
50
+ const s = typeof v === "string" ? v.trim() : "";
51
+ return /^https:\/\//i.test(s) && s.length <= 2048 ? s : null;
52
+ }
53
+ function kindOf(kind, method, key) {
54
+ for (const c of [kind, method, key]) {
55
+ const k = c?.toLowerCase();
56
+ if (!k || k === "other" || k === "generic") continue;
57
+ if (k === "oxxo" || k === "cash" || CASH_METHODS.has(k)) return "cash";
58
+ if (k === "spei" || k === "transfer" || TRANSFER_METHODS.has(k)) return "transfer";
59
+ }
60
+ return "generic";
61
+ }
16
62
  function text(v) {
17
63
  if (typeof v === "number" && Number.isFinite(v)) return String(v);
18
64
  if (typeof v !== "string") return null;
@@ -29,19 +75,30 @@ function moneyOf(v) {
29
75
  function voucherOf(p) {
30
76
  const pay = p?.payment;
31
77
  if (!pay || typeof pay !== "object") return null;
78
+ const ext = pay;
79
+ const method = text(pay.method ?? pay.provider);
80
+ const key = instructionsKeyOf(text(ext.instructions_key));
81
+ const bc = barcodeOf(ext, pay.barcode);
82
+ const clabe = text(pay.clabe);
83
+ let reference = text(pay.reference);
84
+ if (clabe && reference && digits(reference) === digits(clabe)) reference = null;
32
85
  const v = {
33
- method: text(pay.method ?? pay.provider),
34
- reference: text(pay.reference),
35
- barcode: text(pay.barcode),
36
- clabe: text(pay.clabe),
86
+ method,
87
+ reference,
88
+ barcode: bc.draw?.value ?? null,
89
+ barcode_format: bc.draw?.format ?? null,
90
+ barcode_text: bc.text && bc.text !== reference ? bc.text : null,
91
+ clabe,
92
+ beneficiary: text(ext.beneficiary),
93
+ bank: text(ext.bank),
94
+ kind: kindOf(text(ext.kind), method, key),
95
+ instructions_key: key,
96
+ ticket_url: httpsUrl(ext.ticket_url) ?? httpsUrl(ext.voucher_url),
37
97
  expires_at: text(pay.expires_at) ?? text(p?.expires_at),
38
98
  amount: moneyOf(pay.amount) ?? moneyOf(p?.total),
39
99
  instructions: text(pay.instructions)
40
100
  };
41
- return v.reference || v.barcode || v.clabe ? v : null;
42
- }
43
- function barcodeValue(v) {
44
- return v.barcode ?? (v.method?.toLowerCase() === "oxxo" ? v.reference : null);
101
+ return v.reference || v.barcode || v.barcode_text || v.clabe ? v : null;
45
102
  }
46
103
  function code128Modules(value) {
47
104
  if (!value || value.length > 80) return null;
@@ -62,8 +119,44 @@ function code128Modules(value) {
62
119
  codes.push(sum % 103);
63
120
  return codes.map((c) => BARS[c]).join("") + STOP;
64
121
  }
122
+ function code39Modules(value) {
123
+ if (!value || value.length > 40) return null;
124
+ let out = "";
125
+ for (const ch of `*${value}*`) {
126
+ const p = Object.prototype.hasOwnProperty.call(CODE39_PATTERNS, ch) ? CODE39_PATTERNS[ch] : void 0;
127
+ if (!p || ch === "*" && value.includes("*")) return null;
128
+ for (let i = 0; i < 9; i++) out += (i % 2 === 0 ? "1" : "0").repeat(p[i] === "w" ? 3 : 1);
129
+ out += "0";
130
+ }
131
+ return out.slice(0, -1);
132
+ }
133
+ function itfModules(value) {
134
+ if (!value || value.length > 80 || value.length % 2 !== 0 || !/^\d+$/.test(value)) return null;
135
+ let out = "1010";
136
+ for (let i = 0; i < value.length; i += 2) {
137
+ const bars = ITF_DIGITS[Number(value[i])];
138
+ const spaces = ITF_DIGITS[Number(value[i + 1])];
139
+ for (let e = 0; e < 5; e++) out += "1".repeat(bars[e] === "w" ? 3 : 1) + "0".repeat(spaces[e] === "w" ? 3 : 1);
140
+ }
141
+ return out + "11101";
142
+ }
143
+ function ean13Modules(value) {
144
+ if (!/^\d{13}$/.test(value)) return null;
145
+ const d = [...value].map(Number);
146
+ const sum = d.slice(0, 12).reduce((a, n2, i) => a + n2 * (i % 2 === 0 ? 1 : 3), 0);
147
+ if ((10 - sum % 10) % 10 !== d[12]) return null;
148
+ const parity = EAN_PARITY[d[0]];
149
+ let out = "101";
150
+ for (let i = 0; i < 6; i++) out += (parity[i] === "L" ? EAN_L : EAN_G)[d[i + 1]];
151
+ out += "01010";
152
+ for (let i = 7; i < 13; i++) out += EAN_R[d[i]];
153
+ return out + "101";
154
+ }
155
+ function barcodeModules(value, format = "code128") {
156
+ return format === "code39" ? code39Modules(value) : format === "itf" ? itfModules(value) : format === "ean13" ? ean13Modules(value) : code128Modules(value);
157
+ }
65
158
  function barcodeGeometry(value, opts = {}) {
66
- const bits = code128Modules(value);
159
+ const bits = barcodeModules(value, opts.format);
67
160
  if (!bits) return null;
68
161
  const mod = opts.module ?? 2;
69
162
  const quiet = 10;
@@ -80,10 +173,13 @@ function barcodeGeometry(value, opts = {}) {
80
173
  }
81
174
  return { width: (bits.length + quiet * 2) * mod, height: opts.height ?? 70, rects };
82
175
  }
83
- var MAX, BARS, STOP;
176
+ var MAX, CASH_METHODS, TRANSFER_METHODS, digits, BARS, STOP, C39_CHARS, C39_WIDE, CODE39_PATTERNS, ITF_DIGITS, EAN_L, EAN_R, EAN_G, EAN_PARITY;
84
177
  var init_voucher = __esm({
85
178
  "src/account/voucher.ts"() {
86
179
  MAX = 256;
180
+ CASH_METHODS = /* @__PURE__ */ new Set(["oxxo", "cash", "store", "convenience_store", "efectivo", "bank_deposit", "deposit", "paynet", "paycash", "bancomer", "7eleven", "seven_eleven", "farmacias_guadalajara", "circle_k"]);
181
+ TRANSFER_METHODS = /* @__PURE__ */ new Set(["spei", "bank_transfer", "transfer", "wire", "clabe"]);
182
+ digits = (s) => s ? s.replace(/\D/g, "") : "";
87
183
  BARS = [
88
184
  "11011001100",
89
185
  "11001101100",
@@ -193,17 +289,33 @@ var init_voucher = __esm({
193
289
  "11010011100"
194
290
  ];
195
291
  STOP = "1100011101011";
292
+ C39_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. */+%$";
293
+ C39_WIDE = "346038238023348034234368036236058258025458045245568056256456078278027478047247678067267467018128012148014124168016126146137157357135";
294
+ CODE39_PATTERNS = Object.fromEntries(
295
+ [...C39_CHARS].map((ch, i) => [ch, [...Array(9).keys()].map((e) => C39_WIDE.slice(i * 3, i * 3 + 3).includes(String(e)) ? "w" : "n").join("")])
296
+ );
297
+ ITF_DIGITS = ["nnwwn", "wnnnw", "nwnnw", "wwnnn", "nnwnw", "wnwnn", "nwwnn", "nnnww", "wnnwn", "nwnwn"];
298
+ EAN_L = ["0001101", "0011001", "0010011", "0111101", "0100011", "0110001", "0101111", "0111011", "0110111", "0001011"];
299
+ EAN_R = EAN_L.map((c) => [...c].map((b) => b === "1" ? "0" : "1").join(""));
300
+ EAN_G = EAN_R.map((c) => [...c].reverse().join(""));
301
+ EAN_PARITY = ["LLLLLL", "LLGLGG", "LLGGLG", "LLGGGL", "LGLLGG", "LGGLLG", "LGGGLL", "LGLGLG", "LGLGGL", "LGGLGL"];
196
302
  }
197
303
  });
198
304
 
199
- // src/account/voucher-document.ts
200
- var voucher_document_exports = {};
201
- __export(voucher_document_exports, {
202
- esc: () => esc,
203
- renderVoucherDocument: () => renderVoucherDocument
204
- });
205
- function esc(s) {
206
- return s.replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
305
+ // src/account/voucher-text.ts
306
+ function docText(locale) {
307
+ return isEnglish(locale) ? EN : ES;
308
+ }
309
+ function methodName(v, t) {
310
+ return v.method ? own(t.methods, v.method) ?? v.method : t.cash;
311
+ }
312
+ function voucherSteps(v, t) {
313
+ const fn = own(t.steps, v.instructions_key) ?? own(t.steps, v.method) ?? (v.kind === "cash" ? t.steps.cash : v.kind === "transfer" ? t.steps.transfer : t.steps.generic);
314
+ return fn(v);
315
+ }
316
+ function voucherFields(v, t) {
317
+ const f = (key, label, value) => value ? [{ key, label, value }] : [];
318
+ return v.kind === "transfer" ? [...f("beneficiary", t.beneficiary, v.beneficiary), ...f("bank", t.bank, v.bank), ...f("clabe", t.clabe, v.clabe), ...f("reference", t.reference, v.reference), ...f("code", t.code, v.barcode_text)] : [...f("reference", t.reference, v.reference), ...f("clabe", t.clabe, v.clabe), ...f("beneficiary", t.beneficiary, v.beneficiary), ...f("bank", t.bank, v.bank), ...f("code", t.code, v.barcode_text)];
207
319
  }
208
320
  function formatMoney(m, locale) {
209
321
  if (!m) return "";
@@ -223,85 +335,401 @@ function formatDate(iso, locale, timeZone) {
223
335
  return d.toISOString();
224
336
  }
225
337
  }
226
- function barcodeSvg(value, label) {
227
- const g = barcodeGeometry(value);
228
- if (!g) return null;
229
- const rects = g.rects.map((r) => `<rect x="${r.x}" y="0" width="${r.w}" height="${g.height}"/>`).join("");
230
- return `<svg viewBox="0 0 ${g.width} ${g.height}" width="${g.width}" height="${g.height}" role="img" aria-label="${esc(label)} ${esc(value)}" shape-rendering="crispEdges" fill="#000">${rects}</svg>`;
231
- }
232
- function renderVoucherDocument(v, opts = {}) {
233
- const locale = opts.locale ?? "es-MX";
234
- const t = locale.toLowerCase().startsWith("en") ? EN : ES;
235
- const shop = opts.shopName || "";
236
- const method = v.method ? Object.prototype.hasOwnProperty.call(t.methods, v.method.toLowerCase()) ? t.methods[v.method.toLowerCase()] : v.method : t.cash;
237
- const isSpei = !!v.clabe && !v.reference;
238
- const code = barcodeValue(v);
239
- const svg = code ? barcodeSvg(code, t.barcode) : null;
240
- const expires = formatDate(v.expires_at, locale, opts.timeZone);
241
- const field = (label, value) => value ? `<div class="box"><div class="label">${esc(label)}</div><div class="value">${esc(value)}</div></div>` : "";
242
- const steps = isSpei ? t.stepsSpei : t.stepsCash;
243
- const subtitle = [shop, method, opts.number ? `${t.order} ${opts.number}` : ""].filter(Boolean).map(esc).join(" \xB7 ");
244
- return `<!doctype html>
245
- <html lang="${esc(locale)}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
246
- <meta name="robots" content="noindex"><title>${esc(t.title)}${shop ? ` - ${esc(shop)}` : ""}</title><style>${CSS}</style></head>
247
- <body><main>
248
- <div class="head"><div><h1>${esc(t.title)}</h1><div class="muted">${subtitle}</div></div>
249
- ${v.amount ? `<div><div class="label" style="text-align:right">${esc(t.total)}</div><div class="amount">${esc(formatMoney(v.amount, locale))}</div></div>` : ""}</div>
250
- ${field(t.reference, v.reference)}${field(t.clabe, v.clabe)}
251
- ${svg ? `<div class="bar">${svg}<div class="muted" style="font-family:ui-monospace,monospace">${esc(code)}</div></div>` : ""}
252
- ${expires ? `<p><strong>${esc(t.expires)}:</strong> ${esc(expires)}. ${esc(t.expiresNote)}</p>` : ""}
253
- <ol class="steps">${steps.map((s) => `<li>${esc(s)}</li>`).join("")}</ol>
254
- ${v.instructions ? `<p class="note pre muted">${esc(v.instructions)}</p>` : ""}
255
- <p class="note muted">${esc(t.footer)}</p>
256
- ${opts.printButton ? `<div class="actions"><button type="button" id="print">${esc(t.print)}</button></div>` : ""}
257
- </main>
258
- ${opts.printButton || opts.autoPrint ? `<script${opts.nonce ? ` nonce="${esc(opts.nonce)}"` : ""}>(function(){var b=document.getElementById("print");if(b)b.addEventListener("click",function(){window.print()});${opts.autoPrint ? 'window.addEventListener("load",function(){setTimeout(function(){window.print()},300)});' : ""}})();</script>` : ""}
259
- </body></html>`;
260
- }
261
- var ES, EN, CSS;
262
- var init_voucher_document = __esm({
263
- "src/account/voucher-document.ts"() {
264
- init_voucher();
338
+ var KEEP_ES, KEEP_EN, ES, EN, isEnglish, own;
339
+ var init_voucher_text = __esm({
340
+ "src/account/voucher-text.ts"() {
341
+ KEEP_ES = "Conserva tu comprobante: confirmaremos tu pago autom\xE1ticamente.";
342
+ KEEP_EN = "Keep your receipt: we will confirm your payment automatically.";
265
343
  ES = {
266
344
  title: "Ficha de pago",
267
345
  total: "Total a pagar",
268
346
  reference: "Referencia",
269
347
  clabe: "CLABE",
348
+ beneficiary: "Beneficiario",
349
+ bank: "Banco",
270
350
  expires: "Vence",
271
351
  expiresNote: "Pasada esa fecha la ficha deja de ser v\xE1lida.",
272
352
  order: "Pedido",
273
353
  barcode: "C\xF3digo de barras",
274
- print: "Imprimir ficha",
354
+ code: "C\xF3digo",
355
+ print: "Imprimir",
356
+ downloadPdf: "Descargar PDF",
357
+ noScript: "Para imprimir usa la opci\xF3n Imprimir de tu navegador (Ctrl+P) o descarga el PDF.",
358
+ stepsTitle: "C\xF3mo pagar",
275
359
  footer: "Tu pedido se genera cuando se confirma el pago. Esta ficha es de un solo uso; no la compartas.",
276
- stepsCash: [
277
- "Acude a la tienda OXXO (o el comercio indicado) y di que vas a hacer un pago de servicio en efectivo.",
278
- "Muestra el c\xF3digo de barras o dicta la referencia y paga el monto exacto.",
279
- "Conserva tu comprobante: confirmaremos tu pago autom\xE1ticamente."
280
- ],
281
- stepsSpei: ["Entra a la banca en l\xEDnea o app de tu banco.", "Haz una transferencia SPEI a la CLABE indicada por el monto exacto.", "Conserva tu comprobante: confirmaremos tu pago autom\xE1ticamente."],
282
360
  methods: { oxxo: "OXXO", spei: "Transferencia SPEI", bank_transfer: "Transferencia bancaria", bank_deposit: "Dep\xF3sito bancario", cash: "Pago en efectivo" },
283
- cash: "Pago en efectivo"
361
+ cash: "Pago en efectivo",
362
+ steps: {
363
+ oxxo: (v) => [
364
+ "Acude a cualquier tienda OXXO y di que vas a hacer un pago de servicio en efectivo.",
365
+ v.barcode ? "Muestra el c\xF3digo de barras en caja o dicta la referencia, y paga el monto exacto." : "Dicta la referencia en caja y paga el monto exacto.",
366
+ KEEP_ES
367
+ ],
368
+ cash: (v) => [
369
+ "Acude a la tienda o comercio indicado y di que vas a hacer un pago en efectivo.",
370
+ v.barcode ? "Muestra el c\xF3digo de barras o dicta la referencia, y paga el monto exacto." : "Dicta la referencia y paga el monto exacto.",
371
+ KEEP_ES
372
+ ],
373
+ spei: (v) => [
374
+ "Entra a la banca en l\xEDnea o a la app de tu banco.",
375
+ "Haz una transferencia SPEI a la CLABE indicada, por el monto exacto.",
376
+ ...v.reference ? ["Escribe la referencia de esta ficha en el concepto o referencia de la transferencia."] : [],
377
+ KEEP_ES
378
+ ],
379
+ bank_transfer: (v) => [
380
+ "Entra a la banca en l\xEDnea o a la app de tu banco.",
381
+ "Haz una transferencia a la cuenta o CLABE indicada, por el monto exacto.",
382
+ ...v.reference ? ["Escribe la referencia de esta ficha en el concepto de la transferencia."] : [],
383
+ KEEP_ES
384
+ ],
385
+ bank_deposit: (v) => [
386
+ "Acude a una sucursal del banco indicado o a su cajero con dep\xF3sitos.",
387
+ `Deposita el monto exacto a la cuenta${v.reference ? " con la referencia de esta ficha" : " indicada"}.`,
388
+ KEEP_ES
389
+ ],
390
+ transfer: (v) => [
391
+ "Entra a la banca en l\xEDnea o a la app de tu banco.",
392
+ "Haz una transferencia a la CLABE indicada, por el monto exacto.",
393
+ ...v.reference ? ["Escribe la referencia de esta ficha en el concepto de la transferencia."] : [],
394
+ KEEP_ES
395
+ ],
396
+ generic: () => ["Realiza el pago con los datos de esta ficha, por el monto exacto y antes de que venza.", KEEP_ES]
397
+ }
284
398
  };
285
399
  EN = {
286
400
  title: "Payment voucher",
287
401
  total: "Amount to pay",
288
402
  reference: "Reference",
289
403
  clabe: "CLABE",
404
+ beneficiary: "Beneficiary",
405
+ bank: "Bank",
290
406
  expires: "Expires",
291
407
  expiresNote: "After that date the voucher is no longer valid.",
292
408
  order: "Order",
293
409
  barcode: "Barcode",
294
- print: "Print voucher",
410
+ code: "Code",
411
+ print: "Print",
412
+ downloadPdf: "Download PDF",
413
+ noScript: "To print, use your browser's Print option (Ctrl+P) or download the PDF.",
414
+ stepsTitle: "How to pay",
295
415
  footer: "Your order is created when the payment is confirmed. This voucher is single-use; do not share it.",
296
- stepsCash: [
297
- "Go to an OXXO store (or the store indicated) and say you want to make a cash service payment.",
298
- "Show the barcode or read out the reference and pay the exact amount.",
299
- "Keep your receipt: we will confirm your payment automatically."
300
- ],
301
- stepsSpei: ["Open your bank online or app.", "Make a SPEI transfer to the CLABE below for the exact amount.", "Keep your receipt: we will confirm your payment automatically."],
302
416
  methods: { oxxo: "OXXO", spei: "SPEI transfer", bank_transfer: "Bank transfer", bank_deposit: "Bank deposit", cash: "Cash payment" },
303
- cash: "Cash payment"
417
+ cash: "Cash payment",
418
+ steps: {
419
+ oxxo: (v) => [
420
+ "Go to any OXXO store and say you want to make a cash service payment.",
421
+ v.barcode ? "Show the barcode at the register or read out the reference, and pay the exact amount." : "Read out the reference at the register and pay the exact amount.",
422
+ KEEP_EN
423
+ ],
424
+ cash: (v) => [
425
+ "Go to the store indicated and say you want to make a cash payment.",
426
+ v.barcode ? "Show the barcode or read out the reference, and pay the exact amount." : "Read out the reference and pay the exact amount.",
427
+ KEEP_EN
428
+ ],
429
+ spei: (v) => [
430
+ "Open your online banking or your bank app.",
431
+ "Make a SPEI transfer to the CLABE below for the exact amount.",
432
+ ...v.reference ? ["Enter the reference of this voucher as the transfer concept or reference."] : [],
433
+ KEEP_EN
434
+ ],
435
+ bank_transfer: (v) => [
436
+ "Open your online banking or your bank app.",
437
+ "Make a transfer to the account or CLABE below for the exact amount.",
438
+ ...v.reference ? ["Enter the reference of this voucher as the transfer concept."] : [],
439
+ KEEP_EN
440
+ ],
441
+ bank_deposit: (v) => [
442
+ "Go to a branch of the bank indicated or to one of its deposit ATMs.",
443
+ `Deposit the exact amount to the account${v.reference ? " with the reference of this voucher" : " indicated"}.`,
444
+ KEEP_EN
445
+ ],
446
+ transfer: (v) => [
447
+ "Open your online banking or your bank app.",
448
+ "Make a transfer to the CLABE below for the exact amount.",
449
+ ...v.reference ? ["Enter the reference of this voucher as the transfer concept."] : [],
450
+ KEEP_EN
451
+ ],
452
+ generic: () => ["Make the payment with the details of this voucher, for the exact amount and before it expires.", KEEP_EN]
453
+ }
304
454
  };
455
+ isEnglish = (locale) => locale.toLowerCase().startsWith("en");
456
+ own = (o, k) => {
457
+ const key = k?.toLowerCase();
458
+ return key && Object.prototype.hasOwnProperty.call(o, key) ? o[key] : void 0;
459
+ };
460
+ }
461
+ });
462
+
463
+ // src/account/voucher-pdf.ts
464
+ var voucher_pdf_exports = {};
465
+ __export(voucher_pdf_exports, {
466
+ MAX_VOUCHER_PDF_BYTES: () => MAX_VOUCHER_PDF_BYTES,
467
+ pdfString: () => pdfString,
468
+ renderVoucherPdf: () => renderVoucherPdf
469
+ });
470
+ function toBytes(s) {
471
+ const out = [];
472
+ for (const ch of s) {
473
+ const c = ch.codePointAt(0);
474
+ if (c < 32 || c === 127) out.push(32);
475
+ else if (c < 127 || c >= 160 && c <= 255) out.push(c);
476
+ else out.push(CP1252[c] ?? 63);
477
+ }
478
+ return out;
479
+ }
480
+ function pdfString(s) {
481
+ let o = "(";
482
+ for (const b of toBytes(s)) o += b >= 32 && b < 127 && b !== 40 && b !== 41 && b !== 92 ? String.fromCharCode(b) : `\\${b.toString(8).padStart(3, "0")}`;
483
+ return o + ")";
484
+ }
485
+ function charWidth(font, ch) {
486
+ if (font === "F3" || font === "F4") return 600;
487
+ const c = ch.codePointAt(0);
488
+ const table = font === "F2" ? HELV_B : HELV;
489
+ if (c >= 32 && c < 127) return table[c - 32];
490
+ const base = ch.normalize("NFD")[0].codePointAt(0);
491
+ return base >= 32 && base < 127 ? table[base - 32] : 556;
492
+ }
493
+ function wrap(font, size, text2, max) {
494
+ const lines = [];
495
+ for (const para of text2.split(/\r?\n/)) {
496
+ let line = "";
497
+ for (const word of para.split(/\s+/).filter(Boolean)) {
498
+ let w = word;
499
+ while (textWidth(font, size, w) > max) {
500
+ let n2 = 1;
501
+ while (n2 < w.length && textWidth(font, size, w.slice(0, n2 + 1)) <= max) n2++;
502
+ if (line) lines.push(line), line = "";
503
+ lines.push(w.slice(0, n2));
504
+ w = w.slice(n2);
505
+ }
506
+ const next = line ? `${line} ${w}` : w;
507
+ if (line && textWidth(font, size, next) > max) lines.push(line), line = w;
508
+ else line = next;
509
+ }
510
+ lines.push(line);
511
+ }
512
+ return lines;
513
+ }
514
+ function renderVoucherPdf(v, opts = {}) {
515
+ const locale = opts.locale ?? "es-MX";
516
+ const t = docText(locale);
517
+ const [W, H] = opts.paper === "letter" ? [612, 792] : [595.28, 841.89];
518
+ const M = 54;
519
+ const right = W - M;
520
+ const inner = right - M;
521
+ const pages = [];
522
+ let ops = "";
523
+ let y = H - M;
524
+ const newPage = () => {
525
+ if (pages.length >= MAX_PAGES) return false;
526
+ if (ops) pages.push(ops);
527
+ ops = "";
528
+ y = H - M;
529
+ return true;
530
+ };
531
+ const text2 = (font, size, x, s, gray = 0) => {
532
+ ops += `BT ${n(gray)} g /${font} ${size} Tf ${n(x)} ${n(y)} Td ${pdfString(s)} Tj ET
533
+ `;
534
+ };
535
+ const ensure = (h) => {
536
+ if (y - h < M) newPage();
537
+ };
538
+ const paragraph = (font, size, s, opt = {}) => {
539
+ const lead = opt.lead ?? size * 1.4;
540
+ for (const line of wrap(font, size, s, opt.width ?? inner)) {
541
+ ensure(lead);
542
+ y -= lead;
543
+ if (line) text2(font, size, opt.x ?? M, line, opt.gray ?? 0);
544
+ }
545
+ };
546
+ const amount = formatMoney(v.amount, locale);
547
+ const top = y;
548
+ y -= 20;
549
+ text2("F2", 20, M, t.title);
550
+ const subtitle = [opts.shopName, methodName(v, t), opts.number ? `${t.order} ${opts.number}` : ""].filter(Boolean).join(" \xB7 ");
551
+ if (subtitle) {
552
+ y -= 15;
553
+ paragraph("F1", 10, subtitle, { width: amount ? inner * 0.6 : inner, gray: 0.33, lead: 12 });
554
+ }
555
+ const afterTitle = y;
556
+ if (amount) {
557
+ y = top - 10;
558
+ const label = t.total.toUpperCase();
559
+ text2("F1", 8, right - textWidth("F1", 8, label), label, 0.33);
560
+ y -= 22;
561
+ text2("F2", 22, right - textWidth("F2", 22, amount), amount);
562
+ }
563
+ y = Math.min(afterTitle, y) - 10;
564
+ ops += `0 g 2 w ${n(M)} ${n(y)} m ${n(right)} ${n(y)} l S
565
+ `;
566
+ y -= 8;
567
+ for (const f of voucherFields(v, t)) {
568
+ const size = f.value.length > 36 ? 12 : 16;
569
+ const lines = wrap("F3", size, f.value, inner - 24);
570
+ const h = 14 + 12 + lines.length * size * 1.3 + 8;
571
+ ensure(h + 12);
572
+ y -= 12;
573
+ ops += `0 G 0.8 w ${n(M)} ${n(y - h)} ${n(inner)} ${n(h)} re S
574
+ `;
575
+ const boxTop = y;
576
+ y -= 16;
577
+ text2("F1", 8, M + 12, f.label.toUpperCase(), 0.33);
578
+ y -= 4;
579
+ for (const line of lines) {
580
+ y -= size * 1.3;
581
+ text2("F3", size, M + 12, line);
582
+ }
583
+ y = boxTop - h;
584
+ }
585
+ const g = v.barcode ? barcodeGeometry(v.barcode, { ...v.barcode_format ? { format: v.barcode_format } : {}, height: 60 }) : null;
586
+ if (g && v.barcode) {
587
+ const scale = Math.min(1, inner / g.width) * 0.75;
588
+ const bw = g.width * scale;
589
+ const bh = 60;
590
+ ensure(bh + 40);
591
+ y -= 18;
592
+ ops += `0 g
593
+ `;
594
+ const x0 = M + (inner - bw) / 2;
595
+ for (const r of g.rects) ops += `${n(x0 + r.x * scale)} ${n(y - bh)} ${n(r.w * scale)} ${n(bh)} re f
596
+ `;
597
+ y -= bh + 12;
598
+ const cw = textWidth("F4", 10, v.barcode);
599
+ text2("F4", 10, M + (inner - Math.min(cw, inner)) / 2, v.barcode.slice(0, Math.floor(inner / 6)));
600
+ y -= 4;
601
+ }
602
+ const expires = formatDate(v.expires_at, locale, opts.timeZone);
603
+ if (expires) {
604
+ y -= 8;
605
+ paragraph("F2", 10, `${t.expires}: ${expires.replace(/\.$/, "")}.`);
606
+ paragraph("F1", 10, t.expiresNote);
607
+ }
608
+ y -= 10;
609
+ ensure(40);
610
+ paragraph("F2", 12, t.stepsTitle, { lead: 16 });
611
+ voucherSteps(v, t).forEach((s, i) => {
612
+ const lines = wrap("F1", 10, s, inner - 18);
613
+ ensure(lines.length * 14 + 2);
614
+ y -= 2;
615
+ lines.forEach((line, j) => {
616
+ y -= 14;
617
+ if (j === 0) text2("F1", 10, M, `${i + 1}.`);
618
+ text2("F1", 10, M + 18, line);
619
+ });
620
+ });
621
+ if (v.instructions) {
622
+ y -= 6;
623
+ paragraph("F1", 9, v.instructions, { gray: 0.33, lead: 12 });
624
+ }
625
+ y -= 8;
626
+ paragraph("F1", 8, t.footer, { gray: 0.33, lead: 11 });
627
+ pages.push(ops);
628
+ const objs = [];
629
+ const kids = pages.map((_, i) => `${8 + i * 2} 0 R`).join(" ");
630
+ objs[1] = "<< /Type /Catalog /Pages 2 0 R >>";
631
+ objs[2] = `<< /Type /Pages /Kids [${kids}] /Count ${pages.length} >>`;
632
+ ["F1", "F2", "F3", "F4"].forEach((f, i) => objs[3 + i] = `<< /Type /Font /Subtype /Type1 /BaseFont /${FONT_NAMES[f]} /Encoding /WinAnsiEncoding >>`);
633
+ objs[7] = `<< /Title ${pdfString(`${t.title}${opts.shopName ? ` - ${opts.shopName}` : ""}`)} /Producer ${pdfString("@asteby/storefront-react")} >>`;
634
+ pages.forEach((content, i) => {
635
+ objs[8 + i * 2] = `<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${n(W)} ${n(H)}] /Resources << /Font << /F1 3 0 R /F2 4 0 R /F3 5 0 R /F4 6 0 R >> >> /Contents ${9 + i * 2} 0 R >>`;
636
+ objs[9 + i * 2] = `<< /Length ${content.length} >>
637
+ stream
638
+ ${content}endstream`;
639
+ });
640
+ let out = "%PDF-1.4\n%\xE2\xE3\xCF\xD3\n";
641
+ const offsets = [];
642
+ for (let i = 1; i < objs.length; i++) {
643
+ offsets[i] = out.length;
644
+ out += `${i} 0 obj
645
+ ${objs[i]}
646
+ endobj
647
+ `;
648
+ }
649
+ const xref = out.length;
650
+ out += `xref
651
+ 0 ${objs.length}
652
+ 0000000000 65535 f
653
+ `;
654
+ for (let i = 1; i < objs.length; i++) out += `${String(offsets[i]).padStart(10, "0")} 00000 n
655
+ `;
656
+ out += `trailer
657
+ << /Size ${objs.length} /Root 1 0 R /Info 7 0 R >>
658
+ startxref
659
+ ${xref}
660
+ %%EOF
661
+ `;
662
+ if (out.length > MAX_VOUCHER_PDF_BYTES) throw new RangeError("voucher PDF too large");
663
+ const bytes = new Uint8Array(out.length);
664
+ for (let i = 0; i < out.length; i++) bytes[i] = out.charCodeAt(i) & 255;
665
+ return bytes;
666
+ }
667
+ var MAX_VOUCHER_PDF_BYTES, MAX_PAGES, HELV, HELV_B, FONT_NAMES, CP1252, textWidth, n;
668
+ var init_voucher_pdf = __esm({
669
+ "src/account/voucher-pdf.ts"() {
670
+ init_voucher();
671
+ init_voucher_text();
672
+ MAX_VOUCHER_PDF_BYTES = 256 * 1024;
673
+ MAX_PAGES = 3;
674
+ HELV = [278, 278, 355, 556, 556, 889, 667, 191, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556, 333, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584];
675
+ HELV_B = [278, 333, 474, 556, 556, 889, 722, 238, 333, 333, 389, 584, 278, 333, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556, 333, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584];
676
+ FONT_NAMES = { F1: "Helvetica", F2: "Helvetica-Bold", F3: "Courier-Bold", F4: "Courier" };
677
+ CP1252 = { 8364: 128, 8216: 145, 8217: 146, 8220: 147, 8221: 148, 8226: 149, 8211: 150, 8212: 151 };
678
+ textWidth = (font, size, s) => [...s].reduce((w, ch) => w + charWidth(font, ch), 0) * size / 1e3;
679
+ n = (x) => String(Math.round(x * 100) / 100);
680
+ }
681
+ });
682
+
683
+ // src/account/voucher-document.ts
684
+ var voucher_document_exports = {};
685
+ __export(voucher_document_exports, {
686
+ esc: () => esc,
687
+ renderVoucherDocument: () => renderVoucherDocument
688
+ });
689
+ function esc(s) {
690
+ return s.replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
691
+ }
692
+ function barcodeSvg(value, format, label) {
693
+ const g = barcodeGeometry(value, { ...format ? { format } : {} });
694
+ if (!g) return null;
695
+ const rects = g.rects.map((r) => `<rect x="${r.x}" y="0" width="${r.w}" height="${g.height}"/>`).join("");
696
+ return `<svg viewBox="0 0 ${g.width} ${g.height}" width="${g.width}" height="${g.height}" role="img" aria-label="${esc(label)} ${esc(value)}" shape-rendering="crispEdges" fill="#000">${rects}</svg>`;
697
+ }
698
+ function renderVoucherDocument(v, opts = {}) {
699
+ const locale = opts.locale ?? "es-MX";
700
+ const t = docText(locale);
701
+ const shop = opts.shopName || "";
702
+ const method = methodName(v, t);
703
+ const code = v.barcode;
704
+ const svg = code ? barcodeSvg(code, v.barcode_format, t.barcode) : null;
705
+ const expires = formatDate(v.expires_at, locale, opts.timeZone);
706
+ const fields = voucherFields(v, t);
707
+ const steps = voucherSteps(v, t);
708
+ const subtitle = [shop, method, opts.number ? `${t.order} ${opts.number}` : ""].filter(Boolean).map(esc).join(" \xB7 ");
709
+ const actions = opts.printButton || opts.pdfHref;
710
+ return `<!doctype html>
711
+ <html lang="${esc(locale)}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
712
+ <meta name="robots" content="noindex"><title>${esc(t.title)}${shop ? ` - ${esc(shop)}` : ""}</title><style>${CSS}</style></head>
713
+ <body><main aria-labelledby="t">
714
+ <header class="head"><div><h1 id="t">${esc(t.title)}</h1><p class="muted" data-voucher-method="${esc(v.method ?? "")}">${subtitle}</p></div>
715
+ ${v.amount ? `<div><p class="label" style="text-align:right">${esc(t.total)}</p><p class="amount">${esc(formatMoney(v.amount, locale))}</p></div>` : ""}</header>
716
+ ${fields.map((f) => `<section class="box" data-field="${f.key}"><h2 class="label">${esc(f.label)}</h2><p class="value">${esc(f.value)}</p></section>`).join("")}
717
+ ${svg ? `<figure class="bar">${svg}<figcaption class="muted" style="font-family:ui-monospace,monospace">${esc(code)}</figcaption></figure>` : ""}
718
+ ${expires ? `<p><strong>${esc(t.expires)}:</strong> ${esc(expires.replace(/\.$/, ""))}. ${esc(t.expiresNote)}</p>` : ""}
719
+ <h2 class="steps-title">${esc(t.stepsTitle)}</h2>
720
+ <ol class="steps">${steps.map((s) => `<li>${esc(s)}</li>`).join("")}</ol>
721
+ ${v.instructions ? `<p class="note pre muted">${esc(v.instructions)}</p>` : ""}
722
+ <p class="note muted">${esc(t.footer)}</p>
723
+ ${actions ? `<div class="actions" role="group" aria-label="${esc(t.title)}">${opts.pdfHref ? `<a class="btn" href="${esc(opts.pdfHref)}" download="ficha-de-pago.pdf" data-voucher-pdf>${esc(t.downloadPdf)}</a>` : ""}${opts.printButton ? `<button type="button" class="btn alt" id="print" hidden>${esc(t.print)}</button><noscript><p class="note muted">${esc(t.noScript)}</p></noscript>` : ""}</div>` : ""}
724
+ </main>
725
+ ${opts.printButton || opts.autoPrint ? `<script${opts.nonce ? ` nonce="${esc(opts.nonce)}"` : ""}>(function(){var b=document.getElementById("print");if(b){b.hidden=false;b.addEventListener("click",function(){window.print()})}${opts.autoPrint ? 'window.addEventListener("load",function(){setTimeout(function(){window.print()},300)});' : ""}})();</script>` : ""}
726
+ </body></html>`;
727
+ }
728
+ var CSS;
729
+ var init_voucher_document = __esm({
730
+ "src/account/voucher-document.ts"() {
731
+ init_voucher();
732
+ init_voucher_text();
305
733
  CSS = `
306
734
  @page { size: A4; margin: 18mm; }
307
735
  * { box-sizing: border-box; }
@@ -310,17 +738,21 @@ main { max-width: 174mm; margin: 0 auto; padding: 24px 16px; }
310
738
  h1 { font-size: 22px; margin: 0; }
311
739
  .muted { color: #555; }
312
740
  .head { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; border-bottom: 3px solid #111; padding-bottom: 12px; }
313
- .amount { font-size: 28px; font-weight: 700; text-align: right; }
741
+ .amount { margin: 0; font-size: 28px; font-weight: 700; text-align: right; }
314
742
  .box { border: 1px solid #111; padding: 14px 16px; margin-top: 18px; }
315
- .label { font-size: 12px; letter-spacing: .08em; text-transform: uppercase; color: #555; }
316
- .value { font: 700 22px/1.3 ui-monospace, "SF Mono", Menlo, Consolas, monospace; word-break: break-all; letter-spacing: .04em; }
317
- .bar { margin-top: 18px; text-align: center; }
743
+ .label { margin: 0; font-weight: 400; font-size: 12px; letter-spacing: .08em; text-transform: uppercase; color: #555; }
744
+ .value { margin: 0; font: 700 22px/1.3 ui-monospace, "SF Mono", Menlo, Consolas, monospace; word-break: break-all; letter-spacing: .04em; }
745
+ .bar { text-align: center; }
318
746
  .bar svg { max-width: 100%; height: auto; }
319
- .steps { margin: 18px 0 0; padding-left: 20px; }
320
747
  .note { margin-top: 18px; font-size: 12px; }
321
748
  .pre { white-space: pre-line; }
322
- .actions { margin-top: 24px; display: flex; gap: 12px; }
323
- .actions button { font: inherit; padding: 10px 18px; border: 1px solid #111; background: #111; color: #fff; cursor: pointer; }
749
+ .steps-title { font-size: 16px; margin: 18px 0 0; }
750
+ .steps { margin: 6px 0 0; padding-left: 20px; }
751
+ .bar { margin: 18px 0 0; }
752
+ .actions { margin-top: 24px; display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
753
+ .btn { display: inline-block; font: inherit; padding: 10px 18px; border: 2px solid #111; background: #111; color: #fff; cursor: pointer; text-decoration: none; }
754
+ .btn.alt { background: #fff; color: #111; }
755
+ .btn:focus-visible { outline: 3px solid #1a56db; outline-offset: 2px; }
324
756
  @media print { .actions { display: none; } main { padding: 0; } }
325
757
  `;
326
758
  }
@@ -652,16 +1084,30 @@ async function invoiceFileResponse({ storefront: storefront$1, orderId, invoiceI
652
1084
  return failureResponse(err);
653
1085
  }
654
1086
  }
655
- async function pendingVoucherResponse({ storefront, id, download = false, shopName, locale, timeZone, signal }) {
1087
+ async function pendingVoucherResponse({ storefront, id, download = false, format = "html", paper, pdfHref = "?formato=pdf", shopName, locale, timeZone, signal }) {
656
1088
  try {
657
1089
  const page = await storefront.customer.pendingPayments({ first: 100 }, signal ? { signal } : void 0);
658
1090
  const item = page.nodes.find((p) => p.id === id);
659
1091
  const voucher = item ? voucherOf(item) : null;
660
1092
  if (!item || !voucher) return textResponse(404, "Ficha no encontrada");
1093
+ const safeId = id.replace(/[^A-Za-z0-9-]/g, "").slice(0, 8) || "ficha";
1094
+ if (format === "pdf") {
1095
+ const { renderVoucherPdf: renderVoucherPdf2 } = await Promise.resolve().then(() => (init_voucher_pdf(), voucher_pdf_exports));
1096
+ const bytes = renderVoucherPdf2(voucher, { ...shopName ? { shopName } : {}, ...locale ? { locale } : {}, ...timeZone ? { timeZone } : {}, ...paper ? { paper } : {} });
1097
+ return new Response(bytes, {
1098
+ status: 200,
1099
+ headers: {
1100
+ "Content-Type": "application/pdf",
1101
+ "Content-Length": String(bytes.byteLength),
1102
+ "Content-Disposition": 'attachment; filename="ficha-de-pago.pdf"',
1103
+ ...LOCKED_HEADERS,
1104
+ "Content-Security-Policy": "default-src 'none'; sandbox"
1105
+ }
1106
+ });
1107
+ }
661
1108
  const nonce = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(16))));
662
1109
  const { renderVoucherDocument: renderVoucherDocument2 } = await Promise.resolve().then(() => (init_voucher_document(), voucher_document_exports));
663
- const html = renderVoucherDocument2(voucher, { ...shopName ? { shopName } : {}, ...locale ? { locale } : {}, ...timeZone ? { timeZone } : {}, printButton: !download, nonce });
664
- const safeId = id.replace(/[^A-Za-z0-9-]/g, "").slice(0, 8) || "ficha";
1110
+ const html = renderVoucherDocument2(voucher, { ...shopName ? { shopName } : {}, ...locale ? { locale } : {}, ...timeZone ? { timeZone } : {}, printButton: !download, ...download ? {} : { pdfHref }, nonce });
665
1111
  return new Response(html, {
666
1112
  status: 200,
667
1113
  headers: {
@@ -676,6 +1122,9 @@ async function pendingVoucherResponse({ storefront, id, download = false, shopNa
676
1122
  return failureResponse(err);
677
1123
  }
678
1124
  }
1125
+ function pendingVoucherPdfResponse(options) {
1126
+ return pendingVoucherResponse({ ...options, format: "pdf" });
1127
+ }
679
1128
 
680
1129
  exports.MAX_INVOICE_FILE_BYTES = MAX_INVOICE_FILE_BYTES;
681
1130
  exports.SAFE_AUTH_CAPABILITIES = SAFE_AUTH_CAPABILITIES;
@@ -685,6 +1134,7 @@ exports.invoiceFileResponse = invoiceFileResponse;
685
1134
  exports.loadAccountSessions = loadAccountSessions;
686
1135
  exports.loadAccountSettings = loadAccountSettings;
687
1136
  exports.loadAuthCapabilities = loadAuthCapabilities;
1137
+ exports.pendingVoucherPdfResponse = pendingVoucherPdfResponse;
688
1138
  exports.pendingVoucherResponse = pendingVoucherResponse;
689
1139
  exports.safeNextPath = safeNextPath;
690
1140
  exports.toSettingsFailure = toSettingsFailure;