@blamejs/blamejs-shop 0.3.53 → 0.3.55

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.
@@ -1,13 +1,13 @@
1
1
  {
2
- "version": "0.3.53",
2
+ "version": "0.3.55",
3
3
  "assets": {
4
4
  "css/admin.css": {
5
- "integrity": "sha384-/NizdENQkTc96romD0kmnl8220NAvW32QI0aSFuGiPbWE5lYbRxLXU5shsjlx5Ek",
6
- "fingerprinted": "css/admin.f028b2e410387546.css"
5
+ "integrity": "sha384-6k53cvkRrxMgmeStLIoLjVXZQHqIJgTmv1Izd8TYhh1HOC4POgE6GCvx1bsalyEP",
6
+ "fingerprinted": "css/admin.44eb97700c660798.css"
7
7
  },
8
8
  "css/main.css": {
9
- "integrity": "sha384-9rtEz2wKJbpfALccIipRdo/BWVQZi5O4IufqaLO/7jFyLK1xxCaDc/65Apjy8dbs",
10
- "fingerprinted": "css/main.1a1a379d6f58e2c0.css"
9
+ "integrity": "sha384-2nlE5YDlOjcnP/l1EY5kcYwvbV/TboHx+Hi6fAaDNyaau3nzQppJnNyXoLiFE8w1",
10
+ "fingerprinted": "css/main.59afbe779336867a.css"
11
11
  },
12
12
  "js/announcement.js": {
13
13
  "integrity": "sha384-z4zcEMn+tScoVnYRE4nEf8N/oyvpxdpaxTNrT4QO/jURChid4+qjAvWkzatCaAPq",
@@ -44,6 +44,10 @@
44
44
  "js/paypal-checkout.js": {
45
45
  "integrity": "sha384-LI6y/1z0Y9F8Kx8RhW4EwY2WqJPXLwJozCXqnhDT+dTckLHyvhly0SsRpH0bsdui",
46
46
  "fingerprinted": "js/paypal-checkout.b05ab5572cc3728f.js"
47
+ },
48
+ "js/saved-card.js": {
49
+ "integrity": "sha384-Kaj6n+Any4rwCH2lyREHoq30MrAZtEd/fTa+tDnIrMJ4zO01YWRhW5TTujcYyuVn",
50
+ "fingerprinted": "js/saved-card.d7fd750d746dbe4d.js"
47
51
  }
48
52
  }
49
53
  }
@@ -631,6 +631,21 @@ function create(opts) {
631
631
  return row;
632
632
  },
633
633
 
634
+ // Soft-delete a location: clears `active` and stamps `archived_at` so
635
+ // `availableLocations` filters it out while past pickup schedules still
636
+ // resolve the FK. Re-defining the same code via definePickupLocation
637
+ // clears `archived_at` (resurrects it). Returns the updated row, or null
638
+ // when the code matched no location (the route maps null → 404).
639
+ archiveLocation: async function (code) {
640
+ _code(code, "code");
641
+ var now = _now();
642
+ await query(
643
+ "UPDATE pickup_locations SET active = 0, archived_at = ?1, updated_at = ?1 WHERE code = ?2",
644
+ [now, code],
645
+ );
646
+ return await this.getLocation(code);
647
+ },
648
+
634
649
  // Read a hydrated schedule for an order. Returns null on miss.
635
650
  getScheduleByOrder: async function (orderId) {
636
651
  var id = _orderId(orderId);
@@ -678,18 +693,33 @@ function create(opts) {
678
693
  throw new TypeError("click-and-collect.customerSchedules: opts.order must be wired for " +
679
694
  "this read (the customer→order linkage lives on the order primitive)");
680
695
  }
681
- var orderPage = await orderPrim.listForCustomer(id, { limit: MAX_LIST_LIMIT });
682
- var orders = (orderPage && orderPage.rows) || [];
683
- if (!orders.length) return [];
696
+ // Page through the customer's orders via the cursor. The order
697
+ // primitive caps a single page below MAX_LIST_LIMIT, so a single
698
+ // `limit: MAX_LIST_LIMIT` call would throw — request a conservative
699
+ // per-page size and walk pages until exhausted OR we've gathered
700
+ // MAX_LIST_LIMIT order ids (which bounds the IN-list below). A customer
701
+ // with more orders than that sees their most-recent MAX_LIST_LIMIT
702
+ // orders' pickups, which matches the order-list page model.
703
+ var orderIds = [];
704
+ var cursor = null;
705
+ do {
706
+ var orderPage = await orderPrim.listForCustomer(id, { limit: 100, cursor: cursor || undefined });
707
+ var rows0 = (orderPage && orderPage.rows) || [];
708
+ for (var oi = 0; oi < rows0.length && orderIds.length < MAX_LIST_LIMIT; oi += 1) {
709
+ orderIds.push(rows0[oi].id);
710
+ }
711
+ cursor = orderPage && orderPage.next_cursor;
712
+ } while (cursor && orderIds.length < MAX_LIST_LIMIT);
713
+ if (!orderIds.length) return [];
684
714
  // SQL IN-list construction — every value is a UUID that came
685
715
  // out of the order primitive (which itself validates UUIDs at
686
716
  // write time), so the placeholder fan-out is safe. We still
687
717
  // pass them as bound params rather than interpolating.
688
718
  var placeholders = [];
689
719
  var params = [];
690
- for (var i = 0; i < orders.length; i += 1) {
720
+ for (var i = 0; i < orderIds.length; i += 1) {
691
721
  placeholders.push("?" + (i + 1));
692
- params.push(orders[i].id);
722
+ params.push(orderIds[i]);
693
723
  }
694
724
  var sql = "SELECT * FROM pickup_schedules WHERE order_id IN (" +
695
725
  placeholders.join(", ") + ") ORDER BY scheduled_window_start DESC, id DESC";
package/lib/payment.js CHANGED
@@ -43,6 +43,8 @@ var IDEMPOTENT_OPERATIONS = {
43
43
  "subscription.create": true,
44
44
  "subscription.update": true,
45
45
  "subscription.cancel": true,
46
+ "customer.create": true,
47
+ "setup_intent.create": true,
46
48
  // PayPal adapter mutating operations (Orders v2).
47
49
  "paypal_order.create": true,
48
50
  "paypal_capture.create": true,
@@ -348,6 +350,20 @@ function stripe(opts) {
348
350
  if (input.metadata) params.metadata = input.metadata;
349
351
  if (input.description) params.description = input.description;
350
352
  if (input.receipt_email) params.receipt_email = input.receipt_email;
353
+ // Saved-card reuse: a stored payment_method drives an off-session
354
+ // (one-click) charge. Both are optional so the default Elements flow
355
+ // — no payment_method, automatic_payment_methods.enabled — is
356
+ // byte-identical. When a payment_method is supplied we confirm
357
+ // immediately (confirm:true) so the stored card is charged in the
358
+ // single create call rather than requiring a second client round trip.
359
+ if (input.payment_method) {
360
+ if (typeof input.payment_method !== "string" || !input.payment_method.length) {
361
+ throw new TypeError("payment.createPaymentIntent: payment_method must be a non-empty string");
362
+ }
363
+ params.payment_method = input.payment_method;
364
+ params.confirm = true;
365
+ if (input.off_session) params.off_session = true;
366
+ }
351
367
  return _maybeIdempotent("payment_intent.create", idempotencyKey, params, function () {
352
368
  return _stripeCall(opts, "POST", "/payment_intents", params, idempotencyKey);
353
369
  });
@@ -370,6 +386,67 @@ function stripe(opts) {
370
386
  {}, idempotencyKey);
371
387
  },
372
388
 
389
+ // ---- Customer + SetupIntent (saved-card vaulting) ---------------------
390
+ //
391
+ // The card-on-file flow needs a Stripe Customer to attach the saved
392
+ // payment method to, plus a SetupIntent the browser confirms (off the
393
+ // Elements card form) to collect-without-charging. The shop persists
394
+ // only the resulting opaque `pm_…` token — never the PAN/CVV, which
395
+ // stay inside Stripe's PCI scope. `usage: "off_session"` so the saved
396
+ // card is reusable for a later one-click (off-session) charge.
397
+
398
+ // Create (or look up) a Stripe Customer for a shopper. `email` is the
399
+ // only field Stripe needs to dedupe; `metadata` carries the shop's own
400
+ // customer id so a Stripe-dashboard operator can correlate the two.
401
+ createCustomer: function (input, idempotencyKey) {
402
+ if (!input || typeof input !== "object") throw new TypeError("payment.createCustomer: input object required");
403
+ var params = {};
404
+ if (input.email != null) {
405
+ if (typeof input.email !== "string" || !input.email.length) {
406
+ throw new TypeError("payment.createCustomer: email must be a non-empty string when provided");
407
+ }
408
+ params.email = input.email;
409
+ }
410
+ if (input.name) params.name = input.name;
411
+ if (input.metadata) params.metadata = input.metadata;
412
+ return _maybeIdempotent("customer.create", idempotencyKey, params, function () {
413
+ return _stripeCall(opts, "POST", "/customers", params, idempotencyKey);
414
+ });
415
+ },
416
+
417
+ // Open a SetupIntent for the given Stripe Customer. The browser's
418
+ // Elements confirms it against the entered card; on success Stripe
419
+ // returns a `pm_…` the shop reads back via retrieveSetupIntent →
420
+ // retrievePaymentMethod and stores through paymentMethods.add.
421
+ createSetupIntent: function (input, idempotencyKey) {
422
+ if (!input || typeof input !== "object") throw new TypeError("payment.createSetupIntent: input object required");
423
+ _assertSecret(input.customer, "createSetupIntent.customer");
424
+ var params = {
425
+ customer: input.customer,
426
+ automatic_payment_methods: { enabled: true },
427
+ usage: "off_session",
428
+ };
429
+ if (input.metadata) params.metadata = input.metadata;
430
+ return _maybeIdempotent("setup_intent.create", idempotencyKey, params, function () {
431
+ return _stripeCall(opts, "POST", "/setup_intents", params, idempotencyKey);
432
+ });
433
+ },
434
+
435
+ // Read a SetupIntent back (after the browser confirmed it) to learn the
436
+ // resulting payment_method id. Read-only.
437
+ retrieveSetupIntent: function (id) {
438
+ _assertSecret(id, "setup_intent id");
439
+ return _stripeCall(opts, "GET", "/setup_intents/" + encodeURIComponent(id), null, null);
440
+ },
441
+
442
+ // Read a saved payment method's display fields (brand / last4 /
443
+ // exp_month / exp_year) so the shop can render "ending in 4242". The
444
+ // PAN/CVV are never in this payload. Read-only.
445
+ retrievePaymentMethod: function (id) {
446
+ _assertSecret(id, "payment_method id");
447
+ return _stripeCall(opts, "GET", "/payment_methods/" + encodeURIComponent(id), null, null);
448
+ },
449
+
373
450
  // Register a web domain so Stripe enables the wallet methods (Apple
374
451
  // Pay / Google Pay / Link / PayPal) for the Express Checkout Element
375
452
  // served from it. Stripe performs Apple merchant validation + hosts