@aglyn/plugins-commerce 1.0.0-beta.231 → 1.0.0-beta.233

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.
Files changed (266) hide show
  1. package/package.json +13 -13
  2. package/src/lib/components/cart-extras.d.ts +6 -0
  3. package/src/lib/components/cart-extras.js +4 -1
  4. package/src/lib/components/cart-extras.js.map +1 -1
  5. package/src/lib/components/cart-fulfillment.d.ts +117 -0
  6. package/src/lib/components/cart-fulfillment.js +367 -0
  7. package/src/lib/components/cart-fulfillment.js.map +1 -0
  8. package/src/lib/components/cart.js +52 -10
  9. package/src/lib/components/cart.js.map +1 -1
  10. package/src/lib/components/commerce-console-page.js +42 -1
  11. package/src/lib/components/commerce-console-page.js.map +1 -1
  12. package/src/lib/components/console/catalog-organization-card.component.js +1 -0
  13. package/src/lib/components/console/catalog-organization-card.component.js.map +1 -1
  14. package/src/lib/components/console/host-orders-card.component.js +42 -11
  15. package/src/lib/components/console/host-orders-card.component.js.map +1 -1
  16. package/src/lib/components/console/local-delivery-settings-card.component.d.ts +37 -0
  17. package/src/lib/components/console/local-delivery-settings-card.component.js +542 -0
  18. package/src/lib/components/console/local-delivery-settings-card.component.js.map +1 -0
  19. package/src/lib/components/console/locations-card.component.js +177 -3
  20. package/src/lib/components/console/locations-card.component.js.map +1 -1
  21. package/src/lib/components/console/order-detail-dialog.component.js +10 -0
  22. package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
  23. package/src/lib/components/console/order-local-fulfillment-panel.component.d.ts +44 -0
  24. package/src/lib/components/console/order-local-fulfillment-panel.component.js +265 -0
  25. package/src/lib/components/console/order-local-fulfillment-panel.component.js.map +1 -0
  26. package/src/lib/components/console/order-offline-notice.component.d.ts +12 -0
  27. package/src/lib/components/console/order-offline-notice.component.js +53 -0
  28. package/src/lib/components/console/order-offline-notice.component.js.map +1 -0
  29. package/src/lib/components/console/pickup-delivery-queue-card.component.d.ts +35 -0
  30. package/src/lib/components/console/pickup-delivery-queue-card.component.js +222 -0
  31. package/src/lib/components/console/pickup-delivery-queue-card.component.js.map +1 -0
  32. package/src/lib/components/console/pos/pos-api.d.ts +24 -1
  33. package/src/lib/components/console/pos/pos-api.js +13 -0
  34. package/src/lib/components/console/pos/pos-api.js.map +1 -1
  35. package/src/lib/components/console/pos/pos-item-dialog.component.d.ts +12 -0
  36. package/src/lib/components/console/pos/pos-item-dialog.component.js +10 -8
  37. package/src/lib/components/console/pos/pos-item-dialog.component.js.map +1 -1
  38. package/src/lib/components/console/pos/pos-kiosk-queue.component.d.ts +22 -0
  39. package/src/lib/components/console/pos/pos-kiosk-queue.component.js +168 -0
  40. package/src/lib/components/console/pos/pos-kiosk-queue.component.js.map +1 -0
  41. package/src/lib/components/console/pos/pos-tender-dialogs.component.d.ts +18 -0
  42. package/src/lib/components/console/pos/pos-tender-dialogs.component.js +116 -1
  43. package/src/lib/components/console/pos/pos-tender-dialogs.component.js.map +1 -1
  44. package/src/lib/components/console/pos/pos-tender-panel.component.d.ts +8 -0
  45. package/src/lib/components/console/pos/pos-tender-panel.component.js +77 -18
  46. package/src/lib/components/console/pos/pos-tender-panel.component.js.map +1 -1
  47. package/src/lib/components/console/pos/use-pos-display.d.ts +5 -2
  48. package/src/lib/components/console/pos/use-pos-display.js +3 -2
  49. package/src/lib/components/console/pos/use-pos-display.js.map +1 -1
  50. package/src/lib/components/console/pos-devices-card.component.js +2 -2
  51. package/src/lib/components/console/pos-devices-card.component.js.map +1 -1
  52. package/src/lib/components/console/pos-display/pairing-screen.d.ts +5 -2
  53. package/src/lib/components/console/pos-display/pairing-screen.js +7 -5
  54. package/src/lib/components/console/pos-display/pairing-screen.js.map +1 -1
  55. package/src/lib/components/console/pos-display/pos-display-api.d.ts +7 -1
  56. package/src/lib/components/console/pos-display/pos-display-api.js +7 -2
  57. package/src/lib/components/console/pos-display/pos-display-api.js.map +1 -1
  58. package/src/lib/components/console/pos-kiosk/kiosk-checkout.d.ts +41 -0
  59. package/src/lib/components/console/pos-kiosk/kiosk-checkout.js +275 -0
  60. package/src/lib/components/console/pos-kiosk/kiosk-checkout.js.map +1 -0
  61. package/src/lib/components/console/pos-kiosk/kiosk-menu.d.ts +32 -0
  62. package/src/lib/components/console/pos-kiosk/kiosk-menu.js +344 -0
  63. package/src/lib/components/console/pos-kiosk/kiosk-menu.js.map +1 -0
  64. package/src/lib/components/console/pos-kiosk/kiosk-staff.d.ts +12 -0
  65. package/src/lib/components/console/pos-kiosk/kiosk-staff.js +257 -0
  66. package/src/lib/components/console/pos-kiosk/kiosk-staff.js.map +1 -0
  67. package/src/lib/components/console/pos-kiosk/pos-kiosk-api.d.ts +114 -0
  68. package/src/lib/components/console/pos-kiosk/pos-kiosk-api.js +189 -0
  69. package/src/lib/components/console/pos-kiosk/pos-kiosk-api.js.map +1 -0
  70. package/src/lib/components/console/pos-kiosk/use-kiosk-idle.d.ts +16 -0
  71. package/src/lib/components/console/pos-kiosk/use-kiosk-idle.js +80 -0
  72. package/src/lib/components/console/pos-kiosk/use-kiosk-idle.js.map +1 -0
  73. package/src/lib/components/console/pos-kiosk-page.component.d.ts +28 -0
  74. package/src/lib/components/console/pos-kiosk-page.component.js +791 -0
  75. package/src/lib/components/console/pos-kiosk-page.component.js.map +1 -0
  76. package/src/lib/components/console/pos-offline/pos-offline-api.d.ts +44 -0
  77. package/src/lib/components/console/pos-offline/pos-offline-api.js +69 -0
  78. package/src/lib/components/console/pos-offline/pos-offline-api.js.map +1 -0
  79. package/src/lib/components/console/pos-offline/pos-offline-banner.component.d.ts +7 -0
  80. package/src/lib/components/console/pos-offline/pos-offline-banner.component.js +135 -0
  81. package/src/lib/components/console/pos-offline/pos-offline-banner.component.js.map +1 -0
  82. package/src/lib/components/console/pos-offline/pos-offline-checkout.component.d.ts +32 -0
  83. package/src/lib/components/console/pos-offline/pos-offline-checkout.component.js +287 -0
  84. package/src/lib/components/console/pos-offline/pos-offline-checkout.component.js.map +1 -0
  85. package/src/lib/components/console/pos-offline/pos-offline-store.d.ts +63 -0
  86. package/src/lib/components/console/pos-offline/pos-offline-store.js +145 -0
  87. package/src/lib/components/console/pos-offline/pos-offline-store.js.map +1 -0
  88. package/src/lib/components/console/pos-offline/register-offline.d.ts +24 -0
  89. package/src/lib/components/console/pos-offline/register-offline.js +24 -0
  90. package/src/lib/components/console/pos-offline/register-offline.js.map +1 -0
  91. package/src/lib/components/console/pos-offline/use-pos-offline.d.ts +85 -0
  92. package/src/lib/components/console/pos-offline/use-pos-offline.js +542 -0
  93. package/src/lib/components/console/pos-offline/use-pos-offline.js.map +1 -0
  94. package/src/lib/components/console/pos-ops/pos-shift-history-card.component.js +72 -17
  95. package/src/lib/components/console/pos-ops/pos-shift-history-card.component.js.map +1 -1
  96. package/src/lib/components/console/pos-page.component.js +228 -51
  97. package/src/lib/components/console/pos-page.component.js.map +1 -1
  98. package/src/lib/components/console/pos-zones.d.ts +23 -0
  99. package/src/lib/components/console/pos-zones.js +31 -0
  100. package/src/lib/components/console/pos-zones.js.map +1 -0
  101. package/src/lib/components/console/product-editor-dialog.component.js +10 -64
  102. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
  103. package/src/lib/components/console/products-hub-card.component.js +46 -30
  104. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  105. package/src/lib/components/console/shipping-settings-card.component.js +1 -1
  106. package/src/lib/components/console/shipping-settings-card.component.js.map +1 -1
  107. package/src/lib/components/console/stock-movements-card.component.js +48 -16
  108. package/src/lib/components/console/stock-movements-card.component.js.map +1 -1
  109. package/src/lib/components/console/store-zones.d.ts +8 -1
  110. package/src/lib/components/console/store-zones.js +11 -1
  111. package/src/lib/components/console/store-zones.js.map +1 -1
  112. package/src/lib/components/console/storefront-tax-summary-card.component.js +1 -0
  113. package/src/lib/components/console/storefront-tax-summary-card.component.js.map +1 -1
  114. package/src/lib/components/order-status.js +66 -0
  115. package/src/lib/components/order-status.js.map +1 -1
  116. package/src/lib/components/product-detail.js +42 -15
  117. package/src/lib/components/product-detail.js.map +1 -1
  118. package/src/lib/constants/local-fulfillment-list-query.d.ts +48 -0
  119. package/src/lib/constants/local-fulfillment-list-query.js +84 -0
  120. package/src/lib/constants/local-fulfillment-list-query.js.map +1 -0
  121. package/src/lib/constants/orders-list-query.d.ts +7 -2
  122. package/src/lib/constants/orders-list-query.js +89 -8
  123. package/src/lib/constants/orders-list-query.js.map +1 -1
  124. package/src/lib/constants/pos-shift-list-query.d.ts +22 -0
  125. package/src/lib/constants/pos-shift-list-query.js +115 -0
  126. package/src/lib/constants/pos-shift-list-query.js.map +1 -0
  127. package/src/lib/constants/product-list-query.d.ts +6 -1
  128. package/src/lib/constants/product-list-query.js +77 -8
  129. package/src/lib/constants/product-list-query.js.map +1 -1
  130. package/src/lib/constants/stock-movement-query.d.ts +3 -2
  131. package/src/lib/constants/stock-movement-query.js +52 -8
  132. package/src/lib/constants/stock-movement-query.js.map +1 -1
  133. package/src/lib/declarations.server.js +21 -0
  134. package/src/lib/declarations.server.js.map +1 -1
  135. package/src/lib/model/buyer-notifications.d.ts +5 -3
  136. package/src/lib/model/buyer-notifications.js +25 -4
  137. package/src/lib/model/buyer-notifications.js.map +1 -1
  138. package/src/lib/model/commerce-orders.d.ts +103 -1
  139. package/src/lib/model/commerce-orders.js +2 -1
  140. package/src/lib/model/commerce-orders.js.map +1 -1
  141. package/src/lib/model/commerce-pos-kiosk.d.ts +175 -0
  142. package/src/lib/model/commerce-pos-kiosk.js +147 -0
  143. package/src/lib/model/commerce-pos-kiosk.js.map +1 -0
  144. package/src/lib/model/commerce-pos-offline.d.ts +234 -0
  145. package/src/lib/model/commerce-pos-offline.js +301 -0
  146. package/src/lib/model/commerce-pos-offline.js.map +1 -0
  147. package/src/lib/model/commerce-pos-ops.d.ts +16 -6
  148. package/src/lib/model/commerce-pos-ops.js +12 -4
  149. package/src/lib/model/commerce-pos-ops.js.map +1 -1
  150. package/src/lib/model/commerce-pos.d.ts +13 -1
  151. package/src/lib/model/commerce-pos.js +5 -4
  152. package/src/lib/model/commerce-pos.js.map +1 -1
  153. package/src/lib/model/commerce-stock-holds.d.ts +24 -3
  154. package/src/lib/model/commerce-stock-holds.js +26 -8
  155. package/src/lib/model/commerce-stock-holds.js.map +1 -1
  156. package/src/lib/model/commerce.d.ts +7 -1
  157. package/src/lib/model/commerce.js.map +1 -1
  158. package/src/lib/model/index.d.ts +5 -0
  159. package/src/lib/model/index.js +5 -0
  160. package/src/lib/model/index.js.map +1 -1
  161. package/src/lib/model/local-fulfillment-settings.d.ts +178 -0
  162. package/src/lib/model/local-fulfillment-settings.js +361 -0
  163. package/src/lib/model/local-fulfillment-settings.js.map +1 -0
  164. package/src/lib/model/order-list-fields.d.ts +10 -1
  165. package/src/lib/model/order-list-fields.js +1 -0
  166. package/src/lib/model/order-list-fields.js.map +1 -1
  167. package/src/lib/model/order-local-fulfillment.d.ts +131 -0
  168. package/src/lib/model/order-local-fulfillment.js +125 -0
  169. package/src/lib/model/order-local-fulfillment.js.map +1 -0
  170. package/src/lib/model/order-shipping-export.js +4 -0
  171. package/src/lib/model/order-shipping-export.js.map +1 -1
  172. package/src/lib/model/order-status-view.d.ts +20 -1
  173. package/src/lib/model/order-status-view.js +60 -6
  174. package/src/lib/model/order-status-view.js.map +1 -1
  175. package/src/lib/model/product-write.d.ts +186 -0
  176. package/src/lib/model/product-write.js +116 -0
  177. package/src/lib/model/product-write.js.map +1 -0
  178. package/src/lib/plugin-config.js +6 -2
  179. package/src/lib/plugin-config.js.map +1 -1
  180. package/src/lib/plugin.js +11 -0
  181. package/src/lib/plugin.js.map +1 -1
  182. package/src/lib/pos-kiosk-config.d.ts +26 -0
  183. package/src/lib/pos-kiosk-config.js +59 -0
  184. package/src/lib/pos-kiosk-config.js.map +1 -0
  185. package/src/lib/server/api-v1/openapi.js +8 -0
  186. package/src/lib/server/api-v1/openapi.js.map +1 -1
  187. package/src/lib/server/api-v1/order-view.d.ts +19 -0
  188. package/src/lib/server/api-v1/order-view.js +35 -0
  189. package/src/lib/server/api-v1/order-view.js.map +1 -1
  190. package/src/lib/server/billing-webhook.js +238 -87
  191. package/src/lib/server/billing-webhook.js.map +1 -1
  192. package/src/lib/server/cart-checkout.d.ts +2 -0
  193. package/src/lib/server/cart-checkout.js +173 -13
  194. package/src/lib/server/cart-checkout.js.map +1 -1
  195. package/src/lib/server/channel-order-updates.d.ts +50 -0
  196. package/src/lib/server/channel-order-updates.js +290 -0
  197. package/src/lib/server/channel-order-updates.js.map +1 -0
  198. package/src/lib/server/channel-orders.d.ts +85 -0
  199. package/src/lib/server/channel-orders.js +543 -0
  200. package/src/lib/server/channel-orders.js.map +1 -0
  201. package/src/lib/server/checkout-extras.js +18 -4
  202. package/src/lib/server/checkout-extras.js.map +1 -1
  203. package/src/lib/server/checkout.js +71 -14
  204. package/src/lib/server/checkout.js.map +1 -1
  205. package/src/lib/server/local-fulfillment-status.d.ts +86 -0
  206. package/src/lib/server/local-fulfillment-status.js +379 -0
  207. package/src/lib/server/local-fulfillment-status.js.map +1 -0
  208. package/src/lib/server/local-fulfillment.d.ts +190 -0
  209. package/src/lib/server/local-fulfillment.js +580 -0
  210. package/src/lib/server/local-fulfillment.js.map +1 -0
  211. package/src/lib/server/membership-register.js +3 -1
  212. package/src/lib/server/membership-register.js.map +1 -1
  213. package/src/lib/server/order-notifications.js +61 -0
  214. package/src/lib/server/order-notifications.js.map +1 -1
  215. package/src/lib/server/pos-display.d.ts +27 -1
  216. package/src/lib/server/pos-display.js +50 -9
  217. package/src/lib/server/pos-display.js.map +1 -1
  218. package/src/lib/server/pos-kiosk-principal.d.ts +29 -0
  219. package/src/lib/server/pos-kiosk-principal.js +42 -0
  220. package/src/lib/server/pos-kiosk-principal.js.map +1 -0
  221. package/src/lib/server/pos-kiosk.d.ts +54 -0
  222. package/src/lib/server/pos-kiosk.js +972 -0
  223. package/src/lib/server/pos-kiosk.js.map +1 -0
  224. package/src/lib/server/pos-offline-sync.d.ts +85 -0
  225. package/src/lib/server/pos-offline-sync.js +765 -0
  226. package/src/lib/server/pos-offline-sync.js.map +1 -0
  227. package/src/lib/server/pos-order.js +22 -4
  228. package/src/lib/server/pos-order.js.map +1 -1
  229. package/src/lib/server/pos-payment.d.ts +6 -0
  230. package/src/lib/server/pos-payment.js +225 -6
  231. package/src/lib/server/pos-payment.js.map +1 -1
  232. package/src/lib/server/pos-print.js +5 -0
  233. package/src/lib/server/pos-print.js.map +1 -1
  234. package/src/lib/server/pos-return.js +27 -0
  235. package/src/lib/server/pos-return.js.map +1 -1
  236. package/src/lib/server/pos-sale.d.ts +6 -0
  237. package/src/lib/server/pos-sale.js +2 -0
  238. package/src/lib/server/pos-sale.js.map +1 -1
  239. package/src/lib/server/pos-shift.js +8 -1
  240. package/src/lib/server/pos-shift.js.map +1 -1
  241. package/src/lib/server/product-catalog.js +5 -1
  242. package/src/lib/server/product-catalog.js.map +1 -1
  243. package/src/lib/server/product-drafts.d.ts +5 -1
  244. package/src/lib/server/product-drafts.js +3 -3
  245. package/src/lib/server/product-drafts.js.map +1 -1
  246. package/src/lib/server/product-writer.d.ts +19 -0
  247. package/src/lib/server/product-writer.js +261 -0
  248. package/src/lib/server/product-writer.js.map +1 -0
  249. package/src/lib/server/products-write.d.ts +61 -0
  250. package/src/lib/server/products-write.js +375 -0
  251. package/src/lib/server/products-write.js.map +1 -0
  252. package/src/lib/server/refund.js +3 -0
  253. package/src/lib/server/refund.js.map +1 -1
  254. package/src/lib/server/stock-hold.d.ts +9 -1
  255. package/src/lib/server/stock-hold.js +15 -7
  256. package/src/lib/server/stock-hold.js.map +1 -1
  257. package/src/lib/server.js +21 -0
  258. package/src/lib/server.js.map +1 -1
  259. package/src/lib/tenant-emails.js +161 -0
  260. package/src/lib/tenant-emails.js.map +1 -1
  261. package/src/lib/testing/memory-firestore.d.ts +16 -3
  262. package/src/lib/testing/memory-firestore.js +11 -3
  263. package/src/lib/testing/memory-firestore.js.map +1 -1
  264. package/src/lib/transfer/products.server.d.ts +5 -0
  265. package/src/lib/transfer/products.server.js +2 -2
  266. package/src/lib/transfer/products.server.js.map +1 -1
@@ -36,6 +36,7 @@ import { alertLowStockCrossing } from "./low-stock.js";
36
36
  import { decrementVariantStock } from "./reserve-stock.js";
37
37
  import { giftCardSearchTokens } from "../model/gift-card-search.js";
38
38
  import { releaseStockHold } from "./stock-hold.js";
39
+ import { orderLocalFulfillmentFromSession } from "./local-fulfillment.js";
39
40
  import { releasePromotionHold, settlePromotionSlot } from "./promotion-hold.js";
40
41
  import { flagOrderRestock } from "./restock-flag.js";
41
42
  import { storefrontTaxModeOf } from "./storefront-tax.js";
@@ -45,6 +46,7 @@ import { enqueueSupplierDelivery } from "./supplier-outbox.js";
45
46
  import { notifyOrderBuyer, onlineReceiptExtras } from "./order-notifications.js";
46
47
  import { ORDER_PAID_EVENT, ORDER_REFUNDED_EVENT } from "../model/order-events.js";
47
48
  import { checkoutExtrasCents, decodeCheckoutExtrasMetadata } from "@aglyn/aglyn/plugin-manager/plugin-checkout-extras";
49
+ import { checkoutCreditProvider, checkoutCreditSold, decodeCheckoutCreditMetadata } from "@aglyn/aglyn/plugin-manager/plugin-checkout-credits";
48
50
  import { raiseOrderEvent } from "./order-events.js";
49
51
  import { handlePosStripeEvent } from "./pos-terminal.js";
50
52
  import { notifyPosSaleCompleted } from "./pos-sale.js";
@@ -1795,6 +1797,19 @@ async function findOrderForDispute(paymentIntentId) {
1795
1797
  console.error('Gift card hold release failed', giftCardCode, error);
1796
1798
  });
1797
1799
  }
1800
+ // Store credit another plugin held for the checkout (AGL-3640) goes back
1801
+ // the same way: its provider lets the hold go.
1802
+ const expiredCredit = decodeCheckoutCreditMetadata(object == null ? void 0 : object.metadata);
1803
+ if (expiredCredit) {
1804
+ var _checkoutCreditProvider;
1805
+ await ((_checkoutCreditProvider = checkoutCreditProvider(expiredCredit.providerId)) == null ? void 0 : _checkoutCreditProvider.provider.release({
1806
+ hostId: expiredHostId,
1807
+ reference: expiredCredit.reference,
1808
+ holdKey: expiredCredit.holdKey
1809
+ }).catch((error)=>{
1810
+ console.error('Checkout credit hold release failed', expiredCredit.providerId, error);
1811
+ }));
1812
+ }
1798
1813
  return;
1799
1814
  }
1800
1815
  // White-label brand per host (White-Label Phase 3): every storefront email
@@ -2814,8 +2829,8 @@ async function findOrderForDispute(paymentIntentId) {
2814
2829
  var _object_metadata19;
2815
2830
  const { hostId, cartId, feeCents, couponCode } = (_object_metadata19 = object.metadata) != null ? _object_metadata19 : {};
2816
2831
  if (hostId && cartId) {
2817
- var _cartSnapshot_data, _ref40, _ref41, _ref42, _ref43, _ref44, _ref45;
2818
- var _object_customer_details7, _object_metadata20, _object_customer_details8, _object_customer_details9, _object_customer_details10, _object_metadata21, _this2, _object_metadata22;
2832
+ var _cartSnapshot_data, _ref40, _object_shipping_details_address_postal_code, _object_shipping_details_address_country, _ref41, _ref42, _ref43, _ref44, _ref45;
2833
+ var _object_shipping_details, _object_customer_details7, _object_metadata20, _object_customer_details8, _object_customer_details9, _object_customer_details10, _object_metadata21, _this2, _object_metadata22;
2819
2834
  const firestore = firebaseAdmin.app().firestore();
2820
2835
  const hostRef = firestore.collection('hosts').doc(String(hostId));
2821
2836
  const cartRef = hostRef.collection('carts').doc(String(cartId));
@@ -2888,14 +2903,68 @@ async function findOrderForDispute(paymentIntentId) {
2888
2903
  // The optional lines the buyer took (AGL-3635), read back off the
2889
2904
  // session they were charged on; never re-quoted here.
2890
2905
  const cartExtras = decodeCheckoutExtrasMetadata(object == null ? void 0 : object.metadata);
2906
+ // Store credit another plugin held for this checkout (AGL-3640): taken
2907
+ // in THIS transaction, so the redemption and the order are written
2908
+ // together or not at all. What Stripe discounted is the hold; what the
2909
+ // provider gives now is what the order records.
2910
+ const heldCredit = decodeCheckoutCreditMetadata(object == null ? void 0 : object.metadata);
2911
+ const creditEntry = heldCredit ? checkoutCreditProvider(heldCredit.providerId) : null;
2912
+ let creditShortCents = 0;
2913
+ // Picked up or brought by the store's own driver (AGL-3624): where
2914
+ // the buyer chose, read back off the session they paid. Never throws
2915
+ // past here — a failed read records the order as shipped rather than
2916
+ // losing it.
2917
+ const cartLocalFulfillment = await orderLocalFulfillmentFromSession({
2918
+ hostId: String(hostId),
2919
+ hostRef,
2920
+ metadata: object == null ? void 0 : object.metadata,
2921
+ shippingAddress: (object == null ? void 0 : (_object_shipping_details = object.shipping_details) == null ? void 0 : _object_shipping_details.address) ? {
2922
+ postalCode: (_object_shipping_details_address_postal_code = object.shipping_details.address.postal_code) != null ? _object_shipping_details_address_postal_code : null,
2923
+ country: (_object_shipping_details_address_country = object.shipping_details.address.country) != null ? _object_shipping_details_address_country : null
2924
+ } : null,
2925
+ createdAtMs: Date.now()
2926
+ }).catch((error)=>{
2927
+ console.error('commerce cart local fulfillment unreadable', String(object.id), error);
2928
+ return null;
2929
+ });
2891
2930
  const created = await firestore.runTransaction(async (transaction)=>{
2892
- var _counter_get, _ref, _ref1, _ref2, _ref3, _ref4, _shipping_address_line1, _shipping_address_line2, _shipping_address_city, _shipping_address_state, _shipping_address_postal_code, _shipping_address_country, _ref5;
2931
+ var _counter_get, _ref, _ref1, _ref2, _ref3, _ref4, _shipping_address_line1, _shipping_address_line2, _shipping_address_city, _shipping_address_state, _shipping_address_postal_code, _shipping_address_country, _ref5, _ref6;
2893
2932
  var _object_metadata, _object_customer_details, _object_customer_details1;
2894
2933
  const [existing, counter] = await Promise.all([
2895
2934
  transaction.get(orderRef),
2896
2935
  transaction.get(counterRef)
2897
2936
  ]);
2898
2937
  if (existing.exists) return false;
2938
+ // The provider's reads, before this transaction writes anything.
2939
+ let creditStage = null;
2940
+ if (heldCredit && creditEntry) {
2941
+ creditStage = await creditEntry.provider.stage({
2942
+ transaction,
2943
+ hostId: String(hostId),
2944
+ reference: heldCredit.reference,
2945
+ orderId: orderRef.id,
2946
+ nowMs: Date.now(),
2947
+ holdKey: heldCredit.holdKey
2948
+ }).catch((error)=>{
2949
+ console.error('Checkout credit stage failed', heldCredit.providerId, error);
2950
+ return null;
2951
+ });
2952
+ }
2953
+ const creditTaken = heldCredit && creditStage ? Math.max(0, Math.min(heldCredit.amountCents, creditStage.debit({
2954
+ cents: heldCredit.amountCents,
2955
+ key: heldCredit.holdKey,
2956
+ orderId: orderRef.id,
2957
+ channel: 'online'
2958
+ }))) : 0;
2959
+ creditShortCents = heldCredit ? heldCredit.amountCents - creditTaken : 0;
2960
+ const creditSold = heldCredit && creditTaken > 0 ? checkoutCreditSold({
2961
+ providerId: heldCredit.providerId,
2962
+ reference: heldCredit.reference,
2963
+ label: heldCredit.label,
2964
+ last4: heldCredit.last4,
2965
+ amountCents: creditTaken,
2966
+ appliedAs: 'discount'
2967
+ }) : null;
2899
2968
  const number = Number((_counter_get = counter.get('next')) != null ? _counter_get : 1);
2900
2969
  cartOrderNumber = number;
2901
2970
  transaction.set(counterRef, {
@@ -2919,6 +2988,10 @@ async function findOrderForDispute(paymentIntentId) {
2919
2988
  totals
2920
2989
  }, cartExtras.length ? {
2921
2990
  extras: cartExtras
2991
+ } : {}, creditSold ? {
2992
+ credits: [
2993
+ creditSold
2994
+ ]
2922
2995
  } : {}, {
2923
2996
  // WHICH TAX THIS SALE CARRIED (AGL-2451). `totals.taxCents` above
2924
2997
  // says how much; this says who computed it, which is the fact that
@@ -2940,6 +3013,13 @@ async function findOrderForDispute(paymentIntentId) {
2940
3013
  event: 'line-unresolved',
2941
3014
  detail: `${unresolvedLines.length} paid ${unresolvedLines.length === 1 ? 'line' : 'lines'} could not be recorded because the product was ` + 'deleted during checkout, so this order is short of ' + 'what the shopper was charged. Refund the difference ' + 'or fulfill it by hand.'
2942
3015
  }
3016
+ ] : [],
3017
+ ...heldCredit && creditShortCents > 0 ? [
3018
+ {
3019
+ atMs: Date.now(),
3020
+ event: 'credit-short',
3021
+ detail: `$${(heldCredit.amountCents / 100).toFixed(2)} of ${heldCredit.label} was taken off this ` + `order, but only $${((heldCredit.amountCents - creditShortCents) / 100).toFixed(2)} was ` + 'still in the account when it was paid.'
3022
+ }
2943
3023
  ] : []
2944
3024
  ]
2945
3025
  }, unresolvedLines.length ? {
@@ -2952,7 +3032,7 @@ async function findOrderForDispute(paymentIntentId) {
2952
3032
  } : {}, {
2953
3033
  customerName: (_ref2 = object == null ? void 0 : (_object_customer_details = object.customer_details) == null ? void 0 : _object_customer_details.name) != null ? _ref2 : null,
2954
3034
  customerEmail: (_ref3 = object == null ? void 0 : (_object_customer_details1 = object.customer_details) == null ? void 0 : _object_customer_details1.email) != null ? _ref3 : null
2955
- }, (shipping == null ? void 0 : shipping.address) ? {
3035
+ }, (shipping == null ? void 0 : shipping.address) && (cartLocalFulfillment == null ? void 0 : cartLocalFulfillment.fields.fulfillmentMethod) !== 'pickup' ? {
2956
3036
  shippingAddress: {
2957
3037
  name: (_ref4 = shipping == null ? void 0 : shipping.name) != null ? _ref4 : undefined,
2958
3038
  line1: (_shipping_address_line1 = shipping.address.line1) != null ? _shipping_address_line1 : undefined,
@@ -2962,10 +3042,10 @@ async function findOrderForDispute(paymentIntentId) {
2962
3042
  postalCode: (_shipping_address_postal_code = shipping.address.postal_code) != null ? _shipping_address_postal_code : undefined,
2963
3043
  country: (_shipping_address_country = shipping.address.country) != null ? _shipping_address_country : undefined
2964
3044
  }
2965
- } : {}, couponCode ? {
3045
+ } : {}, (_ref5 = cartLocalFulfillment == null ? void 0 : cartLocalFulfillment.fields) != null ? _ref5 : {}, couponCode ? {
2966
3046
  couponCode
2967
3047
  } : {}, {
2968
- amountCents: Number((_ref5 = object == null ? void 0 : object.amount_total) != null ? _ref5 : 0),
3048
+ amountCents: Number((_ref6 = object == null ? void 0 : object.amount_total) != null ? _ref6 : 0),
2969
3049
  feeCents: Number(feeCents != null ? feeCents : 0),
2970
3050
  createdAtMs: Date.now(),
2971
3051
  createdAt: firebaseAdmin.firestore.FieldValue.serverTimestamp()
@@ -2996,6 +3076,34 @@ async function findOrderForDispute(paymentIntentId) {
2996
3076
  link: `/${hostId}/products`
2997
3077
  });
2998
3078
  }
3079
+ // A store-credit hold that no longer covered what Stripe discounted
3080
+ // (AGL-3640): the buyer kept the discount, the account gave less. Said
3081
+ // once, like a lost line, so the merchant can square it.
3082
+ if (heldCredit && creditShortCents > 0) {
3083
+ console.error('commerce cart order credit short', {
3084
+ hostId: String(hostId),
3085
+ orderId: String(object.id),
3086
+ providerId: heldCredit.providerId,
3087
+ shortCents: creditShortCents
3088
+ });
3089
+ void notifyHostManagers(String(hostId), {
3090
+ type: 'content.order',
3091
+ title: 'Store credit on an order came up short',
3092
+ body: `Order ${cartOrderLabel} on {site} was discounted ` + `$${(heldCredit.amountCents / 100).toFixed(2)} for ${heldCredit.label}, but the account ` + `had only $${((heldCredit.amountCents - creditShortCents) / 100).toFixed(2)} left when it was paid.`,
3093
+ link: `/${hostId}/products`
3094
+ });
3095
+ }
3096
+ // The address typed at payment is outside every delivery zone the
3097
+ // store has (AGL-3624): the fee was charged for the postal code the
3098
+ // buyer declared, so the store decides — deliver, or refund.
3099
+ if (cartLocalFulfillment == null ? void 0 : cartLocalFulfillment.outsideZone) {
3100
+ void notifyHostManagers(String(hostId), {
3101
+ type: 'content.order',
3102
+ title: `Check the delivery address on order ${cartOrderLabel}`,
3103
+ body: `Order ${cartOrderLabel} on {site} was booked for local delivery, but the ` + 'address entered at payment is outside your delivery zones. Contact the ' + 'buyer, or refund the delivery.',
3104
+ link: `/${hostId}/products`
3105
+ });
3106
+ }
2999
3107
  await cartRef.delete().catch(()=>undefined);
3000
3108
  // Recoverable checkout closes (AGL-296) so recovery emails stop;
3001
3109
  // the doc also carries the marketing opt-in (AGL-301).
@@ -3058,7 +3166,7 @@ async function findOrderForDispute(paymentIntentId) {
3058
3166
  // The store's receipt switch and the status link (AGL-3610).
3059
3167
  const receiptExtras = await onlineReceiptExtras(String(hostId), String(object.id));
3060
3168
  if (isEmailConfigured() && buyerEmailForReceipt && receiptExtras.enabled) {
3061
- var _ref47, _ref48, _ref49, _ref50;
3169
+ var _ref47, _ref48, _ref49, _ref50, _ref51;
3062
3170
  const receiptSettings = await hostRef.collection('settings').doc('store').get().catch(()=>null);
3063
3171
  const receiptFooter = String((_ref47 = receiptSettings == null ? void 0 : receiptSettings.get('receiptFooter')) != null ? _ref47 : '');
3064
3172
  const linesText = lineItems.map((line)=>`${line.quantity}× ${line.name}${line.variantLabel ? ` (${line.variantLabel})` : ''} — $${(line.unitAmountCents * line.quantity / 100).toFixed(2)}`).concat(// Optional lines the buyer took (AGL-3635), as charged.
@@ -3078,12 +3186,15 @@ async function findOrderForDispute(paymentIntentId) {
3078
3186
  const siteOrigin = String((_ref48 = object == null ? void 0 : object.success_url) != null ? _ref48 : '').replace(/\/\?.*$|\?.*$/, '');
3079
3187
  const downloadLines = lineItems.filter((line)=>line.productType === 'digital').map((line)=>`Download ${line.name}: ${siteOrigin}/api/commerce/download` + `?hostId=${hostId}&orderId=${object.id}` + `&productId=${line.productId}&token=${downloadToken}`).join('\n');
3080
3188
  const orderTotal = `$${(Number((_ref49 = object == null ? void 0 : object.amount_total) != null ? _ref49 : 0) / 100).toFixed(2)}`;
3189
+ // Where to collect it, or when it is coming (AGL-3624).
3190
+ const fulfillmentText = (_ref50 = cartLocalFulfillment == null ? void 0 : cartLocalFulfillment.summary) != null ? _ref50 : '';
3081
3191
  const orderSummary = [
3082
3192
  linesText,
3083
3193
  licenseText,
3084
- downloadLines
3194
+ downloadLines,
3195
+ fulfillmentText
3085
3196
  ].filter(Boolean).join('\n\n');
3086
- const fallbackText = `Thanks for your purchase!\n\n${linesText}\n\n` + (licenseText ? `${licenseText}\n\n` : '') + (downloadLines ? `${downloadLines}\n\n` : '') + `Total: ${orderTotal}\n` + `Order reference: ${object.id}` + (receiptFooter ? `\n\n${receiptFooter}` : '');
3197
+ const fallbackText = `Thanks for your purchase!\n\n${linesText}\n\n` + (licenseText ? `${licenseText}\n\n` : '') + (downloadLines ? `${downloadLines}\n\n` : '') + (fulfillmentText ? `${fulfillmentText}\n\n` : '') + `Total: ${orderTotal}\n` + `Order reference: ${object.id}` + (receiptFooter ? `\n\n${receiptFooter}` : '');
3087
3198
  // Site-owner-designed template when published (AGL-771). The
3088
3199
  // license keys and download links ride in {{order.summary}}.
3089
3200
  const designed = await renderHostEmailWithTokens(firebaseAdmin.app().firestore(), String(hostId), 'order-receipt', _extends({
@@ -3097,7 +3208,7 @@ async function findOrderForDispute(paymentIntentId) {
3097
3208
  }, receiptExtras.tokens));
3098
3209
  await sendEmail(_extends({
3099
3210
  to: buyerEmailForReceipt,
3100
- subject: (_ref50 = designed == null ? void 0 : designed.subject) != null ? _ref50 : `Receipt for your order`,
3211
+ subject: (_ref51 = designed == null ? void 0 : designed.subject) != null ? _ref51 : `Receipt for your order`,
3101
3212
  text: (designed == null ? void 0 : designed.text) || fallbackText
3102
3213
  }, (designed == null ? void 0 : designed.html) ? {
3103
3214
  html: designed.html
@@ -3145,7 +3256,10 @@ async function findOrderForDispute(paymentIntentId) {
3145
3256
  ledger: {
3146
3257
  reason: 'sale',
3147
3258
  orderId: String(object.id)
3148
- }
3259
+ },
3260
+ // Off the pickup location's shelf, or the one deliveries leave
3261
+ // from (AGL-3624); `order.locationId` says so for a cancellation.
3262
+ locationId: (cartLocalFulfillment == null ? void 0 : cartLocalFulfillment.fields.locationId) || undefined
3149
3263
  });
3150
3264
  if (!moved.before || !moved.after) continue;
3151
3265
  productsById.set(line.productId, moved.after);
@@ -3204,12 +3318,12 @@ async function findOrderForDispute(paymentIntentId) {
3204
3318
  }
3205
3319
  }));
3206
3320
  if (couponCode) {
3207
- var _ref51;
3321
+ var _ref52;
3208
3322
  var _object_metadata23;
3209
3323
  await settleRedemption({
3210
3324
  firestore,
3211
3325
  ref: hostRef.collection('coupons').doc(String(couponCode)),
3212
- holdKey: String((_ref51 = (_object_metadata23 = object.metadata) == null ? void 0 : _object_metadata23.couponHoldKey) != null ? _ref51 : ''),
3326
+ holdKey: String((_ref52 = (_object_metadata23 = object.metadata) == null ? void 0 : _object_metadata23.couponHoldKey) != null ? _ref52 : ''),
3213
3327
  orderRef,
3214
3328
  label: `coupon ${couponCode}`,
3215
3329
  detail: `Coupon ${couponCode} was applied to this order but no longer ` + 'exists, so the redemption is uncounted against its limit.'
@@ -3217,10 +3331,10 @@ async function findOrderForDispute(paymentIntentId) {
3217
3331
  }
3218
3332
  // Gift card balance settlement (AGL-322, made a settlement by AGL-2449).
3219
3333
  if ((_object_metadata21 = object.metadata) == null ? void 0 : _object_metadata21.giftCardCode) {
3220
- var _ref52, _ref53;
3334
+ var _ref53, _ref54;
3221
3335
  var _object_metadata24, _object_metadata25;
3222
- const giftCardCents = Number((_ref52 = (_object_metadata24 = object.metadata) == null ? void 0 : _object_metadata24.giftCardCents) != null ? _ref52 : 0);
3223
- const holdKey = String((_ref53 = (_object_metadata25 = object.metadata) == null ? void 0 : _object_metadata25.giftCardHoldKey) != null ? _ref53 : '');
3336
+ const giftCardCents = Number((_ref53 = (_object_metadata24 = object.metadata) == null ? void 0 : _object_metadata24.giftCardCents) != null ? _ref53 : 0);
3337
+ const holdKey = String((_ref54 = (_object_metadata25 = object.metadata) == null ? void 0 : _object_metadata25.giftCardHoldKey) != null ? _ref54 : '');
3224
3338
  const cardRef = hostRef.collection('giftCards').doc(String(object.metadata.giftCardCode));
3225
3339
  const orphanNote = `$${(giftCardCents / 100).toFixed(2)} was applied from gift card ` + `${object.metadata.giftCardCode}, which no longer exists. The ` + 'balance was not deducted from any card.';
3226
3340
  if (!holdKey) {
@@ -3300,7 +3414,7 @@ async function findOrderForDispute(paymentIntentId) {
3300
3414
  const lineProduct = productsById.get(line.productId);
3301
3415
  if (!(lineProduct == null ? void 0 : lineProduct.giftCard)) continue;
3302
3416
  for(let unit = 0; unit < line.quantity; unit += 1){
3303
- var _ref54, _ref55;
3417
+ var _ref55, _ref56;
3304
3418
  var _object_customer_details12, _object_customer_details13, _object_customer_details14;
3305
3419
  const code = `GC-${createHmac('sha256', String(object.id)).update(`${line.productId}:${unit}:${Date.now()}`).digest('hex').slice(0, 12).toUpperCase()}`;
3306
3420
  // THE EMAIL IS GATED ON THE WRITE (AGL-2161). This `.set()` used
@@ -3319,13 +3433,13 @@ async function findOrderForDispute(paymentIntentId) {
3319
3433
  const issued = await hostRef.collection('giftCards').doc(code).set({
3320
3434
  initialCents: line.unitAmountCents,
3321
3435
  balanceCents: line.unitAmountCents,
3322
- recipientEmail: (_ref54 = object == null ? void 0 : (_object_customer_details12 = object.customer_details) == null ? void 0 : _object_customer_details12.email) != null ? _ref54 : null,
3436
+ recipientEmail: (_ref55 = object == null ? void 0 : (_object_customer_details12 = object.customer_details) == null ? void 0 : _object_customer_details12.email) != null ? _ref55 : null,
3323
3437
  orderId: String(object.id),
3324
3438
  // Which line bought it, so a refund that withdraws that line
3325
3439
  // voids this card and no other (AGL-3363).
3326
3440
  productId: line.productId,
3327
3441
  // What the console's Gift cards search asks (AGL-3321).
3328
- searchTokens: giftCardSearchTokens(code, (_ref55 = object == null ? void 0 : (_object_customer_details13 = object.customer_details) == null ? void 0 : _object_customer_details13.email) != null ? _ref55 : null),
3442
+ searchTokens: giftCardSearchTokens(code, (_ref56 = object == null ? void 0 : (_object_customer_details13 = object.customer_details) == null ? void 0 : _object_customer_details13.email) != null ? _ref56 : null),
3329
3443
  createdAtMs: Date.now()
3330
3444
  }).then(()=>true).catch((error)=>{
3331
3445
  console.error('Gift card issuance failed', hostId, code, error);
@@ -3353,7 +3467,7 @@ async function findOrderForDispute(paymentIntentId) {
3353
3467
  }
3354
3468
  const giftTo = object == null ? void 0 : (_object_customer_details14 = object.customer_details) == null ? void 0 : _object_customer_details14.email;
3355
3469
  if (giftTo) {
3356
- var _ref56;
3470
+ var _ref57;
3357
3471
  const giftValue = `$${(line.unitAmountCents / 100).toFixed(2)}`;
3358
3472
  const fallbackText = `Gift card code: ${code}\n` + `Value: ${giftValue}\n\n` + 'Enter it at checkout to apply the balance.';
3359
3473
  // Site-owner-designed template when published (AGL-771). A
@@ -3366,7 +3480,7 @@ async function findOrderForDispute(paymentIntentId) {
3366
3480
  });
3367
3481
  await sendEmail(_extends({
3368
3482
  to: giftTo,
3369
- subject: (_ref56 = designed == null ? void 0 : designed.subject) != null ? _ref56 : 'Your gift card',
3483
+ subject: (_ref57 = designed == null ? void 0 : designed.subject) != null ? _ref57 : 'Your gift card',
3370
3484
  text: (designed == null ? void 0 : designed.text) || fallbackText
3371
3485
  }, (designed == null ? void 0 : designed.html) ? {
3372
3486
  html: designed.html
@@ -3387,12 +3501,12 @@ async function findOrderForDispute(paymentIntentId) {
3387
3501
  }
3388
3502
  // Discounts engine redemptions (AGL-305).
3389
3503
  if ((_object_metadata22 = object.metadata) == null ? void 0 : _object_metadata22.discountId) {
3390
- var _ref57;
3504
+ var _ref58;
3391
3505
  var _object_metadata26;
3392
3506
  await settleRedemption({
3393
3507
  firestore,
3394
3508
  ref: hostRef.collection('discounts').doc(String(object.metadata.discountId)),
3395
- holdKey: String((_ref57 = (_object_metadata26 = object.metadata) == null ? void 0 : _object_metadata26.discountHoldKey) != null ? _ref57 : ''),
3509
+ holdKey: String((_ref58 = (_object_metadata26 = object.metadata) == null ? void 0 : _object_metadata26.discountHoldKey) != null ? _ref58 : ''),
3396
3510
  orderRef,
3397
3511
  label: `discount ${object.metadata.discountId}`,
3398
3512
  detail: `Discount ${object.metadata.discountId} was applied to this ` + 'order but no longer exists, so the redemption is uncounted ' + 'against its limit.'
@@ -3406,7 +3520,7 @@ async function findOrderForDispute(paymentIntentId) {
3406
3520
  var _object_metadata27;
3407
3521
  const { hostId, orderId, productId } = (_object_metadata27 = object.metadata) != null ? _object_metadata27 : {};
3408
3522
  if (hostId && orderId) {
3409
- var _ref58, _ref59, _ref60;
3523
+ var _ref59, _ref60, _ref61;
3410
3524
  var _object_total_details, _object_total_details1;
3411
3525
  const firestore = firebaseAdmin.app().firestore();
3412
3526
  const hostRef = firestore.collection('hosts').doc(String(hostId));
@@ -3445,7 +3559,7 @@ async function findOrderForDispute(paymentIntentId) {
3445
3559
  //
3446
3560
  // The whole map is written rather than one field of it, so a
3447
3561
  // `merge`-set's nested-map merge and a plain replace agree.
3448
- const shippingCents = Math.max(0, Math.round(Number((_ref58 = object == null ? void 0 : (_object_total_details = object.total_details) == null ? void 0 : _object_total_details.amount_shipping) != null ? _ref58 : 0) || 0));
3562
+ const shippingCents = Math.max(0, Math.round(Number((_ref59 = object == null ? void 0 : (_object_total_details = object.total_details) == null ? void 0 : _object_total_details.amount_shipping) != null ? _ref59 : 0) || 0));
3449
3563
  // Tax joins shipping in the fold (AGL-1953). `draft-order.ts` now
3450
3564
  // declares tax — a Stripe Tax Rate for a `manual` store, whose figure
3451
3565
  // it already froze into `totals`, or `automatic_tax` for a `stripe`
@@ -3458,8 +3572,8 @@ async function findOrderForDispute(paymentIntentId) {
3458
3572
  // `amount_tax` 0 here (its own figure stays on the order, and its
3459
3573
  // AGL-1953 witness is metadata, not this field), and a draft composed
3460
3574
  // by a store with no tax reports 0 too.
3461
- const stripeTaxCents = Math.max(0, Math.round(Number((_ref59 = object == null ? void 0 : (_object_total_details1 = object.total_details) == null ? void 0 : _object_total_details1.amount_tax) != null ? _ref59 : 0) || 0));
3462
- const chargedTotalCents = Number((_ref60 = object == null ? void 0 : object.amount_total) != null ? _ref60 : NaN);
3575
+ const stripeTaxCents = Math.max(0, Math.round(Number((_ref60 = object == null ? void 0 : (_object_total_details1 = object.total_details) == null ? void 0 : _object_total_details1.amount_tax) != null ? _ref60 : 0) || 0));
3576
+ const chargedTotalCents = Number((_ref61 = object == null ? void 0 : object.amount_total) != null ? _ref61 : NaN);
3463
3577
  // `shipping_details` ONLY, unlike the cart branch's
3464
3578
  // `?? customer_details` fallback: that address is the BILLING one, and
3465
3579
  // a card sale rung up at a register completes here too — it must not
@@ -3578,7 +3692,7 @@ async function findOrderForDispute(paymentIntentId) {
3578
3692
  });
3579
3693
  }
3580
3694
  if (flipped) {
3581
- var _ref61, _ref62, _ref63, _ref64, _ref65, _ref66;
3695
+ var _ref62, _ref63, _ref64, _ref65, _ref66, _ref67;
3582
3696
  var _object_metadata28, _object_customer_details15, _order_totals, _object_customer_details16;
3583
3697
  const order = paidOrder;
3584
3698
  // A legacy register QR sale is a register sale completing (AGL-3607).
@@ -3604,20 +3718,20 @@ async function findOrderForDispute(paymentIntentId) {
3604
3718
  // the hold existed, which Stripe's at-least-once delivery would run
3605
3719
  // twice. The transition is what makes it once.
3606
3720
  if ((_object_metadata28 = object.metadata) == null ? void 0 : _object_metadata28.discountId) {
3607
- var _ref67;
3721
+ var _ref68;
3608
3722
  var _object_metadata29;
3609
3723
  await settleRedemption({
3610
3724
  firestore,
3611
3725
  ref: hostRef.collection('discounts').doc(String(object.metadata.discountId)),
3612
- holdKey: String((_ref67 = (_object_metadata29 = object.metadata) == null ? void 0 : _object_metadata29.discountHoldKey) != null ? _ref67 : ''),
3726
+ holdKey: String((_ref68 = (_object_metadata29 = object.metadata) == null ? void 0 : _object_metadata29.discountHoldKey) != null ? _ref68 : ''),
3613
3727
  orderRef,
3614
3728
  label: `discount ${object.metadata.discountId}`,
3615
3729
  detail: `Discount ${object.metadata.discountId} was applied to this ` + 'order but no longer exists, so the redemption is uncounted ' + 'against its limit.'
3616
3730
  });
3617
3731
  }
3618
3732
  const draftLabel = CommerceModel.formatOrderNumber(order, String(orderId));
3619
- const draftPayer = (_ref61 = (_ref62 = object == null ? void 0 : (_object_customer_details15 = object.customer_details) == null ? void 0 : _object_customer_details15.email) != null ? _ref62 : order.customerEmail) != null ? _ref61 : null;
3620
- const draftPaidCents = Number((_ref63 = object == null ? void 0 : object.amount_total) != null ? _ref63 : 0) || Number((_ref64 = (_order_totals = order.totals) == null ? void 0 : _order_totals.totalCents) != null ? _ref64 : 0);
3733
+ const draftPayer = (_ref62 = (_ref63 = object == null ? void 0 : (_object_customer_details15 = object.customer_details) == null ? void 0 : _object_customer_details15.email) != null ? _ref63 : order.customerEmail) != null ? _ref62 : null;
3734
+ const draftPaidCents = Number((_ref64 = object == null ? void 0 : object.amount_total) != null ? _ref64 : 0) || Number((_ref65 = (_order_totals = order.totals) == null ? void 0 : _order_totals.totalCents) != null ? _ref65 : 0);
3621
3735
  void notifyHostManagers(String(hostId), {
3622
3736
  type: 'content.order',
3623
3737
  title: `Draft order paid — ${draftLabel}`,
@@ -3637,17 +3751,17 @@ async function findOrderForDispute(paymentIntentId) {
3637
3751
  // figure the draft was priced at, and the two agree by construction,
3638
3752
  // but only one of them is the money that moved. Stored totals remain
3639
3753
  // the fallback for a session shape that reports no total.
3640
- const draftEmail = (_ref65 = (_ref66 = object == null ? void 0 : (_object_customer_details16 = object.customer_details) == null ? void 0 : _object_customer_details16.email) != null ? _ref66 : order.customerEmail) != null ? _ref65 : null;
3754
+ const draftEmail = (_ref66 = (_ref67 = object == null ? void 0 : (_object_customer_details16 = object.customer_details) == null ? void 0 : _object_customer_details16.email) != null ? _ref67 : order.customerEmail) != null ? _ref66 : null;
3641
3755
  if (draftEmail) {
3642
- var _ref68, _ref69, _ref70;
3756
+ var _ref69, _ref70, _ref71;
3643
3757
  var _order_totals1, _object_customer_details17;
3644
- const chargedCents = Number((_ref68 = object == null ? void 0 : object.amount_total) != null ? _ref68 : 0) || Number((_ref69 = (_order_totals1 = order.totals) == null ? void 0 : _order_totals1.totalCents) != null ? _ref69 : 0);
3758
+ const chargedCents = Number((_ref69 = object == null ? void 0 : object.amount_total) != null ? _ref69 : 0) || Number((_ref70 = (_order_totals1 = order.totals) == null ? void 0 : _order_totals1.totalCents) != null ? _ref70 : 0);
3645
3759
  await recordCapturedContact(_extends({
3646
3760
  orgId: '',
3647
3761
  hostId: String(hostId),
3648
3762
  identity: {
3649
3763
  email: draftEmail,
3650
- name: (_ref70 = object == null ? void 0 : (_object_customer_details17 = object.customer_details) == null ? void 0 : _object_customer_details17.name) != null ? _ref70 : null
3764
+ name: (_ref71 = object == null ? void 0 : (_object_customer_details17 = object.customer_details) == null ? void 0 : _object_customer_details17.name) != null ? _ref71 : null
3651
3765
  },
3652
3766
  surface: 'relationship',
3653
3767
  lifecycleFloor: 'customer'
@@ -3674,15 +3788,15 @@ async function findOrderForDispute(paymentIntentId) {
3674
3788
  key: 'paid'
3675
3789
  });
3676
3790
  if (productId) {
3677
- var _productSnapshot_data, _ref71, _ref72;
3791
+ var _productSnapshot_data, _ref72, _ref73;
3678
3792
  var _object_metadata30, _lifted_variants_, _order_lineItems_, _order_lineItems;
3679
3793
  const productRef = hostRef.collection('products').doc(String(productId));
3680
3794
  const productSnapshot = await productRef.get();
3681
3795
  const lifted = CommerceModel.liftLegacyProduct((_productSnapshot_data = productSnapshot.data()) != null ? _productSnapshot_data : {
3682
3796
  name: 'Product'
3683
3797
  });
3684
- const soldVariantId = String((_ref71 = (_object_metadata30 = object.metadata) == null ? void 0 : _object_metadata30.variantId) != null ? _ref71 : '') || ((_lifted_variants_ = lifted.variants[0]) == null ? void 0 : _lifted_variants_.id);
3685
- const quantity = (_ref72 = (_order_lineItems = order.lineItems) == null ? void 0 : (_order_lineItems_ = _order_lineItems[0]) == null ? void 0 : _order_lineItems_.quantity) != null ? _ref72 : 1;
3798
+ const soldVariantId = String((_ref72 = (_object_metadata30 = object.metadata) == null ? void 0 : _object_metadata30.variantId) != null ? _ref72 : '') || ((_lifted_variants_ = lifted.variants[0]) == null ? void 0 : _lifted_variants_.id);
3799
+ const quantity = (_ref73 = (_order_lineItems = order.lineItems) == null ? void 0 : (_order_lineItems_ = _order_lineItems[0]) == null ? void 0 : _order_lineItems_.quantity) != null ? _ref73 : 1;
3686
3800
  if (soldVariantId) {
3687
3801
  // Atomic since AGL-2320.
3688
3802
  const moved = await decrementVariantStock({
@@ -3734,10 +3848,10 @@ async function findOrderForDispute(paymentIntentId) {
3734
3848
  // would decrement it twice.
3735
3849
  const paidProducts = new Map();
3736
3850
  for (const line of (_order_lineItems1 = order.lineItems) != null ? _order_lineItems1 : []){
3737
- var _ref73, _ref74, _line_variantId1;
3851
+ var _ref74, _ref75, _line_variantId1;
3738
3852
  var _lineProduct_variants_;
3739
- const lineProductId = String((_ref73 = line == null ? void 0 : line.productId) != null ? _ref73 : '');
3740
- const soldQty = Math.round(Number((_ref74 = line == null ? void 0 : line.quantity) != null ? _ref74 : 0));
3853
+ const lineProductId = String((_ref74 = line == null ? void 0 : line.productId) != null ? _ref74 : '');
3854
+ const soldQty = Math.round(Number((_ref75 = line == null ? void 0 : line.quantity) != null ? _ref75 : 0));
3741
3855
  if (!lineProductId || !(soldQty > 0)) continue;
3742
3856
  // Firestore reserves `__…__` ids and `.doc()` throws
3743
3857
  // synchronously on one; a corrupt line must not fail the
@@ -3800,8 +3914,8 @@ async function findOrderForDispute(paymentIntentId) {
3800
3914
  var _object_metadata31;
3801
3915
  const { hostId, productId, feeCents, couponCode } = (_object_metadata31 = object.metadata) != null ? _object_metadata31 : {};
3802
3916
  if (hostId && productId) {
3803
- var _ref75, _productForSnapshot_get2, _productForSnapshot_data1, _ref76, _ref77, _ref78, _ref79, _ref80, _ref81, _ref82;
3804
- var _object_metadata32, _buyNowLineItems_, _object_customer_details18, _object_customer_details19, _object_customer_details20, _object_metadata33;
3917
+ var _ref76, _productForSnapshot_get2, _productForSnapshot_data1, _ref77, _ref78, _object_shipping_details_address_postal_code1, _object_shipping_details_address_country1, _ref79, _ref80, _ref81, _ref82, _ref83;
3918
+ var _object_metadata32, _buyNowLineItems_, _object_shipping_details1, _object_customer_details18, _object_customer_details19, _object_customer_details20, _object_metadata33;
3805
3919
  const firestore = firebaseAdmin.app().firestore();
3806
3920
  const hostRef = firestore.collection('hosts').doc(String(hostId));
3807
3921
  // Orders v1 (AGL-283): line-item snapshot + totals + timeline with
@@ -3811,7 +3925,7 @@ async function findOrderForDispute(paymentIntentId) {
3811
3925
  // is bounded to Stripe's at-least-once edge).
3812
3926
  const orderRef = hostRef.collection('orders').doc(String(object.id));
3813
3927
  const counterRef = hostRef.collection('counters').doc('orders');
3814
- const amountCents = Number((_ref75 = object == null ? void 0 : object.amount_total) != null ? _ref75 : 0);
3928
+ const amountCents = Number((_ref76 = object == null ? void 0 : object.amount_total) != null ? _ref76 : 0);
3815
3929
  const productForSnapshot = await hostRef.collection('products').doc(String(productId)).get();
3816
3930
  const snapshotName = String((_productForSnapshot_get2 = productForSnapshot.get('name')) != null ? _productForSnapshot_get2 : 'Product');
3817
3931
  // AGL-1711: the line item and totals used to be fabricated from
@@ -3824,7 +3938,7 @@ async function findOrderForDispute(paymentIntentId) {
3824
3938
  name: snapshotName
3825
3939
  });
3826
3940
  const soldVariant = ((_object_metadata32 = object.metadata) == null ? void 0 : _object_metadata32.variantId) ? liftedForSnapshot.variants.find((item)=>item.id === String(object.metadata.variantId)) : liftedForSnapshot.variants[0];
3827
- const variantOptions = Object.values((_ref76 = soldVariant == null ? void 0 : soldVariant.options) != null ? _ref76 : {});
3941
+ const variantOptions = Object.values((_ref77 = soldVariant == null ? void 0 : soldVariant.options) != null ? _ref77 : {});
3828
3942
  const { lineItems: buyNowLineItems, totals: buyNowTotals } = CommerceModel.computeBuyNowOrder(object, _extends({
3829
3943
  name: snapshotName
3830
3944
  }, variantOptions.length ? {
@@ -3836,13 +3950,28 @@ async function findOrderForDispute(paymentIntentId) {
3836
3950
  } : {}, liftedForSnapshot.supplierId ? {
3837
3951
  supplierId: liftedForSnapshot.supplierId
3838
3952
  } : {}));
3839
- const soldQuantity = (_ref77 = (_buyNowLineItems_ = buyNowLineItems[0]) == null ? void 0 : _buyNowLineItems_.quantity) != null ? _ref77 : 1;
3953
+ const soldQuantity = (_ref78 = (_buyNowLineItems_ = buyNowLineItems[0]) == null ? void 0 : _buyNowLineItems_.quantity) != null ? _ref78 : 1;
3840
3954
  // The number the merchant knows this order by, for the notices below
3841
3955
  // (AGL-3432).
3842
3956
  let buyNowOrderNumber;
3957
+ // Picked up or brought by the store's own driver (AGL-3624), as the
3958
+ // cart branch reads it.
3959
+ const buyNowLocalFulfillment = await orderLocalFulfillmentFromSession({
3960
+ hostId: String(hostId),
3961
+ hostRef,
3962
+ metadata: object == null ? void 0 : object.metadata,
3963
+ shippingAddress: (object == null ? void 0 : (_object_shipping_details1 = object.shipping_details) == null ? void 0 : _object_shipping_details1.address) ? {
3964
+ postalCode: (_object_shipping_details_address_postal_code1 = object.shipping_details.address.postal_code) != null ? _object_shipping_details_address_postal_code1 : null,
3965
+ country: (_object_shipping_details_address_country1 = object.shipping_details.address.country) != null ? _object_shipping_details_address_country1 : null
3966
+ } : null,
3967
+ createdAtMs: Date.now()
3968
+ }).catch((error)=>{
3969
+ console.error('commerce buy-now local fulfillment unreadable', String(object.id), error);
3970
+ return null;
3971
+ });
3843
3972
  const created = await firestore.runTransaction(async (transaction)=>{
3844
- var _counter_get, _ref, _ref1, _ref2, _ref3;
3845
- var _object_metadata, _object_customer_details, _object_customer_details1;
3973
+ var _counter_get, _ref, _ref1, _ref2, _object_shipping_details_name, _object_shipping_details_address_line1, _object_shipping_details_address_line2, _object_shipping_details_address_city, _object_shipping_details_address_state, _object_shipping_details_address_postal_code, _object_shipping_details_address_country, _ref3, _ref4;
3974
+ var _object_metadata, _object_customer_details, _object_shipping_details, _object_customer_details1;
3846
3975
  const [existing, counter] = await Promise.all([
3847
3976
  transaction.get(orderRef),
3848
3977
  transaction.get(counterRef)
@@ -3880,13 +4009,24 @@ async function findOrderForDispute(paymentIntentId) {
3880
4009
  }, typeof (event == null ? void 0 : event.livemode) === 'boolean' ? {
3881
4010
  livemode: event.livemode
3882
4011
  } : {}, {
3883
- customerName: (_ref2 = object == null ? void 0 : (_object_customer_details = object.customer_details) == null ? void 0 : _object_customer_details.name) != null ? _ref2 : null,
4012
+ customerName: (_ref2 = object == null ? void 0 : (_object_customer_details = object.customer_details) == null ? void 0 : _object_customer_details.name) != null ? _ref2 : null
4013
+ }, (object == null ? void 0 : (_object_shipping_details = object.shipping_details) == null ? void 0 : _object_shipping_details.address) ? {
4014
+ shippingAddress: {
4015
+ name: (_object_shipping_details_name = object.shipping_details.name) != null ? _object_shipping_details_name : undefined,
4016
+ line1: (_object_shipping_details_address_line1 = object.shipping_details.address.line1) != null ? _object_shipping_details_address_line1 : undefined,
4017
+ line2: (_object_shipping_details_address_line2 = object.shipping_details.address.line2) != null ? _object_shipping_details_address_line2 : undefined,
4018
+ city: (_object_shipping_details_address_city = object.shipping_details.address.city) != null ? _object_shipping_details_address_city : undefined,
4019
+ state: (_object_shipping_details_address_state = object.shipping_details.address.state) != null ? _object_shipping_details_address_state : undefined,
4020
+ postalCode: (_object_shipping_details_address_postal_code = object.shipping_details.address.postal_code) != null ? _object_shipping_details_address_postal_code : undefined,
4021
+ country: (_object_shipping_details_address_country = object.shipping_details.address.country) != null ? _object_shipping_details_address_country : undefined
4022
+ }
4023
+ } : {}, (_ref3 = buyNowLocalFulfillment == null ? void 0 : buyNowLocalFulfillment.fields) != null ? _ref3 : {}, {
3884
4024
  createdAtMs: Date.now(),
3885
4025
  // Legacy Commerce Starter fields (AGL-90).
3886
4026
  productId,
3887
4027
  amountCents,
3888
4028
  feeCents: Number(feeCents != null ? feeCents : 0),
3889
- customerEmail: (_ref3 = object == null ? void 0 : (_object_customer_details1 = object.customer_details) == null ? void 0 : _object_customer_details1.email) != null ? _ref3 : null
4029
+ customerEmail: (_ref4 = object == null ? void 0 : (_object_customer_details1 = object.customer_details) == null ? void 0 : _object_customer_details1.email) != null ? _ref4 : null
3890
4030
  }, couponCode ? {
3891
4031
  couponCode
3892
4032
  } : {}, {
@@ -3900,10 +4040,18 @@ async function findOrderForDispute(paymentIntentId) {
3900
4040
  const buyNowOrderLabel = CommerceModel.formatOrderNumber({
3901
4041
  number: buyNowOrderNumber
3902
4042
  }, String(object.id));
4043
+ if (buyNowLocalFulfillment == null ? void 0 : buyNowLocalFulfillment.outsideZone) {
4044
+ void notifyHostManagers(String(hostId), {
4045
+ type: 'content.order',
4046
+ title: `Check the delivery address on order ${buyNowOrderLabel}`,
4047
+ body: `Order ${buyNowOrderLabel} on {site} was booked for local delivery, but the ` + 'address entered at payment is outside your delivery zones. Contact the ' + 'buyer, or refund the delivery.',
4048
+ link: `/${hostId}/products`
4049
+ });
4050
+ }
3903
4051
  // In-app order notification (wave v6): host managers see sales
3904
4052
  // in the bell, not just the owner's email: what came in, from whom
3905
4053
  // and where (AGL-3432).
3906
- const buyNowTotal = `$${(Number((_ref78 = object == null ? void 0 : object.amount_total) != null ? _ref78 : 0) / 100).toFixed(2)}`;
4054
+ const buyNowTotal = `$${(Number((_ref79 = object == null ? void 0 : object.amount_total) != null ? _ref79 : 0) / 100).toFixed(2)}`;
3907
4055
  void notifyHostManagers(String(hostId), {
3908
4056
  type: 'content.order',
3909
4057
  title: `New order on {site} — ${buyNowTotal}`,
@@ -4052,15 +4200,15 @@ async function findOrderForDispute(paymentIntentId) {
4052
4200
  hostId: String(hostId),
4053
4201
  identity: {
4054
4202
  email: object == null ? void 0 : (_object_customer_details19 = object.customer_details) == null ? void 0 : _object_customer_details19.email,
4055
- name: (_ref79 = object == null ? void 0 : (_object_customer_details20 = object.customer_details) == null ? void 0 : _object_customer_details20.name) != null ? _ref79 : null
4203
+ name: (_ref80 = object == null ? void 0 : (_object_customer_details20 = object.customer_details) == null ? void 0 : _object_customer_details20.name) != null ? _ref80 : null
4056
4204
  },
4057
4205
  surface: 'relationship',
4058
4206
  lifecycleFloor: 'customer',
4059
- purchaseCents: Number((_ref80 = object == null ? void 0 : object.amount_total) != null ? _ref80 : 0),
4207
+ purchaseCents: Number((_ref81 = object == null ? void 0 : object.amount_total) != null ? _ref81 : 0),
4060
4208
  interaction: {
4061
4209
  source: 'order',
4062
4210
  refId: String(object.id),
4063
- summary: `Placed an order ($${(Number((_ref81 = object == null ? void 0 : object.amount_total) != null ? _ref81 : 0) / 100).toFixed(2)})`
4211
+ summary: `Placed an order ($${(Number((_ref82 = object == null ? void 0 : object.amount_total) != null ? _ref82 : 0) / 100).toFixed(2)})`
4064
4212
  }
4065
4213
  });
4066
4214
  const productRef = hostRef.collection('products').doc(String(productId));
@@ -4078,12 +4226,12 @@ async function findOrderForDispute(paymentIntentId) {
4078
4226
  // serialize; the snapshot above is still read, for the product NAME on
4079
4227
  // the receipt below, and no longer decides any count.
4080
4228
  {
4081
- var _productSnapshot_data2, _ref83;
4229
+ var _productSnapshot_data2, _ref84;
4082
4230
  var _object_metadata34, _lifted_variants_1;
4083
4231
  const lifted = CommerceModel.liftLegacyProduct((_productSnapshot_data2 = productSnapshot.data()) != null ? _productSnapshot_data2 : {
4084
4232
  name: 'Product'
4085
4233
  });
4086
- const soldVariantId = String((_ref83 = (_object_metadata34 = object.metadata) == null ? void 0 : _object_metadata34.variantId) != null ? _ref83 : '') || ((_lifted_variants_1 = lifted.variants[0]) == null ? void 0 : _lifted_variants_1.id);
4234
+ const soldVariantId = String((_ref84 = (_object_metadata34 = object.metadata) == null ? void 0 : _object_metadata34.variantId) != null ? _ref84 : '') || ((_lifted_variants_1 = lifted.variants[0]) == null ? void 0 : _lifted_variants_1.id);
4087
4235
  if (soldVariantId) {
4088
4236
  // AGL-1711: `-1` regardless of how many units were bought, so a
4089
4237
  // 3-unit buy-now sale decremented stock by one and the difference
@@ -4099,7 +4247,9 @@ async function findOrderForDispute(paymentIntentId) {
4099
4247
  ledger: {
4100
4248
  reason: 'sale',
4101
4249
  orderId: String(object.id)
4102
- }
4250
+ },
4251
+ // Off the pickup or delivery location's shelf (AGL-3624).
4252
+ locationId: (buyNowLocalFulfillment == null ? void 0 : buyNowLocalFulfillment.fields.locationId) || undefined
4103
4253
  });
4104
4254
  if (moved.before && moved.after) {
4105
4255
  // Low-stock alert (AGL-281): fires on the crossing sale only,
@@ -4114,14 +4264,14 @@ async function findOrderForDispute(paymentIntentId) {
4114
4264
  // Settlement, the same way round and for the same reason as the cart
4115
4265
  // branch above (AGL-2356): the decrement lands first, then the
4116
4266
  // reservation is dropped.
4117
- await releaseStockHold(hostRef, String((_ref82 = (_object_metadata33 = object.metadata) == null ? void 0 : _object_metadata33.stockHoldKey) != null ? _ref82 : ''));
4267
+ await releaseStockHold(hostRef, String((_ref83 = (_object_metadata33 = object.metadata) == null ? void 0 : _object_metadata33.stockHoldKey) != null ? _ref83 : ''));
4118
4268
  if (couponCode) {
4119
- var _ref84;
4269
+ var _ref85;
4120
4270
  var _object_metadata35;
4121
4271
  await settleRedemption({
4122
4272
  firestore,
4123
4273
  ref: hostRef.collection('coupons').doc(String(couponCode)),
4124
- holdKey: String((_ref84 = (_object_metadata35 = object.metadata) == null ? void 0 : _object_metadata35.couponHoldKey) != null ? _ref84 : ''),
4274
+ holdKey: String((_ref85 = (_object_metadata35 = object.metadata) == null ? void 0 : _object_metadata35.couponHoldKey) != null ? _ref85 : ''),
4125
4275
  orderRef,
4126
4276
  label: `coupon ${couponCode}`,
4127
4277
  detail: `Coupon ${couponCode} was applied to this order but no longer ` + 'exists, so the redemption is uncounted against its limit.'
@@ -4136,22 +4286,23 @@ async function findOrderForDispute(paymentIntentId) {
4136
4286
  // Receipt + seller notification (AGL-96): env-gated like every
4137
4287
  // other outbound email; failures never fail the webhook.
4138
4288
  if (isEmailConfigured()) {
4139
- var _productSnapshot_get, _ref85;
4289
+ var _productSnapshot_get, _ref86;
4140
4290
  var _object_customer_details21, _org, _this3;
4141
4291
  const productName = String((_productSnapshot_get = productSnapshot.get('name')) != null ? _productSnapshot_get : 'your purchase');
4142
- const amount = (Number((_ref85 = object == null ? void 0 : object.amount_total) != null ? _ref85 : 0) / 100).toFixed(2);
4292
+ const amount = (Number((_ref86 = object == null ? void 0 : object.amount_total) != null ? _ref86 : 0) / 100).toFixed(2);
4143
4293
  const buyerEmail = object == null ? void 0 : (_object_customer_details21 = object.customer_details) == null ? void 0 : _object_customer_details21.email;
4144
4294
  const orderTotal = `$${amount}`;
4145
4295
  // The store's receipt switch and the status link (AGL-3610).
4146
4296
  const receiptExtras = await onlineReceiptExtras(String(hostId), String(object.id));
4147
4297
  if (buyerEmail && receiptExtras.enabled) {
4148
- var _ref86, _ref87;
4298
+ var _ref87, _ref88;
4149
4299
  // The store's own Receipt footer, as the cart receipt carries it
4150
4300
  // (AGL-3432).
4151
- const receiptFooter = String((_ref86 = await hostRef.collection('settings').doc('store').get().then((snapshot)=>snapshot.get('receiptFooter')).catch(()=>'')) != null ? _ref86 : '');
4301
+ const receiptFooter = String((_ref87 = await hostRef.collection('settings').doc('store').get().then((snapshot)=>snapshot.get('receiptFooter')).catch(()=>'')) != null ? _ref87 : '');
4152
4302
  // The quantity with the name (AGL-3432), so a three-unit sale
4153
4303
  // does not read as one item at the whole total.
4154
- const receiptLine = `${soldQuantity}× ${productName}`;
4304
+ const receiptLine = `${soldQuantity}× ${productName}` + // Where to collect it, or when it is coming (AGL-3624).
4305
+ ((buyNowLocalFulfillment == null ? void 0 : buyNowLocalFulfillment.summary) ? `\n\n${buyNowLocalFulfillment.summary}` : '');
4155
4306
  const fallbackText = `Thanks for your purchase!\n\n${receiptLine} — $${amount}` + `\nOrder reference: ${object.id}` + (receiptFooter ? `\n\n${receiptFooter}` : '');
4156
4307
  // Site-owner-designed template when published (AGL-771).
4157
4308
  const designed = await renderHostEmailWithTokens(firebaseAdmin.app().firestore(), String(hostId), 'order-receipt', _extends({
@@ -4162,7 +4313,7 @@ async function findOrderForDispute(paymentIntentId) {
4162
4313
  }, receiptExtras.tokens));
4163
4314
  await sendEmail(_extends({
4164
4315
  to: String(buyerEmail),
4165
- subject: (_ref87 = designed == null ? void 0 : designed.subject) != null ? _ref87 : `Receipt: ${productName}`,
4316
+ subject: (_ref88 = designed == null ? void 0 : designed.subject) != null ? _ref88 : `Receipt: ${productName}`,
4166
4317
  text: (designed == null ? void 0 : designed.text) || fallbackText
4167
4318
  }, (designed == null ? void 0 : designed.html) ? {
4168
4319
  html: designed.html
@@ -4196,7 +4347,7 @@ async function findOrderForDispute(paymentIntentId) {
4196
4347
  // SSO.
4197
4348
  const seller = (_this4 = await findUserByUidAcrossPools(sellerUid).catch(()=>null)) == null ? void 0 : _this4.record;
4198
4349
  if (seller == null ? void 0 : seller.email) {
4199
- var _hostSnapshot_get, _ref88;
4350
+ var _hostSnapshot_get, _ref89;
4200
4351
  const siteName = String((_hostSnapshot_get = hostSnapshot.get('displayName')) != null ? _hostSnapshot_get : hostId);
4201
4352
  const fallbackText = `You made a sale on ${siteName}!\n\n${productName} — $${amount}` + (buyerEmail ? `\nBuyer: ${buyerEmail}` : '') + `\nOrder reference: ${object.id}`;
4202
4353
  // Site-owner-designed template when published (AGL-771).
@@ -4209,7 +4360,7 @@ async function findOrderForDispute(paymentIntentId) {
4209
4360
  });
4210
4361
  await sendEmail(_extends({
4211
4362
  to: seller.email,
4212
- subject: (_ref88 = designed == null ? void 0 : designed.subject) != null ? _ref88 : `New order: ${productName}`,
4363
+ subject: (_ref89 = designed == null ? void 0 : designed.subject) != null ? _ref89 : `New order: ${productName}`,
4213
4364
  text: (designed == null ? void 0 : designed.text) || fallbackText
4214
4365
  }, (designed == null ? void 0 : designed.html) ? {
4215
4366
  html: designed.html
@@ -4239,28 +4390,28 @@ async function findOrderForDispute(paymentIntentId) {
4239
4390
  // the refund is written on the order's timeline. Nothing else is moved: a
4240
4391
  // Dashboard refund is staff's act, and they chose its transfer reversal.
4241
4392
  if (type === 'charge.refunded') {
4242
- var _ref89, _ref90;
4393
+ var _ref90, _ref91;
4243
4394
  var _object_payment_intent, _snapshot_ref_parent_parent;
4244
- const paymentIntentId = typeof (object == null ? void 0 : object.payment_intent) === 'string' ? object.payment_intent : String((_ref89 = object == null ? void 0 : (_object_payment_intent = object.payment_intent) == null ? void 0 : _object_payment_intent.id) != null ? _ref89 : '');
4395
+ const paymentIntentId = typeof (object == null ? void 0 : object.payment_intent) === 'string' ? object.payment_intent : String((_ref90 = object == null ? void 0 : (_object_payment_intent = object.payment_intent) == null ? void 0 : _object_payment_intent.id) != null ? _ref90 : '');
4245
4396
  const lookup = paymentIntentId ? await findOrderForDispute(paymentIntentId) : {
4246
4397
  kind: 'not-ours'
4247
4398
  };
4248
4399
  if (lookup.kind !== 'order') return;
4249
4400
  const snapshot = lookup.snapshot;
4250
- const hostId = String((_ref90 = (_snapshot_ref_parent_parent = snapshot.ref.parent.parent) == null ? void 0 : _snapshot_ref_parent_parent.id) != null ? _ref90 : '');
4401
+ const hostId = String((_ref91 = (_snapshot_ref_parent_parent = snapshot.ref.parent.parent) == null ? void 0 : _snapshot_ref_parent_parent.id) != null ? _ref91 : '');
4251
4402
  const external = await recordExternalRefund(snapshot.ref, object);
4252
4403
  if (external && hostId) {
4253
- var _ref91, _ref92, _ref93;
4404
+ var _ref92, _ref93, _ref94;
4254
4405
  var _object_refunds;
4255
4406
  // A Stripe Dashboard refund is a refund to every subscriber too
4256
4407
  // (AGL-3645): accounting books it and the merchant's webhooks hear it,
4257
4408
  // as they do a console refund. Keyed by the charge's refunded total,
4258
4409
  // which the record above only ever advances once per amount.
4259
- const latestRefund = ((_ref91 = object == null ? void 0 : (_object_refunds = object.refunds) == null ? void 0 : _object_refunds.data) != null ? _ref91 : [])[0];
4410
+ const latestRefund = ((_ref92 = object == null ? void 0 : (_object_refunds = object.refunds) == null ? void 0 : _object_refunds.data) != null ? _ref92 : [])[0];
4260
4411
  await raiseOrderEvent(ORDER_REFUNDED_EVENT, {
4261
4412
  hostId,
4262
4413
  orderId: snapshot.id,
4263
- key: `external-refund:${String((_ref92 = object == null ? void 0 : object.id) != null ? _ref92 : paymentIntentId)}:${Number((_ref93 = object == null ? void 0 : object.amount_refunded) != null ? _ref93 : 0)}`,
4414
+ key: `external-refund:${String((_ref93 = object == null ? void 0 : object.id) != null ? _ref93 : paymentIntentId)}:${Number((_ref94 = object == null ? void 0 : object.amount_refunded) != null ? _ref94 : 0)}`,
4264
4415
  extra: {
4265
4416
  refund: {
4266
4417
  id: (latestRefund == null ? void 0 : latestRefund.id) ? String(latestRefund.id) : null,
@@ -4310,16 +4461,16 @@ async function findOrderForDispute(paymentIntentId) {
4310
4461
  // merchant; both report the site so the seller's fraud ledger can name it.
4311
4462
  const risk = paymentRiskEventFrom(type, object);
4312
4463
  if (risk && risk.signal.kind !== 'dispute') {
4313
- var _ref94, _snapshot_data1, _ref95, _ref96;
4464
+ var _ref95, _snapshot_data1, _ref96, _ref97;
4314
4465
  var _snapshot_ref_parent_parent1, _order_totals2;
4315
4466
  const lookup = risk.paymentIntentId ? await findOrderForDispute(risk.paymentIntentId) : {
4316
4467
  kind: 'not-ours'
4317
4468
  };
4318
4469
  if (lookup.kind !== 'order') return;
4319
4470
  const snapshot = lookup.snapshot;
4320
- const hostId = String((_ref94 = (_snapshot_ref_parent_parent1 = snapshot.ref.parent.parent) == null ? void 0 : _snapshot_ref_parent_parent1.id) != null ? _ref94 : '');
4471
+ const hostId = String((_ref95 = (_snapshot_ref_parent_parent1 = snapshot.ref.parent.parent) == null ? void 0 : _snapshot_ref_parent_parent1.id) != null ? _ref95 : '');
4321
4472
  const order = CommerceModel.liftLegacyOrder((_snapshot_data1 = snapshot.data()) != null ? _snapshot_data1 : {});
4322
- const orderCents = Number((_ref95 = (_ref96 = (_order_totals2 = order.totals) == null ? void 0 : _order_totals2.totalCents) != null ? _ref96 : order.amountCents) != null ? _ref95 : 0);
4473
+ const orderCents = Number((_ref96 = (_ref97 = (_order_totals2 = order.totals) == null ? void 0 : _order_totals2.totalCents) != null ? _ref97 : order.amountCents) != null ? _ref96 : 0);
4323
4474
  await recordPaymentRiskOnRecord({
4324
4475
  ref: snapshot.ref,
4325
4476
  signal: risk.signal,
@@ -4382,7 +4533,7 @@ async function findOrderForDispute(paymentIntentId) {
4382
4533
  // finds). AGL-1554 will add its own `closed` branch for the marketplace
4383
4534
  // side; the two coexist because each self-selects by finding its own record.
4384
4535
  if (type === 'charge.dispute.created' || type === 'charge.dispute.closed') {
4385
- var _dispute_payment_intent, _ref97;
4536
+ var _dispute_payment_intent, _ref98;
4386
4537
  var _snapshot_ref_parent_parent2;
4387
4538
  const dispute = object != null ? object : {};
4388
4539
  const paymentIntentId = String((_dispute_payment_intent = dispute.payment_intent) != null ? _dispute_payment_intent : '');
@@ -4408,9 +4559,9 @@ async function findOrderForDispute(paymentIntentId) {
4408
4559
  const claimed = lookup.kind === 'order' || lookup.kind === 'unresolved';
4409
4560
  const snapshot = lookup.kind === 'order' ? lookup.snapshot : null;
4410
4561
  if (snapshot) {
4411
- var _ref98;
4562
+ var _ref99;
4412
4563
  var _snapshot_ref_parent_parent3;
4413
- const hostId = String((_ref98 = (_snapshot_ref_parent_parent3 = snapshot.ref.parent.parent) == null ? void 0 : _snapshot_ref_parent_parent3.id) != null ? _ref98 : '');
4564
+ const hostId = String((_ref99 = (_snapshot_ref_parent_parent3 = snapshot.ref.parent.parent) == null ? void 0 : _snapshot_ref_parent_parent3.id) != null ? _ref99 : '');
4414
4565
  if (type === 'charge.dispute.created') {
4415
4566
  var _snapshot_data2;
4416
4567
  // A chargeback's gift cards are frozen while it is open (AGL-3363).
@@ -4428,7 +4579,7 @@ async function findOrderForDispute(paymentIntentId) {
4428
4579
  }, notifyRiskEvent);
4429
4580
  const { opened, record } = await recordDisputeOpened(snapshot, dispute);
4430
4581
  if (opened && hostId) {
4431
- var _snapshot_data3, _record_evidenceDueByMs, _ref99;
4582
+ var _snapshot_data3, _record_evidenceDueByMs, _ref100;
4432
4583
  // Time-critical — Stripe's evidence window is days, not weeks — so
4433
4584
  // this is awaited rather than fired off with `void`: the handler is
4434
4585
  // serverless and work left running past the response is work the
@@ -4447,16 +4598,16 @@ async function findOrderForDispute(paymentIntentId) {
4447
4598
  },
4448
4599
  amount: `$${(record.amountCents / 100).toFixed(2)}`,
4449
4600
  evidenceDueByMs: (_record_evidenceDueByMs = record.evidenceDueByMs) != null ? _record_evidenceDueByMs : null,
4450
- dedupeKey: `sale:${String((_ref99 = dispute == null ? void 0 : dispute.id) != null ? _ref99 : snapshot.id)}`,
4601
+ dedupeKey: `sale:${String((_ref100 = dispute == null ? void 0 : dispute.id) != null ? _ref100 : snapshot.id)}`,
4451
4602
  staffEvidence: record.reason ? `Reason: ${record.reason.replace(/_/g, ' ')}.` : null
4452
4603
  });
4453
4604
  }
4454
4605
  } else {
4455
- var _ref100, _snapshot_data4, _ref101;
4606
+ var _ref101, _snapshot_data4, _ref102;
4456
4607
  // Lost: the money went back, so the cards it bought are voided. Won
4457
4608
  // (or closed on a warning): the dispute's freeze lifts; a freeze a
4458
4609
  // fraud warning or review put on stays for the merchant (AGL-3363).
4459
- await applyOrderGiftCardRisk(snapshot, String((_ref100 = dispute == null ? void 0 : dispute.status) != null ? _ref100 : '') === 'lost' ? {
4610
+ await applyOrderGiftCardRisk(snapshot, String((_ref101 = dispute == null ? void 0 : dispute.status) != null ? _ref101 : '') === 'lost' ? {
4460
4611
  kind: 'void',
4461
4612
  reason: 'dispute-lost'
4462
4613
  } : {
@@ -4527,7 +4678,7 @@ async function findOrderForDispute(paymentIntentId) {
4527
4678
  // transiently-failed reversal gets its retry — and gated inside on
4528
4679
  // the order's own `reversedTransferCents` marker, so it runs at most
4529
4680
  // once per dispute however many times Stripe delivers.
4530
- if (String((_ref101 = dispute == null ? void 0 : dispute.status) != null ? _ref101 : '') === 'lost') {
4681
+ if (String((_ref102 = dispute == null ? void 0 : dispute.status) != null ? _ref102 : '') === 'lost') {
4531
4682
  const pulledBackCents = await reverseSellerShare(snapshot.ref, dispute);
4532
4683
  // The merchant-experience half of the AGL-1794 decision, and not an
4533
4684
  // optional extra: the reversal takes money out of the connected
@@ -4557,7 +4708,7 @@ async function findOrderForDispute(paymentIntentId) {
4557
4708
  return _extends({
4558
4709
  claimed
4559
4710
  }, snapshot ? {
4560
- hostId: String((_ref97 = (_snapshot_ref_parent_parent2 = snapshot.ref.parent.parent) == null ? void 0 : _snapshot_ref_parent_parent2.id) != null ? _ref97 : '')
4711
+ hostId: String((_ref98 = (_snapshot_ref_parent_parent2 = snapshot.ref.parent.parent) == null ? void 0 : _snapshot_ref_parent_parent2.id) != null ? _ref98 : '')
4561
4712
  } : {});
4562
4713
  }
4563
4714
  };