@asteby/storefront 0.13.0 → 0.15.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/dist/mock.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as IdentityRulesV1, aS as RegionFiscalId, Z as IdentityDocumentSpec, aQ as Region, b0 as Shop, aE as Product, x as Collection, b7 as Theme, am as Page, af as Menu, b as Article, C as Cart, A as Address, a5 as InvoiceRequest, z as Customer, ak as Order, ap as PaymentAttemptResult, a7 as InvoiceStage, B as CustomerCapabilities, aL as PromotionAnnouncement, a$ as ShippingRate, av as PaymentMethod } from './types-BQ3S6VjF.cjs';
1
+ import { a0 as IdentityRulesV1, b8 as RegionFiscalId, _ as IdentityDocumentSpec, b6 as Region, bp as Shop, aV as Product, x as Collection, bw as Theme, aA as Page, aj as Menu, b as Article, C as Cart, A as Address, a9 as InvoiceRequest, z as Customer, aq as Order, aD as PaymentAttemptResult, aS as PendingPayment, ab as InvoiceStage, B as CustomerCapabilities, b0 as PromotionAnnouncement, bo as ShippingRate, aK as PaymentMethod } from './types-cwXbVCwe.cjs';
2
2
  import './geo.cjs';
3
3
 
4
4
  /**
@@ -153,6 +153,8 @@ type MockState = {
153
153
  }>;
154
154
  /** `contact|sku` of accepted "notify me" subscriptions. */
155
155
  restock: Set<string>;
156
+ /** Cash vouchers waiting for payment (`GET /customer/pending-payments`); `cus_1` only. */
157
+ pendingPayments: PendingPayment[];
156
158
  /** Invoice requested per order id. */
157
159
  invoices: Map<string, {
158
160
  stage: InvoiceStage;
package/dist/mock.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as IdentityRulesV1, aS as RegionFiscalId, Z as IdentityDocumentSpec, aQ as Region, b0 as Shop, aE as Product, x as Collection, b7 as Theme, am as Page, af as Menu, b as Article, C as Cart, A as Address, a5 as InvoiceRequest, z as Customer, ak as Order, ap as PaymentAttemptResult, a7 as InvoiceStage, B as CustomerCapabilities, aL as PromotionAnnouncement, a$ as ShippingRate, av as PaymentMethod } from './types-C_rsJRCl.js';
1
+ import { a0 as IdentityRulesV1, b8 as RegionFiscalId, _ as IdentityDocumentSpec, b6 as Region, bp as Shop, aV as Product, x as Collection, bw as Theme, aA as Page, aj as Menu, b as Article, C as Cart, A as Address, a9 as InvoiceRequest, z as Customer, aq as Order, aD as PaymentAttemptResult, aS as PendingPayment, ab as InvoiceStage, B as CustomerCapabilities, b0 as PromotionAnnouncement, bo as ShippingRate, aK as PaymentMethod } from './types-CuamKhDe.js';
2
2
  import './geo.js';
3
3
 
4
4
  /**
@@ -153,6 +153,8 @@ type MockState = {
153
153
  }>;
154
154
  /** `contact|sku` of accepted "notify me" subscriptions. */
155
155
  restock: Set<string>;
156
+ /** Cash vouchers waiting for payment (`GET /customer/pending-payments`); `cus_1` only. */
157
+ pendingPayments: PendingPayment[];
156
158
  /** Invoice requested per order id. */
157
159
  invoices: Map<string, {
158
160
  stage: InvoiceStage;
package/dist/mock.js CHANGED
@@ -1,6 +1,210 @@
1
- import { resolveAvailability } from './chunk-HBSB7XS2.js';
1
+ import { resolveAvailability, isTestShipment, sortTrackingEvents, orderProgress, nextAction, orderShipments, shipmentsStatus } from './chunk-IPUAZ3LQ.js';
2
2
  import { isValidPoint, rankByDistance } from './chunk-45R47HYO.js';
3
3
 
4
+ // src/mock/account-seed.ts
5
+ var money = (cents, currency) => ({ amount: (cents / 100).toFixed(2), currency_code: currency });
6
+ var line = (title, variant, quantity, unitCents, currency) => ({
7
+ title,
8
+ variant_title: variant,
9
+ sku: null,
10
+ quantity,
11
+ unit: money(unitCents, currency),
12
+ total: money(unitCents * quantity, currency),
13
+ image: null
14
+ });
15
+ var address = { first_name: "Ana", last_name: "Prueba", city: "Quer\xE9taro", province: "Qro.", zip: "76000", country_code: "MX" };
16
+ function buildAccountOrders(currency) {
17
+ const inTransit = {
18
+ id: "shp_seed_1",
19
+ number: "ENV-00001",
20
+ kind: "outbound",
21
+ status: "in_transit",
22
+ environment: "production",
23
+ test_mode: false,
24
+ carrier: "Estafeta",
25
+ carrier_logo_key: "estafeta",
26
+ service: "Terrestre",
27
+ tracking_number: "8012345678",
28
+ tracking_url: "https://rastreo.example.com/estafeta/8012345678",
29
+ shipped_at: "2026-08-12T15:00:00.000Z",
30
+ delivered_at: null,
31
+ promised_by: "2026-08-16",
32
+ label_url: null,
33
+ events: [
34
+ { id: "evt_3", status: "in_transit", description: "En tr\xE1nsito hacia tu ciudad", location: "Quer\xE9taro, Qro.", occurred_at: "2026-08-14T09:30:00.000Z", source: "carrier" },
35
+ { id: "evt_2", status: "in_transit", description: "Sali\xF3 del centro de distribuci\xF3n", location: "Ciudad de M\xE9xico, CDMX", occurred_at: "2026-08-13T07:10:00.000Z", source: "carrier" },
36
+ { id: "evt_1", status: "label_created", description: "Gu\xEDa generada", location: null, occurred_at: "2026-08-12T15:00:00.000Z", source: "store" }
37
+ ],
38
+ map: {
39
+ origin: { label: "Ciudad de M\xE9xico, CDMX", lat: 19.43, lng: -99.13, precision: "city" },
40
+ destination: { label: "Quer\xE9taro, Qro.", lat: 20.6, lng: -100.4, precision: "city" },
41
+ path: [{ label: "Quer\xE9taro, Qro.", lat: 20.59, lng: -100.39, precision: "city" }]
42
+ }
43
+ };
44
+ const invoice = {
45
+ id: "inv_seed_1",
46
+ status: "issued",
47
+ kind: "invoice",
48
+ stage: "issued",
49
+ action: "download",
50
+ can_request: false,
51
+ folio: "8F3C1A52-6E0B-4B58-9C0D-2B7D6B4D7A11",
52
+ series: "A",
53
+ folio_number: "1042",
54
+ total: money(129900, currency),
55
+ issued_at: "2026-08-13T10:00:00.000Z",
56
+ test_mode: false,
57
+ environment: "production"
58
+ };
59
+ const creditNote = {
60
+ id: "inv_seed_1_nc",
61
+ status: "issued",
62
+ kind: "credit_note",
63
+ stage: "issued",
64
+ action: "download",
65
+ can_request: false,
66
+ folio: "1D9B7E20-4A5C-4C4B-8F11-6A0E3C9D5B02",
67
+ series: "NC",
68
+ folio_number: "7",
69
+ total: money(2e4, currency),
70
+ issued_at: "2026-08-15T10:00:00.000Z",
71
+ related_to: "inv_seed_1",
72
+ test_mode: false,
73
+ environment: "production"
74
+ };
75
+ return [
76
+ {
77
+ id: "ord_seed_shipped",
78
+ number: "0999",
79
+ status: "shipped",
80
+ created_at: "2026-08-12T12:00:00.000Z",
81
+ total: money(129900, currency),
82
+ customer_id: "cus_1",
83
+ lines: [line("Kit de frenos", "Delantero", 1, 109900, currency), line("L\xEDquido de frenos", "500 ml", 1, 2e4, currency)],
84
+ delivery_method: "shipping",
85
+ payment_status: "paid",
86
+ subtotal: money(111983, currency),
87
+ tax: money(17917, currency),
88
+ shipping: money(0, currency),
89
+ discount: money(0, currency),
90
+ tax_included: true,
91
+ shipping_method: "Est\xE1ndar",
92
+ shipping_address: address,
93
+ returns: [],
94
+ shipments: [inTransit],
95
+ invoices: [invoice, creditNote],
96
+ payments: [{ method_label: "Tarjeta Visa terminada en 4242", family: "card", status: "partially_refunded", amount: money(129900, currency), paid_at: "2026-08-12T12:01:00.000Z" }],
97
+ refunds: [{ id: "rfd_seed_1", amount: money(2e4, currency), status: "completed", method_label: "Tarjeta Visa terminada en 4242", created_at: "2026-08-15T09:00:00.000Z", completed_at: "2026-08-15T10:00:00.000Z", return_id: null }]
98
+ },
99
+ {
100
+ id: "ord_seed_preparing",
101
+ number: "0998",
102
+ status: "fulfilling",
103
+ created_at: "2026-08-11T12:00:00.000Z",
104
+ total: money(54900, currency),
105
+ customer_id: "cus_1",
106
+ lines: [line("Filtro de aceite", null, 3, 18300, currency)],
107
+ delivery_method: "shipping",
108
+ payment_status: "paid",
109
+ shipping_address: address,
110
+ shipments: [],
111
+ invoices: [],
112
+ payments: [{ method_label: "OXXO", family: "cash_voucher", status: "paid", amount: money(54900, currency), paid_at: "2026-08-11T18:00:00.000Z" }],
113
+ refunds: []
114
+ },
115
+ {
116
+ id: "ord_seed_sandbox",
117
+ number: "0997",
118
+ status: "shipped",
119
+ created_at: "2026-08-10T12:00:00.000Z",
120
+ total: money(31e3, currency),
121
+ customer_id: "cus_1",
122
+ lines: [line("Buj\xEDa iridio", null, 4, 7750, currency)],
123
+ delivery_method: "shipping",
124
+ payment_status: "paid",
125
+ shipments: [
126
+ {
127
+ id: "shp_seed_test",
128
+ kind: "outbound",
129
+ status: "label_created",
130
+ environment: "sandbox",
131
+ test_mode: true,
132
+ carrier: "Estafeta",
133
+ carrier_logo_key: "estafeta",
134
+ tracking_number: null,
135
+ tracking_url: null,
136
+ events: [],
137
+ map: null
138
+ }
139
+ ],
140
+ invoices: [
141
+ { id: "inv_seed_test", status: "issued", kind: "invoice", stage: "issued", action: "download", can_request: false, folio: "00000000-0000-4000-8000-000000000000", test_mode: true, environment: "sandbox" }
142
+ ],
143
+ payments: [],
144
+ refunds: []
145
+ },
146
+ {
147
+ id: "ord_seed_pickup",
148
+ number: "0996",
149
+ status: "ready_for_pickup",
150
+ created_at: "2026-08-09T12:00:00.000Z",
151
+ total: money(88e3, currency),
152
+ customer_id: "cus_1",
153
+ lines: [line("Bater\xEDa 12V", null, 1, 88e3, currency)],
154
+ delivery_method: "pickup",
155
+ pickup: { location_id: null, name: "Sucursal Centro", address: "Av. Reforma 100", city: "Ciudad de M\xE9xico", phone: null, ready_at: "2026-08-10T10:00:00.000Z", delivered_at: null },
156
+ payment_status: "paid",
157
+ shipments: [],
158
+ invoices: [],
159
+ payments: [],
160
+ refunds: [],
161
+ degraded: ["invoices"]
162
+ }
163
+ ];
164
+ }
165
+ function buildPendingPayments(currency, nowMs) {
166
+ return [
167
+ {
168
+ id: "chk_seed_oxxo",
169
+ number: null,
170
+ status: "pending_payment",
171
+ created_at: new Date(nowMs - 36e5).toISOString(),
172
+ total: money(45e3, currency),
173
+ expires_at: new Date(nowMs + 47 * 36e5).toISOString(),
174
+ payment: {
175
+ provider: "mercadopago",
176
+ method: "oxxo",
177
+ reference: "12345678901234",
178
+ barcode: "12345678901234",
179
+ clabe: null,
180
+ voucher_url: "https://pagos.example.com/ficha/abc",
181
+ instructions: "Paga en cualquier OXXO antes de la fecha de vencimiento.",
182
+ expires_at: new Date(nowMs + 47 * 36e5).toISOString(),
183
+ amount: money(45e3, currency)
184
+ }
185
+ },
186
+ {
187
+ id: "chk_seed_spei",
188
+ number: null,
189
+ status: "expired",
190
+ created_at: new Date(nowMs - 9 * 864e5).toISOString(),
191
+ total: money(12e4, currency),
192
+ expires_at: new Date(nowMs - 2 * 864e5).toISOString(),
193
+ payment: {
194
+ provider: "mercadopago",
195
+ method: "spei",
196
+ reference: null,
197
+ barcode: null,
198
+ clabe: "646180000000000001",
199
+ voucher_url: null,
200
+ instructions: null,
201
+ expires_at: new Date(nowMs - 2 * 864e5).toISOString(),
202
+ amount: money(12e4, currency)
203
+ }
204
+ }
205
+ ];
206
+ }
207
+
4
208
  // src/mock/region-mx.seed.ts
5
209
  var SEED_MX_CSF = {
6
210
  "kind": "MX:csf",
@@ -994,7 +1198,7 @@ var PRODUCT_SPECS = [
994
1198
  { handle: "llanta-265-70-r16-at", title: "Llanta 265/70 R16 All Terrain", collection: "llantas", type: "Llanta", vendor: "Marca Sur", price: 3599, attributes: { ...tire(265, 70, 16), load_index: 112, speed_rating: "T", runflat: false }, tags: ["camioneta"], description: "Para camioneta, asfalto y terracer\xEDa." },
995
1199
  { handle: "llanta-175-70-r13-city", title: "Llanta 175/70 R13 City", collection: "llantas", type: "Llanta", vendor: "Marca Centro", price: 999, attributes: { ...tire(175, 70, 13), load_index: 82, speed_rating: "T", runflat: false }, tags: ["auto"], stock: 0, description: "Econ\xF3mica y durable para ciudad." }
996
1200
  ];
997
- var money = (n, currency) => ({ amount: n.toFixed(2), currency_code: currency });
1201
+ var money2 = (n, currency) => ({ amount: n.toFixed(2), currency_code: currency });
998
1202
  function cartesian(options) {
999
1203
  let combos = [{}];
1000
1204
  for (const opt of options) {
@@ -1024,8 +1228,8 @@ function buildProducts(currency, imageUrl = MOCK_IMAGE_URL) {
1024
1228
  sku: `${spec.handle.toUpperCase().replace(/-/g, "")}-${vi + 1}`.slice(0, 32),
1025
1229
  title,
1026
1230
  selected_options: Object.entries(opts).map(([name, value]) => ({ name, value })),
1027
- price: money(price, currency),
1028
- compare_at_price: spec.compareAt ? money(spec.compareAt + delta, currency) : null,
1231
+ price: money2(price, currency),
1232
+ compare_at_price: spec.compareAt ? money2(spec.compareAt + delta, currency) : null,
1029
1233
  available_for_sale: stock > 0,
1030
1234
  quantity_available: stock,
1031
1235
  image: firstValue ? image(imageUrl, `${spec.handle}-${firstValue}`, `${spec.title} ${firstValue}`) : null,
@@ -1050,8 +1254,8 @@ function buildProducts(currency, imageUrl = MOCK_IMAGE_URL) {
1050
1254
  images,
1051
1255
  options,
1052
1256
  variants,
1053
- price_range: { min: money(Math.min(...prices), currency), max: money(Math.max(...prices), currency) },
1054
- compare_at_price_range: compares.length ? { min: money(Math.min(...compares), currency), max: money(Math.max(...compares), currency) } : null,
1257
+ price_range: { min: money2(Math.min(...prices), currency), max: money2(Math.max(...prices), currency) },
1258
+ compare_at_price_range: compares.length ? { min: money2(Math.min(...compares), currency), max: money2(Math.max(...compares), currency) } : null,
1055
1259
  available_for_sale: variants.some((v) => v.available_for_sale),
1056
1260
  seo: { title: `${spec.title} | Mi tienda`, description: spec.description },
1057
1261
  updated_at: new Date(BASE_DATE - pi * 864e5).toISOString()
@@ -1113,7 +1317,7 @@ function buildLocations(currency) {
1113
1317
  opening_hours: { weekly: weekdays("10:00", "18:00", [1, 2, 3, 4, 5, 6]) },
1114
1318
  services: ["pickup"],
1115
1319
  contacts: contacts(null),
1116
- pickup: { enabled: true, fee: money(30, currency), prep_days: 1 }
1320
+ pickup: { enabled: true, fee: money2(30, currency), prep_days: 1 }
1117
1321
  },
1118
1322
  {
1119
1323
  id: "br_occidente",
@@ -1153,7 +1357,7 @@ function buildShop(currency, imageUrl = MOCK_IMAGE_URL) {
1153
1357
  logo: { url: imageUrl("logo", 256, 256), alt: "Mi tienda", width: 256, height: 256 },
1154
1358
  contact: { email: "hola@mi-tienda.example", phone: "+52 55 0000 0000", whatsapp: "+5215500000000" },
1155
1359
  branches: buildLocations(currency),
1156
- policies: { free_shipping_min: money(999, currency), returns_days: 30 }
1360
+ policies: { free_shipping_min: money2(999, currency), returns_days: 30 }
1157
1361
  };
1158
1362
  }
1159
1363
  function buildTheme() {
@@ -1394,8 +1598,10 @@ function createMockStorefront(options = {}) {
1394
1598
  lines: [
1395
1599
  { title: "Playera b\xE1sica", variant_title: "M / Blanco", sku: null, quantity: 1, unit: fromCents(24900, currency), total: fromCents(24900, currency), image: null }
1396
1600
  ]
1397
- }
1601
+ },
1602
+ ...buildAccountOrders(currency)
1398
1603
  ],
1604
+ pendingPayments: buildPendingPayments(currency, Date.now()),
1399
1605
  otps: /* @__PURE__ */ new Map(),
1400
1606
  passwords: /* @__PURE__ */ new Map(),
1401
1607
  pendingRegistrations: /* @__PURE__ */ new Map(),
@@ -1693,6 +1899,11 @@ function createMockStorefront(options = {}) {
1693
1899
  return inv;
1694
1900
  }
1695
1901
  const orderInvoice = (order) => {
1902
+ const doc = order.invoices?.find((d) => d.kind === "invoice");
1903
+ if (doc) {
1904
+ const { id: _id, kind: _k, total: _t, issued_at: _i, cancelled_at: _c, series: _s, folio_number: _f, related_to: _r, links_expire_at: _l, test_mode: _tm, environment: _e, ...summary } = doc;
1905
+ return { ...summary, url: null, xml_url: null };
1906
+ }
1696
1907
  const entry = state.invoices.get(order.id);
1697
1908
  const paid = order.status !== "pending_payment";
1698
1909
  if (entry) {
@@ -1853,21 +2064,21 @@ function createMockStorefront(options = {}) {
1853
2064
  if (!Array.isArray(body.lines)) throw new HttpError(400, "validation", "`lines` must be an array", "lines");
1854
2065
  const updates = body.lines;
1855
2066
  updates.forEach((u, i) => {
1856
- const line = cart.lines.find((l) => l.id === u.id);
1857
- if (!line) throw new HttpError(400, "validation", `Unknown line ${String(u.id)}`, `lines[${i}].id`);
2067
+ const line2 = cart.lines.find((l) => l.id === u.id);
2068
+ if (!line2) throw new HttpError(400, "validation", `Unknown line ${String(u.id)}`, `lines[${i}].id`);
1858
2069
  if (typeof u.quantity !== "number" || !Number.isInteger(u.quantity) || u.quantity < 0) {
1859
2070
  throw new HttpError(400, "validation", "quantity must be a non-negative integer", `lines[${i}].quantity`);
1860
2071
  }
1861
2072
  });
1862
2073
  checkStock(
1863
2074
  updates.flatMap((u, i) => {
1864
- const line = cart.lines.find((l) => l.id === u.id);
1865
- return u.quantity > 0 ? [{ variant: findVariant(line.variant_id).variant, quantity: u.quantity, lineId: line.id, field: `lines[${i}].quantity` }] : [];
2075
+ const line2 = cart.lines.find((l) => l.id === u.id);
2076
+ return u.quantity > 0 ? [{ variant: findVariant(line2.variant_id).variant, quantity: u.quantity, lineId: line2.id, field: `lines[${i}].quantity` }] : [];
1866
2077
  })
1867
2078
  );
1868
2079
  for (const u of updates) {
1869
- const line = cart.lines.find((l) => l.id === u.id);
1870
- line.quantity = u.quantity;
2080
+ const line2 = cart.lines.find((l) => l.id === u.id);
2081
+ line2.quantity = u.quantity;
1871
2082
  }
1872
2083
  cart.lines = cart.lines.filter((l) => l.quantity > 0);
1873
2084
  cart.updated_at = now();
@@ -1999,7 +2210,7 @@ function createMockStorefront(options = {}) {
1999
2210
  }
2000
2211
  syncAvailability(state.products);
2001
2212
  const orderId = nextId("ord");
2002
- const number = String(1001 + state.orders.length - 1);
2213
+ const number = String(Math.max(1e3, ...state.orders.map((o) => Number(o.number) || 0)) + 1);
2003
2214
  const customerId = cart.buyer.customer_id ?? state.customers.find((c) => c.email && c.email === cart.buyer.email)?.id ?? null;
2004
2215
  state.orders.push({
2005
2216
  id: orderId,
@@ -2280,17 +2491,34 @@ function createMockStorefront(options = {}) {
2280
2491
  }
2281
2492
  return { body: publicCustomer(c), cache: "private" };
2282
2493
  });
2283
- const stripOrder = ({ customer_id: _c, ...o }) => ({ ...o, invoice: orderInvoice(o) });
2494
+ const orderView = ({ customer_id: _c, ...o }, detail) => {
2495
+ const order = { ...o, invoice: orderInvoice(o) };
2496
+ if (order.shipments) order.shipments = order.shipments.map((s) => isTestShipment(s) ? { ...s, tracking_number: null, tracking_url: null } : { ...s, events: sortTrackingEvents(s.events) });
2497
+ order.progress = orderProgress(order);
2498
+ order.next_action = nextAction(order);
2499
+ const live = orderShipments(order).filter((s) => !isTestShipment(s));
2500
+ if (order.shipments?.length) {
2501
+ order.shipments_summary = { count: order.shipments.length, status: shipmentsStatus(order) ?? "pending", carrier: live[0]?.carrier ?? null, ...live[0]?.tracking_url ? { tracking_url: live[0].tracking_url } : {} };
2502
+ }
2503
+ if (!detail) {
2504
+ delete order.shipments;
2505
+ delete order.invoices;
2506
+ delete order.payments;
2507
+ delete order.refunds;
2508
+ delete order.shipping_address;
2509
+ }
2510
+ return order;
2511
+ };
2284
2512
  route("GET", "/customer/orders", "orders:read", ({ headers, search }) => {
2285
2513
  const c = requireCustomer(headers);
2286
- const orders = state.orders.filter((o) => o.customer_id === c.id).sort((a, b) => b.created_at.localeCompare(a.created_at)).map(stripOrder);
2514
+ const orders = state.orders.filter((o) => o.customer_id === c.id).sort((a, b) => b.created_at.localeCompare(a.created_at)).map((o) => orderView(o, false));
2287
2515
  return { body: paginate(orders, search, 20), cache: "private" };
2288
2516
  });
2289
2517
  const findOrder = (headers, id) => {
2290
2518
  const c = requireCustomer(headers);
2291
2519
  return requireNode(state.orders.find((o) => o.id === id && o.customer_id === c.id), `Order ${id}`, "id");
2292
2520
  };
2293
- route("GET", "/customer/orders/:id", "orders:read", ({ headers, params }) => ({ body: stripOrder(findOrder(headers, params[0])), cache: "private" }));
2521
+ route("GET", "/customer/orders/:id", "orders:read", ({ headers, params }) => ({ body: orderView(findOrder(headers, params[0]), true), cache: "private" }));
2294
2522
  route("POST", "/customer/orders/:id/returns", "customer:write", ({ headers, params, body }) => {
2295
2523
  const order = findOrder(headers, params[0]);
2296
2524
  if (!Array.isArray(body.lines) || body.lines.length === 0) throw new HttpError(400, "validation", "lines are required", "lines");
@@ -2307,16 +2535,37 @@ function createMockStorefront(options = {}) {
2307
2535
  state.invoices.set(order.id, { stage: "queued", request });
2308
2536
  return { status: 202, body: { status: "requested", url: null, stage: "queued", invoice: orderInvoice(order) }, cache: "private" };
2309
2537
  });
2538
+ route("GET", "/customer/pending-payments", "orders:read", ({ headers, search }) => {
2539
+ const c = requireCustomer(headers);
2540
+ const nodes = c.id === "cus_1" ? state.pendingPayments : [];
2541
+ return { body: paginate(nodes, search, 20), cache: "private" };
2542
+ });
2543
+ route("GET", "/customer/orders/:id/invoices/:invoice_id/:kind", "orders:read", ({ headers, params }) => {
2544
+ const order = findOrder(headers, params[0]);
2545
+ const doc = order.invoices?.find((d) => d.id === params[1]);
2546
+ if (!doc || params[2] !== "pdf" && params[2] !== "xml") throw new HttpError(404, "not_found", "Invoice file not found", "invoice_id");
2547
+ const pdf = params[2] === "pdf";
2548
+ const text = pdf ? `%PDF-1.4
2549
+ % mock invoice ${doc.id}
2550
+ %%EOF
2551
+ ` : `<?xml version="1.0"?><cfdi:Comprobante mock="true" id="${doc.id}"/>`;
2552
+ const file = {
2553
+ filename: `${doc.kind === "credit_note" ? "nota-de-credito" : "factura"}-${doc.id}.${params[2]}`,
2554
+ content_type: pdf ? "application/pdf" : "application/xml",
2555
+ content_base64: btoa(text)
2556
+ };
2557
+ return { body: file, headers: { "Cache-Control": "no-store" } };
2558
+ });
2310
2559
  route("GET", "/customer/addresses", "customer:read", ({ headers }) => {
2311
2560
  const c = requireCustomer(headers);
2312
2561
  return { body: { nodes: state.addresses.get(c.id) ?? [] }, cache: "private" };
2313
2562
  });
2314
2563
  route("POST", "/customer/addresses", "customer:write", ({ headers, body }) => {
2315
2564
  const c = requireCustomer(headers);
2316
- const address = { ...validateAddress(body), id: nextId("addr") };
2317
- state.addresses.set(c.id, [...state.addresses.get(c.id) ?? [], address]);
2318
- if (!c.default_address_id) c.default_address_id = address.id;
2319
- return { status: 201, body: address, cache: "private" };
2565
+ const address2 = { ...validateAddress(body), id: nextId("addr") };
2566
+ state.addresses.set(c.id, [...state.addresses.get(c.id) ?? [], address2]);
2567
+ if (!c.default_address_id) c.default_address_id = address2.id;
2568
+ return { status: 201, body: address2, cache: "private" };
2320
2569
  });
2321
2570
  route("PATCH", "/customer/addresses/:id", "customer:write", ({ headers, params, body }) => {
2322
2571
  const c = requireCustomer(headers);