@misiki/gocommerce-connector 0.2.0 → 0.3.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/index.cjs CHANGED
@@ -190,6 +190,16 @@ function toVariant(v, p, optionless) {
190
190
  metadata: v.metadata ?? null
191
191
  };
192
192
  }
193
+ function attributesOf(p) {
194
+ const answers = p.metadata?.category;
195
+ if (!answers || typeof answers !== "object" || Array.isArray(answers)) return [];
196
+ return Object.entries(answers).flatMap(([handle, raw]) => {
197
+ const values = (Array.isArray(raw) ? raw : [raw]).map((v) => String(v ?? "").trim()).filter(Boolean);
198
+ if (!values.length) return [];
199
+ const name = handle.replace(/[-_]+/g, " ").replace(/^./, (c) => c.toUpperCase());
200
+ return [{ name, value: values.join(", "), handle }];
201
+ });
202
+ }
193
203
  function toProduct(p, extra = {}) {
194
204
  const live = liveVariants(p);
195
205
  const lead = live[0] ?? arr(p.variants)[0];
@@ -255,7 +265,7 @@ function toProduct(p, extra = {}) {
255
265
  rating: null,
256
266
  ratingCount: 0,
257
267
  reviewCount: 0,
258
- attributes: [],
268
+ attributes: attributesOf(p),
259
269
  metadata: p.metadata ?? null,
260
270
  createdAt: p.created_at,
261
271
  updatedAt: p.updated_at
@@ -748,7 +758,7 @@ function storedCartId() {
748
758
  if (!v || v === "undefined" || v === "null") return null;
749
759
  return v;
750
760
  }
751
- var rememberCartId = (id) => writeLocal("cart_id", id);
761
+ var rememberCartId = (id2) => writeLocal("cart_id", id2);
752
762
  var extrasKey = (cartId) => `gocommerce_cart_${cartId}`;
753
763
  var cartExtras = (cartId) => readJSON(extrasKey(cartId)) ?? {};
754
764
  function saveCartExtras(cartId, patch) {
@@ -816,7 +826,8 @@ var BaseService = class _BaseService {
816
826
  * as what it is rather than as a JSON parse error three frames away.
817
827
  */
818
828
  async engine(path, req = {}) {
819
- const url = this.engineURL(path, req.query);
829
+ const lang = (req.method ?? "GET") === "GET" ? this.creds.language : void 0;
830
+ const url = this.engineURL(path, lang ? { lang, ...req.query } : req.query);
820
831
  const headers = { Accept: "application/json", ...req.headers };
821
832
  if (req.body !== void 0) headers["Content-Type"] = "application/json";
822
833
  if (this.creds.store) headers["X-Store"] = this.creds.store;
@@ -1099,8 +1110,11 @@ var CatalogService = class extends BaseService {
1099
1110
  sorted
1100
1111
  };
1101
1112
  }
1113
+ const attr = Object.entries(q.attributes ?? {}).flatMap(
1114
+ ([handle, values]) => values.filter(Boolean).map((v) => `${handle}:${v}`)
1115
+ );
1102
1116
  const env = await this.engine("/api/products", {
1103
- query: { q: search, page, limit, channel: this.creds.channel }
1117
+ query: { q: search, page, limit, channel: this.creds.channel, attr }
1104
1118
  });
1105
1119
  return { ...toPage(env, (p) => toProduct(p)), categoryHierarchy: hierarchy, sorted };
1106
1120
  }
@@ -1235,6 +1249,16 @@ var num = (v) => {
1235
1249
  const n = Number(v);
1236
1250
  return Number.isFinite(n) ? n : void 0;
1237
1251
  };
1252
+ function attributesFrom(p) {
1253
+ const out = {};
1254
+ for (const [key, value] of p) {
1255
+ if (!key.startsWith("attributes.")) continue;
1256
+ const handle = key.slice("attributes.".length);
1257
+ const values = value.split(",").map((v) => decodeURIComponent(v).trim()).filter(Boolean);
1258
+ if (handle && values.length) out[handle] = [...out[handle] ?? [], ...values];
1259
+ }
1260
+ return Object.keys(out).length ? out : void 0;
1261
+ }
1238
1262
  var SearchService = class extends CatalogService {
1239
1263
  emptyResult() {
1240
1264
  return {
@@ -1256,7 +1280,8 @@ var SearchService = class extends CatalogService {
1256
1280
  sort: p.get("sort") ?? void 0,
1257
1281
  category: slug || p.get("categories")?.split(",")[0] || void 0,
1258
1282
  priceFrom: num(p.get("priceFrom")) ?? num(from ?? null),
1259
- priceTo: num(p.get("priceTo")) ?? num(to ?? null)
1283
+ priceTo: num(p.get("priceTo")) ?? num(to ?? null),
1284
+ attributes: attributesFrom(p)
1260
1285
  });
1261
1286
  return {
1262
1287
  data: res.data,
@@ -1388,10 +1413,10 @@ var REMOVE = -9999999;
1388
1413
  var CartService = class extends BaseService {
1389
1414
  facts = new StoreFacts(this._fetch);
1390
1415
  async getCartByCartId(cartId) {
1391
- const id = clean(cartId);
1392
- if (!id) return emptyCart(await this.currency());
1416
+ const id2 = clean(cartId);
1417
+ if (!id2) return emptyCart(await this.currency());
1393
1418
  try {
1394
- return await this.present(await this.data(`/api/carts/${encodeURIComponent(id)}`));
1419
+ return await this.present(await this.data(`/api/carts/${encodeURIComponent(id2)}`));
1395
1420
  } catch (err) {
1396
1421
  if (err instanceof GoCommerceError && err.status === 404) return emptyCart(await this.currency());
1397
1422
  throw err;
@@ -1406,9 +1431,9 @@ var CartService = class extends BaseService {
1406
1431
  * to clear the bag on the success page.
1407
1432
  */
1408
1433
  async refereshCart() {
1409
- const id = storedCartId();
1410
- const cart = await this.getCartByCartId(id);
1411
- if (id && cart.status === "converted") forgetCartExtras(id);
1434
+ const id2 = storedCartId();
1435
+ const cart = await this.getCartByCartId(id2);
1436
+ if (id2 && cart.status === "converted") forgetCartExtras(id2);
1412
1437
  return cart;
1413
1438
  }
1414
1439
  /**
@@ -1421,21 +1446,21 @@ var CartService = class extends BaseService {
1421
1446
  qty = 1,
1422
1447
  variantId
1423
1448
  }) {
1424
- let id = clean(cartId) ?? storedCartId();
1425
- if (lineId && id) {
1426
- const cart = await this.data(`/api/carts/${encodeURIComponent(id)}`);
1449
+ let id2 = clean(cartId) ?? storedCartId();
1450
+ if (lineId && id2) {
1451
+ const cart = await this.data(`/api/carts/${encodeURIComponent(id2)}`);
1427
1452
  const line = (cart.line_items ?? []).find((l) => String(l.id) === String(lineId));
1428
1453
  if (line) {
1429
1454
  const next = qty === REMOVE ? 0 : line.quantity + Number(qty);
1430
- return this.setQuantity(id, line.id, next);
1455
+ return this.setQuantity(id2, line.id, next);
1431
1456
  }
1432
1457
  }
1433
- if (qty === REMOVE || Number(qty) <= 0) return this.getCartByCartId(id);
1458
+ if (qty === REMOVE || Number(qty) <= 0) return this.getCartByCartId(id2);
1434
1459
  if (!variantId) throw new GoCommerceError("Please choose an option before adding this to your bag.", 400);
1435
- if (!id) id = await this.open();
1460
+ if (!id2) id2 = await this.open();
1436
1461
  try {
1437
1462
  return await this.present(
1438
- await this.data(`/api/carts/${encodeURIComponent(id)}/line-items`, {
1463
+ await this.data(`/api/carts/${encodeURIComponent(id2)}/line-items`, {
1439
1464
  method: "POST",
1440
1465
  body: { variant_id: Number(variantId), quantity: Number(qty) }
1441
1466
  })
@@ -1459,15 +1484,15 @@ var CartService = class extends BaseService {
1459
1484
  lineId,
1460
1485
  qty
1461
1486
  }) {
1462
- const id = clean(cartId) ?? storedCartId();
1463
- if (!id) return emptyCart(await this.currency());
1464
- return this.setQuantity(id, lineId, Number(qty));
1487
+ const id2 = clean(cartId) ?? storedCartId();
1488
+ if (!id2) return emptyCart(await this.currency());
1489
+ return this.setQuantity(id2, lineId, Number(qty));
1465
1490
  }
1466
1491
  async removeCart({ cartId, lineId }) {
1467
- const id = clean(cartId) ?? storedCartId();
1468
- if (!id) return emptyCart(await this.currency());
1492
+ const id2 = clean(cartId) ?? storedCartId();
1493
+ if (!id2) return emptyCart(await this.currency());
1469
1494
  return this.present(
1470
- await this.data(`/api/carts/${encodeURIComponent(id)}/line-items/${encodeURIComponent(String(lineId))}`, {
1495
+ await this.data(`/api/carts/${encodeURIComponent(id2)}/line-items/${encodeURIComponent(String(lineId))}`, {
1471
1496
  method: "DELETE"
1472
1497
  })
1473
1498
  );
@@ -1478,8 +1503,8 @@ var CartService = class extends BaseService {
1478
1503
  * and the rest is kept for the checkout request.
1479
1504
  */
1480
1505
  async updateCart2(args) {
1481
- const id = clean(args.cartId) ?? storedCartId();
1482
- if (!id) return emptyCart(await this.currency());
1506
+ const id2 = clean(args.cartId) ?? storedCartId();
1507
+ if (!id2) return emptyCart(await this.currency());
1483
1508
  const patch = {};
1484
1509
  if (args.phone !== void 0) patch.phone = args.phone || void 0;
1485
1510
  if (args.shippingAddress !== void 0) {
@@ -1489,20 +1514,20 @@ var CartService = class extends BaseService {
1489
1514
  if (args.billingAddress !== void 0 || args.isBillingAddressSameAsShipping) {
1490
1515
  patch.billingAddress = args.isBillingAddressSameAsShipping ? null : args.billingAddress ?? null;
1491
1516
  }
1492
- const before = cartExtras(id);
1517
+ const before = cartExtras(id2);
1493
1518
  const country = (a) => String(a?.countryCode || a?.country || "").toUpperCase();
1494
1519
  if (args.shippingAddress !== void 0 && country(args.shippingAddress) !== country(before.shippingAddress)) {
1495
1520
  patch.shippingRateId = null;
1496
1521
  patch.shippingRate = null;
1497
1522
  }
1498
- saveCartExtras(id, patch);
1523
+ saveCartExtras(id2, patch);
1499
1524
  const email = args.email ?? (args.shippingAddress?.email ? String(args.shippingAddress.email) : void 0);
1500
1525
  if (email !== void 0) {
1501
1526
  return this.present(
1502
- await this.data(`/api/carts/${encodeURIComponent(id)}/email`, { method: "PUT", body: { email } })
1527
+ await this.data(`/api/carts/${encodeURIComponent(id2)}/email`, { method: "PUT", body: { email } })
1503
1528
  );
1504
1529
  }
1505
- return this.getCartByCartId(id);
1530
+ return this.getCartByCartId(id2);
1506
1531
  }
1507
1532
  /**
1508
1533
  * A discount code. The engine prices it against the basket now and decides
@@ -1510,34 +1535,34 @@ var CartService = class extends BaseService {
1510
1535
  * because the cart itself does not carry the code.
1511
1536
  */
1512
1537
  async applyCoupon({ cartId, couponCode }) {
1513
- const id = clean(cartId) ?? storedCartId();
1514
- if (!id) throw new GoCommerceError("Add something to your bag before applying a code.", 400);
1515
- const cart = await this.data(`/api/carts/${encodeURIComponent(id)}`);
1516
- const discount = await this.data(`/api/carts/${encodeURIComponent(id)}/discount`, {
1538
+ const id2 = clean(cartId) ?? storedCartId();
1539
+ if (!id2) throw new GoCommerceError("Add something to your bag before applying a code.", 400);
1540
+ const cart = await this.data(`/api/carts/${encodeURIComponent(id2)}`);
1541
+ const discount = await this.data(`/api/carts/${encodeURIComponent(id2)}/discount`, {
1517
1542
  method: "PUT",
1518
1543
  body: { code: String(couponCode ?? "").trim(), ...cart.email ? { email: cart.email } : {} }
1519
1544
  });
1520
- saveCartExtras(id, { discount: { ...discount, code: discount.code || String(couponCode).trim() } });
1545
+ saveCartExtras(id2, { discount: { ...discount, code: discount.code || String(couponCode).trim() } });
1521
1546
  return this.present(cart);
1522
1547
  }
1523
1548
  async removeCoupon(cartId) {
1524
- const id = clean(cartId) ?? storedCartId();
1525
- if (!id) return emptyCart(await this.currency());
1526
- await this.engine(`/api/carts/${encodeURIComponent(id)}/discount`, { method: "DELETE" });
1527
- saveCartExtras(id, { discount: null });
1528
- return this.getCartByCartId(id);
1549
+ const id2 = clean(cartId) ?? storedCartId();
1550
+ if (!id2) return emptyCart(await this.currency());
1551
+ await this.engine(`/api/carts/${encodeURIComponent(id2)}/discount`, { method: "DELETE" });
1552
+ saveCartExtras(id2, { discount: null });
1553
+ return this.getCartByCartId(id2);
1529
1554
  }
1530
1555
  /** The delivery option the shopper picked, priced from the engine's own quote. */
1531
1556
  async updateShippingRate({ cartId, shippingRateId }) {
1532
- const id = clean(cartId) ?? storedCartId();
1533
- if (!id) return emptyCart(await this.currency());
1534
- const ship = cartExtras(id).shippingAddress;
1557
+ const id2 = clean(cartId) ?? storedCartId();
1558
+ if (!id2) return emptyCart(await this.currency());
1559
+ const ship = cartExtras(id2).shippingAddress;
1535
1560
  const country = String(ship?.countryCode || ship?.country || "").toUpperCase();
1536
1561
  if (!country) throw new GoCommerceError("Add a delivery address first.", 400);
1537
- const quote2 = await this.data("/api/checkout/rates", { query: { cart: id, country, state: ship?.state ? String(ship.state) : void 0 } });
1562
+ const quote2 = await this.data("/api/checkout/rates", { query: { cart: id2, country, state: ship?.state ? String(ship.state) : void 0 } });
1538
1563
  const rate = (quote2.rates ?? []).find((r) => String(r.rate_id) === String(shippingRateId));
1539
1564
  if (!rate) throw new GoCommerceError("That delivery option is not available for this address any more.", 409);
1540
- saveCartExtras(id, {
1565
+ saveCartExtras(id2, {
1541
1566
  shippingRateId: String(rate.rate_id),
1542
1567
  shippingRate: {
1543
1568
  id: String(rate.rate_id),
@@ -1546,7 +1571,7 @@ var CartService = class extends BaseService {
1546
1571
  currency: rate.price.currency
1547
1572
  }
1548
1573
  });
1549
- return this.getCartByCartId(id);
1574
+ return this.getCartByCartId(id2);
1550
1575
  }
1551
1576
  /** The payment method is chosen at checkout, by which checkout route is called. */
1552
1577
  async updateCartPaymentMethod({ cartId }) {
@@ -1592,25 +1617,25 @@ var CartService = class extends BaseService {
1592
1617
  return { slug: p.slug, thumbnail: v?.image?.url ?? p.image_url ?? null };
1593
1618
  });
1594
1619
  }
1595
- productFor(id) {
1620
+ productFor(id2) {
1596
1621
  return remember(
1597
- `${this.engineBase()}|product|${id}`,
1598
- () => this.data(`/api/products/${id}`).catch(() => null)
1622
+ `${this.engineBase()}|product|${id2}`,
1623
+ () => this.data(`/api/products/${id2}`).catch(() => null)
1599
1624
  );
1600
1625
  }
1601
1626
  };
1602
- var clean = (id) => id && id !== "undefined" && id !== "null" ? id : null;
1627
+ var clean = (id2) => id2 && id2 !== "undefined" && id2 !== "null" ? id2 : null;
1603
1628
  var cartService = new CartService();
1604
1629
 
1605
1630
  // src/services/checkout-service.ts
1606
1631
  var CheckoutService = class extends BaseService {
1607
1632
  /** Delivery options for the address on the cart: `{ data: [{ id, name, base_rate }] }`. */
1608
1633
  async getShippingRates({ cartId } = {}) {
1609
- const id = cartId || storedCartId();
1610
- const ship = id ? cartExtras(id).shippingAddress : null;
1634
+ const id2 = cartId || storedCartId();
1635
+ const ship = id2 ? cartExtras(id2).shippingAddress : null;
1611
1636
  const country = String(ship?.countryCode || ship?.country || "").toUpperCase();
1612
- if (!id || !country) return { data: [] };
1613
- const quote2 = await this.data("/api/checkout/rates", { query: { cart: id, country, state: ship?.state ? String(ship.state) : void 0 } });
1637
+ if (!id2 || !country) return { data: [] };
1638
+ const quote2 = await this.data("/api/checkout/rates", { query: { cart: id2, country, state: ship?.state ? String(ship.state) : void 0 } });
1614
1639
  return {
1615
1640
  data: (quote2.rates ?? []).map((r) => ({
1616
1641
  id: String(r.rate_id),
@@ -1664,10 +1689,11 @@ var CheckoutService = class extends BaseService {
1664
1689
  if (!order) throw new GoCommerceError("That payment does not belong to an order placed from this browser.", 404);
1665
1690
  return { order_no: order.number, status: "processing" };
1666
1691
  }
1667
- // The family's other gateways. GoCommerce's Stripe is a Payment Element
1668
- // (a client secret, not a hosted page), and PayPal, Cashfree, PhonePe and
1669
- // Affirm are not engine gateways at all; paymentMethodService never lists
1670
- // them, so these are reached only by a storefront that hard-codes one.
1692
+ // The family's other gateway flows. GoCommerce's Stripe is a Payment
1693
+ // Element (a client secret, not a hosted page) — `checkout('stripe')` returns
1694
+ // that secret for a storefront that mounts the element. Cashfree, PhonePe and
1695
+ // Affirm are not engine gateways at all. paymentMethodService never offers
1696
+ // these codes, so they are reached only by a storefront that hard-codes one.
1671
1697
  async checkoutStripe() {
1672
1698
  throw unsupported(
1673
1699
  "checkoutStripe",
@@ -1677,11 +1703,34 @@ var CheckoutService = class extends BaseService {
1677
1703
  async checkoutStripeCapture() {
1678
1704
  throw unsupported("checkoutStripeCapture", "Stripe confirms payments to the engine by webhook");
1679
1705
  }
1680
- async checkoutPaypal() {
1681
- throw unsupported("checkoutPaypal", "the engine has no PayPal gateway");
1706
+ /**
1707
+ * The storefront's generic redirect flow, which it reaches through the code
1708
+ * `PAYPAL`. The engine has no PayPal gateway; paymentMethodService puts the
1709
+ * store's first hosted-page gateway — Adyen, Paddle, Lemon Squeezy, Creem,
1710
+ * Hyperswitch or RevenueCat — under that code, with its own name and a
1711
+ * neutral icon, because this is the one storefront flow that sends a shopper
1712
+ * to a URL and back. The shopper returns to the success page; the gateway's
1713
+ * webhook tells the engine the order is paid.
1714
+ */
1715
+ async checkoutPaypal({ cartId, origin } = {}) {
1716
+ const gateway = await this.redirectGateway();
1717
+ if (!gateway) throw unsupported("checkoutPaypal", "this store has no hosted-page payment gateway switched on");
1718
+ const id2 = cartId || storedCartId() || "";
1719
+ const back = origin ? `${origin.replace(/\/+$/, "")}/checkout/success?cart_id=${encodeURIComponent(id2)}` : void 0;
1720
+ const result = await this.place(gateway, id2, back ? { return_url: back } : {});
1721
+ const url = result.payment.client_data?.url;
1722
+ if (!url) throw new GoCommerceError("The payment page did not open. Please try again.", 502);
1723
+ return { redirect_url: url, order_no: result.order.number };
1682
1724
  }
1683
- async capturePaypalPayment() {
1684
- throw unsupported("capturePaypalPayment", "the engine has no PayPal gateway");
1725
+ /**
1726
+ * Nothing to capture: a hosted-page gateway confirms the payment to the
1727
+ * engine by webhook. Answering, rather than throwing, sends the storefront's
1728
+ * process page on to the success page instead of the failure page.
1729
+ */
1730
+ async capturePaypalPayment({
1731
+ order_no
1732
+ } = {}) {
1733
+ return { order_no: order_no ?? null, status: "processing" };
1685
1734
  }
1686
1735
  async checkoutCashfree() {
1687
1736
  throw unsupported("checkoutCashfree", "the engine has no Cashfree gateway");
@@ -1704,6 +1753,57 @@ var CheckoutService = class extends BaseService {
1704
1753
  async checkoutPOS() {
1705
1754
  throw unsupported("checkoutPOS", "point-of-sale orders are placed from the admin panel");
1706
1755
  }
1756
+ /**
1757
+ * Any payment method the engine has switched on, by its engine code —
1758
+ * `cod`, `stripe`, `razorpay`, `adyen`, `paddle`, `helcim`… For a storefront
1759
+ * that drives the payment itself: the answer says what to do next.
1760
+ *
1761
+ * - `kind: 'none'` — nothing to pay now (cash on delivery).
1762
+ * - `kind: 'client_action'` — mount the gateway's own widget with
1763
+ * `clientData`: Stripe's Payment Element takes `clientSecret`, Razorpay's
1764
+ * modal `razorpay_order_id` and `key_id`, Helcim's `checkout_token`.
1765
+ * - `kind: 'redirect'` — send the shopper to `redirectUrl`; `returnUrl` is
1766
+ * where the gateway brings them back.
1767
+ *
1768
+ * Either way the gateway's webhook tells the engine when the money arrives;
1769
+ * read `orderService.getOrder(orderNo).paymentStatus` to see it.
1770
+ */
1771
+ async checkout(code, {
1772
+ cartId,
1773
+ returnUrl,
1774
+ paymentData,
1775
+ metadata
1776
+ } = {}) {
1777
+ const result = await this.place(String(code).toLowerCase(), cartId, {
1778
+ ...returnUrl ? { return_url: returnUrl } : {},
1779
+ ...paymentData ? { payment_data: paymentData } : {},
1780
+ ...metadata ? { metadata } : {}
1781
+ });
1782
+ const data = result.payment.client_data ?? {};
1783
+ return {
1784
+ orderNo: result.order.number,
1785
+ order: toOrder(result.order),
1786
+ payment: {
1787
+ kind: result.payment.kind,
1788
+ provider: result.payment.provider,
1789
+ reference: result.payment.reference ?? null,
1790
+ clientSecret: data.client_secret ?? null,
1791
+ redirectUrl: data.url ?? null,
1792
+ clientData: data
1793
+ }
1794
+ };
1795
+ }
1796
+ /** Every payment method the engine has switched on, by engine code and name — drivable here or not. */
1797
+ async listMethods() {
1798
+ const options = await this.data(
1799
+ "/api/checkout"
1800
+ );
1801
+ return { methods: options.methods ?? [], currency: options.currency };
1802
+ }
1803
+ async redirectGateway() {
1804
+ const { methods } = await this.listMethods();
1805
+ return methods.find((m) => REDIRECT_GATEWAYS.has(m.code))?.code;
1806
+ }
1707
1807
  /**
1708
1808
  * One checkout attempt.
1709
1809
  *
@@ -1714,27 +1814,28 @@ var CheckoutService = class extends BaseService {
1714
1814
  * request — a different address, a different method — gets a new key, since
1715
1815
  * the engine refuses a key replayed with a different body.
1716
1816
  */
1717
- async place(code, cartId) {
1718
- const id = cartId || storedCartId();
1719
- if (!id) throw new GoCommerceError("Your bag is empty.", 400);
1720
- const extras = cartExtras(id);
1817
+ async place(code, cartId, extra = {}) {
1818
+ const id2 = cartId || storedCartId();
1819
+ if (!id2) throw new GoCommerceError("Your bag is empty.", 400);
1820
+ const extras = cartExtras(id2);
1721
1821
  const ship = extras.shippingAddress;
1722
1822
  if (!ship) throw new GoCommerceError("Add a delivery address first.", 400);
1723
- const cart = await this.data(`/api/carts/${encodeURIComponent(id)}`);
1823
+ const cart = await this.data(`/api/carts/${encodeURIComponent(id2)}`);
1724
1824
  const email = cart.email || (ship.email ? String(ship.email) : "");
1725
1825
  if (!email) throw new GoCommerceError("Add an email address so we can send your receipt.", 400);
1726
1826
  const address = toEngineAddress(ship);
1727
1827
  const body = {
1728
- cart_id: id,
1828
+ cart_id: id2,
1729
1829
  email,
1730
1830
  ...extras.phone || address.phone ? { phone: String(extras.phone || address.phone) } : {},
1731
1831
  ...address.name ? { name: address.name } : {},
1732
1832
  address,
1733
- ...extras.shippingRateId ? { shipping_rate_id: Number(extras.shippingRateId) } : {}
1833
+ ...extras.shippingRateId ? { shipping_rate_id: Number(extras.shippingRateId) } : {},
1834
+ ...extra
1734
1835
  };
1735
1836
  const serialised = JSON.stringify(body);
1736
1837
  const key = extras.attempt?.body === `${code}:${serialised}` ? extras.attempt.key : newKey();
1737
- saveCartExtras(id, { attempt: { key, body: `${code}:${serialised}` } });
1838
+ saveCartExtras(id2, { attempt: { key, body: `${code}:${serialised}` } });
1738
1839
  const result = await this.data(`/api/checkout/${encodeURIComponent(code)}`, {
1739
1840
  method: "POST",
1740
1841
  body,
@@ -1744,7 +1845,7 @@ var CheckoutService = class extends BaseService {
1744
1845
  rememberOrder({
1745
1846
  number: result.order.number,
1746
1847
  token: result.order.access_token,
1747
- cartId: id,
1848
+ cartId: id2,
1748
1849
  reference: result.payment.client_data?.razorpay_order_id ?? result.payment.reference
1749
1850
  });
1750
1851
  if (sessionToken()) {
@@ -1755,10 +1856,11 @@ var CheckoutService = class extends BaseService {
1755
1856
  }).catch(() => void 0);
1756
1857
  }
1757
1858
  }
1758
- saveCartExtras(id, { attempt: null });
1859
+ saveCartExtras(id2, { attempt: null });
1759
1860
  return result;
1760
1861
  }
1761
1862
  };
1863
+ var REDIRECT_GATEWAYS = /* @__PURE__ */ new Set(["adyen", "paddle", "lemonsqueezy", "creem", "hyperswitch", "revenuecat"]);
1762
1864
  var unsupported = (method, why) => new NotSupportedError("CheckoutService", method, why);
1763
1865
  function newKey() {
1764
1866
  const c = globalThis.crypto;
@@ -1778,23 +1880,25 @@ var PaymentMethodService = class extends BaseService {
1778
1880
  async list(_args = {}) {
1779
1881
  const options = await this.facts.checkoutOptions();
1780
1882
  const methods = options.methods ?? [];
1883
+ const redirect = methods.find((m) => REDIRECT_GATEWAYS.has(m.code))?.code;
1884
+ const drive = (code) => DRIVABLE[code] ?? (code === redirect ? { code: "PAYPAL", img: "/payment/card.svg" } : void 0);
1781
1885
  const data = methods.filter((m) => {
1782
- if (DRIVABLE[m.code]) return true;
1886
+ if (drive(m.code)) return true;
1783
1887
  if (!reported2.has(m.code)) {
1784
1888
  reported2.add(m.code);
1785
1889
  console.warn(
1786
- `[gocommerce] payment method "${m.code}" is switched on but this storefront cannot drive its flow, so it is not offered.`
1890
+ `[gocommerce] payment method "${m.code}" is switched on but this storefront cannot drive its flow, so it is not offered here; checkoutService.checkout('${m.code}') still reaches it.`
1787
1891
  );
1788
1892
  }
1789
1893
  return false;
1790
1894
  }).map((m, i) => ({
1791
1895
  id: m.code,
1792
- code: DRIVABLE[m.code].code,
1896
+ code: drive(m.code).code,
1793
1897
  engineCode: m.code,
1794
1898
  name: m.name,
1795
1899
  title: m.name,
1796
1900
  description: "",
1797
- img: DRIVABLE[m.code].img ?? null,
1901
+ img: drive(m.code).img ?? null,
1798
1902
  rank: i,
1799
1903
  active: true,
1800
1904
  isActive: true
@@ -1965,6 +2069,42 @@ var UserService = class extends BaseService {
1965
2069
  startSession(auth);
1966
2070
  return toUser(auth.record);
1967
2071
  }
2072
+ /**
2073
+ * Mails the signed-in shopper a link to confirm their address. The engine
2074
+ * sends none at signup, and a confirmed address is what customer-group
2075
+ * prices are keyed on. 409 once confirmed; 429 within a minute of the last.
2076
+ */
2077
+ async requestEmailVerification() {
2078
+ return this.account("/x/identity/me/email-verification", { method: "POST", auth: true });
2079
+ }
2080
+ /** Extends the session another thirty days. The token stays the same. */
2081
+ async refreshSession() {
2082
+ return startSession(await this.account("/x/identity/refresh", { method: "POST", auth: true }));
2083
+ }
2084
+ /**
2085
+ * Files a guest order under the signed-in account, so it appears in their
2086
+ * order history. The order's access token is the proof of ownership: pass
2087
+ * it, or leave it out for an order placed from this browser, whose token was
2088
+ * kept at checkout.
2089
+ *
2090
+ * Never done automatically at sign-in. On a shared computer that would file
2091
+ * one person's orders — address and all — under whoever signed in next.
2092
+ */
2093
+ async claimOrder({ number, token }) {
2094
+ const proof = token ?? placedOrder(number)?.token;
2095
+ if (!proof) {
2096
+ throw new GoCommerceError(
2097
+ "This order was not placed from this browser; enter the code from its confirmation.",
2098
+ 400
2099
+ );
2100
+ }
2101
+ const order = await this.account("/x/identity/me/orders", {
2102
+ method: "POST",
2103
+ auth: true,
2104
+ body: { number: String(number), token: proof }
2105
+ });
2106
+ return toOrder(order);
2107
+ }
1968
2108
  async deleteUser() {
1969
2109
  throw new NotSupportedError("UserService", "deleteUser", "an account is closed by the store, on request");
1970
2110
  }
@@ -2062,16 +2202,16 @@ var AddressService = class extends BaseService {
2062
2202
  page: env.meta?.page ?? page
2063
2203
  };
2064
2204
  }
2065
- async fetchAddress(id) {
2205
+ async fetchAddress(id2) {
2066
2206
  return fromIdentityAddress(
2067
- await this.data(`/x/identity/me/addresses/${enc(id)}`, { auth: true }),
2207
+ await this.data(`/x/identity/me/addresses/${enc(id2)}`, { auth: true }),
2068
2208
  currentMe()?.email
2069
2209
  );
2070
2210
  }
2071
2211
  /** New when it has no id (or the form's `'new'`), an edit when it has one — the storefront saves both here. */
2072
2212
  async saveAddress(address) {
2073
- const id = address?.id && address.id !== "new" ? String(address.id) : null;
2074
- if (id) return this.editAddress(id, address);
2213
+ const id2 = address?.id && address.id !== "new" ? String(address.id) : null;
2214
+ if (id2) return this.editAddress(id2, address);
2075
2215
  const a = await this.data("/x/identity/me/addresses", {
2076
2216
  method: "POST",
2077
2217
  auth: true,
@@ -2079,20 +2219,20 @@ var AddressService = class extends BaseService {
2079
2219
  });
2080
2220
  return fromIdentityAddress(a, currentMe()?.email);
2081
2221
  }
2082
- async editAddress(id, address) {
2083
- const a = await this.data(`/x/identity/me/addresses/${enc(id)}`, {
2222
+ async editAddress(id2, address) {
2223
+ const a = await this.data(`/x/identity/me/addresses/${enc(id2)}`, {
2084
2224
  method: "PATCH",
2085
2225
  auth: true,
2086
2226
  body: toIdentityAddress(address)
2087
2227
  });
2088
2228
  return fromIdentityAddress(a, currentMe()?.email);
2089
2229
  }
2090
- async deleteAddress(id) {
2091
- await this.engine(`/x/identity/me/addresses/${enc(id)}`, { method: "DELETE", auth: true });
2092
- return { id: String(id), deleted: true };
2230
+ async deleteAddress(id2) {
2231
+ await this.engine(`/x/identity/me/addresses/${enc(id2)}`, { method: "DELETE", auth: true });
2232
+ return { id: String(id2), deleted: true };
2093
2233
  }
2094
2234
  };
2095
- var enc = (id) => encodeURIComponent(String(id));
2235
+ var enc = (id2) => encodeURIComponent(String(id2));
2096
2236
  var addressService = new AddressService();
2097
2237
 
2098
2238
  // src/services/wishlist-service.ts
@@ -2144,6 +2284,18 @@ var WishlistService = class extends BaseService {
2144
2284
  const saved = new Set((list?.items ?? []).map((i) => String(i.product_id)));
2145
2285
  return (pairs ?? []).map((p) => ({ ...p, exists: saved.has(String(p.productId)) }));
2146
2286
  }
2287
+ /**
2288
+ * The address the store may write to when a saved product is back in stock
2289
+ * or on sale; `''` takes it off. A list that does not exist yet is made.
2290
+ */
2291
+ async setEmail(email) {
2292
+ const list = await this.current() ?? await this.create();
2293
+ await this.data(`/x/wishlist/${enc2(list.token)}`, {
2294
+ method: "PATCH",
2295
+ body: { email: String(email ?? "").trim() }
2296
+ });
2297
+ return { email: String(email ?? "").trim() || null };
2298
+ }
2147
2299
  async current() {
2148
2300
  const token = wishlistToken();
2149
2301
  if (!token) return null;
@@ -2226,6 +2378,15 @@ var MenuService = class extends BaseService {
2226
2378
  if (menus) return { data: menus };
2227
2379
  return resolveRestLocally("get", "/api/menu");
2228
2380
  }
2381
+ /** Every menu the store keeps, by handle — not only the header and footer. */
2382
+ async listMenus() {
2383
+ return await this.data("/x/navigation/menus") ?? [];
2384
+ }
2385
+ /** One menu by handle, in the storefront's shape: `{ menuId, name, items: [{ id, name, link, items }] }`. */
2386
+ async getMenu(handle) {
2387
+ const m = await this.data(`/x/navigation/menus/${encodeURIComponent(handle)}`);
2388
+ return { menuId: m.handle, name: m.title, items: (m.items ?? []).map(toItem) };
2389
+ }
2229
2390
  /** The header and footer menus, or `null` when the module is not there. Cached: a menu is store-level. */
2230
2391
  async engineMenus() {
2231
2392
  if (!await this.facts.pluginOn("navigation")) return null;
@@ -2244,6 +2405,24 @@ var MenuService = class extends BaseService {
2244
2405
  };
2245
2406
  var menuService = new MenuService();
2246
2407
 
2408
+ // src/services/newsletter-service.ts
2409
+ var NewsletterService = class extends BaseService {
2410
+ async subscribe({ email, name, source }) {
2411
+ return this.data("/x/newsletter/subscribe", {
2412
+ method: "POST",
2413
+ body: { email: String(email ?? "").trim(), ...name ? { name } : {}, ...source ? { source } : {} }
2414
+ });
2415
+ }
2416
+ /** The token from the unsubscribe link in a newsletter email. */
2417
+ async unsubscribe(token) {
2418
+ return this.data("/x/newsletter/unsubscribe", {
2419
+ method: "POST",
2420
+ body: { token: String(token ?? "") }
2421
+ });
2422
+ }
2423
+ };
2424
+ var newsletterService = new NewsletterService();
2425
+
2247
2426
  // src/services/store-service.ts
2248
2427
  function symbolFor(code) {
2249
2428
  try {
@@ -2277,6 +2456,22 @@ var StoreService = class extends BaseService {
2277
2456
  }
2278
2457
  return super.get(path);
2279
2458
  }
2459
+ /**
2460
+ * The storefront's newsletter box posts `{ email }` to Litekart's
2461
+ * `/api/newsletter/subscribe` through this verb; ext/newsletter is where
2462
+ * that list lives here.
2463
+ */
2464
+ async post(path, data) {
2465
+ if (path.split("?")[0] === "/api/newsletter/subscribe") {
2466
+ const { email, name } = data ?? {};
2467
+ return await new NewsletterService(this._fetch).subscribe({
2468
+ email: email ?? "",
2469
+ name,
2470
+ source: "storefront"
2471
+ });
2472
+ }
2473
+ return super.post(path, data);
2474
+ }
2280
2475
  async live() {
2281
2476
  const [ready, plugins, menus, base] = await Promise.all([
2282
2477
  this.facts.ready(),
@@ -2582,9 +2777,304 @@ var varniCustomProductService = new VarniCustomProductService();
2582
2777
  var pluginsService = pluginService;
2583
2778
  var settingsService = settingService;
2584
2779
 
2780
+ // src/services/variant-service.ts
2781
+ var VariantService = class extends BaseService {
2782
+ /** A product's variants, inactive ones included (filter on `active`), in the merchant's order. */
2783
+ async list(productId, { page = 1, limit = 50 } = {}) {
2784
+ const env = await this.engine("/api/variants", { query: { product_id: productId, page, limit } });
2785
+ return { data: (env.data ?? []).map(withAmounts), meta: env.meta };
2786
+ }
2787
+ async getOne(id2) {
2788
+ return withAmounts(await this.data(`/api/variants/${encodeURIComponent(String(id2))}`));
2789
+ }
2790
+ /**
2791
+ * What each approved seller asks for this variant, cheapest first. A cart
2792
+ * line takes a variant, not an offer, so this informs a shopper rather than
2793
+ * choosing who fulfils.
2794
+ */
2795
+ async offers(variantId) {
2796
+ const offers = await this.data(`/api/variants/${encodeURIComponent(String(variantId))}/offers`) ?? [];
2797
+ return offers.map((o) => ({ ...o, price: { ...o.price, amount: toMajor(o.price) } }));
2798
+ }
2799
+ };
2800
+ function withAmounts(v) {
2801
+ return {
2802
+ ...v,
2803
+ price: { ...v.price, amount: toMajor(v.price) },
2804
+ compare_at_price: v.compare_at_price ? { ...v.compare_at_price, amount: toMajor(v.compare_at_price) } : void 0
2805
+ };
2806
+ }
2807
+ var variantService = new VariantService();
2808
+
2809
+ // src/services/b2b-service.ts
2810
+ var B2bService = class extends BaseService {
2811
+ // ------------------------------------------------------------ company
2812
+ /** The company, the caller's role in it, and its credit: limit, outstanding, available, overdue. */
2813
+ async me() {
2814
+ const me = await this.data("/x/b2b/me", { auth: true });
2815
+ const { notes: _notes, metadata: _metadata, ...company } = me.company;
2816
+ return { company, role: me.role, credit: me.credit };
2817
+ }
2818
+ async members() {
2819
+ return await this.data("/x/b2b/members", { auth: true }) ?? [];
2820
+ }
2821
+ /** Admin only. Demoting the last admin is refused (409). */
2822
+ async setMemberRole(customerId, role) {
2823
+ return this.data(`/x/b2b/members/${id(customerId)}`, { method: "PATCH", auth: true, body: { role } });
2824
+ }
2825
+ /** An admin removes anyone; any member may remove themselves. */
2826
+ async removeMember(customerId) {
2827
+ await this.engine(`/x/b2b/members/${id(customerId)}`, { method: "DELETE", auth: true });
2828
+ }
2829
+ // ------------------------------------------------------------ invitations
2830
+ /** Admin only. Open invitations: not accepted, not expired. */
2831
+ async invitations() {
2832
+ return await this.data("/x/b2b/invitations", { auth: true }) ?? [];
2833
+ }
2834
+ /** Admin only. Emails a link; a second invitation to the same address replaces the first. */
2835
+ async invite({ email, role }) {
2836
+ return this.data("/x/b2b/invitations", {
2837
+ method: "POST",
2838
+ auth: true,
2839
+ body: { email: String(email ?? "").trim(), role }
2840
+ });
2841
+ }
2842
+ async withdrawInvitation(invitationId) {
2843
+ await this.engine(`/x/b2b/invitations/${id(invitationId)}`, { method: "DELETE", auth: true });
2844
+ }
2845
+ /**
2846
+ * The token from the invitation email, accepted by the account it was sent
2847
+ * to — which also confirms that account's email address.
2848
+ */
2849
+ async acceptInvitation(token) {
2850
+ return this.data("/x/b2b/invitations/accept", {
2851
+ method: "POST",
2852
+ auth: true,
2853
+ body: { token: String(token ?? "").trim() }
2854
+ });
2855
+ }
2856
+ // ------------------------------------------------------------ filling a basket
2857
+ /**
2858
+ * A quick order: up to 500 lines by SKU or variant id, into the bag (or a new
2859
+ * one), at the company's prices. A line that will not go in is reported in
2860
+ * `rejected` with its reason; the rest are added.
2861
+ */
2862
+ async addLines({
2863
+ cartId,
2864
+ lines
2865
+ }) {
2866
+ const res = await this.data("/x/b2b/cart/lines", {
2867
+ method: "POST",
2868
+ auth: true,
2869
+ body: {
2870
+ ...cartId ?? storedCartId() ? { cart_id: cartId ?? storedCartId() } : {},
2871
+ lines: (lines ?? []).map((l) => ({
2872
+ ...l.variantId ? { variant_id: Number(l.variantId) } : {},
2873
+ ...l.sku ? { sku: String(l.sku).trim() } : {},
2874
+ quantity: Number(l.quantity)
2875
+ }))
2876
+ }
2877
+ });
2878
+ rememberCartId(res.cart.id);
2879
+ return { cart: toCart(res.cart, cartExtras(res.cart.id)), rejected: res.rejected ?? [] };
2880
+ }
2881
+ /** A past order's lines in a new bag, at today's prices. Nothing is placed. */
2882
+ async reorder(orderId) {
2883
+ const res = await this.data(
2884
+ `/x/b2b/orders/${id(orderId)}/reorder`,
2885
+ {
2886
+ method: "POST",
2887
+ auth: true
2888
+ }
2889
+ );
2890
+ rememberCartId(res.cart.id);
2891
+ return { cart: toCart(res.cart, cartExtras(res.cart.id)), rejected: res.rejected ?? [] };
2892
+ }
2893
+ // ------------------------------------------------------------ placing an order
2894
+ /**
2895
+ * Places the bag through the company: on account by default, with a PO
2896
+ * number, optionally only some lines (`lineIds`). A buyer over the company's
2897
+ * approval threshold gets `{ kind: 'approval' }` instead of an order — the
2898
+ * bag stays as it was, and an approver places it later.
2899
+ *
2900
+ * Over the credit limit is 403 `credit_limit_exceeded`; `me().credit` says
2901
+ * how much is available before the shopper tries.
2902
+ */
2903
+ async checkout({
2904
+ cartId,
2905
+ lineIds,
2906
+ shippingRateId,
2907
+ returnUrl,
2908
+ paymentData,
2909
+ ...opts
2910
+ } = {}) {
2911
+ const cart = cartId || storedCartId();
2912
+ if (!cart) throw new GoCommerceError("Your bag is empty.", 400);
2913
+ const extras = cartExtras(cart);
2914
+ const rate = shippingRateId ?? extras.shippingRateId;
2915
+ const body = {
2916
+ cart_id: cart,
2917
+ ...this.placeBody(opts, extras.shippingAddress ?? void 0),
2918
+ ...rate ? { shipping_rate_id: Number(rate) } : {},
2919
+ ...lineIds?.length ? { line_ids: lineIds.map(Number) } : {},
2920
+ ...returnUrl ? { return_url: returnUrl } : {},
2921
+ ...paymentData ? { payment_data: paymentData } : {}
2922
+ };
2923
+ const res = await this.data("/x/b2b/checkout", {
2924
+ method: "POST",
2925
+ auth: true,
2926
+ body,
2927
+ headers: { "Idempotency-Key": attemptKey(`b2b:${JSON.stringify(body)}`) }
2928
+ });
2929
+ return this.placed(res, cart);
2930
+ }
2931
+ // ------------------------------------------------------------ the company's orders
2932
+ /** Admins and approvers see the company's; a buyer sees their own. `q` matches the number, PO or email. */
2933
+ async orders({
2934
+ page = 1,
2935
+ limit = 20,
2936
+ q,
2937
+ overdue
2938
+ } = {}) {
2939
+ return this.engine("/x/b2b/orders", {
2940
+ auth: true,
2941
+ query: { page, limit, q, ...overdue ? { overdue: "true" } : {} }
2942
+ });
2943
+ }
2944
+ // ------------------------------------------------------------ approvals
2945
+ async approvals({
2946
+ page = 1,
2947
+ limit = 20,
2948
+ status
2949
+ } = {}) {
2950
+ return this.engine("/x/b2b/approvals", { auth: true, query: { page, limit, status } });
2951
+ }
2952
+ async approval(approvalId) {
2953
+ return this.data(`/x/b2b/approvals/${id(approvalId)}`, { auth: true });
2954
+ }
2955
+ /**
2956
+ * Admin or approver: places the requested order at the prices it was
2957
+ * requested at. Credit limits and holds still apply. The payment intent is
2958
+ * the approver's to act on — for an account order there is nothing to do.
2959
+ */
2960
+ async approve(approvalId) {
2961
+ const res = await this.data(`/x/b2b/approvals/${id(approvalId)}/approve`, {
2962
+ method: "POST",
2963
+ auth: true
2964
+ });
2965
+ return { approval: res.approval, orderNo: res.order.number, order: toOrder(res.order), payment: res.payment };
2966
+ }
2967
+ async reject(approvalId, reason) {
2968
+ return this.data(`/x/b2b/approvals/${id(approvalId)}/reject`, {
2969
+ method: "POST",
2970
+ auth: true,
2971
+ ...reason ? { body: { reason } } : {}
2972
+ });
2973
+ }
2974
+ /** The buyer who asked withdraws the request. */
2975
+ async cancelApproval(approvalId) {
2976
+ return this.data(`/x/b2b/approvals/${id(approvalId)}/cancel`, { method: "POST", auth: true });
2977
+ }
2978
+ // ------------------------------------------------------------ quotes
2979
+ async quotes({ page = 1, limit = 20, status } = {}) {
2980
+ return this.engine("/x/b2b/quotes", { auth: true, query: { page, limit, status } });
2981
+ }
2982
+ async quote(quoteId) {
2983
+ return this.data(`/x/b2b/quotes/${id(quoteId)}`, { auth: true });
2984
+ }
2985
+ /** Asks the store to price these lines. The store answers with unit prices and an expiry. */
2986
+ async requestQuote({ lines, note }) {
2987
+ return this.data("/x/b2b/quotes", {
2988
+ method: "POST",
2989
+ auth: true,
2990
+ body: {
2991
+ lines: (lines ?? []).map((l) => ({ variant_id: Number(l.variantId), quantity: Number(l.quantity) })),
2992
+ ...note ? { note } : {}
2993
+ }
2994
+ });
2995
+ }
2996
+ /** Places the quote at its agreed prices — or, for a buyer over the threshold, asks for approval. */
2997
+ async acceptQuote(quoteId, opts = {}) {
2998
+ const res = await this.data(`/x/b2b/quotes/${id(quoteId)}/accept`, {
2999
+ method: "POST",
3000
+ auth: true,
3001
+ body: this.placeBody(opts)
3002
+ });
3003
+ return this.placed(res);
3004
+ }
3005
+ async declineQuote(quoteId) {
3006
+ return this.data(`/x/b2b/quotes/${id(quoteId)}/decline`, { method: "POST", auth: true });
3007
+ }
3008
+ // ------------------------------------------------------------ dealer leads
3009
+ /**
3010
+ * The public "find a dealer" form: no account needed. The engine routes it to
3011
+ * the dealer whose territory covers the address — or keeps it — and answers
3012
+ * the same either way, so the form never reveals who got it.
3013
+ */
3014
+ async submitLead(lead) {
3015
+ return this.data("/x/b2b/leads", {
3016
+ method: "POST",
3017
+ body: {
3018
+ ...lead.name ? { name: lead.name } : {},
3019
+ ...lead.email ? { email: lead.email } : {},
3020
+ ...lead.phone ? { phone: lead.phone } : {},
3021
+ ...lead.message ? { message: lead.message } : {},
3022
+ ...lead.country ? { country: lead.country } : {},
3023
+ ...lead.state ? { state: lead.state } : {},
3024
+ ...lead.postalCode ? { postal_code: lead.postalCode } : {},
3025
+ ...lead.productId ? { product_id: Number(lead.productId) } : {},
3026
+ ...lead.variantId ? { variant_id: Number(lead.variantId) } : {},
3027
+ ...lead.source ? { source: lead.source } : {}
3028
+ }
3029
+ });
3030
+ }
3031
+ /** A dealer's admins and approvers: the leads routed to their company. */
3032
+ async leads({ page = 1, limit = 20, status } = {}) {
3033
+ return this.engine("/x/b2b/leads", { auth: true, query: { page, limit, status } });
3034
+ }
3035
+ async setLeadStatus(leadId, status) {
3036
+ return this.data(`/x/b2b/leads/${id(leadId)}`, { method: "PATCH", auth: true, body: { status } });
3037
+ }
3038
+ // ------------------------------------------------------------ internals
3039
+ /** The fields checkout and quote acceptance share. Only declared ones: the engine refuses the rest. */
3040
+ placeBody(opts, fallback) {
3041
+ const raw = opts.address ?? fallback;
3042
+ if (!raw) throw new GoCommerceError("Add a delivery address first.", 400);
3043
+ const address = "line1" in raw ? raw : toEngineAddress(raw);
3044
+ return {
3045
+ address,
3046
+ ...opts.paymentMethod ? { payment_method: String(opts.paymentMethod).toLowerCase() } : {},
3047
+ ...opts.poNumber ? { po_number: String(opts.poNumber).trim() } : {},
3048
+ ...opts.name ?? address.name ? { name: opts.name ?? address.name } : {},
3049
+ ...opts.phone ?? address.phone ? { phone: opts.phone ?? address.phone } : {}
3050
+ };
3051
+ }
3052
+ placed(res, cartId) {
3053
+ if (res.approval && !res.order) return { kind: "approval", approval: res.approval };
3054
+ const order = res.order;
3055
+ if (order.access_token && cartId) {
3056
+ rememberOrder({ number: order.number, token: order.access_token, cartId, reference: res.payment?.reference });
3057
+ }
3058
+ return { kind: "order", orderNo: order.number, order: toOrder(order), payment: res.payment };
3059
+ }
3060
+ };
3061
+ var id = (v) => encodeURIComponent(String(v));
3062
+ var keys = /* @__PURE__ */ new Map();
3063
+ function attemptKey(request) {
3064
+ let key = keys.get(request);
3065
+ if (!key) {
3066
+ const c = globalThis.crypto;
3067
+ key = c?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
3068
+ keys.set(request, key);
3069
+ }
3070
+ return key;
3071
+ }
3072
+ var b2bService = new B2bService();
3073
+
2585
3074
  exports.AddressService = AddressService;
2586
3075
  exports.AuthService = AuthService;
2587
3076
  exports.AutocompleteService = AutocompleteService;
3077
+ exports.B2bService = B2bService;
2588
3078
  exports.BannerService = BannerService;
2589
3079
  exports.BaseService = BaseService;
2590
3080
  exports.BlogService = BlogService;
@@ -2609,6 +3099,7 @@ exports.HomeService = HomeService;
2609
3099
  exports.InitService = InitService;
2610
3100
  exports.MeilisearchService = MeilisearchService;
2611
3101
  exports.MenuService = MenuService;
3102
+ exports.NewsletterService = NewsletterService;
2612
3103
  exports.NotSupportedError = NotSupportedError;
2613
3104
  exports.OrderService = OrderService;
2614
3105
  exports.PageService = PageService;
@@ -2619,6 +3110,7 @@ exports.PopularSearchService = PopularSearchService;
2619
3110
  exports.PopularityService = PopularityService;
2620
3111
  exports.ProductService = ProductService;
2621
3112
  exports.ProfileService = ProfileService;
3113
+ exports.REDIRECT_GATEWAYS = REDIRECT_GATEWAYS;
2622
3114
  exports.ReelsService = ReelsService;
2623
3115
  exports.RegionService = RegionService;
2624
3116
  exports.ReviewService = ReviewService;
@@ -2629,6 +3121,7 @@ exports.StateService = StateService;
2629
3121
  exports.StoreService = StoreService;
2630
3122
  exports.UploadService = UploadService;
2631
3123
  exports.UserService = UserService;
3124
+ exports.VariantService = VariantService;
2632
3125
  exports.VarniCustomDesignService = VarniCustomDesignService;
2633
3126
  exports.VarniCustomProductService = VarniCustomProductService;
2634
3127
  exports.VendorService = VendorService;
@@ -2637,6 +3130,7 @@ exports.WishlistService = WishlistService;
2637
3130
  exports.addressService = addressService;
2638
3131
  exports.authService = authService;
2639
3132
  exports.autocompleteService = autocompleteService;
3133
+ exports.b2bService = b2bService;
2640
3134
  exports.bannerService = bannerService;
2641
3135
  exports.blogService = blogService;
2642
3136
  exports.cartService = cartService;
@@ -2668,6 +3162,7 @@ exports.initService = initService;
2668
3162
  exports.meilisearchService = meilisearchService;
2669
3163
  exports.menuService = menuService;
2670
3164
  exports.minorToMajor = minorToMajor;
3165
+ exports.newsletterService = newsletterService;
2671
3166
  exports.orderService = orderService;
2672
3167
  exports.pageService = pageService;
2673
3168
  exports.paymentMethodService = paymentMethodService;
@@ -2710,6 +3205,7 @@ exports.toVariant = toVariant;
2710
3205
  exports.toVendor = toVendor;
2711
3206
  exports.uploadService = uploadService;
2712
3207
  exports.userService = userService;
3208
+ exports.variantService = variantService;
2713
3209
  exports.varniCustomDesignService = varniCustomDesignService;
2714
3210
  exports.varniCustomProductService = varniCustomProductService;
2715
3211
  exports.vendorService = vendorService;