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

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 (590) hide show
  1. package/package.json +13 -13
  2. package/src/index.d.ts +0 -1
  3. package/src/index.js +0 -1
  4. package/src/index.js.map +1 -1
  5. package/src/lib/barcode/barcode-decode.d.ts +71 -0
  6. package/src/lib/barcode/barcode-decode.js +498 -0
  7. package/src/lib/barcode/barcode-decode.js.map +1 -0
  8. package/src/lib/barcode/barcode-encode.d.ts +44 -0
  9. package/src/lib/barcode/barcode-encode.js +138 -0
  10. package/src/lib/barcode/barcode-encode.js.map +1 -0
  11. package/src/lib/barcode/barcode-scanner.component.d.ts +23 -0
  12. package/src/lib/barcode/barcode-scanner.component.js +260 -0
  13. package/src/lib/barcode/barcode-scanner.component.js.map +1 -0
  14. package/src/lib/barcode/scan-button.component.d.ts +10 -0
  15. package/src/lib/barcode/scan-button.component.js +62 -0
  16. package/src/lib/barcode/scan-button.component.js.map +1 -0
  17. package/src/lib/barcode/scanner-wedge.d.ts +35 -0
  18. package/src/lib/barcode/scanner-wedge.js +108 -0
  19. package/src/lib/barcode/scanner-wedge.js.map +1 -0
  20. package/src/lib/components/account.js +10 -1
  21. package/src/lib/components/account.js.map +1 -1
  22. package/src/lib/components/cart-extras.d.ts +45 -0
  23. package/src/lib/components/cart-extras.js +127 -0
  24. package/src/lib/components/cart-extras.js.map +1 -0
  25. package/src/lib/components/cart.js +82 -15
  26. package/src/lib/components/cart.js.map +1 -1
  27. package/src/lib/components/checkout-return-notice.d.ts +26 -0
  28. package/src/lib/components/checkout-return-notice.js +48 -0
  29. package/src/lib/components/checkout-return-notice.js.map +1 -0
  30. package/src/lib/components/commerce-console-page.js +124 -0
  31. package/src/lib/components/commerce-console-page.js.map +1 -1
  32. package/src/lib/components/commerce-console-sections.d.ts +1 -1
  33. package/src/lib/components/commerce-console-sections.js +4 -0
  34. package/src/lib/components/commerce-console-sections.js.map +1 -1
  35. package/src/lib/components/console/bulk-fulfill.d.ts +44 -0
  36. package/src/lib/components/console/bulk-fulfill.js +84 -0
  37. package/src/lib/components/console/bulk-fulfill.js.map +1 -0
  38. package/src/lib/components/console/customer-notifications-card.component.d.ts +19 -0
  39. package/src/lib/components/console/customer-notifications-card.component.js +149 -0
  40. package/src/lib/components/console/customer-notifications-card.component.js.map +1 -0
  41. package/src/lib/components/console/host-orders-card.component.js +84 -5
  42. package/src/lib/components/console/host-orders-card.component.js.map +1 -1
  43. package/src/lib/components/console/locations-card.component.js +164 -57
  44. package/src/lib/components/console/locations-card.component.js.map +1 -1
  45. package/src/lib/components/console/order-detail-dialog.component.js +209 -61
  46. package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
  47. package/src/lib/components/console/order-fulfillment-panel.component.d.ts +48 -0
  48. package/src/lib/components/console/order-fulfillment-panel.component.js +542 -0
  49. package/src/lib/components/console/order-fulfillment-panel.component.js.map +1 -0
  50. package/src/lib/components/console/order-invoice.component.d.ts +33 -0
  51. package/src/lib/components/console/order-invoice.component.js +81 -0
  52. package/src/lib/components/console/order-invoice.component.js.map +1 -0
  53. package/src/lib/components/console/order-receipt-resend.component.d.ts +21 -0
  54. package/src/lib/components/console/order-receipt-resend.component.js +203 -0
  55. package/src/lib/components/console/order-receipt-resend.component.js.map +1 -0
  56. package/src/lib/components/console/order-returns.component.d.ts +36 -0
  57. package/src/lib/components/console/order-returns.component.js +142 -0
  58. package/src/lib/components/console/order-returns.component.js.map +1 -0
  59. package/src/lib/components/console/order-shipping-actions.component.d.ts +54 -0
  60. package/src/lib/components/console/order-shipping-actions.component.js +149 -0
  61. package/src/lib/components/console/order-shipping-actions.component.js.map +1 -0
  62. package/src/lib/components/console/order-webhooks-card.component.d.ts +50 -0
  63. package/src/lib/components/console/order-webhooks-card.component.js +623 -0
  64. package/src/lib/components/console/order-webhooks-card.component.js.map +1 -0
  65. package/src/lib/components/console/order-zones.d.ts +155 -0
  66. package/src/lib/components/console/order-zones.js +40 -0
  67. package/src/lib/components/console/order-zones.js.map +1 -0
  68. package/src/lib/components/console/payment-methods-card.component.d.ts +67 -0
  69. package/src/lib/components/console/payment-methods-card.component.js +395 -0
  70. package/src/lib/components/console/payment-methods-card.component.js.map +1 -0
  71. package/src/lib/components/console/pos/pos-api.d.ts +124 -0
  72. package/src/lib/components/console/pos/pos-api.js +101 -0
  73. package/src/lib/components/console/pos/pos-api.js.map +1 -0
  74. package/src/lib/components/console/pos/pos-cart-panel.component.d.ts +36 -0
  75. package/src/lib/components/console/pos/pos-cart-panel.component.js +160 -0
  76. package/src/lib/components/console/pos/pos-cart-panel.component.js.map +1 -0
  77. package/src/lib/components/console/pos/pos-item-dialog.component.d.ts +39 -0
  78. package/src/lib/components/console/pos/pos-item-dialog.component.js +352 -0
  79. package/src/lib/components/console/pos/pos-item-dialog.component.js.map +1 -0
  80. package/src/lib/components/console/pos/pos-product-grid.component.d.ts +47 -0
  81. package/src/lib/components/console/pos/pos-product-grid.component.js +272 -0
  82. package/src/lib/components/console/pos/pos-product-grid.component.js.map +1 -0
  83. package/src/lib/components/console/pos/pos-receipt-panel.component.d.ts +27 -0
  84. package/src/lib/components/console/pos/pos-receipt-panel.component.js +282 -0
  85. package/src/lib/components/console/pos/pos-receipt-panel.component.js.map +1 -0
  86. package/src/lib/components/console/pos/pos-tender-dialogs.component.d.ts +53 -0
  87. package/src/lib/components/console/pos/pos-tender-dialogs.component.js +445 -0
  88. package/src/lib/components/console/pos/pos-tender-dialogs.component.js.map +1 -0
  89. package/src/lib/components/console/pos/pos-tender-panel.component.d.ts +31 -0
  90. package/src/lib/components/console/pos/pos-tender-panel.component.js +640 -0
  91. package/src/lib/components/console/pos/pos-tender-panel.component.js.map +1 -0
  92. package/src/lib/components/console/pos/use-pos-display.d.ts +43 -0
  93. package/src/lib/components/console/pos/use-pos-display.js +161 -0
  94. package/src/lib/components/console/pos/use-pos-display.js.map +1 -0
  95. package/src/lib/components/console/pos-devices-card.component.d.ts +14 -0
  96. package/src/lib/components/console/pos-devices-card.component.js +546 -0
  97. package/src/lib/components/console/pos-devices-card.component.js.map +1 -0
  98. package/src/lib/components/console/pos-display/brand-screen.d.ts +33 -0
  99. package/src/lib/components/console/pos-display/brand-screen.js +85 -0
  100. package/src/lib/components/console/pos-display/brand-screen.js.map +1 -0
  101. package/src/lib/components/console/pos-display/cart-screen.d.ts +29 -0
  102. package/src/lib/components/console/pos-display/cart-screen.js +166 -0
  103. package/src/lib/components/console/pos-display/cart-screen.js.map +1 -0
  104. package/src/lib/components/console/pos-display/pairing-screen.d.ts +24 -0
  105. package/src/lib/components/console/pos-display/pairing-screen.js +127 -0
  106. package/src/lib/components/console/pos-display/pairing-screen.js.map +1 -0
  107. package/src/lib/components/console/pos-display/pos-display-api.d.ts +90 -0
  108. package/src/lib/components/console/pos-display/pos-display-api.js +140 -0
  109. package/src/lib/components/console/pos-display/pos-display-api.js.map +1 -0
  110. package/src/lib/components/console/pos-display/receipt-screen.d.ts +35 -0
  111. package/src/lib/components/console/pos-display/receipt-screen.js +200 -0
  112. package/src/lib/components/console/pos-display/receipt-screen.js.map +1 -0
  113. package/src/lib/components/console/pos-display/tip-screen.d.ts +36 -0
  114. package/src/lib/components/console/pos-display/tip-screen.js +211 -0
  115. package/src/lib/components/console/pos-display/tip-screen.js.map +1 -0
  116. package/src/lib/components/console/pos-display-page.component.d.ts +16 -0
  117. package/src/lib/components/console/pos-display-page.component.js +407 -0
  118. package/src/lib/components/console/pos-display-page.component.js.map +1 -0
  119. package/src/lib/components/console/pos-ops/pos-customer-lookup.component.d.ts +40 -0
  120. package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js +289 -0
  121. package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js.map +1 -0
  122. package/src/lib/components/console/pos-ops/pos-last-receipt.component.d.ts +34 -0
  123. package/src/lib/components/console/pos-ops/pos-last-receipt.component.js +61 -0
  124. package/src/lib/components/console/pos-ops/pos-last-receipt.component.js.map +1 -0
  125. package/src/lib/components/console/pos-ops/pos-operations-bar.component.d.ts +38 -0
  126. package/src/lib/components/console/pos-ops/pos-operations-bar.component.js +129 -0
  127. package/src/lib/components/console/pos-ops/pos-operations-bar.component.js.map +1 -0
  128. package/src/lib/components/console/pos-ops/pos-ops-api.d.ts +37 -0
  129. package/src/lib/components/console/pos-ops/pos-ops-api.js +60 -0
  130. package/src/lib/components/console/pos-ops/pos-ops-api.js.map +1 -0
  131. package/src/lib/components/console/pos-ops/pos-pin-pad.component.d.ts +48 -0
  132. package/src/lib/components/console/pos-ops/pos-pin-pad.component.js +220 -0
  133. package/src/lib/components/console/pos-ops/pos-pin-pad.component.js.map +1 -0
  134. package/src/lib/components/console/pos-ops/pos-receipt-actions.component.d.ts +63 -0
  135. package/src/lib/components/console/pos-ops/pos-receipt-actions.component.js +186 -0
  136. package/src/lib/components/console/pos-ops/pos-receipt-actions.component.js.map +1 -0
  137. package/src/lib/components/console/pos-ops/pos-receipt.d.ts +39 -0
  138. package/src/lib/components/console/pos-ops/pos-receipt.js +182 -0
  139. package/src/lib/components/console/pos-ops/pos-receipt.js.map +1 -0
  140. package/src/lib/components/console/pos-ops/pos-return-dialog.component.d.ts +40 -0
  141. package/src/lib/components/console/pos-ops/pos-return-dialog.component.js +465 -0
  142. package/src/lib/components/console/pos-ops/pos-return-dialog.component.js.map +1 -0
  143. package/src/lib/components/console/pos-ops/pos-shift-history-card.component.d.ts +29 -0
  144. package/src/lib/components/console/pos-ops/pos-shift-history-card.component.js +253 -0
  145. package/src/lib/components/console/pos-ops/pos-shift-history-card.component.js.map +1 -0
  146. package/src/lib/components/console/pos-ops/pos-shift-panel.component.d.ts +34 -0
  147. package/src/lib/components/console/pos-ops/pos-shift-panel.component.js +494 -0
  148. package/src/lib/components/console/pos-ops/pos-shift-panel.component.js.map +1 -0
  149. package/src/lib/components/console/pos-ops/pos-shift-report.component.d.ts +37 -0
  150. package/src/lib/components/console/pos-ops/pos-shift-report.component.js +102 -0
  151. package/src/lib/components/console/pos-ops/pos-shift-report.component.js.map +1 -0
  152. package/src/lib/components/console/pos-ops/pos-staff-pins-card.component.d.ts +30 -0
  153. package/src/lib/components/console/pos-ops/pos-staff-pins-card.component.js +277 -0
  154. package/src/lib/components/console/pos-ops/pos-staff-pins-card.component.js.map +1 -0
  155. package/src/lib/components/console/pos-ops/register-ops.d.ts +28 -0
  156. package/src/lib/components/console/pos-ops/register-ops.js +28 -0
  157. package/src/lib/components/console/pos-ops/register-ops.js.map +1 -0
  158. package/src/lib/components/console/pos-ops/use-pos-cashier.d.ts +46 -0
  159. package/src/lib/components/console/pos-ops/use-pos-cashier.js +128 -0
  160. package/src/lib/components/console/pos-ops/use-pos-cashier.js.map +1 -0
  161. package/src/lib/components/console/pos-ops/use-pos-sale-receipt.d.ts +36 -0
  162. package/src/lib/components/console/pos-ops/use-pos-sale-receipt.js +64 -0
  163. package/src/lib/components/console/pos-ops/use-pos-sale-receipt.js.map +1 -0
  164. package/src/lib/components/console/pos-page.component.d.ts +13 -5
  165. package/src/lib/components/console/pos-page.component.js +729 -657
  166. package/src/lib/components/console/pos-page.component.js.map +1 -1
  167. package/src/lib/components/console/product-editor-dialog.component.js +51 -7
  168. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
  169. package/src/lib/components/console/product-labels-dialog.component.d.ts +13 -0
  170. package/src/lib/components/console/product-labels-dialog.component.js +197 -0
  171. package/src/lib/components/console/product-labels-dialog.component.js.map +1 -0
  172. package/src/lib/components/console/product-register-fields.component.d.ts +19 -0
  173. package/src/lib/components/console/product-register-fields.component.js +241 -0
  174. package/src/lib/components/console/product-register-fields.component.js.map +1 -0
  175. package/src/lib/components/console/product-zones.d.ts +58 -2
  176. package/src/lib/components/console/product-zones.js +10 -2
  177. package/src/lib/components/console/product-zones.js.map +1 -1
  178. package/src/lib/components/console/products-hub-card.component.js +51 -11
  179. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  180. package/src/lib/components/console/register-hardware-card.component.d.ts +33 -0
  181. package/src/lib/components/console/register-hardware-card.component.js +826 -0
  182. package/src/lib/components/console/register-hardware-card.component.js.map +1 -0
  183. package/src/lib/components/console/registers-card.component.js +24 -5
  184. package/src/lib/components/console/registers-card.component.js.map +1 -1
  185. package/src/lib/components/console/return-actions.d.ts +38 -0
  186. package/src/lib/components/console/return-actions.js +76 -0
  187. package/src/lib/components/console/return-actions.js.map +1 -0
  188. package/src/lib/components/console/return-detail-dialog.component.d.ts +34 -0
  189. package/src/lib/components/console/return-detail-dialog.component.js +718 -0
  190. package/src/lib/components/console/return-detail-dialog.component.js.map +1 -0
  191. package/src/lib/components/console/return-settings-card.component.d.ts +34 -0
  192. package/src/lib/components/console/return-settings-card.component.js +195 -0
  193. package/src/lib/components/console/return-settings-card.component.js.map +1 -0
  194. package/src/lib/components/console/return-zones.d.ts +87 -0
  195. package/src/lib/components/console/return-zones.js +31 -0
  196. package/src/lib/components/console/return-zones.js.map +1 -0
  197. package/src/lib/components/console/returns-card.component.d.ts +45 -0
  198. package/src/lib/components/console/returns-card.component.js +159 -0
  199. package/src/lib/components/console/returns-card.component.js.map +1 -0
  200. package/src/lib/components/console/shipping-settings-card.component.js +161 -5
  201. package/src/lib/components/console/shipping-settings-card.component.js.map +1 -1
  202. package/src/lib/components/console/shippingeasy-card.component.d.ts +59 -0
  203. package/src/lib/components/console/shippingeasy-card.component.js +378 -0
  204. package/src/lib/components/console/shippingeasy-card.component.js.map +1 -0
  205. package/src/lib/components/console/shipstation-card.component.d.ts +35 -0
  206. package/src/lib/components/console/shipstation-card.component.js +345 -0
  207. package/src/lib/components/console/shipstation-card.component.js.map +1 -0
  208. package/src/lib/components/console/start-return-dialog.component.d.ts +40 -0
  209. package/src/lib/components/console/start-return-dialog.component.js +243 -0
  210. package/src/lib/components/console/start-return-dialog.component.js.map +1 -0
  211. package/src/lib/components/console/stock-movements-card.component.js +2 -1
  212. package/src/lib/components/console/stock-movements-card.component.js.map +1 -1
  213. package/src/lib/components/console/store-settings-card.component.js +2 -66
  214. package/src/lib/components/console/store-settings-card.component.js.map +1 -1
  215. package/src/lib/components/console/store-zones.d.ts +42 -0
  216. package/src/lib/components/console/store-zones.js +39 -0
  217. package/src/lib/components/console/store-zones.js.map +1 -0
  218. package/src/lib/components/order-status.d.ts +36 -0
  219. package/src/lib/components/order-status.js +502 -0
  220. package/src/lib/components/order-status.js.map +1 -0
  221. package/src/lib/components/product-detail.js +44 -2
  222. package/src/lib/components/product-detail.js.map +1 -1
  223. package/src/lib/components/return-request.d.ts +32 -0
  224. package/src/lib/components/return-request.js +576 -0
  225. package/src/lib/components/return-request.js.map +1 -0
  226. package/src/lib/components/storefront-payment-appearance.d.ts +32 -0
  227. package/src/lib/components/storefront-payment-appearance.js +114 -0
  228. package/src/lib/components/storefront-payment-appearance.js.map +1 -0
  229. package/src/lib/components/storefront-payment-element.d.ts +57 -1
  230. package/src/lib/components/storefront-payment-element.js +332 -33
  231. package/src/lib/components/storefront-payment-element.js.map +1 -1
  232. package/src/lib/constants/order-status.d.ts +22 -0
  233. package/src/lib/constants/order-status.js +22 -0
  234. package/src/lib/constants/order-status.js.map +1 -0
  235. package/src/lib/constants/return-request.d.ts +27 -0
  236. package/src/lib/constants/return-request.js +32 -0
  237. package/src/lib/constants/return-request.js.map +1 -0
  238. package/src/lib/constants/stock-movement-query.js +2 -1
  239. package/src/lib/constants/stock-movement-query.js.map +1 -1
  240. package/src/lib/declarations.console-server.js +9 -0
  241. package/src/lib/declarations.console-server.js.map +1 -1
  242. package/src/lib/declarations.server.d.ts +2 -1
  243. package/src/lib/declarations.server.js +47 -1
  244. package/src/lib/declarations.server.js.map +1 -1
  245. package/src/lib/model/buyer-notifications.d.ts +59 -0
  246. package/src/lib/model/buyer-notifications.js +97 -0
  247. package/src/lib/model/buyer-notifications.js.map +1 -0
  248. package/src/lib/model/commerce-orders.d.ts +127 -1
  249. package/src/lib/model/commerce-orders.js +24 -15
  250. package/src/lib/model/commerce-orders.js.map +1 -1
  251. package/src/lib/model/commerce-payment-methods.d.ts +146 -0
  252. package/src/lib/model/commerce-payment-methods.js +247 -0
  253. package/src/lib/model/commerce-payment-methods.js.map +1 -0
  254. package/src/lib/model/commerce-pos-ops.d.ts +437 -0
  255. package/src/lib/model/commerce-pos-ops.js +721 -0
  256. package/src/lib/model/commerce-pos-ops.js.map +1 -0
  257. package/src/lib/model/commerce-pos.d.ts +263 -0
  258. package/src/lib/model/commerce-pos.js +375 -0
  259. package/src/lib/model/commerce-pos.js.map +1 -0
  260. package/src/lib/model/commerce-printers.d.ts +211 -0
  261. package/src/lib/model/commerce-printers.js +181 -0
  262. package/src/lib/model/commerce-printers.js.map +1 -0
  263. package/src/lib/model/commerce-receipt.d.ts +97 -0
  264. package/src/lib/model/commerce-receipt.js +180 -0
  265. package/src/lib/model/commerce-receipt.js.map +1 -0
  266. package/src/lib/model/commerce-returns.d.ts +207 -0
  267. package/src/lib/model/commerce-returns.js +300 -0
  268. package/src/lib/model/commerce-returns.js.map +1 -0
  269. package/src/lib/model/commerce-shipping.d.ts +44 -1
  270. package/src/lib/model/commerce-shipping.js +53 -9
  271. package/src/lib/model/commerce-shipping.js.map +1 -1
  272. package/src/lib/model/commerce-tax-engine.d.ts +90 -0
  273. package/src/lib/model/commerce-tax-engine.js +129 -0
  274. package/src/lib/model/commerce-tax-engine.js.map +1 -0
  275. package/src/lib/model/commerce.d.ts +88 -2
  276. package/src/lib/model/commerce.js +89 -1
  277. package/src/lib/model/commerce.js.map +1 -1
  278. package/src/lib/model/index.d.ts +15 -0
  279. package/src/lib/model/index.js +15 -0
  280. package/src/lib/model/index.js.map +1 -1
  281. package/src/lib/model/order-events.d.ts +154 -0
  282. package/src/lib/model/order-events.js +153 -0
  283. package/src/lib/model/order-events.js.map +1 -0
  284. package/src/lib/model/order-fulfillment.d.ts +117 -0
  285. package/src/lib/model/order-fulfillment.js +217 -0
  286. package/src/lib/model/order-fulfillment.js.map +1 -0
  287. package/src/lib/model/order-history.d.ts +58 -0
  288. package/src/lib/model/order-history.js +22 -0
  289. package/src/lib/model/order-history.js.map +1 -0
  290. package/src/lib/model/order-list-fields.d.ts +9 -1
  291. package/src/lib/model/order-list-fields.js +7 -1
  292. package/src/lib/model/order-list-fields.js.map +1 -1
  293. package/src/lib/model/order-shipping-export.d.ts +91 -0
  294. package/src/lib/model/order-shipping-export.js +192 -0
  295. package/src/lib/model/order-shipping-export.js.map +1 -0
  296. package/src/lib/model/order-status-view.d.ts +103 -0
  297. package/src/lib/model/order-status-view.js +156 -0
  298. package/src/lib/model/order-status-view.js.map +1 -0
  299. package/src/lib/model/order-webhooks.d.ts +142 -0
  300. package/src/lib/model/order-webhooks.js +91 -0
  301. package/src/lib/model/order-webhooks.js.map +1 -0
  302. package/src/lib/model/product-channel.d.ts +56 -0
  303. package/src/lib/model/product-channel.js +86 -0
  304. package/src/lib/model/product-channel.js.map +1 -0
  305. package/src/lib/model/product-modifiers.d.ts +80 -0
  306. package/src/lib/model/product-modifiers.js +140 -0
  307. package/src/lib/model/product-modifiers.js.map +1 -0
  308. package/src/lib/model/shippingeasy.d.ts +94 -0
  309. package/src/lib/model/shippingeasy.js +267 -0
  310. package/src/lib/model/shippingeasy.js.map +1 -0
  311. package/src/lib/model/shipstation.d.ts +125 -0
  312. package/src/lib/model/shipstation.js +399 -0
  313. package/src/lib/model/shipstation.js.map +1 -0
  314. package/src/lib/model/smart-collection-reads.d.ts +32 -0
  315. package/src/lib/model/smart-collection-reads.js +63 -0
  316. package/src/lib/model/smart-collection-reads.js.map +1 -0
  317. package/src/lib/model/tracking-url.d.ts +74 -0
  318. package/src/lib/model/tracking-url.js +159 -0
  319. package/src/lib/model/tracking-url.js.map +1 -0
  320. package/src/lib/plugin-config.d.ts +13 -0
  321. package/src/lib/plugin-config.js +77 -5
  322. package/src/lib/plugin-config.js.map +1 -1
  323. package/src/lib/plugin.js +23 -0
  324. package/src/lib/plugin.js.map +1 -1
  325. package/src/lib/pos-ops-config.d.ts +39 -0
  326. package/src/lib/pos-ops-config.js +88 -0
  327. package/src/lib/pos-ops-config.js.map +1 -0
  328. package/src/lib/printing/print-document.d.ts +106 -0
  329. package/src/lib/printing/print-document.js +464 -0
  330. package/src/lib/printing/print-document.js.map +1 -0
  331. package/src/lib/printing/product-labels.d.ts +58 -0
  332. package/src/lib/printing/product-labels.js +116 -0
  333. package/src/lib/printing/product-labels.js.map +1 -0
  334. package/src/lib/printing/render-epson.d.ts +57 -0
  335. package/src/lib/printing/render-epson.js +135 -0
  336. package/src/lib/printing/render-epson.js.map +1 -0
  337. package/src/lib/printing/render-star.d.ts +45 -0
  338. package/src/lib/printing/render-star.js +100 -0
  339. package/src/lib/printing/render-star.js.map +1 -0
  340. package/src/lib/printing/render-text.d.ts +25 -0
  341. package/src/lib/printing/render-text.js +53 -0
  342. package/src/lib/printing/render-text.js.map +1 -0
  343. package/src/lib/server/api-v1/openapi.js +27 -4
  344. package/src/lib/server/api-v1/openapi.js.map +1 -1
  345. package/src/lib/server/api-v1/order-view.d.ts +67 -0
  346. package/src/lib/server/api-v1/order-view.js +120 -0
  347. package/src/lib/server/api-v1/order-view.js.map +1 -0
  348. package/src/lib/server/api-v1/orders-and-products.js +58 -65
  349. package/src/lib/server/api-v1/orders-and-products.js.map +1 -1
  350. package/src/lib/server/billing-webhook.js +135 -28
  351. package/src/lib/server/billing-webhook.js.map +1 -1
  352. package/src/lib/server/cancel-order.js +29 -0
  353. package/src/lib/server/cancel-order.js.map +1 -1
  354. package/src/lib/server/carrier-rates-availability.d.ts +26 -0
  355. package/src/lib/server/carrier-rates-availability.js +76 -0
  356. package/src/lib/server/carrier-rates-availability.js.map +1 -0
  357. package/src/lib/server/carrier-shipping.d.ts +84 -0
  358. package/src/lib/server/carrier-shipping.js +135 -0
  359. package/src/lib/server/carrier-shipping.js.map +1 -0
  360. package/src/lib/server/cart-checkout.d.ts +2 -0
  361. package/src/lib/server/cart-checkout.js +226 -22
  362. package/src/lib/server/cart-checkout.js.map +1 -1
  363. package/src/lib/server/checkout-extras.d.ts +64 -0
  364. package/src/lib/server/checkout-extras.js +157 -0
  365. package/src/lib/server/checkout-extras.js.map +1 -0
  366. package/src/lib/server/{feed.d.ts → checkout-status.d.ts} +2 -7
  367. package/src/lib/server/checkout-status.js +111 -0
  368. package/src/lib/server/checkout-status.js.map +1 -0
  369. package/src/lib/server/checkout.js +86 -9
  370. package/src/lib/server/checkout.js.map +1 -1
  371. package/src/lib/server/draft-order.js +34 -5
  372. package/src/lib/server/draft-order.js.map +1 -1
  373. package/src/lib/server/fulfill-order.d.ts +127 -10
  374. package/src/lib/server/fulfill-order.js +451 -49
  375. package/src/lib/server/fulfill-order.js.map +1 -1
  376. package/src/lib/server/legacy-feed.d.ts +28 -0
  377. package/src/lib/server/legacy-feed.js +52 -0
  378. package/src/lib/server/legacy-feed.js.map +1 -0
  379. package/src/lib/server/native-checkout.d.ts +34 -15
  380. package/src/lib/server/native-checkout.js +32 -17
  381. package/src/lib/server/native-checkout.js.map +1 -1
  382. package/src/lib/server/order-event-host-emit.d.ts +23 -0
  383. package/src/lib/server/order-event-host-emit.js +30 -0
  384. package/src/lib/server/order-event-host-emit.js.map +1 -0
  385. package/src/lib/server/order-event-triggers.d.ts +37 -0
  386. package/src/lib/server/order-event-triggers.js +119 -0
  387. package/src/lib/server/order-event-triggers.js.map +1 -0
  388. package/src/lib/server/order-events.d.ts +62 -0
  389. package/src/lib/server/order-events.js +109 -0
  390. package/src/lib/server/order-events.js.map +1 -0
  391. package/src/lib/server/order-history.d.ts +23 -0
  392. package/src/lib/server/order-history.js +65 -0
  393. package/src/lib/server/order-history.js.map +1 -0
  394. package/src/lib/server/order-notifications.d.ts +185 -0
  395. package/src/lib/server/order-notifications.js +567 -0
  396. package/src/lib/server/order-notifications.js.map +1 -0
  397. package/src/lib/server/order-receipt-send.d.ts +39 -0
  398. package/src/lib/server/order-receipt-send.js +151 -0
  399. package/src/lib/server/order-receipt-send.js.map +1 -0
  400. package/src/lib/server/order-status-token.d.ts +31 -0
  401. package/src/lib/server/order-status-token.js +75 -0
  402. package/src/lib/server/order-status-token.js.map +1 -0
  403. package/src/lib/server/order-status.d.ts +58 -0
  404. package/src/lib/server/order-status.js +141 -0
  405. package/src/lib/server/order-status.js.map +1 -0
  406. package/src/lib/server/order-webhooks.d.ts +108 -0
  407. package/src/lib/server/order-webhooks.js +622 -0
  408. package/src/lib/server/order-webhooks.js.map +1 -0
  409. package/src/lib/server/orders-shipping-prepare.d.ts +26 -0
  410. package/src/lib/server/orders-shipping-prepare.js +121 -0
  411. package/src/lib/server/orders-shipping-prepare.js.map +1 -0
  412. package/src/lib/server/payment-method-domains.d.ts +209 -0
  413. package/src/lib/server/payment-method-domains.js +444 -0
  414. package/src/lib/server/payment-method-domains.js.map +1 -0
  415. package/src/lib/server/payment-methods.d.ts +77 -0
  416. package/src/lib/server/payment-methods.js +284 -0
  417. package/src/lib/server/payment-methods.js.map +1 -0
  418. package/src/lib/server/pos-auth.d.ts +50 -0
  419. package/src/lib/server/pos-auth.js +121 -0
  420. package/src/lib/server/pos-auth.js.map +1 -0
  421. package/src/lib/server/pos-customer.d.ts +49 -0
  422. package/src/lib/server/pos-customer.js +257 -0
  423. package/src/lib/server/pos-customer.js.map +1 -0
  424. package/src/lib/server/pos-display.d.ts +57 -0
  425. package/src/lib/server/pos-display.js +419 -0
  426. package/src/lib/server/pos-display.js.map +1 -0
  427. package/src/lib/server/pos-fee-month.d.ts +34 -0
  428. package/src/lib/server/pos-fee-month.js +36 -0
  429. package/src/lib/server/pos-fee-month.js.map +1 -0
  430. package/src/lib/server/pos-ops-gate.d.ts +161 -0
  431. package/src/lib/server/pos-ops-gate.js +295 -0
  432. package/src/lib/server/pos-ops-gate.js.map +1 -0
  433. package/src/lib/server/pos-ops-routes.d.ts +22 -0
  434. package/src/lib/server/pos-ops-routes.js +36 -0
  435. package/src/lib/server/pos-ops-routes.js.map +1 -0
  436. package/src/lib/server/pos-order.d.ts +1 -18
  437. package/src/lib/server/pos-order.js +222 -42
  438. package/src/lib/server/pos-order.js.map +1 -1
  439. package/src/lib/server/pos-payment.d.ts +39 -0
  440. package/src/lib/server/pos-payment.js +922 -0
  441. package/src/lib/server/pos-payment.js.map +1 -0
  442. package/src/lib/server/pos-print.d.ts +82 -0
  443. package/src/lib/server/pos-print.js +193 -0
  444. package/src/lib/server/pos-print.js.map +1 -0
  445. package/src/lib/server/pos-readers.d.ts +27 -0
  446. package/src/lib/server/pos-readers.js +276 -0
  447. package/src/lib/server/pos-readers.js.map +1 -0
  448. package/src/lib/server/pos-refund-authority.d.ts +60 -0
  449. package/src/lib/server/pos-refund-authority.js +72 -0
  450. package/src/lib/server/pos-refund-authority.js.map +1 -0
  451. package/src/lib/server/pos-return.d.ts +58 -0
  452. package/src/lib/server/pos-return.js +891 -0
  453. package/src/lib/server/pos-return.js.map +1 -0
  454. package/src/lib/server/pos-sale-stamp.d.ts +66 -0
  455. package/src/lib/server/pos-sale-stamp.js +84 -0
  456. package/src/lib/server/pos-sale-stamp.js.map +1 -0
  457. package/src/lib/server/pos-sale.d.ts +170 -0
  458. package/src/lib/server/pos-sale.js +461 -0
  459. package/src/lib/server/pos-sale.js.map +1 -0
  460. package/src/lib/server/pos-shift.d.ts +45 -0
  461. package/src/lib/server/pos-shift.js +484 -0
  462. package/src/lib/server/pos-shift.js.map +1 -0
  463. package/src/lib/server/pos-staff-pin.d.ts +26 -0
  464. package/src/lib/server/pos-staff-pin.js +339 -0
  465. package/src/lib/server/pos-staff-pin.js.map +1 -0
  466. package/src/lib/server/pos-stripe.d.ts +91 -0
  467. package/src/lib/server/pos-stripe.js +129 -0
  468. package/src/lib/server/pos-stripe.js.map +1 -0
  469. package/src/lib/server/pos-terminal-connection-token.d.ts +48 -0
  470. package/src/lib/server/pos-terminal-connection-token.js +166 -0
  471. package/src/lib/server/pos-terminal-connection-token.js.map +1 -0
  472. package/src/lib/server/pos-terminal.d.ts +218 -0
  473. package/src/lib/server/pos-terminal.js +1014 -0
  474. package/src/lib/server/pos-terminal.js.map +1 -0
  475. package/src/lib/server/print-queue.d.ts +124 -0
  476. package/src/lib/server/print-queue.js +301 -0
  477. package/src/lib/server/print-queue.js.map +1 -0
  478. package/src/lib/server/printer-poll.d.ts +75 -0
  479. package/src/lib/server/printer-poll.js +325 -0
  480. package/src/lib/server/printer-poll.js.map +1 -0
  481. package/src/lib/server/printer-secret.d.ts +39 -0
  482. package/src/lib/server/printer-secret.js +56 -0
  483. package/src/lib/server/printer-secret.js.map +1 -0
  484. package/src/lib/server/printers.d.ts +114 -0
  485. package/src/lib/server/printers.js +546 -0
  486. package/src/lib/server/printers.js.map +1 -0
  487. package/src/lib/server/product-ai-capability.d.ts +42 -0
  488. package/src/lib/server/product-ai-capability.js +159 -0
  489. package/src/lib/server/product-ai-capability.js.map +1 -0
  490. package/src/lib/server/product-catalog.d.ts +42 -0
  491. package/src/lib/server/product-catalog.js +305 -0
  492. package/src/lib/server/product-catalog.js.map +1 -0
  493. package/src/lib/server/product-drafts.d.ts +119 -0
  494. package/src/lib/server/product-drafts.js +384 -0
  495. package/src/lib/server/product-drafts.js.map +1 -0
  496. package/src/lib/server/refund.js +110 -45
  497. package/src/lib/server/refund.js.map +1 -1
  498. package/src/lib/server/reserve.js +2 -0
  499. package/src/lib/server/reserve.js.map +1 -1
  500. package/src/lib/server/return-status-action.d.ts +37 -0
  501. package/src/lib/server/return-status-action.js +57 -0
  502. package/src/lib/server/return-status-action.js.map +1 -0
  503. package/src/lib/server/returns.d.ts +119 -0
  504. package/src/lib/server/returns.js +938 -0
  505. package/src/lib/server/returns.js.map +1 -0
  506. package/src/lib/server/shipment-records.d.ts +36 -0
  507. package/src/lib/server/shipment-records.js +289 -0
  508. package/src/lib/server/shipment-records.js.map +1 -0
  509. package/src/lib/server/shipping-connector-gates.d.ts +40 -0
  510. package/src/lib/server/shipping-connector-gates.js +97 -0
  511. package/src/lib/server/shipping-connector-gates.js.map +1 -0
  512. package/src/lib/server/shipping-connectors.d.ts +29 -0
  513. package/src/lib/server/shipping-connectors.js +236 -0
  514. package/src/lib/server/shipping-connectors.js.map +1 -0
  515. package/src/lib/server/shippingeasy.d.ts +238 -0
  516. package/src/lib/server/shippingeasy.js +787 -0
  517. package/src/lib/server/shippingeasy.js.map +1 -0
  518. package/src/lib/server/shipstation.d.ts +129 -0
  519. package/src/lib/server/shipstation.js +483 -0
  520. package/src/lib/server/shipstation.js.map +1 -0
  521. package/src/lib/server/site-page-resolver.d.ts +5 -0
  522. package/src/lib/server/site-page-resolver.js +156 -1
  523. package/src/lib/server/site-page-resolver.js.map +1 -1
  524. package/src/lib/server/stock-levels.d.ts +21 -0
  525. package/src/lib/server/stock-levels.js +263 -0
  526. package/src/lib/server/stock-levels.js.map +1 -0
  527. package/src/lib/server/stripe-refund.d.ts +47 -0
  528. package/src/lib/server/stripe-refund.js +87 -0
  529. package/src/lib/server/stripe-refund.js.map +1 -0
  530. package/src/lib/server/supplier-update.js +46 -5
  531. package/src/lib/server/supplier-update.js.map +1 -1
  532. package/src/lib/server/tax-engine-quote.d.ts +47 -0
  533. package/src/lib/server/tax-engine-quote.js +105 -0
  534. package/src/lib/server/tax-engine-quote.js.map +1 -0
  535. package/src/lib/server.js +146 -2
  536. package/src/lib/server.js.map +1 -1
  537. package/src/lib/site.js +12 -0
  538. package/src/lib/site.js.map +1 -1
  539. package/src/lib/subprocessors.d.ts +26 -0
  540. package/src/lib/subprocessors.js +50 -0
  541. package/src/lib/subprocessors.js.map +1 -0
  542. package/src/lib/tenant-emails.js +461 -3
  543. package/src/lib/tenant-emails.js.map +1 -1
  544. package/src/lib/testing/fake-firestore.d.ts +45 -0
  545. package/src/lib/testing/fake-firestore.js +189 -0
  546. package/src/lib/testing/fake-firestore.js.map +1 -0
  547. package/src/lib/testing/memory-firestore.d.ts +123 -0
  548. package/src/lib/testing/memory-firestore.js +274 -0
  549. package/src/lib/testing/memory-firestore.js.map +1 -0
  550. package/src/lib/testing/pos-ops-harness.d.ts +50 -0
  551. package/src/lib/testing/pos-ops-harness.js +184 -0
  552. package/src/lib/testing/pos-ops-harness.js.map +1 -0
  553. package/src/lib/testing/pos-ops-memory-firestore.d.ts +55 -0
  554. package/src/lib/testing/pos-ops-memory-firestore.js +376 -0
  555. package/src/lib/testing/pos-ops-memory-firestore.js.map +1 -0
  556. package/src/lib/transfer/records-transfer.d.ts +11 -1
  557. package/src/lib/transfer/records-transfer.js +127 -5
  558. package/src/lib/transfer/records-transfer.js.map +1 -1
  559. package/src/lib/transfer/records.server.d.ts +9 -2
  560. package/src/lib/transfer/records.server.js +58 -7
  561. package/src/lib/transfer/records.server.js.map +1 -1
  562. package/src/lib/transfer/register-transfer-resources.js +40 -1
  563. package/src/lib/transfer/register-transfer-resources.js.map +1 -1
  564. package/src/lib/transfer/register-transfer-ui.js +4 -1
  565. package/src/lib/transfer/register-transfer-ui.js.map +1 -1
  566. package/src/lib/transfer/shipping-presets.d.ts +23 -0
  567. package/src/lib/transfer/shipping-presets.js +156 -0
  568. package/src/lib/transfer/shipping-presets.js.map +1 -0
  569. package/src/lib/transfer/tracking-transfer.d.ts +101 -0
  570. package/src/lib/transfer/tracking-transfer.js +546 -0
  571. package/src/lib/transfer/tracking-transfer.js.map +1 -0
  572. package/src/lib/transfer/tracking.server.d.ts +37 -0
  573. package/src/lib/transfer/tracking.server.js +393 -0
  574. package/src/lib/transfer/tracking.server.js.map +1 -0
  575. package/src/lib/transfer/transfer-keys.d.ts +2 -1
  576. package/src/lib/transfer/transfer-keys.js +6 -2
  577. package/src/lib/transfer/transfer-keys.js.map +1 -1
  578. package/src/lib/utils/code128.d.ts +37 -0
  579. package/src/lib/utils/code128.js +199 -0
  580. package/src/lib/utils/code128.js.map +1 -0
  581. package/src/lib/utils/order-invoice.d.ts +55 -0
  582. package/src/lib/utils/order-invoice.js +127 -0
  583. package/src/lib/utils/order-invoice.js.map +1 -0
  584. package/src/lib/utils/use-checkout-return-notice.d.ts +34 -0
  585. package/src/lib/utils/use-checkout-return-notice.js +78 -0
  586. package/src/lib/utils/use-checkout-return-notice.js.map +1 -0
  587. package/src/lib/utils/use-storefront-purchase-event.js +4 -0
  588. package/src/lib/utils/use-storefront-purchase-event.js.map +1 -1
  589. package/src/lib/server/feed.js +0 -69
  590. package/src/lib/server/feed.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/server/api-v1/orders-and-products.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { checkEntitlement, isHostPluginEnabled } from '@aglyn/aglyn/server'\nimport {\n ApiErrors,\n apiJson,\n listResponse,\n} from '@aglyn/tenant-data-admin'\nimport {\n type ApiV1Context,\n paginate,\n readJsonBody,\n requireScope,\n serialize,\n} from '@aglyn/tenant-data-admin/server/api-v1-kit'\nimport { BUNDLE_ID } from '../../constants/bundle-common'\nimport { type OrderFulfilmentTarget, recordOrderShipment } from '../fulfill-order'\n\n/**\n * A site's orders and products on the customer REST API,\n * `/v1/sites/{siteId}/orders/…` and `/v1/sites/{siteId}/products/…`\n * (AGL-1928, AGL-2461, AGL-3080). Registered from this plugin's console\n * server declarations; the console's `/v1` router owns the pipeline in\n * front — the key, the plan's API access, the request quota, the rate limit —\n * and refuses a site the key's organization does not own before it hands a\n * request here.\n */\n\n/**\n * Commerce resources need the `commerce` entitlement as well as the scope\n * (AGL-1928). `apiAccess` alone is the wrong gate here: it says the org may\n * call the API at all, not that it may still use the store. AGL-1873 closed\n * exactly this class on the write side — two money doors that asked the\n * plugin switch (`org.enabledPlugins`) instead of the plan, so a lapsed org\n * kept selling — and the same reasoning applies to a read. `enabledPlugins`\n * is the customer's own on/off switch and survives a downgrade; the plan does\n * not, and the published rule is that paid features stop at the door when the\n * plan no longer includes them.\n *\n * Answered as `plan_required` rather than `not_found`, deliberately. Hiding a\n * store that plainly exists behind a 404 sends an integrator hunting a wrong\n * site id; the honest answer names the plan.\n */\nfunction requireCommerce(ctx: ApiV1Context): Response | null {\n return checkEntitlement(ctx.org, 'commerce')\n ? null\n : ApiErrors.planRequired({\n message: 'Commerce is not included in this organization’s plan',\n code: 'commerce',\n headers: ctx.headers,\n })\n}\n\nfunction hostRef(ctx: ApiV1Context, hostId: string) {\n return ctx.firestore.collection('hosts').doc(hostId)\n}\n\n/**\n * Orders carry a legacy Commerce Starter shape (AGL-90) alongside the modern\n * one: `amountCents`/`feeCents` at the top level rather than a `totals` map.\n * The console lifts them through `CommerceModel.liftLegacyOrder` and reads\n * `lifted.totals?.totalCents ?? order.amountCents`. The API cannot publish two\n * shapes for one object, so the legacy fields are folded into `totals` here\n * and a client only ever sees the modern one. `channel` defaults to `online`\n * for the same reason the console's list does — an absent channel is a\n * pre-channel order, not an unknown one.\n */\nfunction orderView(doc: FirebaseFirestore.DocumentSnapshot) {\n const data = doc.data() ?? {}\n const totals = (data.totals ?? {}) as Record<string, unknown>\n const legacyTotal = Number(data.amountCents ?? NaN)\n const totalCents = Number.isFinite(Number(totals.totalCents))\n ? Number(totals.totalCents)\n : Number.isFinite(legacyTotal)\n ? legacyTotal\n : null\n const legacyFee = Number(data.feeCents ?? NaN)\n return {\n id: doc.id,\n object: 'order',\n number: typeof data.number === 'number' ? data.number : null,\n status: (data.status as string) ?? null,\n channel: (data.channel as string) ?? 'online',\n currency: 'usd',\n customerEmail: data.customerEmail ?? null,\n customerName: data.customerName ?? null,\n lineItems: serialize(data.lineItems ?? []),\n totals: {\n itemsCents: Number(totals.itemsCents ?? 0),\n shippingCents: Number(totals.shippingCents ?? 0),\n taxCents: Number(totals.taxCents ?? 0),\n discountCents: Number(totals.discountCents ?? 0),\n totalCents,\n // The Connect application fee. NOT subtracted from `totalCents` — it is\n // Aglyn's cut of a total the shopper already paid in full, so a client\n // that nets it out of revenue would understate what it collected.\n feeCents: Number.isFinite(Number(totals.feeCents))\n ? Number(totals.feeCents)\n : Number.isFinite(legacyFee)\n ? legacyFee\n : 0,\n },\n // Money already handed back, for any reason. A chargeback lands here too,\n // so `refundedCents > 0` does not by itself mean the merchant chose it.\n refundedCents: Number(data.refundedCents ?? 0),\n disputed: Boolean(data.dispute),\n shippingAddress: serialize(data.shippingAddress) ?? null,\n couponCode: data.couponCode ?? null,\n // Shipment records — the half of fulfilment an integration can use\n // without a write (AGL-2460). `status` says an order is `fulfilled`; it\n // does not say which carrier took it or under what tracking number, so a\n // 3PL or accounting reconcile could see THAT an order shipped and never\n // WHICH shipment it was. The console's order dialog shows both, and an\n // order that has been shipped twice (a split shipment) is indistinguish-\n // able from one shipped once when only the status is published.\n //\n // `atMs` is a number of milliseconds, not a Firestore Timestamp, so\n // `serialize` passes it through untouched. It is republished as `at` in\n // ISO 8601 to match `created` and every other time this API emits: one\n // object publishing two time formats is a bug an integrator finds late,\n // in their own timezone conversion, and blames on their own code.\n fulfillments: (Array.isArray(data.fulfillments) ? data.fulfillments : []).map(\n (entry: Record<string, unknown>) => {\n const atMs = Number((entry ?? {}).atMs)\n return {\n id: (entry ?? {}).id ?? null,\n lineItemIds: Array.isArray((entry ?? {}).lineItemIds)\n ? (entry as { lineItemIds: unknown[] }).lineItemIds\n : [],\n carrier: (entry ?? {}).carrier ?? null,\n trackingNumber: (entry ?? {}).trackingNumber ?? null,\n trackingUrl: (entry ?? {}).trackingUrl ?? null,\n at: Number.isFinite(atMs) ? new Date(atMs).toISOString() : null,\n }\n },\n ),\n created: serialize(data.createdAt) ?? null,\n }\n}\n\nexport async function handleOrders(\n request: Request,\n ctx: ApiV1Context,\n segments: string[],\n url: URL,\n): Promise<Response> {\n const [, hostId, , orderId] = segments\n // `orders:write` is checked BEFORE `orders:read`, matching `handleSites`\n // and `handleScopedMedia` (AGL-900): a fulfilment key that only records\n // shipments must not be told it lacks a read scope it was never meant to\n // hold.\n if (request.method === 'PATCH' && orderId) {\n const deniedWrite = requireScope(ctx, 'orders:write')\n if (deniedWrite) return deniedWrite\n const unentitledWrite = requireCommerce(ctx)\n if (unentitledWrite) return unentitledWrite\n return updateOrder(request, ctx, hostId, orderId)\n }\n const denied = requireScope(ctx, 'orders:read')\n if (denied) return denied\n const unentitled = requireCommerce(ctx)\n if (unentitled) return unentitled\n if (request.method !== 'GET') {\n return ApiErrors.methodNotAllowed({\n headers: { ...ctx.headers, Allow: orderId ? 'GET, PATCH' : 'GET' },\n })\n }\n const collection = hostRef(ctx, hostId).collection('orders')\n\n if (orderId) {\n const snap = await collection.doc(orderId).get()\n if (!snap.exists) {\n return ApiErrors.notFound({ message: 'No such order', headers: ctx.headers })\n }\n return apiJson(orderView(snap), { headers: ctx.headers })\n }\n\n let query: FirebaseFirestore.Query = collection\n const status = url.searchParams.get('status')\n if (status) query = query.where('status', '==', status)\n const channel = url.searchParams.get('channel')\n // `online` is the DEFAULT, not a stored value on older orders, so filtering\n // for it in Firestore would silently drop every pre-channel order. Those are\n // exactly the oldest orders an accounting backfill is reaching for, so this\n // one value is filtered after the read instead. The page can therefore come\n // back shorter than `limit` while `has_more` is still true — which the\n // published pagination contract already tells clients to expect (check\n // `has_more`, never a page's length).\n if (channel && channel !== 'online') query = query.where('channel', '==', channel)\n const { docs, nextCursor } = await paginate(query, url)\n const data = docs\n .map(orderView)\n .filter((order) => (channel === 'online' ? order.channel === 'online' : true))\n return listResponse(data, nextCursor, ctx.headers)\n}\n\n/**\n * Statuses this endpoint will move an order TO, and the two it names as\n * refused. Kept as data so the 400 can list them and the docs can be checked\n * against the same source the handler branches on.\n */\nconst ORDER_WRITE_TARGETS: OrderFulfilmentTarget[] = ['fulfilled', 'delivered']\n\n/**\n * Transitions that exist in the commerce model and are DELIBERATELY not\n * reachable here — refused by name with a 400 that says why, never silently\n * ignored. `cancelled` releases held stock under its own transaction and\n * `refunded` moves money under another; admitting either here would hand a\n * caller a door around exactly the specifics those two routes exist to\n * enforce, and an API key is the credential least able to answer the\n * questions they ask.\n */\nconst ORDER_WRITE_REFUSED: Record<string, string> = {\n cancelled:\n 'Canceling an order releases held stock, so it is not part of this endpoint. Cancel it in the console.',\n refunded:\n 'Refunding an order moves money, so it is not part of this endpoint. Refund it in the console.',\n}\n\n/**\n * `PATCH /v1/sites/{siteId}/orders/{orderId}` — record a shipment (AGL-2461).\n *\n * ## The write is the console's, shared\n *\n * The transition rule, the transaction that re-asks it under the write, the\n * fulfillment append and the timeline entry are `recordOrderShipment`'s,\n * the one implementation the console's `commerce/fulfill-order` route calls\n * too. A second copy of `ORDER_TRANSITIONS` here would be the bug rather\n * than the fix: two tables drift, and drift here means the API writing an\n * order status the console forbids — `paid → delivered` skipping\n * fulfilment, or a write onto a `refunded` order. That is the class\n * AGL-1818/AGL-1819 exist to close, and it is money-adjacent.\n *\n * ## What THIS function is responsible for: all of the authorization\n *\n * `recordOrderShipment` is pre-authorized by contract — it takes `hostId`\n * on trust — so every gate is here, and all four are load-bearing:\n *\n * 1. `orders:write` on the key (checked by the caller, above).\n * 2. The `commerce` PLAN entitlement (checked by the caller, above) — the\n * plan, never the plugin switch, which is the AGL-1873 distinction.\n * 3. **Org owns the site** — the console's `handleSites` refuses an unowned\n * `hostId` with a 404 before it hands any site resource to its plugin,\n * which is what keeps this from being a cross-tenant write primitive\n * addressable by anyone who can guess a host id. Deliberately NOT\n * re-checked here: a second copy would be a gate no test can redden\n * (removing either one alone leaves the other answering), and an unproven\n * guard on a cross-tenant write is worse than the one guard a test\n * actually holds. `api-v1-order-fulfilment.spec.ts`'s \"CANNOT move\n * another org's order\" case is that test, and it fails when\n * `handleSites`' `orgOwnsHost` is removed.\n * 4. **This plugin is switched on for this site.** The resource is\n * registered for every organization the console serves, so its being here\n * says nothing about one org's configuration; without this an org that\n * switched commerce off for a site would still accept writes into it,\n * which is the per-site enablement rule (AGL-1014) the plugin API\n * dispatcher applies to every other commerce door.\n *\n * ## No `Idempotency-Key`\n *\n * None is needed and none is accepted: the write returns without writing when\n * the order is already in the target status, so a retry lands the same state\n * AND returns the same `200` with the same order body — the contract\n * `updateRecord`, `updateContact` and `updateFormSubmission` are held to.\n */\nasync function updateOrder(\n request: Request,\n ctx: ApiV1Context,\n hostId: string,\n orderId: string,\n): Promise<Response> {\n const body = await readJsonBody(request)\n const unknown = Object.keys(body).filter(\n (key) => key !== 'status' && key !== 'carrier' && key !== 'trackingNumber',\n )\n if (unknown.length > 0) {\n // Named, not dropped — the `updateFormSubmission` / `updateContact` rule.\n // A silently ignored `trackingUrl` here reads as \"we recorded your\n // shipment as you described it\" when half of it went nowhere, and the\n // caller is a warehouse system that will never look again.\n return ApiErrors.badRequest({\n message:\n 'Only `status`, `carrier` and `trackingNumber` can be set on an order',\n code: 'validation_failed',\n fields: Object.fromEntries(\n unknown.map((key) => [key, 'Not writable on an order']),\n ),\n headers: ctx.headers,\n })\n }\n\n const status = String(body.status ?? '')\n const refusal = ORDER_WRITE_REFUSED[status]\n if (refusal) {\n return ApiErrors.badRequest({\n message: refusal,\n code: 'validation_failed',\n fields: { status: refusal },\n headers: ctx.headers,\n })\n }\n if (!(ORDER_WRITE_TARGETS as string[]).includes(status)) {\n return ApiErrors.badRequest({\n message: 'Order failed validation',\n code: 'validation_failed',\n fields: {\n status: `Must be one of: ${ORDER_WRITE_TARGETS.join(', ')}`,\n },\n headers: ctx.headers,\n })\n }\n // Bounded exactly as the console route bounds them, so one field cannot be\n // used to stuff an order document through a door the console keeps narrow.\n const carrier = String(body.carrier ?? '').slice(0, 40)\n const trackingNumber = String(body.trackingNumber ?? '').slice(0, 60)\n\n // Gate 4: this plugin, on for this site.\n const hostSnap = await hostRef(ctx, hostId).get()\n if (!isHostPluginEnabled(ctx.org, hostSnap.data(), BUNDLE_ID)) {\n return ApiErrors.notFound({\n message: 'No such site',\n headers: ctx.headers,\n })\n }\n\n const outcome = await recordOrderShipment({\n hostId,\n orderId,\n to: status as OrderFulfilmentTarget,\n carrier,\n trackingNumber,\n })\n if (outcome.outcome === 'no_such_order') {\n return ApiErrors.notFound({\n message: 'No such order',\n headers: ctx.headers,\n })\n }\n if (outcome.outcome === 'blocked') {\n // A NEW 409 code (`order_transition`), because an integrator has to be\n // able to tell \"the order moved on without me\" apart from every other\n // conflict this API can raise — it is the one a fulfilment poller will\n // actually hit, and the one it must not retry forever.\n return ApiErrors.conflict({\n message: `Orders in \"${outcome.from}\" cannot be marked ${status}`,\n code: 'order_transition',\n headers: ctx.headers,\n })\n }\n // The order object, on both `recorded` and `already` — a retry lands the\n // same state and reads the same 200. Re-read after the write so the body\n // shows the shipment that was just recorded rather than the one before it.\n return apiJson(orderView(await hostRef(ctx, hostId).collection('orders').doc(orderId).get()), {\n headers: ctx.headers,\n })\n}\n\n/**\n * Price and stock live on VARIANTS, never on the product — a product-level\n * `inventory` exists only as a denormalized sum the console rewrites on every\n * decrement, and a product-level `priceUsd` is the legacy single-variant\n * shape. Publishing either as the product's price would be wrong the moment a\n * product has two variants, so the variant array is the contract and the\n * product carries only the roll-up, clearly named.\n *\n * `inventory: null` on a variant means UNTRACKED and `0` means SOLD OUT.\n * Collapsing them (the `?? 0` an integrator writes on the first day) turns\n * every untracked product into an out-of-stock one, so the distinction is\n * carried through verbatim rather than defaulted.\n */\nfunction variantView(variant: Record<string, unknown>) {\n const inventory = variant.inventory\n return {\n id: variant.id ?? null,\n sku: variant.sku ?? null,\n barcode: variant.barcode ?? null,\n options: variant.options ?? {},\n priceUsd: typeof variant.priceUsd === 'number' ? variant.priceUsd : null,\n compareAtPriceUsd:\n typeof variant.compareAtPriceUsd === 'number'\n ? variant.compareAtPriceUsd\n : null,\n weightGrams:\n typeof variant.weightGrams === 'number' ? variant.weightGrams : null,\n inventory: typeof inventory === 'number' ? inventory : null,\n inventoryTracked: typeof inventory === 'number',\n }\n}\n\nfunction productView(doc: FirebaseFirestore.DocumentSnapshot) {\n const data = doc.data() ?? {}\n const variants = Array.isArray(data.variants)\n ? (data.variants as Array<Record<string, unknown>>)\n : []\n const tracked = variants.filter((v) => typeof v.inventory === 'number')\n return {\n id: doc.id,\n object: 'product',\n name: data.name ?? null,\n slug: data.slug ?? null,\n description: data.description ?? null,\n type: data.type ?? null,\n status: data.status ?? null,\n tags: data.tags ?? [],\n categoryIds: data.categoryIds ?? [],\n mediaUrls: data.mediaUrls ?? [],\n options: data.options ?? [],\n variants: variants.map(variantView),\n // The sum across TRACKED variants only, and `null` when none of them is\n // tracked — so an untracked catalogue reads as \"we don't count this\"\n // rather than as a store with nothing left to sell.\n inventory: tracked.length\n ? tracked.reduce((sum, v) => sum + Number(v.inventory ?? 0), 0)\n : null,\n subscription: serialize(data.subscription) ?? null,\n created: data.createdAtMs ? new Date(Number(data.createdAtMs)).toISOString() : null,\n updated: data.updatedAtMs ? new Date(Number(data.updatedAtMs)).toISOString() : null,\n }\n}\n\nexport async function handleProducts(\n request: Request,\n ctx: ApiV1Context,\n segments: string[],\n url: URL,\n): Promise<Response> {\n const [, hostId, , productId] = segments\n const denied = requireScope(ctx, 'products:read')\n if (denied) return denied\n const unentitled = requireCommerce(ctx)\n if (unentitled) return unentitled\n if (request.method !== 'GET') {\n return ApiErrors.methodNotAllowed({ headers: ctx.headers })\n }\n const collection = hostRef(ctx, hostId).collection('products')\n\n if (productId) {\n const snap = await collection.doc(productId).get()\n // A soft-deleted product is gone as far as a customer is concerned. The\n // console filters `deletedAt` client-side; the API must not hand back a\n // product the merchant deleted just because the document survives.\n if (!snap.exists || snap.get('deletedAt')) {\n return ApiErrors.notFound({ message: 'No such product', headers: ctx.headers })\n }\n return apiJson(productView(snap), { headers: ctx.headers })\n }\n\n let query: FirebaseFirestore.Query = collection\n const status = url.searchParams.get('status')\n if (status) query = query.where('status', '==', status)\n const { docs, nextCursor } = await paginate(query, url)\n const data = docs.filter((doc) => !doc.get('deletedAt')).map(productView)\n return listResponse(data, nextCursor, ctx.headers)\n}\n\n"],"names":["checkEntitlement","isHostPluginEnabled","ApiErrors","apiJson","listResponse","paginate","readJsonBody","requireScope","serialize","BUNDLE_ID","recordOrderShipment","requireCommerce","ctx","org","planRequired","message","code","headers","hostRef","hostId","firestore","collection","doc","orderView","data","totals","legacyTotal","Number","amountCents","NaN","totalCents","isFinite","legacyFee","feeCents","id","object","number","status","channel","currency","customerEmail","customerName","lineItems","itemsCents","shippingCents","taxCents","discountCents","refundedCents","disputed","Boolean","dispute","shippingAddress","couponCode","fulfillments","Array","isArray","map","entry","atMs","lineItemIds","carrier","trackingNumber","trackingUrl","at","Date","toISOString","created","createdAt","handleOrders","request","segments","url","orderId","method","deniedWrite","unentitledWrite","updateOrder","denied","unentitled","methodNotAllowed","Allow","snap","get","exists","notFound","query","searchParams","where","docs","nextCursor","filter","order","ORDER_WRITE_TARGETS","ORDER_WRITE_REFUSED","cancelled","refunded","body","unknown","Object","keys","key","length","badRequest","fields","fromEntries","String","refusal","includes","join","slice","hostSnap","outcome","to","conflict","from","variantView","variant","inventory","sku","barcode","options","priceUsd","compareAtPriceUsd","weightGrams","inventoryTracked","productView","variants","tracked","v","name","slug","description","type","tags","categoryIds","mediaUrls","reduce","sum","subscription","createdAtMs","updated","updatedAtMs","handleProducts","productId"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,gBAAgB,EAAEC,mBAAmB,QAAQ,sBAAqB;AAC3E,SACEC,SAAS,EACTC,OAAO,EACPC,YAAY,QACP,2BAA0B;AACjC,SAEEC,QAAQ,EACRC,YAAY,EACZC,YAAY,EACZC,SAAS,QACJ,6CAA4C;AACnD,SAASC,SAAS,QAAQ,mCAA+B;AACzD,SAAqCC,mBAAmB,QAAQ,sBAAkB;AAElF;;;;;;;;CAQC,GAED;;;;;;;;;;;;;;CAcC,GACD,SAASC,gBAAgBC,GAAiB;IACxC,OAAOZ,iBAAiBY,IAAIC,GAAG,EAAE,cAC7B,OACAX,UAAUY,YAAY,CAAC;QACrBC,SAAS;QACTC,MAAM;QACNC,SAASL,IAAIK,OAAO;IACtB;AACN;AAEA,SAASC,QAAQN,GAAiB,EAAEO,MAAc;IAChD,OAAOP,IAAIQ,SAAS,CAACC,UAAU,CAAC,SAASC,GAAG,CAACH;AAC/C;AAEA;;;;;;;;;CASC,GACD,SAASI,UAAUD,GAAuC;QAC3CA,WACGE,cACWA,mBAMFA,gBAKdA,cACCA,eAEKA,qBACDA,oBACOA,iBAEAC,oBACGA,uBACLA,kBACKA,uBAaFD,qBAELhB,YACLgB,kBA6BHhB;IApEX,MAAMgB,QAAOF,YAAAA,IAAIE,IAAI,cAARF,YAAc,CAAC;IAC5B,MAAMG,UAAUD,eAAAA,KAAKC,MAAM,YAAXD,eAAe,CAAC;IAChC,MAAME,cAAcC,QAAOH,oBAAAA,KAAKI,WAAW,YAAhBJ,oBAAoBK;IAC/C,MAAMC,aAAaH,OAAOI,QAAQ,CAACJ,OAAOF,OAAOK,UAAU,KACvDH,OAAOF,OAAOK,UAAU,IACxBH,OAAOI,QAAQ,CAACL,eACdA,cACA;IACN,MAAMM,YAAYL,QAAOH,iBAAAA,KAAKS,QAAQ,YAAbT,iBAAiBK;IAC1C,OAAO;QACLK,IAAIZ,IAAIY,EAAE;QACVC,QAAQ;QACRC,QAAQ,OAAOZ,KAAKY,MAAM,KAAK,WAAWZ,KAAKY,MAAM,GAAG;QACxDC,MAAM,GAAGb,eAAAA,KAAKa,MAAM,YAAXb,eAA0B;QACnCc,OAAO,GAAGd,gBAAAA,KAAKc,OAAO,YAAZd,gBAA2B;QACrCe,UAAU;QACVC,aAAa,GAAEhB,sBAAAA,KAAKgB,aAAa,YAAlBhB,sBAAsB;QACrCiB,YAAY,GAAEjB,qBAAAA,KAAKiB,YAAY,YAAjBjB,qBAAqB;QACnCkB,WAAWlC,WAAUgB,kBAAAA,KAAKkB,SAAS,YAAdlB,kBAAkB,EAAE;QACzCC,QAAQ;YACNkB,YAAYhB,QAAOF,qBAAAA,OAAOkB,UAAU,YAAjBlB,qBAAqB;YACxCmB,eAAejB,QAAOF,wBAAAA,OAAOmB,aAAa,YAApBnB,wBAAwB;YAC9CoB,UAAUlB,QAAOF,mBAAAA,OAAOoB,QAAQ,YAAfpB,mBAAmB;YACpCqB,eAAenB,QAAOF,wBAAAA,OAAOqB,aAAa,YAApBrB,wBAAwB;YAC9CK;YACA,wEAAwE;YACxE,uEAAuE;YACvE,kEAAkE;YAClEG,UAAUN,OAAOI,QAAQ,CAACJ,OAAOF,OAAOQ,QAAQ,KAC5CN,OAAOF,OAAOQ,QAAQ,IACtBN,OAAOI,QAAQ,CAACC,aACdA,YACA;QACR;QACA,0EAA0E;QAC1E,wEAAwE;QACxEe,eAAepB,QAAOH,sBAAAA,KAAKuB,aAAa,YAAlBvB,sBAAsB;QAC5CwB,UAAUC,QAAQzB,KAAK0B,OAAO;QAC9BC,eAAe,GAAE3C,aAAAA,UAAUgB,KAAK2B,eAAe,aAA9B3C,aAAmC;QACpD4C,UAAU,GAAE5B,mBAAAA,KAAK4B,UAAU,YAAf5B,mBAAmB;QAC/B,mEAAmE;QACnE,wEAAwE;QACxE,yEAAyE;QACzE,wEAAwE;QACxE,uEAAuE;QACvE,yEAAyE;QACzE,gEAAgE;QAChE,EAAE;QACF,oEAAoE;QACpE,wEAAwE;QACxE,uEAAuE;QACvE,wEAAwE;QACxE,kEAAkE;QAClE6B,cAAc,AAACC,CAAAA,MAAMC,OAAO,CAAC/B,KAAK6B,YAAY,IAAI7B,KAAK6B,YAAY,GAAG,EAAE,AAAD,EAAGG,GAAG,CAC3E,CAACC;gBAGO,KAIK,UACO,iBACH;YARf,MAAMC,OAAO/B,OAAO,CAAC8B,gBAAAA,QAAS,CAAC,GAAGC,IAAI;YACtC,OAAO;gBACLxB,EAAE,GAAE,MAAA,CAACuB,gBAAAA,QAAS,CAAC,GAAGvB,EAAE,YAAhB,MAAoB;gBACxByB,aAAaL,MAAMC,OAAO,CAAC,CAACE,gBAAAA,QAAS,CAAC,GAAGE,WAAW,IAChD,AAACF,MAAqCE,WAAW,GACjD,EAAE;gBACNC,OAAO,GAAE,WAAA,CAACH,gBAAAA,QAAS,CAAC,GAAGG,OAAO,YAArB,WAAyB;gBAClCC,cAAc,GAAE,kBAAA,CAACJ,gBAAAA,QAAS,CAAC,GAAGI,cAAc,YAA5B,kBAAgC;gBAChDC,WAAW,GAAE,eAAA,CAACL,gBAAAA,QAAS,CAAC,GAAGK,WAAW,YAAzB,eAA6B;gBAC1CC,IAAIpC,OAAOI,QAAQ,CAAC2B,QAAQ,IAAIM,KAAKN,MAAMO,WAAW,KAAK;YAC7D;QACF;QAEFC,OAAO,GAAE1D,cAAAA,UAAUgB,KAAK2C,SAAS,aAAxB3D,cAA6B;IACxC;AACF;AAEA,OAAO,eAAe4D,aACpBC,OAAgB,EAChBzD,GAAiB,EACjB0D,QAAkB,EAClBC,GAAQ;IAER,MAAM,GAAGpD,UAAUqD,QAAQ,GAAGF;IAC9B,yEAAyE;IACzE,wEAAwE;IACxE,yEAAyE;IACzE,QAAQ;IACR,IAAID,QAAQI,MAAM,KAAK,WAAWD,SAAS;QACzC,MAAME,cAAcnE,aAAaK,KAAK;QACtC,IAAI8D,aAAa,OAAOA;QACxB,MAAMC,kBAAkBhE,gBAAgBC;QACxC,IAAI+D,iBAAiB,OAAOA;QAC5B,OAAOC,YAAYP,SAASzD,KAAKO,QAAQqD;IAC3C;IACA,MAAMK,SAAStE,aAAaK,KAAK;IACjC,IAAIiE,QAAQ,OAAOA;IACnB,MAAMC,aAAanE,gBAAgBC;IACnC,IAAIkE,YAAY,OAAOA;IACvB,IAAIT,QAAQI,MAAM,KAAK,OAAO;QAC5B,OAAOvE,UAAU6E,gBAAgB,CAAC;YAChC9D,SAAS,aAAKL,IAAIK,OAAO;gBAAE+D,OAAOR,UAAU,eAAe;;QAC7D;IACF;IACA,MAAMnD,aAAaH,QAAQN,KAAKO,QAAQE,UAAU,CAAC;IAEnD,IAAImD,SAAS;QACX,MAAMS,OAAO,MAAM5D,WAAWC,GAAG,CAACkD,SAASU,GAAG;QAC9C,IAAI,CAACD,KAAKE,MAAM,EAAE;YAChB,OAAOjF,UAAUkF,QAAQ,CAAC;gBAAErE,SAAS;gBAAiBE,SAASL,IAAIK,OAAO;YAAC;QAC7E;QACA,OAAOd,QAAQoB,UAAU0D,OAAO;YAAEhE,SAASL,IAAIK,OAAO;QAAC;IACzD;IAEA,IAAIoE,QAAiChE;IACrC,MAAMgB,SAASkC,IAAIe,YAAY,CAACJ,GAAG,CAAC;IACpC,IAAI7C,QAAQgD,QAAQA,MAAME,KAAK,CAAC,UAAU,MAAMlD;IAChD,MAAMC,UAAUiC,IAAIe,YAAY,CAACJ,GAAG,CAAC;IACrC,4EAA4E;IAC5E,6EAA6E;IAC7E,4EAA4E;IAC5E,4EAA4E;IAC5E,uEAAuE;IACvE,uEAAuE;IACvE,sCAAsC;IACtC,IAAI5C,WAAWA,YAAY,UAAU+C,QAAQA,MAAME,KAAK,CAAC,WAAW,MAAMjD;IAC1E,MAAM,EAAEkD,IAAI,EAAEC,UAAU,EAAE,GAAG,MAAMpF,SAASgF,OAAOd;IACnD,MAAM/C,OAAOgE,KACVhC,GAAG,CAACjC,WACJmE,MAAM,CAAC,CAACC,QAAWrD,YAAY,WAAWqD,MAAMrD,OAAO,KAAK,WAAW;IAC1E,OAAOlC,aAAaoB,MAAMiE,YAAY7E,IAAIK,OAAO;AACnD;AAEA;;;;CAIC,GACD,MAAM2E,sBAA+C;IAAC;IAAa;CAAY;AAE/E;;;;;;;;CAQC,GACD,MAAMC,sBAA8C;IAClDC,WACE;IACFC,UACE;AACJ;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6CC,GACD,eAAenB,YACbP,OAAgB,EAChBzD,GAAiB,EACjBO,MAAc,EACdqD,OAAe;QAsBOwB,cAsBCA,eACOA;IA3C9B,MAAMA,OAAO,MAAM1F,aAAa+D;IAChC,MAAM4B,UAAUC,OAAOC,IAAI,CAACH,MAAMN,MAAM,CACtC,CAACU,MAAQA,QAAQ,YAAYA,QAAQ,aAAaA,QAAQ;IAE5D,IAAIH,QAAQI,MAAM,GAAG,GAAG;QACtB,0EAA0E;QAC1E,mEAAmE;QACnE,sEAAsE;QACtE,2DAA2D;QAC3D,OAAOnG,UAAUoG,UAAU,CAAC;YAC1BvF,SACE;YACFC,MAAM;YACNuF,QAAQL,OAAOM,WAAW,CACxBP,QAAQzC,GAAG,CAAC,CAAC4C,MAAQ;oBAACA;oBAAK;iBAA2B;YAExDnF,SAASL,IAAIK,OAAO;QACtB;IACF;IAEA,MAAMoB,SAASoE,QAAOT,eAAAA,KAAK3D,MAAM,YAAX2D,eAAe;IACrC,MAAMU,UAAUb,mBAAmB,CAACxD,OAAO;IAC3C,IAAIqE,SAAS;QACX,OAAOxG,UAAUoG,UAAU,CAAC;YAC1BvF,SAAS2F;YACT1F,MAAM;YACNuF,QAAQ;gBAAElE,QAAQqE;YAAQ;YAC1BzF,SAASL,IAAIK,OAAO;QACtB;IACF;IACA,IAAI,CAAC,AAAC2E,oBAAiCe,QAAQ,CAACtE,SAAS;QACvD,OAAOnC,UAAUoG,UAAU,CAAC;YAC1BvF,SAAS;YACTC,MAAM;YACNuF,QAAQ;gBACNlE,QAAQ,CAAC,gBAAgB,EAAEuD,oBAAoBgB,IAAI,CAAC,OAAO;YAC7D;YACA3F,SAASL,IAAIK,OAAO;QACtB;IACF;IACA,2EAA2E;IAC3E,2EAA2E;IAC3E,MAAM2C,UAAU6C,QAAOT,gBAAAA,KAAKpC,OAAO,YAAZoC,gBAAgB,IAAIa,KAAK,CAAC,GAAG;IACpD,MAAMhD,iBAAiB4C,QAAOT,uBAAAA,KAAKnC,cAAc,YAAnBmC,uBAAuB,IAAIa,KAAK,CAAC,GAAG;IAElE,yCAAyC;IACzC,MAAMC,WAAW,MAAM5F,QAAQN,KAAKO,QAAQ+D,GAAG;IAC/C,IAAI,CAACjF,oBAAoBW,IAAIC,GAAG,EAAEiG,SAAStF,IAAI,IAAIf,YAAY;QAC7D,OAAOP,UAAUkF,QAAQ,CAAC;YACxBrE,SAAS;YACTE,SAASL,IAAIK,OAAO;QACtB;IACF;IAEA,MAAM8F,UAAU,MAAMrG,oBAAoB;QACxCS;QACAqD;QACAwC,IAAI3E;QACJuB;QACAC;IACF;IACA,IAAIkD,QAAQA,OAAO,KAAK,iBAAiB;QACvC,OAAO7G,UAAUkF,QAAQ,CAAC;YACxBrE,SAAS;YACTE,SAASL,IAAIK,OAAO;QACtB;IACF;IACA,IAAI8F,QAAQA,OAAO,KAAK,WAAW;QACjC,uEAAuE;QACvE,sEAAsE;QACtE,uEAAuE;QACvE,uDAAuD;QACvD,OAAO7G,UAAU+G,QAAQ,CAAC;YACxBlG,SAAS,CAAC,WAAW,EAAEgG,QAAQG,IAAI,CAAC,mBAAmB,EAAE7E,QAAQ;YACjErB,MAAM;YACNC,SAASL,IAAIK,OAAO;QACtB;IACF;IACA,yEAAyE;IACzE,yEAAyE;IACzE,2EAA2E;IAC3E,OAAOd,QAAQoB,UAAU,MAAML,QAAQN,KAAKO,QAAQE,UAAU,CAAC,UAAUC,GAAG,CAACkD,SAASU,GAAG,KAAK;QAC5FjE,SAASL,IAAIK,OAAO;IACtB;AACF;AAEA;;;;;;;;;;;;CAYC,GACD,SAASkG,YAAYC,OAAgC;QAG7CA,aACCA,cACIA,kBACAA;IALX,MAAMC,YAAYD,QAAQC,SAAS;IACnC,OAAO;QACLnF,EAAE,GAAEkF,cAAAA,QAAQlF,EAAE,YAAVkF,cAAc;QAClBE,GAAG,GAAEF,eAAAA,QAAQE,GAAG,YAAXF,eAAe;QACpBG,OAAO,GAAEH,mBAAAA,QAAQG,OAAO,YAAfH,mBAAmB;QAC5BI,OAAO,GAAEJ,mBAAAA,QAAQI,OAAO,YAAfJ,mBAAmB,CAAC;QAC7BK,UAAU,OAAOL,QAAQK,QAAQ,KAAK,WAAWL,QAAQK,QAAQ,GAAG;QACpEC,mBACE,OAAON,QAAQM,iBAAiB,KAAK,WACjCN,QAAQM,iBAAiB,GACzB;QACNC,aACE,OAAOP,QAAQO,WAAW,KAAK,WAAWP,QAAQO,WAAW,GAAG;QAClEN,WAAW,OAAOA,cAAc,WAAWA,YAAY;QACvDO,kBAAkB,OAAOP,cAAc;IACzC;AACF;AAEA,SAASQ,YAAYvG,GAAuC;QAC7CA,WAQLE,YACAA,YACOA,mBACPA,YACEA,cACFA,YACOA,mBACFA,iBACFA,eAQKhB;IAxBhB,MAAMgB,QAAOF,YAAAA,IAAIE,IAAI,cAARF,YAAc,CAAC;IAC5B,MAAMwG,WAAWxE,MAAMC,OAAO,CAAC/B,KAAKsG,QAAQ,IACvCtG,KAAKsG,QAAQ,GACd,EAAE;IACN,MAAMC,UAAUD,SAASpC,MAAM,CAAC,CAACsC,IAAM,OAAOA,EAAEX,SAAS,KAAK;IAC9D,OAAO;QACLnF,IAAIZ,IAAIY,EAAE;QACVC,QAAQ;QACR8F,IAAI,GAAEzG,aAAAA,KAAKyG,IAAI,YAATzG,aAAa;QACnB0G,IAAI,GAAE1G,aAAAA,KAAK0G,IAAI,YAAT1G,aAAa;QACnB2G,WAAW,GAAE3G,oBAAAA,KAAK2G,WAAW,YAAhB3G,oBAAoB;QACjC4G,IAAI,GAAE5G,aAAAA,KAAK4G,IAAI,YAAT5G,aAAa;QACnBa,MAAM,GAAEb,eAAAA,KAAKa,MAAM,YAAXb,eAAe;QACvB6G,IAAI,GAAE7G,aAAAA,KAAK6G,IAAI,YAAT7G,aAAa,EAAE;QACrB8G,WAAW,GAAE9G,oBAAAA,KAAK8G,WAAW,YAAhB9G,oBAAoB,EAAE;QACnC+G,SAAS,GAAE/G,kBAAAA,KAAK+G,SAAS,YAAd/G,kBAAkB,EAAE;QAC/BgG,OAAO,GAAEhG,gBAAAA,KAAKgG,OAAO,YAAZhG,gBAAgB,EAAE;QAC3BsG,UAAUA,SAAStE,GAAG,CAAC2D;QACvB,wEAAwE;QACxE,qEAAqE;QACrE,oDAAoD;QACpDE,WAAWU,QAAQ1B,MAAM,GACrB0B,QAAQS,MAAM,CAAC,CAACC,KAAKT;gBAAmBA;mBAAbS,MAAM9G,QAAOqG,eAAAA,EAAEX,SAAS,YAAXW,eAAe;WAAI,KAC3D;QACJU,YAAY,GAAElI,aAAAA,UAAUgB,KAAKkH,YAAY,aAA3BlI,aAAgC;QAC9C0D,SAAS1C,KAAKmH,WAAW,GAAG,IAAI3E,KAAKrC,OAAOH,KAAKmH,WAAW,GAAG1E,WAAW,KAAK;QAC/E2E,SAASpH,KAAKqH,WAAW,GAAG,IAAI7E,KAAKrC,OAAOH,KAAKqH,WAAW,GAAG5E,WAAW,KAAK;IACjF;AACF;AAEA,OAAO,eAAe6E,eACpBzE,OAAgB,EAChBzD,GAAiB,EACjB0D,QAAkB,EAClBC,GAAQ;IAER,MAAM,GAAGpD,UAAU4H,UAAU,GAAGzE;IAChC,MAAMO,SAAStE,aAAaK,KAAK;IACjC,IAAIiE,QAAQ,OAAOA;IACnB,MAAMC,aAAanE,gBAAgBC;IACnC,IAAIkE,YAAY,OAAOA;IACvB,IAAIT,QAAQI,MAAM,KAAK,OAAO;QAC5B,OAAOvE,UAAU6E,gBAAgB,CAAC;YAAE9D,SAASL,IAAIK,OAAO;QAAC;IAC3D;IACA,MAAMI,aAAaH,QAAQN,KAAKO,QAAQE,UAAU,CAAC;IAEnD,IAAI0H,WAAW;QACb,MAAM9D,OAAO,MAAM5D,WAAWC,GAAG,CAACyH,WAAW7D,GAAG;QAChD,wEAAwE;QACxE,wEAAwE;QACxE,mEAAmE;QACnE,IAAI,CAACD,KAAKE,MAAM,IAAIF,KAAKC,GAAG,CAAC,cAAc;YACzC,OAAOhF,UAAUkF,QAAQ,CAAC;gBAAErE,SAAS;gBAAmBE,SAASL,IAAIK,OAAO;YAAC;QAC/E;QACA,OAAOd,QAAQ0H,YAAY5C,OAAO;YAAEhE,SAASL,IAAIK,OAAO;QAAC;IAC3D;IAEA,IAAIoE,QAAiChE;IACrC,MAAMgB,SAASkC,IAAIe,YAAY,CAACJ,GAAG,CAAC;IACpC,IAAI7C,QAAQgD,QAAQA,MAAME,KAAK,CAAC,UAAU,MAAMlD;IAChD,MAAM,EAAEmD,IAAI,EAAEC,UAAU,EAAE,GAAG,MAAMpF,SAASgF,OAAOd;IACnD,MAAM/C,OAAOgE,KAAKE,MAAM,CAAC,CAACpE,MAAQ,CAACA,IAAI4D,GAAG,CAAC,cAAc1B,GAAG,CAACqE;IAC7D,OAAOzH,aAAaoB,MAAMiE,YAAY7E,IAAIK,OAAO;AACnD"}
1
+ {"version":3,"sources":["../../../../../../../../libs/plugins/commerce/src/lib/server/api-v1/orders-and-products.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { checkEntitlement, isHostPluginEnabled } from '@aglyn/aglyn/server'\nimport {\n ApiErrors,\n apiJson,\n listResponse,\n} from '@aglyn/tenant-data-admin'\nimport {\n type ApiV1Context,\n paginate,\n readJsonBody,\n requireScope,\n serialize,\n} from '@aglyn/tenant-data-admin/server/api-v1-kit'\nimport { BUNDLE_ID } from '../../constants/bundle-common'\nimport { type OrderFulfilmentTarget, recordOrderShipment } from '../fulfill-order'\nimport { orderViewFromData } from './order-view'\n\n/**\n * A site's orders and products on the customer REST API,\n * `/v1/sites/{siteId}/orders/…` and `/v1/sites/{siteId}/products/…`\n * (AGL-1928, AGL-2461, AGL-3080). Registered from this plugin's console\n * server declarations; the console's `/v1` router owns the pipeline in\n * front — the key, the plan's API access, the request quota, the rate limit —\n * and refuses a site the key's organization does not own before it hands a\n * request here.\n */\n\n/**\n * Commerce resources need the `commerce` entitlement as well as the scope\n * (AGL-1928). `apiAccess` alone is the wrong gate here: it says the org may\n * call the API at all, not that it may still use the store. AGL-1873 closed\n * exactly this class on the write side — two money doors that asked the\n * plugin switch (`org.enabledPlugins`) instead of the plan, so a lapsed org\n * kept selling — and the same reasoning applies to a read. `enabledPlugins`\n * is the customer's own on/off switch and survives a downgrade; the plan does\n * not, and the published rule is that paid features stop at the door when the\n * plan no longer includes them.\n *\n * Answered as `plan_required` rather than `not_found`, deliberately. Hiding a\n * store that plainly exists behind a 404 sends an integrator hunting a wrong\n * site id; the honest answer names the plan.\n */\nfunction requireCommerce(ctx: ApiV1Context): Response | null {\n return checkEntitlement(ctx.org, 'commerce')\n ? null\n : ApiErrors.planRequired({\n message: 'Commerce is not included in this organization’s plan',\n code: 'commerce',\n headers: ctx.headers,\n })\n}\n\nfunction hostRef(ctx: ApiV1Context, hostId: string) {\n return ctx.firestore.collection('hosts').doc(hostId)\n}\n\n/**\n * Orders carry a legacy Commerce Starter shape (AGL-90) alongside the modern\n * one: `amountCents`/`feeCents` at the top level rather than a `totals` map.\n * The console lifts them through `CommerceModel.liftLegacyOrder` and reads\n * `lifted.totals?.totalCents ?? order.amountCents`. The API cannot publish two\n * shapes for one object, so the legacy fields are folded into `totals` here\n * and a client only ever sees the modern one. `channel` defaults to `online`\n * for the same reason the console's list does — an absent channel is a\n * pre-channel order, not an unknown one.\n */\nfunction orderView(doc: FirebaseFirestore.DocumentSnapshot) {\n return orderViewFromData(doc.id, doc.data() ?? {})\n}\n\nexport async function handleOrders(\n request: Request,\n ctx: ApiV1Context,\n segments: string[],\n url: URL,\n): Promise<Response> {\n const [, hostId, , orderId] = segments\n // `orders:write` is checked BEFORE `orders:read`, matching `handleSites`\n // and `handleScopedMedia` (AGL-900): a fulfilment key that only records\n // shipments must not be told it lacks a read scope it was never meant to\n // hold.\n if (request.method === 'PATCH' && orderId) {\n const deniedWrite = requireScope(ctx, 'orders:write')\n if (deniedWrite) return deniedWrite\n const unentitledWrite = requireCommerce(ctx)\n if (unentitledWrite) return unentitledWrite\n return updateOrder(request, ctx, hostId, orderId)\n }\n const denied = requireScope(ctx, 'orders:read')\n if (denied) return denied\n const unentitled = requireCommerce(ctx)\n if (unentitled) return unentitled\n if (request.method !== 'GET') {\n return ApiErrors.methodNotAllowed({\n headers: { ...ctx.headers, Allow: orderId ? 'GET, PATCH' : 'GET' },\n })\n }\n const collection = hostRef(ctx, hostId).collection('orders')\n\n if (orderId) {\n const snap = await collection.doc(orderId).get()\n if (!snap.exists) {\n return ApiErrors.notFound({ message: 'No such order', headers: ctx.headers })\n }\n return apiJson(orderView(snap), { headers: ctx.headers })\n }\n\n let query: FirebaseFirestore.Query = collection\n const status = url.searchParams.get('status')\n if (status) query = query.where('status', '==', status)\n const channel = url.searchParams.get('channel')\n // `online` is the DEFAULT, not a stored value on older orders, so filtering\n // for it in Firestore would silently drop every pre-channel order. Those are\n // exactly the oldest orders an accounting backfill is reaching for, so this\n // one value is filtered after the read instead. The page can therefore come\n // back shorter than `limit` while `has_more` is still true — which the\n // published pagination contract already tells clients to expect (check\n // `has_more`, never a page's length).\n if (channel && channel !== 'online') query = query.where('channel', '==', channel)\n const { docs, nextCursor } = await paginate(query, url)\n const data = docs\n .map(orderView)\n .filter((order) => (channel === 'online' ? order.channel === 'online' : true))\n return listResponse(data, nextCursor, ctx.headers)\n}\n\n/**\n * Statuses this endpoint will move an order TO, and the two it names as\n * refused. Kept as data so the 400 can list them and the docs can be checked\n * against the same source the handler branches on.\n */\nconst ORDER_WRITE_TARGETS: OrderFulfilmentTarget[] = ['fulfilled', 'delivered']\n\n/** The keys a PATCH may carry (AGL-3611 added the last three). */\nconst ORDER_WRITABLE_KEYS = ['status', 'carrier', 'trackingNumber', 'trackingUrl', 'lineItems', 'notify']\n\n/**\n * Transitions that exist in the commerce model and are DELIBERATELY not\n * reachable here — refused by name with a 400 that says why, never silently\n * ignored. `cancelled` releases held stock under its own transaction and\n * `refunded` moves money under another; admitting either here would hand a\n * caller a door around exactly the specifics those two routes exist to\n * enforce, and an API key is the credential least able to answer the\n * questions they ask.\n */\nconst ORDER_WRITE_REFUSED: Record<string, string> = {\n cancelled:\n 'Canceling an order releases held stock, so it is not part of this endpoint. Cancel it in the console.',\n refunded:\n 'Refunding an order moves money, so it is not part of this endpoint. Refund it in the console.',\n}\n\n/**\n * `PATCH /v1/sites/{siteId}/orders/{orderId}` — record a shipment (AGL-2461).\n *\n * ## The write is the console's, shared\n *\n * The transition rule, the transaction that re-asks it under the write, the\n * fulfillment append and the timeline entry are `recordOrderShipment`'s,\n * the one implementation the console's `commerce/fulfill-order` route calls\n * too. A second copy of `ORDER_TRANSITIONS` here would be the bug rather\n * than the fix: two tables drift, and drift here means the API writing an\n * order status the console forbids — `paid → delivered` skipping\n * fulfilment, or a write onto a `refunded` order. That is the class\n * AGL-1818/AGL-1819 exist to close, and it is money-adjacent.\n *\n * ## What THIS function is responsible for: all of the authorization\n *\n * `recordOrderShipment` is pre-authorized by contract — it takes `hostId`\n * on trust — so every gate is here, and all four are load-bearing:\n *\n * 1. `orders:write` on the key (checked by the caller, above).\n * 2. The `commerce` PLAN entitlement (checked by the caller, above) — the\n * plan, never the plugin switch, which is the AGL-1873 distinction.\n * 3. **Org owns the site** — the console's `handleSites` refuses an unowned\n * `hostId` with a 404 before it hands any site resource to its plugin,\n * which is what keeps this from being a cross-tenant write primitive\n * addressable by anyone who can guess a host id. Deliberately NOT\n * re-checked here: a second copy would be a gate no test can redden\n * (removing either one alone leaves the other answering), and an unproven\n * guard on a cross-tenant write is worse than the one guard a test\n * actually holds. `api-v1-order-fulfilment.spec.ts`'s \"CANNOT move\n * another org's order\" case is that test, and it fails when\n * `handleSites`' `orgOwnsHost` is removed.\n * 4. **This plugin is switched on for this site.** The resource is\n * registered for every organization the console serves, so its being here\n * says nothing about one org's configuration; without this an org that\n * switched commerce off for a site would still accept writes into it,\n * which is the per-site enablement rule (AGL-1014) the plugin API\n * dispatcher applies to every other commerce door.\n *\n * ## No `Idempotency-Key`\n *\n * None is needed and none is accepted: the write returns without writing when\n * the order is already in the target status, so a retry lands the same state\n * AND returns the same `200` with the same order body — the contract\n * `updateRecord`, `updateContact` and `updateFormSubmission` are held to.\n */\nasync function updateOrder(\n request: Request,\n ctx: ApiV1Context,\n hostId: string,\n orderId: string,\n): Promise<Response> {\n const body = await readJsonBody(request)\n const unknown = Object.keys(body).filter((key) => !ORDER_WRITABLE_KEYS.includes(key))\n if (unknown.length > 0) {\n // Named, not dropped — the `updateFormSubmission` / `updateContact` rule.\n // A silently ignored `trackingUrl` here reads as \"we recorded your\n // shipment as you described it\" when half of it went nowhere, and the\n // caller is a warehouse system that will never look again.\n return ApiErrors.badRequest({\n message:\n 'Only `status`, `carrier`, `trackingNumber`, `trackingUrl`, `lineItems` and `notify` can be set on an order',\n code: 'validation_failed',\n fields: Object.fromEntries(\n unknown.map((key) => [key, 'Not writable on an order']),\n ),\n headers: ctx.headers,\n })\n }\n\n const status = String(body.status ?? '')\n const refusal = ORDER_WRITE_REFUSED[status]\n if (refusal) {\n return ApiErrors.badRequest({\n message: refusal,\n code: 'validation_failed',\n fields: { status: refusal },\n headers: ctx.headers,\n })\n }\n if (!(ORDER_WRITE_TARGETS as string[]).includes(status)) {\n return ApiErrors.badRequest({\n message: 'Order failed validation',\n code: 'validation_failed',\n fields: {\n status: `Must be one of: ${ORDER_WRITE_TARGETS.join(', ')}`,\n },\n headers: ctx.headers,\n })\n }\n // Bounded exactly as the console route bounds them, so one field cannot be\n // used to stuff an order document through a door the console keeps narrow.\n const carrier = String(body.carrier ?? '').slice(0, 40)\n const trackingNumber = String(body.trackingNumber ?? '').slice(0, 60)\n // Partial shipments (AGL-3611): `lineItems: [{ lineItemId, quantity }]`.\n // Absent keeps the old meaning, everything still to ship.\n let lineItems: Array<{ lineItemId: number; quantity: number }> | undefined\n if (body.lineItems !== undefined) {\n if (status !== 'fulfilled' || !Array.isArray(body.lineItems) || body.lineItems.length === 0) {\n return ApiErrors.badRequest({\n message: 'Order failed validation',\n code: 'validation_failed',\n fields: {\n lineItems:\n status !== 'fulfilled'\n ? 'Only a `fulfilled` update names line items'\n : 'Must be a non-empty array of { lineItemId, quantity }',\n },\n headers: ctx.headers,\n })\n }\n lineItems = (body.lineItems as unknown[]).slice(0, 500).map((entry) => ({\n lineItemId: Number((entry as Record<string, unknown>)?.lineItemId),\n quantity: Number((entry as Record<string, unknown>)?.quantity),\n }))\n }\n const idempotencyKey = request.headers.get('idempotency-key') ?? undefined\n\n // Gate 4: this plugin, on for this site.\n const hostSnap = await hostRef(ctx, hostId).get()\n if (!isHostPluginEnabled(ctx.org, hostSnap.data(), BUNDLE_ID)) {\n return ApiErrors.notFound({\n message: 'No such site',\n headers: ctx.headers,\n })\n }\n\n const outcome = await recordOrderShipment({\n hostId,\n orderId,\n to: status as OrderFulfilmentTarget,\n carrier,\n trackingNumber,\n ...(lineItems ? { lineItems } : {}),\n ...(typeof body.trackingUrl === 'string' ? { trackingUrl: body.trackingUrl } : {}),\n ...(body.notify === false ? { notify: false } : {}),\n ...(idempotencyKey ? { idempotencyKey } : {}),\n })\n if (outcome.outcome === 'invalid_lines') {\n return outcome.problem.problem === 'over_fulfilled'\n ? ApiErrors.conflict({\n message: outcome.message,\n code: 'over_fulfilled',\n headers: ctx.headers,\n })\n : ApiErrors.badRequest({\n message: 'Order failed validation',\n code: 'validation_failed',\n fields: { lineItems: outcome.message },\n headers: ctx.headers,\n })\n }\n if (outcome.outcome === 'no_such_order') {\n return ApiErrors.notFound({\n message: 'No such order',\n headers: ctx.headers,\n })\n }\n if (outcome.outcome === 'blocked') {\n // A NEW 409 code (`order_transition`), because an integrator has to be\n // able to tell \"the order moved on without me\" apart from every other\n // conflict this API can raise — it is the one a fulfilment poller will\n // actually hit, and the one it must not retry forever.\n return ApiErrors.conflict({\n message: `Orders in \"${outcome.from}\" cannot be marked ${status}`,\n code: 'order_transition',\n headers: ctx.headers,\n })\n }\n // The order object, on both `recorded` and `already` — a retry lands the\n // same state and reads the same 200. Re-read after the write so the body\n // shows the shipment that was just recorded rather than the one before it.\n return apiJson(orderView(await hostRef(ctx, hostId).collection('orders').doc(orderId).get()), {\n headers: ctx.headers,\n })\n}\n\n/**\n * Price and stock live on VARIANTS, never on the product — a product-level\n * `inventory` exists only as a denormalized sum the console rewrites on every\n * decrement, and a product-level `priceUsd` is the legacy single-variant\n * shape. Publishing either as the product's price would be wrong the moment a\n * product has two variants, so the variant array is the contract and the\n * product carries only the roll-up, clearly named.\n *\n * `inventory: null` on a variant means UNTRACKED and `0` means SOLD OUT.\n * Collapsing them (the `?? 0` an integrator writes on the first day) turns\n * every untracked product into an out-of-stock one, so the distinction is\n * carried through verbatim rather than defaulted.\n */\nfunction variantView(variant: Record<string, unknown>) {\n const inventory = variant.inventory\n return {\n id: variant.id ?? null,\n sku: variant.sku ?? null,\n barcode: variant.barcode ?? null,\n options: variant.options ?? {},\n priceUsd: typeof variant.priceUsd === 'number' ? variant.priceUsd : null,\n compareAtPriceUsd:\n typeof variant.compareAtPriceUsd === 'number'\n ? variant.compareAtPriceUsd\n : null,\n weightGrams:\n typeof variant.weightGrams === 'number' ? variant.weightGrams : null,\n inventory: typeof inventory === 'number' ? inventory : null,\n inventoryTracked: typeof inventory === 'number',\n }\n}\n\nfunction productView(doc: FirebaseFirestore.DocumentSnapshot) {\n const data = doc.data() ?? {}\n const variants = Array.isArray(data.variants)\n ? (data.variants as Array<Record<string, unknown>>)\n : []\n const tracked = variants.filter((v) => typeof v.inventory === 'number')\n return {\n id: doc.id,\n object: 'product',\n name: data.name ?? null,\n slug: data.slug ?? null,\n description: data.description ?? null,\n type: data.type ?? null,\n status: data.status ?? null,\n tags: data.tags ?? [],\n categoryIds: data.categoryIds ?? [],\n mediaUrls: data.mediaUrls ?? [],\n options: data.options ?? [],\n variants: variants.map(variantView),\n // The sum across TRACKED variants only, and `null` when none of them is\n // tracked — so an untracked catalogue reads as \"we don't count this\"\n // rather than as a store with nothing left to sell.\n inventory: tracked.length\n ? tracked.reduce((sum, v) => sum + Number(v.inventory ?? 0), 0)\n : null,\n subscription: serialize(data.subscription) ?? null,\n created: data.createdAtMs ? new Date(Number(data.createdAtMs)).toISOString() : null,\n updated: data.updatedAtMs ? new Date(Number(data.updatedAtMs)).toISOString() : null,\n }\n}\n\nexport async function handleProducts(\n request: Request,\n ctx: ApiV1Context,\n segments: string[],\n url: URL,\n): Promise<Response> {\n const [, hostId, , productId] = segments\n const denied = requireScope(ctx, 'products:read')\n if (denied) return denied\n const unentitled = requireCommerce(ctx)\n if (unentitled) return unentitled\n if (request.method !== 'GET') {\n return ApiErrors.methodNotAllowed({ headers: ctx.headers })\n }\n const collection = hostRef(ctx, hostId).collection('products')\n\n if (productId) {\n const snap = await collection.doc(productId).get()\n // A soft-deleted product is gone as far as a customer is concerned. The\n // console filters `deletedAt` client-side; the API must not hand back a\n // product the merchant deleted just because the document survives.\n if (!snap.exists || snap.get('deletedAt')) {\n return ApiErrors.notFound({ message: 'No such product', headers: ctx.headers })\n }\n return apiJson(productView(snap), { headers: ctx.headers })\n }\n\n let query: FirebaseFirestore.Query = collection\n const status = url.searchParams.get('status')\n if (status) query = query.where('status', '==', status)\n const { docs, nextCursor } = await paginate(query, url)\n const data = docs.filter((doc) => !doc.get('deletedAt')).map(productView)\n return listResponse(data, nextCursor, ctx.headers)\n}\n\n"],"names":["checkEntitlement","isHostPluginEnabled","ApiErrors","apiJson","listResponse","paginate","readJsonBody","requireScope","serialize","BUNDLE_ID","recordOrderShipment","orderViewFromData","requireCommerce","ctx","org","planRequired","message","code","headers","hostRef","hostId","firestore","collection","doc","orderView","id","data","handleOrders","request","segments","url","orderId","method","deniedWrite","unentitledWrite","updateOrder","denied","unentitled","methodNotAllowed","Allow","snap","get","exists","notFound","query","status","searchParams","where","channel","docs","nextCursor","map","filter","order","ORDER_WRITE_TARGETS","ORDER_WRITABLE_KEYS","ORDER_WRITE_REFUSED","cancelled","refunded","body","unknown","Object","keys","key","includes","length","badRequest","fields","fromEntries","String","refusal","join","carrier","slice","trackingNumber","lineItems","undefined","Array","isArray","entry","lineItemId","Number","quantity","idempotencyKey","hostSnap","outcome","to","trackingUrl","notify","problem","conflict","from","variantView","variant","inventory","sku","barcode","options","priceUsd","compareAtPriceUsd","weightGrams","inventoryTracked","productView","variants","tracked","v","object","name","slug","description","type","tags","categoryIds","mediaUrls","reduce","sum","subscription","created","createdAtMs","Date","toISOString","updated","updatedAtMs","handleProducts","productId"],"mappings":"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"}
@@ -39,8 +39,15 @@ import { releaseStockHold } from "./stock-hold.js";
39
39
  import { releasePromotionHold, settlePromotionSlot } from "./promotion-hold.js";
40
40
  import { flagOrderRestock } from "./restock-flag.js";
41
41
  import { storefrontTaxModeOf } from "./storefront-tax.js";
42
+ import { taxEngineStampFromMetadata } from "../model/commerce-tax-engine.js";
42
43
  import { recordStorefrontTax } from "./storefront-tax-record.js";
43
44
  import { enqueueSupplierDelivery } from "./supplier-outbox.js";
45
+ import { notifyOrderBuyer, onlineReceiptExtras } from "./order-notifications.js";
46
+ import { ORDER_PAID_EVENT, ORDER_REFUNDED_EVENT } from "../model/order-events.js";
47
+ import { checkoutExtrasCents, decodeCheckoutExtrasMetadata } from "@aglyn/aglyn/plugin-manager/plugin-checkout-extras";
48
+ import { raiseOrderEvent } from "./order-events.js";
49
+ import { handlePosStripeEvent } from "./pos-terminal.js";
50
+ import { notifyPosSaleCompleted } from "./pos-sale.js";
44
51
  /**
45
52
  * Assigns unassigned license keys for a digital product (AGL-308):
46
53
  * stamps order/email on the key docs, returns the key strings, and
@@ -262,9 +269,18 @@ async function findOrderForDispute(paymentIntentId) {
262
269
  kind: 'unresolved',
263
270
  reason: 'missing-index'
264
271
  };
265
- if (matches.empty) return {
266
- kind: 'not-ours'
267
- };
272
+ if (matches.empty) {
273
+ // A register sale paid with SEVERAL cards keeps every charge in
274
+ // `paymentIntentIds` and none in `paymentIntentId` (AGL-3607).
275
+ const split = await firebaseAdmin.app().firestore().collectionGroup('orders').where('paymentIntentIds', 'array-contains', paymentIntentId).limit(2).get().catch(()=>null);
276
+ if ((split == null ? void 0 : split.docs.length) === 1) return {
277
+ kind: 'order',
278
+ snapshot: split.docs[0]
279
+ };
280
+ return {
281
+ kind: 'not-ours'
282
+ };
283
+ }
268
284
  if (matches.docs.length > 1) {
269
285
  console.error('Dispute matched more than one order; reversing none', paymentIntentId);
270
286
  return {
@@ -556,7 +572,8 @@ async function findOrderForDispute(paymentIntentId) {
556
572
  }), snapshot.id), reversedCents > 0 ? {
557
573
  refundedCents: reversedTotal
558
574
  } : {}, closedTheOrder ? {
559
- status: 'refunded'
575
+ status: 'refunded',
576
+ updatedAtMs: Date.now()
560
577
  } : {}, {
561
578
  dispute: settled,
562
579
  timeline: CommerceModel.appendOrderEvent(order, 'dispute', lost ? `${amount} charged back (dispute lost)` + (closedTheOrder ? ' (full)' : '') : `Dispute ${outcome.replace(/_/g, ' ')} — no money reversed`, closedAtMs)
@@ -1704,6 +1721,18 @@ async function findOrderForDispute(paymentIntentId) {
1704
1721
  })) {
1705
1722
  return;
1706
1723
  }
1724
+ // THE REGISTER'S CARD PAYMENTS (AGL-3607): Terminal reader actions, register
1725
+ // PaymentIntents and register QR pages. Each names one payment on an open
1726
+ // sale and is settled by re-reading Stripe in `pos-terminal.ts`; every other
1727
+ // event falls through untouched.
1728
+ const posPayment = await handlePosStripeEvent({
1729
+ type,
1730
+ object
1731
+ });
1732
+ if (posPayment) return {
1733
+ claimed: true,
1734
+ hostId: posPayment.hostId
1735
+ };
1707
1736
  // A DEAD SESSION GIVES ITS RESERVATIONS BACK (AGL-2453).
1708
1737
  //
1709
1738
  // Stripe expires a Checkout Session 24 hours after creation, and emits this
@@ -2402,6 +2431,12 @@ async function findOrderForDispute(paymentIntentId) {
2402
2431
  }
2403
2432
  return true;
2404
2433
  });
2434
+ // Other plugins and the merchant's webhooks hear of the sale (AGL-3611); keyed, so a redelivery raises it once.
2435
+ await raiseOrderEvent(ORDER_PAID_EVENT, {
2436
+ hostId: invoiceHostId,
2437
+ orderId: invoiceId,
2438
+ key: 'paid'
2439
+ });
2405
2440
  // THE SALES TAX COMES BACK TO THE PLATFORM (AGL-1956), and like the
2406
2441
  // stop below it runs BEFORE the `recorded` short-circuit: a cycle
2407
2442
  // already on the ledger is exactly the cycle whose reversal may still
@@ -2850,6 +2885,9 @@ async function findOrderForDispute(paymentIntentId) {
2850
2885
  // transaction for the notices below (AGL-3432). The document id is
2851
2886
  // the Stripe session id, which appears nowhere in the console.
2852
2887
  let cartOrderNumber;
2888
+ // The optional lines the buyer took (AGL-3635), read back off the
2889
+ // session they were charged on; never re-quoted here.
2890
+ const cartExtras = decodeCheckoutExtrasMetadata(object == null ? void 0 : object.metadata);
2853
2891
  const created = await firestore.runTransaction(async (transaction)=>{
2854
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;
2855
2893
  var _object_metadata, _object_customer_details, _object_customer_details1;
@@ -2869,7 +2907,8 @@ async function findOrderForDispute(paymentIntentId) {
2869
2907
  // to be dropped here, storing `shippingCents: 0` on every online
2870
2908
  // order while the shopper's shipping sat inside `amount_total`.
2871
2909
  const totals = CommerceModel.computeCheckoutSessionTotals(lineItems, object, {
2872
- feeCents: Number(feeCents != null ? feeCents : 0)
2910
+ feeCents: Number(feeCents != null ? feeCents : 0),
2911
+ extrasCents: checkoutExtrasCents(cartExtras)
2873
2912
  });
2874
2913
  // With the fields the orders list queries by (AGL-3321).
2875
2914
  transaction.set(orderRef, CommerceModel.withOrderListFields(orderRef.id, _extends({
@@ -2877,7 +2916,10 @@ async function findOrderForDispute(paymentIntentId) {
2877
2916
  status: 'paid',
2878
2917
  channel: 'online',
2879
2918
  lineItems,
2880
- totals,
2919
+ totals
2920
+ }, cartExtras.length ? {
2921
+ extras: cartExtras
2922
+ } : {}, {
2881
2923
  // WHICH TAX THIS SALE CARRIED (AGL-2451). `totals.taxCents` above
2882
2924
  // says how much; this says who computed it, which is the fact that
2883
2925
  // decides whose registration the money is held under. The same
@@ -2885,7 +2927,8 @@ async function findOrderForDispute(paymentIntentId) {
2885
2927
  // the order and the filed row cannot disagree. A manual cart's tax
2886
2928
  // rides a real Stripe Tax Rate since AGL-1953 and therefore has a
2887
2929
  // populated breakdown — the flag is what tells the two apart.
2888
- taxMode: storefrontTaxModeOf(object, Number((_ref = object == null ? void 0 : (_object_metadata = object.metadata) == null ? void 0 : _object_metadata.taxCents) != null ? _ref : 0)),
2930
+ taxMode: storefrontTaxModeOf(object, Number((_ref = object == null ? void 0 : (_object_metadata = object.metadata) == null ? void 0 : _object_metadata.taxCents) != null ? _ref : 0))
2931
+ }, taxEngineStampFromMetadata(object == null ? void 0 : object.metadata), {
2889
2932
  timeline: [
2890
2933
  {
2891
2934
  atMs: Date.now(),
@@ -3004,13 +3047,22 @@ async function findOrderForDispute(paymentIntentId) {
3004
3047
  merge: true
3005
3048
  }).catch(()=>undefined);
3006
3049
  }
3050
+ // Other plugins and the merchant's webhooks hear of the sale (AGL-3611); keyed, so a redelivery raises it once.
3051
+ await raiseOrderEvent(ORDER_PAID_EVENT, {
3052
+ hostId: String(hostId),
3053
+ orderId: orderRef.id,
3054
+ key: 'paid'
3055
+ });
3007
3056
  // Branded receipt (AGL-296): env-gated like every outbound email.
3008
3057
  const buyerEmailForReceipt = object == null ? void 0 : (_object_customer_details7 = object.customer_details) == null ? void 0 : _object_customer_details7.email;
3009
- if (isEmailConfigured() && buyerEmailForReceipt) {
3058
+ // The store's receipt switch and the status link (AGL-3610).
3059
+ const receiptExtras = await onlineReceiptExtras(String(hostId), String(object.id));
3060
+ if (isEmailConfigured() && buyerEmailForReceipt && receiptExtras.enabled) {
3010
3061
  var _ref47, _ref48, _ref49, _ref50;
3011
3062
  const receiptSettings = await hostRef.collection('settings').doc('store').get().catch(()=>null);
3012
3063
  const receiptFooter = String((_ref47 = receiptSettings == null ? void 0 : receiptSettings.get('receiptFooter')) != null ? _ref47 : '');
3013
- const linesText = lineItems.map((line)=>`${line.quantity}× ${line.name}${line.variantLabel ? ` (${line.variantLabel})` : ''} — $${(line.unitAmountCents * line.quantity / 100).toFixed(2)}`).join('\n');
3064
+ 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.
3065
+ cartExtras.map((extra)=>`${extra.label} — $${(extra.amountCents / 100).toFixed(2)}`)).join('\n');
3014
3066
  // The keys assigned above the gate (AGL-2149); the receipt only
3015
3067
  // reports them.
3016
3068
  const licenseText = Object.entries(licenseKeysByProduct).flatMap(([keyProductId, keys])=>{
@@ -3034,7 +3086,7 @@ async function findOrderForDispute(paymentIntentId) {
3034
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}` : '');
3035
3087
  // Site-owner-designed template when published (AGL-771). The
3036
3088
  // license keys and download links ride in {{order.summary}}.
3037
- const designed = await renderHostEmailWithTokens(firebaseAdmin.app().firestore(), String(hostId), 'order-receipt', {
3089
+ const designed = await renderHostEmailWithTokens(firebaseAdmin.app().firestore(), String(hostId), 'order-receipt', _extends({
3038
3090
  'order.summary': orderSummary,
3039
3091
  'order.total': orderTotal,
3040
3092
  'order.ref': String(object.id),
@@ -3042,7 +3094,7 @@ async function findOrderForDispute(paymentIntentId) {
3042
3094
  // built-in copy always renders (AGL-3370), so a footer that is
3043
3095
  // only in the fallback text above never reaches a buyer.
3044
3096
  'store.receiptFooter': receiptFooter
3045
- });
3097
+ }, receiptExtras.tokens));
3046
3098
  await sendEmail(_extends({
3047
3099
  to: buyerEmailForReceipt,
3048
3100
  subject: (_ref50 = designed == null ? void 0 : designed.subject) != null ? _ref50 : `Receipt for your order`,
@@ -3484,7 +3536,9 @@ async function findOrderForDispute(paymentIntentId) {
3484
3536
  taxMode: storefrontTaxModeOf(object, Number((_ref2 = object == null ? void 0 : (_object_metadata = object.metadata) == null ? void 0 : _object_metadata.taxCents) != null ? _ref2 : 0)),
3485
3537
  paymentIntentId: String((_ref3 = object == null ? void 0 : object.payment_intent) != null ? _ref3 : '') || null,
3486
3538
  customerEmail: (_ref4 = (_ref5 = object == null ? void 0 : (_object_customer_details1 = object.customer_details) == null ? void 0 : _object_customer_details1.email) != null ? _ref5 : lifted.customerEmail) != null ? _ref4 : null,
3487
- timeline: CommerceModel.appendOrderEvent(lifted, 'paid')
3539
+ timeline: CommerceModel.appendOrderEvent(lifted, 'paid'),
3540
+ // A paid draft is new to a shipping tool's feed (AGL-3613).
3541
+ updatedAtMs: Date.now()
3488
3542
  }, shippingCents > 0 || stripeTaxCents > 0 ? {
3489
3543
  totals: _extends({}, (_lifted_totals = lifted.totals) != null ? _lifted_totals : {}, shippingCents > 0 ? {
3490
3544
  shippingCents
@@ -3527,6 +3581,16 @@ async function findOrderForDispute(paymentIntentId) {
3527
3581
  var _ref61, _ref62, _ref63, _ref64, _ref65, _ref66;
3528
3582
  var _object_metadata28, _object_customer_details15, _order_totals, _object_customer_details16;
3529
3583
  const order = paidOrder;
3584
+ // A legacy register QR sale is a register sale completing (AGL-3607).
3585
+ if (order.channel === 'pos') {
3586
+ await notifyPosSaleCompleted({
3587
+ hostId: String(hostId),
3588
+ orderId: String(orderId),
3589
+ order: _extends({}, order, {
3590
+ status: 'paid'
3591
+ })
3592
+ });
3593
+ }
3530
3594
  // Discounts engine redemptions (AGL-305), for this branch's BOTH
3531
3595
  // tenants: a console draft order and a POS card sale, which carry the
3532
3596
  // same `commerce-draft` metadata type. The cart branch has settled
@@ -3597,6 +3661,18 @@ async function findOrderForDispute(paymentIntentId) {
3597
3661
  }
3598
3662
  }));
3599
3663
  }
3664
+ // The receipt a payment link or a POS card sale never sent (AGL-3610).
3665
+ await notifyOrderBuyer({
3666
+ hostId: String(hostId),
3667
+ orderId: String(orderId)
3668
+ }, 'receipt', {
3669
+ email: draftEmail
3670
+ });
3671
+ await raiseOrderEvent(ORDER_PAID_EVENT, {
3672
+ hostId: String(hostId),
3673
+ orderId: String(orderId),
3674
+ key: 'paid'
3675
+ });
3600
3676
  if (productId) {
3601
3677
  var _productSnapshot_data, _ref71, _ref72;
3602
3678
  var _object_metadata30, _lifted_variants_, _order_lineItems_, _order_lineItems;
@@ -3791,7 +3867,8 @@ async function findOrderForDispute(paymentIntentId) {
3791
3867
  // tax (AGL-1711), so the session reports `amount_tax: 0` and
3792
3868
  // `metadata[taxCents]` — the key `checkout.ts` writes — is the only
3793
3869
  // witness. Same resolver as the filed tax row, same witness.
3794
- taxMode: storefrontTaxModeOf(object, Number((_ref = object == null ? void 0 : (_object_metadata = object.metadata) == null ? void 0 : _object_metadata.taxCents) != null ? _ref : 0)),
3870
+ taxMode: storefrontTaxModeOf(object, Number((_ref = object == null ? void 0 : (_object_metadata = object.metadata) == null ? void 0 : _object_metadata.taxCents) != null ? _ref : 0))
3871
+ }, taxEngineStampFromMetadata(object == null ? void 0 : object.metadata), {
3795
3872
  timeline: [
3796
3873
  {
3797
3874
  atMs: Date.now(),
@@ -4050,6 +4127,12 @@ async function findOrderForDispute(paymentIntentId) {
4050
4127
  detail: `Coupon ${couponCode} was applied to this order but no longer ` + 'exists, so the redemption is uncounted against its limit.'
4051
4128
  });
4052
4129
  }
4130
+ // Other plugins and the merchant's webhooks hear of the sale (AGL-3611); keyed, so a redelivery raises it once.
4131
+ await raiseOrderEvent(ORDER_PAID_EVENT, {
4132
+ hostId: String(hostId),
4133
+ orderId: orderRef.id,
4134
+ key: 'paid'
4135
+ });
4053
4136
  // Receipt + seller notification (AGL-96): env-gated like every
4054
4137
  // other outbound email; failures never fail the webhook.
4055
4138
  if (isEmailConfigured()) {
@@ -4059,7 +4142,9 @@ async function findOrderForDispute(paymentIntentId) {
4059
4142
  const amount = (Number((_ref85 = object == null ? void 0 : object.amount_total) != null ? _ref85 : 0) / 100).toFixed(2);
4060
4143
  const buyerEmail = object == null ? void 0 : (_object_customer_details21 = object.customer_details) == null ? void 0 : _object_customer_details21.email;
4061
4144
  const orderTotal = `$${amount}`;
4062
- if (buyerEmail) {
4145
+ // The store's receipt switch and the status link (AGL-3610).
4146
+ const receiptExtras = await onlineReceiptExtras(String(hostId), String(object.id));
4147
+ if (buyerEmail && receiptExtras.enabled) {
4063
4148
  var _ref86, _ref87;
4064
4149
  // The store's own Receipt footer, as the cart receipt carries it
4065
4150
  // (AGL-3432).
@@ -4069,12 +4154,12 @@ async function findOrderForDispute(paymentIntentId) {
4069
4154
  const receiptLine = `${soldQuantity}× ${productName}`;
4070
4155
  const fallbackText = `Thanks for your purchase!\n\n${receiptLine} — $${amount}` + `\nOrder reference: ${object.id}` + (receiptFooter ? `\n\n${receiptFooter}` : '');
4071
4156
  // Site-owner-designed template when published (AGL-771).
4072
- const designed = await renderHostEmailWithTokens(firebaseAdmin.app().firestore(), String(hostId), 'order-receipt', {
4157
+ const designed = await renderHostEmailWithTokens(firebaseAdmin.app().firestore(), String(hostId), 'order-receipt', _extends({
4073
4158
  'order.summary': receiptLine,
4074
4159
  'order.total': orderTotal,
4075
4160
  'order.ref': String(object.id),
4076
4161
  'store.receiptFooter': receiptFooter
4077
- });
4162
+ }, receiptExtras.tokens));
4078
4163
  await sendEmail(_extends({
4079
4164
  to: String(buyerEmail),
4080
4165
  subject: (_ref87 = designed == null ? void 0 : designed.subject) != null ? _ref87 : `Receipt: ${productName}`,
@@ -4164,6 +4249,28 @@ async function findOrderForDispute(paymentIntentId) {
4164
4249
  const snapshot = lookup.snapshot;
4165
4250
  const hostId = String((_ref90 = (_snapshot_ref_parent_parent = snapshot.ref.parent.parent) == null ? void 0 : _snapshot_ref_parent_parent.id) != null ? _ref90 : '');
4166
4251
  const external = await recordExternalRefund(snapshot.ref, object);
4252
+ if (external && hostId) {
4253
+ var _ref91, _ref92, _ref93;
4254
+ var _object_refunds;
4255
+ // A Stripe Dashboard refund is a refund to every subscriber too
4256
+ // (AGL-3645): accounting books it and the merchant's webhooks hear it,
4257
+ // as they do a console refund. Keyed by the charge's refunded total,
4258
+ // 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];
4260
+ await raiseOrderEvent(ORDER_REFUNDED_EVENT, {
4261
+ hostId,
4262
+ 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)}`,
4264
+ extra: {
4265
+ refund: {
4266
+ id: (latestRefund == null ? void 0 : latestRefund.id) ? String(latestRefund.id) : null,
4267
+ amountCents: external.newCents,
4268
+ lineItemIds: [],
4269
+ full: external.fullyRefunded
4270
+ }
4271
+ }
4272
+ });
4273
+ }
4167
4274
  if (external && hostId && snapshot.ref.parent.parent) {
4168
4275
  const outcome = await applyExternalRefundToGiftCards({
4169
4276
  firestore: firebaseAdmin.app().firestore(),
@@ -4203,16 +4310,16 @@ async function findOrderForDispute(paymentIntentId) {
4203
4310
  // merchant; both report the site so the seller's fraud ledger can name it.
4204
4311
  const risk = paymentRiskEventFrom(type, object);
4205
4312
  if (risk && risk.signal.kind !== 'dispute') {
4206
- var _ref91, _snapshot_data1, _ref92, _ref93;
4313
+ var _ref94, _snapshot_data1, _ref95, _ref96;
4207
4314
  var _snapshot_ref_parent_parent1, _order_totals2;
4208
4315
  const lookup = risk.paymentIntentId ? await findOrderForDispute(risk.paymentIntentId) : {
4209
4316
  kind: 'not-ours'
4210
4317
  };
4211
4318
  if (lookup.kind !== 'order') return;
4212
4319
  const snapshot = lookup.snapshot;
4213
- const hostId = String((_ref91 = (_snapshot_ref_parent_parent1 = snapshot.ref.parent.parent) == null ? void 0 : _snapshot_ref_parent_parent1.id) != null ? _ref91 : '');
4320
+ const hostId = String((_ref94 = (_snapshot_ref_parent_parent1 = snapshot.ref.parent.parent) == null ? void 0 : _snapshot_ref_parent_parent1.id) != null ? _ref94 : '');
4214
4321
  const order = CommerceModel.liftLegacyOrder((_snapshot_data1 = snapshot.data()) != null ? _snapshot_data1 : {});
4215
- const orderCents = Number((_ref92 = (_ref93 = (_order_totals2 = order.totals) == null ? void 0 : _order_totals2.totalCents) != null ? _ref93 : order.amountCents) != null ? _ref92 : 0);
4322
+ const orderCents = Number((_ref95 = (_ref96 = (_order_totals2 = order.totals) == null ? void 0 : _order_totals2.totalCents) != null ? _ref96 : order.amountCents) != null ? _ref95 : 0);
4216
4323
  await recordPaymentRiskOnRecord({
4217
4324
  ref: snapshot.ref,
4218
4325
  signal: risk.signal,
@@ -4275,7 +4382,7 @@ async function findOrderForDispute(paymentIntentId) {
4275
4382
  // finds). AGL-1554 will add its own `closed` branch for the marketplace
4276
4383
  // side; the two coexist because each self-selects by finding its own record.
4277
4384
  if (type === 'charge.dispute.created' || type === 'charge.dispute.closed') {
4278
- var _dispute_payment_intent, _ref94;
4385
+ var _dispute_payment_intent, _ref97;
4279
4386
  var _snapshot_ref_parent_parent2;
4280
4387
  const dispute = object != null ? object : {};
4281
4388
  const paymentIntentId = String((_dispute_payment_intent = dispute.payment_intent) != null ? _dispute_payment_intent : '');
@@ -4301,9 +4408,9 @@ async function findOrderForDispute(paymentIntentId) {
4301
4408
  const claimed = lookup.kind === 'order' || lookup.kind === 'unresolved';
4302
4409
  const snapshot = lookup.kind === 'order' ? lookup.snapshot : null;
4303
4410
  if (snapshot) {
4304
- var _ref95;
4411
+ var _ref98;
4305
4412
  var _snapshot_ref_parent_parent3;
4306
- const hostId = String((_ref95 = (_snapshot_ref_parent_parent3 = snapshot.ref.parent.parent) == null ? void 0 : _snapshot_ref_parent_parent3.id) != null ? _ref95 : '');
4413
+ const hostId = String((_ref98 = (_snapshot_ref_parent_parent3 = snapshot.ref.parent.parent) == null ? void 0 : _snapshot_ref_parent_parent3.id) != null ? _ref98 : '');
4307
4414
  if (type === 'charge.dispute.created') {
4308
4415
  var _snapshot_data2;
4309
4416
  // A chargeback's gift cards are frozen while it is open (AGL-3363).
@@ -4321,7 +4428,7 @@ async function findOrderForDispute(paymentIntentId) {
4321
4428
  }, notifyRiskEvent);
4322
4429
  const { opened, record } = await recordDisputeOpened(snapshot, dispute);
4323
4430
  if (opened && hostId) {
4324
- var _snapshot_data3, _record_evidenceDueByMs, _ref96;
4431
+ var _snapshot_data3, _record_evidenceDueByMs, _ref99;
4325
4432
  // Time-critical — Stripe's evidence window is days, not weeks — so
4326
4433
  // this is awaited rather than fired off with `void`: the handler is
4327
4434
  // serverless and work left running past the response is work the
@@ -4340,16 +4447,16 @@ async function findOrderForDispute(paymentIntentId) {
4340
4447
  },
4341
4448
  amount: `$${(record.amountCents / 100).toFixed(2)}`,
4342
4449
  evidenceDueByMs: (_record_evidenceDueByMs = record.evidenceDueByMs) != null ? _record_evidenceDueByMs : null,
4343
- dedupeKey: `sale:${String((_ref96 = dispute == null ? void 0 : dispute.id) != null ? _ref96 : snapshot.id)}`,
4450
+ dedupeKey: `sale:${String((_ref99 = dispute == null ? void 0 : dispute.id) != null ? _ref99 : snapshot.id)}`,
4344
4451
  staffEvidence: record.reason ? `Reason: ${record.reason.replace(/_/g, ' ')}.` : null
4345
4452
  });
4346
4453
  }
4347
4454
  } else {
4348
- var _ref97, _snapshot_data4, _ref98;
4455
+ var _ref100, _snapshot_data4, _ref101;
4349
4456
  // Lost: the money went back, so the cards it bought are voided. Won
4350
4457
  // (or closed on a warning): the dispute's freeze lifts; a freeze a
4351
4458
  // fraud warning or review put on stays for the merchant (AGL-3363).
4352
- await applyOrderGiftCardRisk(snapshot, String((_ref97 = dispute == null ? void 0 : dispute.status) != null ? _ref97 : '') === 'lost' ? {
4459
+ await applyOrderGiftCardRisk(snapshot, String((_ref100 = dispute == null ? void 0 : dispute.status) != null ? _ref100 : '') === 'lost' ? {
4353
4460
  kind: 'void',
4354
4461
  reason: 'dispute-lost'
4355
4462
  } : {
@@ -4420,7 +4527,7 @@ async function findOrderForDispute(paymentIntentId) {
4420
4527
  // transiently-failed reversal gets its retry — and gated inside on
4421
4528
  // the order's own `reversedTransferCents` marker, so it runs at most
4422
4529
  // once per dispute however many times Stripe delivers.
4423
- if (String((_ref98 = dispute == null ? void 0 : dispute.status) != null ? _ref98 : '') === 'lost') {
4530
+ if (String((_ref101 = dispute == null ? void 0 : dispute.status) != null ? _ref101 : '') === 'lost') {
4424
4531
  const pulledBackCents = await reverseSellerShare(snapshot.ref, dispute);
4425
4532
  // The merchant-experience half of the AGL-1794 decision, and not an
4426
4533
  // optional extra: the reversal takes money out of the connected
@@ -4450,7 +4557,7 @@ async function findOrderForDispute(paymentIntentId) {
4450
4557
  return _extends({
4451
4558
  claimed
4452
4559
  }, snapshot ? {
4453
- hostId: String((_ref94 = (_snapshot_ref_parent_parent2 = snapshot.ref.parent.parent) == null ? void 0 : _snapshot_ref_parent_parent2.id) != null ? _ref94 : '')
4560
+ hostId: String((_ref97 = (_snapshot_ref_parent_parent2 = snapshot.ref.parent.parent) == null ? void 0 : _snapshot_ref_parent_parent2.id) != null ? _ref97 : '')
4454
4561
  } : {});
4455
4562
  }
4456
4563
  };