@aglyn/plugins-commerce 1.0.0-beta.278 → 1.0.0-beta.283

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 (212) hide show
  1. package/package.json +17 -14
  2. package/src/lib/components/commerce-console-page.js +33 -1
  3. package/src/lib/components/commerce-console-page.js.map +1 -1
  4. package/src/lib/components/console/payments-settings-card.component.js +1 -1
  5. package/src/lib/components/console/payments-settings-card.component.js.map +1 -1
  6. package/src/lib/components/console/pos/pos-hardware-api.d.ts +122 -0
  7. package/src/lib/components/console/pos/pos-hardware-api.js +90 -0
  8. package/src/lib/components/console/pos/pos-hardware-api.js.map +1 -0
  9. package/src/lib/components/console/pos/pos-reader-splash-api.d.ts +58 -0
  10. package/src/lib/components/console/pos/pos-reader-splash-api.js +101 -0
  11. package/src/lib/components/console/pos/pos-reader-splash-api.js.map +1 -0
  12. package/src/lib/components/console/pos-devices-card.component.js +5 -0
  13. package/src/lib/components/console/pos-devices-card.component.js.map +1 -1
  14. package/src/lib/components/console/pos-hardware-card.component.d.ts +26 -0
  15. package/src/lib/components/console/pos-hardware-card.component.js +456 -0
  16. package/src/lib/components/console/pos-hardware-card.component.js.map +1 -0
  17. package/src/lib/components/console/pos-hardware-orders-staff-page.component.d.ts +14 -0
  18. package/src/lib/components/console/pos-hardware-orders-staff-page.component.js +519 -0
  19. package/src/lib/components/console/pos-hardware-orders-staff-page.component.js.map +1 -0
  20. package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.d.ts +27 -0
  21. package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js +444 -0
  22. package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js.map +1 -0
  23. package/src/lib/components/console/pos-ops/pos-check-in-zone.component.d.ts +9 -0
  24. package/src/lib/components/console/pos-ops/pos-check-in-zone.component.js +37 -0
  25. package/src/lib/components/console/pos-ops/pos-check-in-zone.component.js.map +1 -0
  26. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.d.ts +24 -0
  27. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js +346 -0
  28. package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js.map +1 -0
  29. package/src/lib/components/console/pos-ops/pos-customer-lookup.component.d.ts +5 -1
  30. package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js +83 -4
  31. package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js.map +1 -1
  32. package/src/lib/components/console/pos-ops/pos-day-totals-card.component.d.ts +35 -0
  33. package/src/lib/components/console/pos-ops/pos-day-totals-card.component.js +546 -0
  34. package/src/lib/components/console/pos-ops/pos-day-totals-card.component.js.map +1 -0
  35. package/src/lib/components/console/pos-ops/pos-operations-bar.component.d.ts +2 -1
  36. package/src/lib/components/console/pos-ops/pos-operations-bar.component.js +20 -1
  37. package/src/lib/components/console/pos-ops/pos-operations-bar.component.js.map +1 -1
  38. package/src/lib/components/console/pos-ops/pos-ops-api.d.ts +1 -1
  39. package/src/lib/components/console/pos-ops/pos-ops-api.js.map +1 -1
  40. package/src/lib/components/console/pos-page.component.js +6 -3
  41. package/src/lib/components/console/pos-page.component.js.map +1 -1
  42. package/src/lib/components/console/pos-reader-splash-setting.component.d.ts +22 -0
  43. package/src/lib/components/console/pos-reader-splash-setting.component.js +275 -0
  44. package/src/lib/components/console/pos-reader-splash-setting.component.js.map +1 -0
  45. package/src/lib/components/console/pos-reader-splash-staff-page.component.d.ts +14 -0
  46. package/src/lib/components/console/pos-reader-splash-staff-page.component.js +301 -0
  47. package/src/lib/components/console/pos-reader-splash-staff-page.component.js.map +1 -0
  48. package/src/lib/components/console/pos-zones.d.ts +37 -3
  49. package/src/lib/components/console/pos-zones.js +11 -1
  50. package/src/lib/components/console/pos-zones.js.map +1 -1
  51. package/src/lib/components/console/product-check-in-fields.component.d.ts +20 -0
  52. package/src/lib/components/console/product-check-in-fields.component.js +172 -0
  53. package/src/lib/components/console/product-check-in-fields.component.js.map +1 -0
  54. package/src/lib/components/console/product-editor-dialog.component.js +48 -9
  55. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
  56. package/src/lib/components/console/product-register-fields.component.d.ts +12 -0
  57. package/src/lib/components/console/product-register-fields.component.js +39 -1
  58. package/src/lib/components/console/product-register-fields.component.js.map +1 -1
  59. package/src/lib/components/console/products-hub-card.component.js +21 -6
  60. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  61. package/src/lib/components/console/reservations-card.component.js +239 -70
  62. package/src/lib/components/console/reservations-card.component.js.map +1 -1
  63. package/src/lib/components/reservation-widget.js +16 -0
  64. package/src/lib/components/reservation-widget.js.map +1 -1
  65. package/src/lib/constants/operator-alerts.d.ts +6 -0
  66. package/src/lib/constants/operator-alerts.js +20 -1
  67. package/src/lib/constants/operator-alerts.js.map +1 -1
  68. package/src/lib/declarations.console-server.js +7 -0
  69. package/src/lib/declarations.console-server.js.map +1 -1
  70. package/src/lib/declarations.d.ts +26 -0
  71. package/src/lib/declarations.js +34 -0
  72. package/src/lib/declarations.js.map +1 -0
  73. package/src/lib/model/commerce-check-in-product.d.ts +37 -0
  74. package/src/lib/model/commerce-check-in-product.js +49 -0
  75. package/src/lib/model/commerce-check-in-product.js.map +1 -0
  76. package/src/lib/model/commerce-check-in.d.ts +187 -0
  77. package/src/lib/model/commerce-check-in.js +468 -0
  78. package/src/lib/model/commerce-check-in.js.map +1 -0
  79. package/src/lib/model/commerce-pos-day-report.d.ts +209 -0
  80. package/src/lib/model/commerce-pos-day-report.js +572 -0
  81. package/src/lib/model/commerce-pos-day-report.js.map +1 -0
  82. package/src/lib/model/commerce-pos-offline.d.ts +65 -3
  83. package/src/lib/model/commerce-pos-offline.js +104 -7
  84. package/src/lib/model/commerce-pos-offline.js.map +1 -1
  85. package/src/lib/model/commerce-pos-ops.d.ts +3 -0
  86. package/src/lib/model/commerce-pos-ops.js +1 -0
  87. package/src/lib/model/commerce-pos-ops.js.map +1 -1
  88. package/src/lib/model/commerce-pos.d.ts +43 -3
  89. package/src/lib/model/commerce-pos.js +73 -5
  90. package/src/lib/model/commerce-pos.js.map +1 -1
  91. package/src/lib/model/commerce-reservations.d.ts +73 -7
  92. package/src/lib/model/commerce-reservations.js +90 -10
  93. package/src/lib/model/commerce-reservations.js.map +1 -1
  94. package/src/lib/model/commerce.d.ts +40 -0
  95. package/src/lib/model/commerce.js +22 -4
  96. package/src/lib/model/commerce.js.map +1 -1
  97. package/src/lib/model/index.d.ts +1 -0
  98. package/src/lib/model/index.js +1 -0
  99. package/src/lib/model/index.js.map +1 -1
  100. package/src/lib/model/plugin-permissions.d.ts +1 -1
  101. package/src/lib/model/plugin-permissions.js +2 -1
  102. package/src/lib/model/plugin-permissions.js.map +1 -1
  103. package/src/lib/model/pos-hardware.d.ts +288 -0
  104. package/src/lib/model/pos-hardware.js +499 -0
  105. package/src/lib/model/pos-hardware.js.map +1 -0
  106. package/src/lib/model/pos-offline-card-limits.d.ts +20 -0
  107. package/src/lib/model/pos-offline-card-limits.js +28 -0
  108. package/src/lib/model/pos-offline-card-limits.js.map +1 -0
  109. package/src/lib/model/pos-reader-splash.d.ts +114 -0
  110. package/src/lib/model/pos-reader-splash.js +141 -0
  111. package/src/lib/model/pos-reader-splash.js.map +1 -0
  112. package/src/lib/model/product-write.d.ts +6 -0
  113. package/src/lib/plugin-config.d.ts +10 -0
  114. package/src/lib/plugin-config.js +40 -0
  115. package/src/lib/plugin-config.js.map +1 -1
  116. package/src/lib/plugin.js +28 -0
  117. package/src/lib/plugin.js.map +1 -1
  118. package/src/lib/server/cart-checkout.js +3 -1
  119. package/src/lib/server/cart-checkout.js.map +1 -1
  120. package/src/lib/server/cart.js +12 -1
  121. package/src/lib/server/cart.js.map +1 -1
  122. package/src/lib/server/catalog.js +49 -12
  123. package/src/lib/server/catalog.js.map +1 -1
  124. package/src/lib/server/checkout-extras.js +1 -1
  125. package/src/lib/server/checkout-extras.js.map +1 -1
  126. package/src/lib/server/checkout.js +7 -0
  127. package/src/lib/server/checkout.js.map +1 -1
  128. package/src/lib/server/person-eraser.d.ts +8 -1
  129. package/src/lib/server/person-eraser.js +23 -1
  130. package/src/lib/server/person-eraser.js.map +1 -1
  131. package/src/lib/server/pos-check-in.d.ts +49 -0
  132. package/src/lib/server/pos-check-in.js +652 -0
  133. package/src/lib/server/pos-check-in.js.map +1 -0
  134. package/src/lib/server/pos-day-report.d.ts +46 -0
  135. package/src/lib/server/pos-day-report.js +299 -0
  136. package/src/lib/server/pos-day-report.js.map +1 -0
  137. package/src/lib/server/pos-hardware-admin.d.ts +50 -0
  138. package/src/lib/server/pos-hardware-admin.js +366 -0
  139. package/src/lib/server/pos-hardware-admin.js.map +1 -0
  140. package/src/lib/server/pos-hardware-notify.d.ts +27 -0
  141. package/src/lib/server/pos-hardware-notify.js +142 -0
  142. package/src/lib/server/pos-hardware-notify.js.map +1 -0
  143. package/src/lib/server/pos-hardware-user-eraser.d.ts +26 -0
  144. package/src/lib/server/pos-hardware-user-eraser.js +67 -0
  145. package/src/lib/server/pos-hardware-user-eraser.js.map +1 -0
  146. package/src/lib/server/pos-hardware-webhook.d.ts +31 -0
  147. package/src/lib/server/pos-hardware-webhook.js +176 -0
  148. package/src/lib/server/pos-hardware-webhook.js.map +1 -0
  149. package/src/lib/server/pos-hardware.d.ts +58 -0
  150. package/src/lib/server/pos-hardware.js +234 -0
  151. package/src/lib/server/pos-hardware.js.map +1 -0
  152. package/src/lib/server/pos-offline-card-reconcile.d.ts +38 -0
  153. package/src/lib/server/pos-offline-card-reconcile.js +58 -0
  154. package/src/lib/server/pos-offline-card-reconcile.js.map +1 -0
  155. package/src/lib/server/pos-offline-sync.d.ts +32 -0
  156. package/src/lib/server/pos-offline-sync.js +191 -15
  157. package/src/lib/server/pos-offline-sync.js.map +1 -1
  158. package/src/lib/server/pos-ops-routes.d.ts +4 -2
  159. package/src/lib/server/pos-ops-routes.js +14 -2
  160. package/src/lib/server/pos-ops-routes.js.map +1 -1
  161. package/src/lib/server/pos-order.js +39 -51
  162. package/src/lib/server/pos-order.js.map +1 -1
  163. package/src/lib/server/pos-payment.js +8 -4
  164. package/src/lib/server/pos-payment.js.map +1 -1
  165. package/src/lib/server/pos-reader-splash-admin.d.ts +36 -0
  166. package/src/lib/server/pos-reader-splash-admin.js +295 -0
  167. package/src/lib/server/pos-reader-splash-admin.js.map +1 -0
  168. package/src/lib/server/pos-reader-splash-render.d.ts +40 -0
  169. package/src/lib/server/pos-reader-splash-render.js +138 -0
  170. package/src/lib/server/pos-reader-splash-render.js.map +1 -0
  171. package/src/lib/server/pos-reader-splash-server.d.ts +32 -0
  172. package/src/lib/server/pos-reader-splash-server.js +32 -0
  173. package/src/lib/server/pos-reader-splash-server.js.map +1 -0
  174. package/src/lib/server/pos-reader-splash.d.ts +105 -0
  175. package/src/lib/server/pos-reader-splash.js +548 -0
  176. package/src/lib/server/pos-reader-splash.js.map +1 -0
  177. package/src/lib/server/pos-sale.d.ts +2 -1
  178. package/src/lib/server/pos-sale.js +3 -1
  179. package/src/lib/server/pos-sale.js.map +1 -1
  180. package/src/lib/server/pos-terminal.d.ts +67 -4
  181. package/src/lib/server/pos-terminal.js +239 -32
  182. package/src/lib/server/pos-terminal.js.map +1 -1
  183. package/src/lib/server/process-restock.js +4 -0
  184. package/src/lib/server/process-restock.js.map +1 -1
  185. package/src/lib/server/product-card.js +2 -2
  186. package/src/lib/server/product-card.js.map +1 -1
  187. package/src/lib/server/product-catalog.d.ts +2 -0
  188. package/src/lib/server/product-catalog.js +11 -4
  189. package/src/lib/server/product-catalog.js.map +1 -1
  190. package/src/lib/server/product.js +3 -1
  191. package/src/lib/server/product.js.map +1 -1
  192. package/src/lib/server/related.js +8 -1
  193. package/src/lib/server/related.js.map +1 -1
  194. package/src/lib/server/reservation-availability-query.d.ts +35 -0
  195. package/src/lib/server/reservation-availability-query.js +34 -0
  196. package/src/lib/server/reservation-availability-query.js.map +1 -0
  197. package/src/lib/server/reservation-availability.d.ts +21 -0
  198. package/src/lib/server/reservation-availability.js +64 -10
  199. package/src/lib/server/reservation-availability.js.map +1 -1
  200. package/src/lib/server/reservation-walk-in.d.ts +28 -0
  201. package/src/lib/server/reservation-walk-in.js +166 -0
  202. package/src/lib/server/reservation-walk-in.js.map +1 -0
  203. package/src/lib/server/reserve.js +13 -10
  204. package/src/lib/server/reserve.js.map +1 -1
  205. package/src/lib/server/reviews.js +3 -1
  206. package/src/lib/server/reviews.js.map +1 -1
  207. package/src/lib/server/site-page-resolver.js +5 -2
  208. package/src/lib/server/site-page-resolver.js.map +1 -1
  209. package/src/lib/server.js +26 -0
  210. package/src/lib/server.js.map +1 -1
  211. package/src/lib/testing/fake-firestore.js +72 -0
  212. package/src/lib/testing/fake-firestore.js.map +1 -1
@@ -0,0 +1,499 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ /*==========================================
17
+ * THE POS HARDWARE SHOP (AGL-4038), PHASE 1: THE MODEL.
18
+ *
19
+ * A merchant buys Stripe Terminal readers from the platform, the platform
20
+ * places the order in Stripe's Terminal hardware shop and Stripe ships it
21
+ * straight to the merchant. The platform is the seller of record, because the
22
+ * register's card payments are destination charges on the PLATFORM account
23
+ * and a reader must be registered there to take them.
24
+ *
25
+ * THE CATALOG IS CODE, THE PRICES ARE CONFIG. Which readers the register
26
+ * works with is a fact about this codebase; what Stripe charges for them, the
27
+ * markup on top and what shipping costs are business decisions that change
28
+ * without a release. Every figure comes from the console's environment, read
29
+ * by {@link parsePosHardwareConfig}, and a missing or malformed figure keeps
30
+ * the shop HIDDEN: it is a gate only the platform can lift, so no workspace is
31
+ * shown a control it cannot use.
32
+ *
33
+ * Everything here is pure, so the price math, the metadata a Checkout Session
34
+ * carries and the status rules are specced without Firestore or Stripe.
35
+ *=========================================*/ import { PLATFORM_BRAND_NAME } from "@aglyn/aglyn/app-utils/platform-brand";
36
+ /** The register's currency, and so the shop's: US dollars. */ export const POS_HARDWARE_CURRENCY = 'usd';
37
+ /**
38
+ * Where the shop ships. Stripe's Terminal hardware shop ships to many
39
+ * countries, but the register sells in US dollars only and the readers a
40
+ * merchant buys here are registered to a US Location, so the shop is US-only
41
+ * (Stripe itself excludes the US Virgin Islands, military addresses and
42
+ * P.O. boxes).
43
+ */ export const POS_HARDWARE_SHIP_COUNTRIES = [
44
+ 'US'
45
+ ];
46
+ /** The most of one item, and the most units, a single order may hold. */ export const POS_HARDWARE_MAX_PER_LINE = 10;
47
+ export const POS_HARDWARE_MAX_UNITS = 20;
48
+ /** The env names the shop reads, named once for the reader and the docs. */ export const POS_HARDWARE_ENV = {
49
+ /** `true` releases the shop. Absent or anything else keeps it hidden. */ enabled: 'POS_HARDWARE_SHOP_ENABLED',
50
+ /** JSON `{ "<sku>": <Stripe's price in cents> }`. */ prices: 'POS_HARDWARE_PRICES',
51
+ /**
52
+ * The markup on Stripe's price, an integer in basis points (100 = 1%).
53
+ * Absent, {@link POS_HARDWARE_DEFAULT_MARKUP_BPS} applies.
54
+ */ markupBps: 'POS_HARDWARE_MARKUP_BPS',
55
+ /** JSON `{ "standard" | "express" | "priority": <cents> }`. */ shipping: 'POS_HARDWARE_SHIPPING',
56
+ /** `true` turns Stripe automatic tax on for hardware checkouts. */ automaticTax: 'POS_HARDWARE_AUTOMATIC_TAX'
57
+ };
58
+ /**
59
+ * The markup when the deployment sets none: 3000 basis points on Stripe's
60
+ * price. The business sets it; this is the one place the default lives, and
61
+ * `POS_HARDWARE_MARKUP_BPS` overrides it.
62
+ */ export const POS_HARDWARE_DEFAULT_MARKUP_BPS = 3000;
63
+ /**
64
+ * Every item the shop can sell. An item is offered only once its Stripe
65
+ * price is configured, so one Stripe stops selling disappears by deleting its
66
+ * price, not by a release.
67
+ */ export const POS_HARDWARE_SKUS = [
68
+ {
69
+ id: 'stripe-reader-s710',
70
+ kind: 'reader',
71
+ stripeProductName: 'Stripe Reader S710',
72
+ displayName: 'Stripe Reader S710',
73
+ use: 'Handheld with its own 4G connection, for events and parking lots'
74
+ },
75
+ {
76
+ id: 'stripe-reader-s700',
77
+ kind: 'reader',
78
+ stripeProductName: 'Stripe Reader S700',
79
+ displayName: 'Stripe Reader S700',
80
+ use: 'Handheld or countertop reader on Wi-Fi'
81
+ },
82
+ {
83
+ id: 'bbpos-wisepos-e',
84
+ kind: 'reader',
85
+ stripeProductName: 'BBPOS WisePOS E',
86
+ displayName: 'BBPOS WisePOS E',
87
+ use: 'Countertop reader on Wi-Fi'
88
+ },
89
+ {
90
+ id: 'stripe-reader-m2',
91
+ kind: 'reader',
92
+ stripeProductName: 'Stripe Reader M2',
93
+ displayName: 'Stripe Reader M2',
94
+ use: `Pairs with the ${PLATFORM_BRAND_NAME} POS app on your phone or tablet by Bluetooth`
95
+ },
96
+ {
97
+ id: 'stripe-reader-s700-dock',
98
+ kind: 'accessory',
99
+ stripeProductName: 'Stripe Reader S700 Dock',
100
+ displayName: 'Charging dock for the S700 and S710',
101
+ use: 'Keeps the reader charged at the counter and adds an Ethernet port'
102
+ },
103
+ {
104
+ id: 'bbpos-wisepos-e-dock',
105
+ kind: 'accessory',
106
+ stripeProductName: 'BBPOS WisePOS E Dock',
107
+ displayName: 'Charging dock for the WisePOS E',
108
+ use: 'Keeps the reader charged at the counter and adds an Ethernet port'
109
+ },
110
+ {
111
+ id: 'stripe-reader-m2-mount',
112
+ kind: 'accessory',
113
+ stripeProductName: 'Stripe Reader M2 Mount',
114
+ displayName: 'Mount for the M2',
115
+ use: 'Holds the M2 in place at the counter'
116
+ }
117
+ ];
118
+ export function posHardwareSku(id) {
119
+ var _POS_HARDWARE_SKUS_find;
120
+ return (_POS_HARDWARE_SKUS_find = POS_HARDWARE_SKUS.find((sku)=>sku.id === id)) != null ? _POS_HARDWARE_SKUS_find : null;
121
+ }
122
+ export const POS_HARDWARE_SHIPPING_LABELS = {
123
+ standard: 'Standard shipping',
124
+ express: 'Express shipping',
125
+ priority: 'Priority shipping'
126
+ };
127
+ const SHIPPING_METHODS = Object.keys(POS_HARDWARE_SHIPPING_LABELS);
128
+ export function isPosHardwareShippingMethod(value) {
129
+ return typeof value === 'string' && SHIPPING_METHODS.includes(value);
130
+ }
131
+ /** The highest single figure the parser accepts, in cents: $10,000. */ const MAX_FIGURE_CENTS = 1000000;
132
+ /** The highest markup the parser accepts: 1000%. */ const MAX_MARKUP_BPS = 100000;
133
+ const isCents = (value, min)=>typeof value === 'number' && Number.isSafeInteger(value) && value >= min && value <= MAX_FIGURE_CENTS;
134
+ function parseJsonObject(raw) {
135
+ const text = String(raw != null ? raw : '').trim();
136
+ if (!text) return null;
137
+ try {
138
+ const parsed = JSON.parse(text);
139
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
140
+ } catch (unused) {
141
+ return null;
142
+ }
143
+ }
144
+ /**
145
+ * The shop's configuration from the environment, or `null` while the shop is
146
+ * hidden: not released, no reader priced, a malformed markup, or no shipping
147
+ * figure. An ABSENT markup is the default
148
+ * ({@link POS_HARDWARE_DEFAULT_MARKUP_BPS}), so a deployment can never sell
149
+ * at cost by forgetting a variable; `0` is a valid, explicit choice to sell
150
+ * at Stripe's price. A markup that is set but unreadable hides the shop
151
+ * rather than guessing.
152
+ *
153
+ * Unknown item ids and malformed figures are dropped one by one rather than
154
+ * failing the whole shop, so a typo in one price hides that one item.
155
+ */ export function parsePosHardwareConfig(env) {
156
+ var _env_POS_HARDWARE_ENV_enabled, _env_POS_HARDWARE_ENV_markupBps, _env_POS_HARDWARE_ENV_automaticTax;
157
+ if (String((_env_POS_HARDWARE_ENV_enabled = env[POS_HARDWARE_ENV.enabled]) != null ? _env_POS_HARDWARE_ENV_enabled : '').trim().toLowerCase() !== 'true') return null;
158
+ const markupText = String((_env_POS_HARDWARE_ENV_markupBps = env[POS_HARDWARE_ENV.markupBps]) != null ? _env_POS_HARDWARE_ENV_markupBps : '').trim();
159
+ if (markupText && !/^\d+$/.test(markupText)) return null;
160
+ const markupBps = markupText ? Number(markupText) : POS_HARDWARE_DEFAULT_MARKUP_BPS;
161
+ if (!Number.isSafeInteger(markupBps) || markupBps > MAX_MARKUP_BPS) return null;
162
+ const prices = parseJsonObject(env[POS_HARDWARE_ENV.prices]);
163
+ if (!prices) return null;
164
+ const costs = {};
165
+ for (const sku of POS_HARDWARE_SKUS){
166
+ const cost = prices[sku.id];
167
+ if (isCents(cost, 1)) costs[sku.id] = cost;
168
+ }
169
+ const anyReader = POS_HARDWARE_SKUS.some((sku)=>sku.kind === 'reader' && costs[sku.id] !== undefined);
170
+ if (!anyReader) return null;
171
+ const shippingRaw = parseJsonObject(env[POS_HARDWARE_ENV.shipping]);
172
+ if (!shippingRaw) return null;
173
+ const shipping = SHIPPING_METHODS.flatMap((method)=>{
174
+ const cents = shippingRaw[method];
175
+ return isCents(cents, 0) ? [
176
+ {
177
+ method,
178
+ cents
179
+ }
180
+ ] : [];
181
+ });
182
+ if (!shipping.length) return null;
183
+ return {
184
+ costs,
185
+ markupBps,
186
+ shipping,
187
+ automaticTax: String((_env_POS_HARDWARE_ENV_automaticTax = env[POS_HARDWARE_ENV.automaticTax]) != null ? _env_POS_HARDWARE_ENV_automaticTax : '').trim().toLowerCase() === 'true'
188
+ };
189
+ }
190
+ /**
191
+ * What the merchant pays for one item: Stripe's price plus the markup,
192
+ * rounded half up to the cent, in integer math throughout.
193
+ *
194
+ * `cost + round(cost × bps / 10 000)`. The markup is basis points ON COST, so
195
+ * a 50% markup is `5000` and a price at which half of it is profit (a 50%
196
+ * margin) is `10000`. Whichever the business means, it is this one number.
197
+ */ export function posHardwareUnitCents(costCents, markupBps) {
198
+ if (!Number.isSafeInteger(costCents) || costCents <= 0) return 0;
199
+ if (!Number.isSafeInteger(markupBps) || markupBps < 0) return costCents;
200
+ return costCents + Math.floor((costCents * markupBps + 5000) / 10000);
201
+ }
202
+ /** The items on offer, readers first, in catalog order. */ export function posHardwareOffers(config) {
203
+ return POS_HARDWARE_SKUS.flatMap((sku)=>{
204
+ const cost = config.costs[sku.id];
205
+ if (cost === undefined) return [];
206
+ return [
207
+ {
208
+ sku: sku.id,
209
+ kind: sku.kind,
210
+ name: sku.displayName,
211
+ use: sku.use,
212
+ unitCents: posHardwareUnitCents(cost, config.markupBps)
213
+ }
214
+ ];
215
+ });
216
+ }
217
+ /**
218
+ * A merchant's basket priced on the SERVER: every item must be on offer, each
219
+ * quantity a whole number from 1 to {@link POS_HARDWARE_MAX_PER_LINE}, and
220
+ * the order at most {@link POS_HARDWARE_MAX_UNITS} units. A repeated item is
221
+ * folded into one line. Prices come from the config, never the request.
222
+ */ export function pricePosHardwareCart(config, requested) {
223
+ if (!Array.isArray(requested) || requested.length === 0) {
224
+ return {
225
+ ok: false,
226
+ error: 'Choose at least one item.'
227
+ };
228
+ }
229
+ const offers = new Map(posHardwareOffers(config).map((offer)=>[
230
+ offer.sku,
231
+ offer
232
+ ]));
233
+ const quantities = new Map();
234
+ for (const entry of requested){
235
+ var _ref, _quantities_get;
236
+ const sku = String((_ref = entry == null ? void 0 : entry.sku) != null ? _ref : '');
237
+ const quantity = Number(entry == null ? void 0 : entry.quantity);
238
+ if (!offers.has(sku)) return {
239
+ ok: false,
240
+ error: 'That item is not available.'
241
+ };
242
+ if (!Number.isInteger(quantity) || quantity < 1) {
243
+ return {
244
+ ok: false,
245
+ error: 'Each quantity must be a whole number of at least 1.'
246
+ };
247
+ }
248
+ quantities.set(sku, ((_quantities_get = quantities.get(sku)) != null ? _quantities_get : 0) + quantity);
249
+ }
250
+ let units = 0;
251
+ const lines = [];
252
+ for (const [sku, quantity] of quantities){
253
+ if (quantity > POS_HARDWARE_MAX_PER_LINE) {
254
+ return {
255
+ ok: false,
256
+ error: `Order at most ${POS_HARDWARE_MAX_PER_LINE} of each item.`
257
+ };
258
+ }
259
+ units += quantity;
260
+ const offer = offers.get(sku);
261
+ lines.push({
262
+ sku: offer.sku,
263
+ name: offer.name,
264
+ quantity,
265
+ unitCents: offer.unitCents,
266
+ lineCents: offer.unitCents * quantity
267
+ });
268
+ }
269
+ if (units > POS_HARDWARE_MAX_UNITS) {
270
+ return {
271
+ ok: false,
272
+ error: `One order holds at most ${POS_HARDWARE_MAX_UNITS} items.`
273
+ };
274
+ }
275
+ // Accessories alone are allowed: a merchant may want a second dock.
276
+ return {
277
+ ok: true,
278
+ lines,
279
+ subtotalCents: lines.reduce((sum, line)=>sum + line.lineCents, 0)
280
+ };
281
+ }
282
+ /*==========================================
283
+ * THE CHECKOUT SESSION'S METADATA.
284
+ *
285
+ * The webhook builds the order from the session Stripe hands back, so the
286
+ * lines ride in its metadata in a compact form (`sku*qty*unit;…`, well inside
287
+ * Stripe's 500-character value limit for the at most seven distinct items),
288
+ * and the webhook re-reads every one against the catalog rather than trusting
289
+ * a name from the wire.
290
+ *=========================================*/ export const POS_HARDWARE_METADATA_TYPE = 'pos-hardware-order';
291
+ export function encodePosHardwareLines(lines) {
292
+ return lines.map((line)=>`${line.sku}*${line.quantity}*${line.unitCents}`).join(';');
293
+ }
294
+ export function decodePosHardwareLines(encoded) {
295
+ const text = String(encoded != null ? encoded : '').trim();
296
+ if (!text) return null;
297
+ const lines = [];
298
+ for (const part of text.split(';')){
299
+ const [skuId, quantityText, unitText] = part.split('*');
300
+ const sku = posHardwareSku(String(skuId != null ? skuId : ''));
301
+ const quantity = Number(quantityText);
302
+ const unitCents = Number(unitText);
303
+ if (!sku) return null;
304
+ if (!Number.isInteger(quantity) || quantity < 1 || quantity > POS_HARDWARE_MAX_PER_LINE) return null;
305
+ if (!Number.isSafeInteger(unitCents) || unitCents < 1) return null;
306
+ lines.push({
307
+ sku: sku.id,
308
+ name: sku.displayName,
309
+ quantity,
310
+ unitCents,
311
+ lineCents: quantity * unitCents
312
+ });
313
+ }
314
+ return lines;
315
+ }
316
+ export const POS_HARDWARE_ORDER_STATUSES = [
317
+ 'paid',
318
+ 'ordered',
319
+ 'shipped',
320
+ 'delivered',
321
+ 'registered',
322
+ 'canceled',
323
+ 'refunded'
324
+ ];
325
+ export function isPosHardwareOrderStatus(value) {
326
+ return typeof value === 'string' && POS_HARDWARE_ORDER_STATUSES.includes(value);
327
+ }
328
+ /** What the merchant reads on the Hardware card. */ export const POS_HARDWARE_STATUS_LABELS = {
329
+ paid: 'Order received',
330
+ ordered: 'Being prepared',
331
+ shipped: 'Shipped',
332
+ delivered: 'Delivered',
333
+ registered: 'Set up',
334
+ canceled: 'Canceled',
335
+ refunded: 'Refunded'
336
+ };
337
+ /** What staff read on the orders page: where the order is in fulfillment. */ export const POS_HARDWARE_STAFF_STATUS_LABELS = {
338
+ paid: 'Paid',
339
+ ordered: 'Ordered from Stripe',
340
+ shipped: 'Shipped',
341
+ delivered: 'Delivered',
342
+ registered: 'Registered',
343
+ canceled: 'Canceled',
344
+ refunded: 'Refunded'
345
+ };
346
+ /**
347
+ * The forward steps staff may move an order through. Canceling and refunding
348
+ * are separate acts with their own rule ({@link posHardwareRefundable}),
349
+ * because each returns money.
350
+ */ const NEXT_STATUS = {
351
+ paid: 'ordered',
352
+ ordered: 'shipped',
353
+ shipped: 'delivered',
354
+ delivered: 'registered'
355
+ };
356
+ export function nextPosHardwareStatus(status) {
357
+ var _NEXT_STATUS_status;
358
+ return (_NEXT_STATUS_status = NEXT_STATUS[status]) != null ? _NEXT_STATUS_status : null;
359
+ }
360
+ /**
361
+ * Whether the order may still be canceled or refunded in full: only before it
362
+ * ships. Once a parcel is on its way the money comes back through a return of
363
+ * the hardware, which is a separate process this phase does not automate.
364
+ */ export function posHardwareRefundable(status) {
365
+ return status === 'paid' || status === 'ordered';
366
+ }
367
+ /** The timestamp field each status stamps when it is reached. */ export const POS_HARDWARE_STATUS_STAMP = {
368
+ paid: 'paidAtMs',
369
+ ordered: 'orderedAtMs',
370
+ shipped: 'shippedAtMs',
371
+ delivered: 'deliveredAtMs',
372
+ registered: 'registeredAtMs',
373
+ canceled: 'canceledAtMs',
374
+ refunded: 'refundedAtMs'
375
+ };
376
+ /** The Hardware card's anchor: Checkout returns to it and the emails link at it. */ export const POS_HARDWARE_ANCHOR = 'pos-hardware';
377
+ /** The collection under `orgs/{orgId}`, named once. */ export const POS_HARDWARE_ORDERS_COLLECTION = 'hardwareOrders';
378
+ /*==========================================
379
+ * THE SALE RECORD THE PLATFORM KEEPS (AGL-4038).
380
+ *
381
+ * A hardware order is the PLATFORM's own sale: the platform is the seller of
382
+ * record and owes the sales tax on it. So beside every order the server keeps
383
+ * `posHardwareSales/{orderId}`, a TOP-LEVEL record of what tax and accounting
384
+ * need — the lines, the amounts, the tax, Stripe's ids, the status dates and
385
+ * the region it shipped to — and nothing about the person who bought it.
386
+ *
387
+ * It is the `platformRevenue` / `storefrontTaxCollected` shape on purpose
388
+ * (AGL-1811, AGL-1904): keyed to the workspace by FIELD, outside
389
+ * `orgs/{orgId}`, so the workspace erasure's `recursiveDelete(orgs/{orgId})`
390
+ * cannot see it, and never in any org-keyed sweep. GDPR Art. 17(3)(b) exempts
391
+ * records kept to comply with a legal obligation. The order under the
392
+ * workspace, with the buyer's name, street, phone, email and tracking, is
393
+ * erased with the workspace as before.
394
+ *
395
+ * Written in the same transaction as every write to the order, from the
396
+ * order as it stands after that write, so the two never disagree and a
397
+ * missing record heals on the next write.
398
+ *=========================================*/ /** The top-level collection of retained hardware sale records. */ export const POS_HARDWARE_SALES_COLLECTION = 'posHardwareSales';
399
+ const stampOrNull = (value)=>typeof value === 'number' && Number.isFinite(value) ? value : null;
400
+ const textOrNull = (value)=>typeof value === 'string' && value.trim() ? value.trim() : null;
401
+ /** The tax region of an order: its address cut down, or what an erasure left. */ export function posHardwareTaxRegion(order) {
402
+ var _order_shipping, _order_shipping1;
403
+ const address = (_order_shipping = order.shipping) == null ? void 0 : _order_shipping.address;
404
+ if (address == null ? void 0 : address.country) {
405
+ return {
406
+ country: address.country,
407
+ state: textOrNull(address.state),
408
+ postalCode: textOrNull(address.postalCode)
409
+ };
410
+ }
411
+ const kept = (_order_shipping1 = order.shipping) == null ? void 0 : _order_shipping1.taxRegion;
412
+ return (kept == null ? void 0 : kept.country) ? {
413
+ country: kept.country,
414
+ state: textOrNull(kept.state),
415
+ postalCode: textOrNull(kept.postalCode)
416
+ } : null;
417
+ }
418
+ /**
419
+ * The retained sale record of an order, as it stands. Every field is picked
420
+ * by name, so a field added to the order later reaches the record only when
421
+ * someone decides it should.
422
+ */ export function posHardwareSaleRecord(orderId, order) {
423
+ var _order_lines, _order_subtotalCents, _ref, _ref1, _order_taxCents, _order_totalCents, _order_currency, _order_createdAtMs, _ref2, _order_updatedAtMs;
424
+ var _order_shipping, _order_shipping1;
425
+ return {
426
+ orderId,
427
+ orgId: order.orgId,
428
+ lines: ((_order_lines = order.lines) != null ? _order_lines : []).map((line)=>({
429
+ sku: line.sku,
430
+ name: line.name,
431
+ quantity: line.quantity,
432
+ unitCents: line.unitCents,
433
+ lineCents: line.lineCents
434
+ })),
435
+ subtotalCents: (_order_subtotalCents = order.subtotalCents) != null ? _order_subtotalCents : 0,
436
+ shippingMethod: (_ref = (_order_shipping = order.shipping) == null ? void 0 : _order_shipping.method) != null ? _ref : 'standard',
437
+ shippingCents: (_ref1 = (_order_shipping1 = order.shipping) == null ? void 0 : _order_shipping1.cents) != null ? _ref1 : 0,
438
+ taxCents: (_order_taxCents = order.taxCents) != null ? _order_taxCents : 0,
439
+ totalCents: (_order_totalCents = order.totalCents) != null ? _order_totalCents : 0,
440
+ currency: (_order_currency = order.currency) != null ? _order_currency : POS_HARDWARE_CURRENCY,
441
+ shipTo: posHardwareTaxRegion(order),
442
+ stripeCheckoutSessionId: textOrNull(order.stripeCheckoutSessionId),
443
+ stripePaymentIntentId: textOrNull(order.stripePaymentIntentId),
444
+ stripeHardwareOrderId: textOrNull(order.stripeHardwareOrderId),
445
+ refundId: textOrNull(order.refundId),
446
+ refundedCents: stampOrNull(order.refundedCents),
447
+ status: order.status,
448
+ livemode: order.livemode === true,
449
+ createdAtMs: (_order_createdAtMs = order.createdAtMs) != null ? _order_createdAtMs : 0,
450
+ updatedAtMs: (_ref2 = (_order_updatedAtMs = order.updatedAtMs) != null ? _order_updatedAtMs : order.createdAtMs) != null ? _ref2 : 0,
451
+ paidAtMs: stampOrNull(order.paidAtMs),
452
+ orderedAtMs: stampOrNull(order.orderedAtMs),
453
+ shippedAtMs: stampOrNull(order.shippedAtMs),
454
+ deliveredAtMs: stampOrNull(order.deliveredAtMs),
455
+ registeredAtMs: stampOrNull(order.registeredAtMs),
456
+ canceledAtMs: stampOrNull(order.canceledAtMs),
457
+ refundedAtMs: stampOrNull(order.refundedAtMs)
458
+ };
459
+ }
460
+ /**
461
+ * What an account erasure does to an order the person placed (AGL-4038): the
462
+ * workspace keeps the order and every amount on it; the buyer comes off it —
463
+ * who started it, the email, the name, the phone, the street and the
464
+ * tracking, which names the parcel to a carrier who would say where it went.
465
+ * The country, state and postal code stay, because sales tax is sourced by
466
+ * them. Deterministic but for the stamp, and an order already erased no
467
+ * longer answers to the person's uid, so a second run finds nothing.
468
+ */ export function posHardwareBuyerErasedPatch(order, now) {
469
+ var _ref, _ref1, _ref2, _ref3;
470
+ // The whole map, read in the same transaction: a dotted-path update would
471
+ // leave a field added to `shipping` later standing with the person in it.
472
+ const shipping = order.shipping;
473
+ return {
474
+ createdBy: null,
475
+ contactEmail: null,
476
+ shipping: {
477
+ method: (_ref = shipping == null ? void 0 : shipping.method) != null ? _ref : 'standard',
478
+ label: (_ref1 = shipping == null ? void 0 : shipping.label) != null ? _ref1 : POS_HARDWARE_SHIPPING_LABELS[(_ref2 = shipping == null ? void 0 : shipping.method) != null ? _ref2 : 'standard'],
479
+ cents: (_ref3 = shipping == null ? void 0 : shipping.cents) != null ? _ref3 : 0,
480
+ name: null,
481
+ phone: null,
482
+ address: null,
483
+ taxRegion: posHardwareTaxRegion(order)
484
+ },
485
+ tracking: null,
486
+ buyerErasedAtMs: now,
487
+ updatedAtMs: now
488
+ };
489
+ }
490
+ /** An order id: `hw_` and twenty hex characters. */ export const POS_HARDWARE_ORDER_ID = /^hw_[a-f0-9]{20}$/;
491
+ /** Dollars and cents for the card and the emails. */ export function formatPosHardwareCents(cents) {
492
+ const value = Number.isFinite(cents) ? cents : 0;
493
+ return new Intl.NumberFormat('en-US', {
494
+ style: 'currency',
495
+ currency: 'USD'
496
+ }).format(value / 100);
497
+ }
498
+
499
+ //# sourceMappingURL=pos-hardware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/pos-hardware.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\n/*==========================================\n * THE POS HARDWARE SHOP (AGL-4038), PHASE 1: THE MODEL.\n *\n * A merchant buys Stripe Terminal readers from the platform, the platform\n * places the order in Stripe's Terminal hardware shop and Stripe ships it\n * straight to the merchant. The platform is the seller of record, because the\n * register's card payments are destination charges on the PLATFORM account\n * and a reader must be registered there to take them.\n *\n * THE CATALOG IS CODE, THE PRICES ARE CONFIG. Which readers the register\n * works with is a fact about this codebase; what Stripe charges for them, the\n * markup on top and what shipping costs are business decisions that change\n * without a release. Every figure comes from the console's environment, read\n * by {@link parsePosHardwareConfig}, and a missing or malformed figure keeps\n * the shop HIDDEN: it is a gate only the platform can lift, so no workspace is\n * shown a control it cannot use.\n *\n * Everything here is pure, so the price math, the metadata a Checkout Session\n * carries and the status rules are specced without Firestore or Stripe.\n *=========================================*/\n\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn/app-utils/platform-brand'\n\n/** The register's currency, and so the shop's: US dollars. */\nexport const POS_HARDWARE_CURRENCY = 'usd'\n\n/**\n * Where the shop ships. Stripe's Terminal hardware shop ships to many\n * countries, but the register sells in US dollars only and the readers a\n * merchant buys here are registered to a US Location, so the shop is US-only\n * (Stripe itself excludes the US Virgin Islands, military addresses and\n * P.O. boxes).\n */\nexport const POS_HARDWARE_SHIP_COUNTRIES: readonly string[] = ['US']\n\n/** The most of one item, and the most units, a single order may hold. */\nexport const POS_HARDWARE_MAX_PER_LINE = 10\nexport const POS_HARDWARE_MAX_UNITS = 20\n\n/** The env names the shop reads, named once for the reader and the docs. */\nexport const POS_HARDWARE_ENV = {\n /** `true` releases the shop. Absent or anything else keeps it hidden. */\n enabled: 'POS_HARDWARE_SHOP_ENABLED',\n /** JSON `{ \"<sku>\": <Stripe's price in cents> }`. */\n prices: 'POS_HARDWARE_PRICES',\n /**\n * The markup on Stripe's price, an integer in basis points (100 = 1%).\n * Absent, {@link POS_HARDWARE_DEFAULT_MARKUP_BPS} applies.\n */\n markupBps: 'POS_HARDWARE_MARKUP_BPS',\n /** JSON `{ \"standard\" | \"express\" | \"priority\": <cents> }`. */\n shipping: 'POS_HARDWARE_SHIPPING',\n /** `true` turns Stripe automatic tax on for hardware checkouts. */\n automaticTax: 'POS_HARDWARE_AUTOMATIC_TAX',\n} as const\n\n/**\n * The markup when the deployment sets none: 3000 basis points on Stripe's\n * price. The business sets it; this is the one place the default lives, and\n * `POS_HARDWARE_MARKUP_BPS` overrides it.\n */\nexport const POS_HARDWARE_DEFAULT_MARKUP_BPS = 3000\n\nexport type PosHardwareSkuId =\n | 'stripe-reader-s710'\n | 'stripe-reader-s700'\n | 'stripe-reader-m2'\n | 'bbpos-wisepos-e'\n | 'stripe-reader-s700-dock'\n | 'bbpos-wisepos-e-dock'\n | 'stripe-reader-m2-mount'\n\nexport interface PosHardwareSku {\n id: PosHardwareSkuId\n kind: 'reader' | 'accessory'\n /** What Stripe's hardware shop calls it, for the staff member ordering it. */\n stripeProductName: string\n /** What the merchant sees. */\n displayName: string\n /** One line on what it is for. */\n use: string\n}\n\n/**\n * Every item the shop can sell. An item is offered only once its Stripe\n * price is configured, so one Stripe stops selling disappears by deleting its\n * price, not by a release.\n */\nexport const POS_HARDWARE_SKUS: readonly PosHardwareSku[] = [\n {\n id: 'stripe-reader-s710',\n kind: 'reader',\n stripeProductName: 'Stripe Reader S710',\n displayName: 'Stripe Reader S710',\n use: 'Handheld with its own 4G connection, for events and parking lots',\n },\n {\n id: 'stripe-reader-s700',\n kind: 'reader',\n stripeProductName: 'Stripe Reader S700',\n displayName: 'Stripe Reader S700',\n use: 'Handheld or countertop reader on Wi-Fi',\n },\n {\n id: 'bbpos-wisepos-e',\n kind: 'reader',\n stripeProductName: 'BBPOS WisePOS E',\n displayName: 'BBPOS WisePOS E',\n use: 'Countertop reader on Wi-Fi',\n },\n {\n id: 'stripe-reader-m2',\n kind: 'reader',\n stripeProductName: 'Stripe Reader M2',\n displayName: 'Stripe Reader M2',\n use: `Pairs with the ${PLATFORM_BRAND_NAME} POS app on your phone or tablet by Bluetooth`,\n },\n {\n id: 'stripe-reader-s700-dock',\n kind: 'accessory',\n stripeProductName: 'Stripe Reader S700 Dock',\n displayName: 'Charging dock for the S700 and S710',\n use: 'Keeps the reader charged at the counter and adds an Ethernet port',\n },\n {\n id: 'bbpos-wisepos-e-dock',\n kind: 'accessory',\n stripeProductName: 'BBPOS WisePOS E Dock',\n displayName: 'Charging dock for the WisePOS E',\n use: 'Keeps the reader charged at the counter and adds an Ethernet port',\n },\n {\n id: 'stripe-reader-m2-mount',\n kind: 'accessory',\n stripeProductName: 'Stripe Reader M2 Mount',\n displayName: 'Mount for the M2',\n use: 'Holds the M2 in place at the counter',\n },\n]\n\nexport function posHardwareSku(id: string): PosHardwareSku | null {\n return POS_HARDWARE_SKUS.find((sku) => sku.id === id) ?? null\n}\n\nexport type PosHardwareShippingMethod = 'standard' | 'express' | 'priority'\n\nexport const POS_HARDWARE_SHIPPING_LABELS: Record<PosHardwareShippingMethod, string> = {\n standard: 'Standard shipping',\n express: 'Express shipping',\n priority: 'Priority shipping',\n}\n\nconst SHIPPING_METHODS = Object.keys(POS_HARDWARE_SHIPPING_LABELS) as PosHardwareShippingMethod[]\n\nexport function isPosHardwareShippingMethod(value: unknown): value is PosHardwareShippingMethod {\n return typeof value === 'string' && (SHIPPING_METHODS as string[]).includes(value)\n}\n\n/** The shop's configuration, once every figure it needs is present and sane. */\nexport interface PosHardwareConfig {\n /** Stripe's price for each configured item, in cents. */\n costs: Partial<Record<PosHardwareSkuId, number>>\n markupBps: number\n shipping: Array<{ method: PosHardwareShippingMethod; cents: number }>\n automaticTax: boolean\n}\n\n/** The highest single figure the parser accepts, in cents: $10,000. */\nconst MAX_FIGURE_CENTS = 1_000_000\n/** The highest markup the parser accepts: 1000%. */\nconst MAX_MARKUP_BPS = 100_000\n\nconst isCents = (value: unknown, min: number): value is number =>\n typeof value === 'number' &&\n Number.isSafeInteger(value) &&\n value >= min &&\n value <= MAX_FIGURE_CENTS\n\nfunction parseJsonObject(raw: string | undefined): Record<string, unknown> | null {\n const text = String(raw ?? '').trim()\n if (!text) return null\n try {\n const parsed = JSON.parse(text) as unknown\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : null\n } catch {\n return null\n }\n}\n\n/**\n * The shop's configuration from the environment, or `null` while the shop is\n * hidden: not released, no reader priced, a malformed markup, or no shipping\n * figure. An ABSENT markup is the default\n * ({@link POS_HARDWARE_DEFAULT_MARKUP_BPS}), so a deployment can never sell\n * at cost by forgetting a variable; `0` is a valid, explicit choice to sell\n * at Stripe's price. A markup that is set but unreadable hides the shop\n * rather than guessing.\n *\n * Unknown item ids and malformed figures are dropped one by one rather than\n * failing the whole shop, so a typo in one price hides that one item.\n */\nexport function parsePosHardwareConfig(\n env: Record<string, string | undefined>,\n): PosHardwareConfig | null {\n if (String(env[POS_HARDWARE_ENV.enabled] ?? '').trim().toLowerCase() !== 'true') return null\n\n const markupText = String(env[POS_HARDWARE_ENV.markupBps] ?? '').trim()\n if (markupText && !/^\\d+$/.test(markupText)) return null\n const markupBps = markupText ? Number(markupText) : POS_HARDWARE_DEFAULT_MARKUP_BPS\n if (!Number.isSafeInteger(markupBps) || markupBps > MAX_MARKUP_BPS) return null\n\n const prices = parseJsonObject(env[POS_HARDWARE_ENV.prices])\n if (!prices) return null\n const costs: Partial<Record<PosHardwareSkuId, number>> = {}\n for (const sku of POS_HARDWARE_SKUS) {\n const cost = prices[sku.id]\n if (isCents(cost, 1)) costs[sku.id] = cost\n }\n const anyReader = POS_HARDWARE_SKUS.some((sku) => sku.kind === 'reader' && costs[sku.id] !== undefined)\n if (!anyReader) return null\n\n const shippingRaw = parseJsonObject(env[POS_HARDWARE_ENV.shipping])\n if (!shippingRaw) return null\n const shipping = SHIPPING_METHODS.flatMap((method) => {\n const cents = shippingRaw[method]\n return isCents(cents, 0) ? [{ method, cents }] : []\n })\n if (!shipping.length) return null\n\n return {\n costs,\n markupBps,\n shipping,\n automaticTax:\n String(env[POS_HARDWARE_ENV.automaticTax] ?? '').trim().toLowerCase() === 'true',\n }\n}\n\n/**\n * What the merchant pays for one item: Stripe's price plus the markup,\n * rounded half up to the cent, in integer math throughout.\n *\n * `cost + round(cost × bps / 10 000)`. The markup is basis points ON COST, so\n * a 50% markup is `5000` and a price at which half of it is profit (a 50%\n * margin) is `10000`. Whichever the business means, it is this one number.\n */\nexport function posHardwareUnitCents(costCents: number, markupBps: number): number {\n if (!Number.isSafeInteger(costCents) || costCents <= 0) return 0\n if (!Number.isSafeInteger(markupBps) || markupBps < 0) return costCents\n return costCents + Math.floor((costCents * markupBps + 5_000) / 10_000)\n}\n\n/** An item as the shop offers it: the merchant's price, never Stripe's. */\nexport interface PosHardwareOffer {\n sku: PosHardwareSkuId\n kind: PosHardwareSku['kind']\n name: string\n use: string\n unitCents: number\n}\n\n/** The items on offer, readers first, in catalog order. */\nexport function posHardwareOffers(config: PosHardwareConfig): PosHardwareOffer[] {\n return POS_HARDWARE_SKUS.flatMap((sku) => {\n const cost = config.costs[sku.id]\n if (cost === undefined) return []\n return [\n {\n sku: sku.id,\n kind: sku.kind,\n name: sku.displayName,\n use: sku.use,\n unitCents: posHardwareUnitCents(cost, config.markupBps),\n },\n ]\n })\n}\n\nexport interface PosHardwareLine {\n sku: PosHardwareSkuId\n name: string\n quantity: number\n unitCents: number\n lineCents: number\n}\n\nexport type PosHardwareCartResult =\n | { ok: true; lines: PosHardwareLine[]; subtotalCents: number }\n | { ok: false; error: string }\n\n/**\n * A merchant's basket priced on the SERVER: every item must be on offer, each\n * quantity a whole number from 1 to {@link POS_HARDWARE_MAX_PER_LINE}, and\n * the order at most {@link POS_HARDWARE_MAX_UNITS} units. A repeated item is\n * folded into one line. Prices come from the config, never the request.\n */\nexport function pricePosHardwareCart(\n config: PosHardwareConfig,\n requested: unknown,\n): PosHardwareCartResult {\n if (!Array.isArray(requested) || requested.length === 0) {\n return { ok: false, error: 'Choose at least one item.' }\n }\n const offers = new Map(posHardwareOffers(config).map((offer) => [offer.sku as string, offer]))\n const quantities = new Map<string, number>()\n for (const entry of requested) {\n const sku = String((entry as { sku?: unknown })?.sku ?? '')\n const quantity = Number((entry as { quantity?: unknown })?.quantity)\n if (!offers.has(sku)) return { ok: false, error: 'That item is not available.' }\n if (!Number.isInteger(quantity) || quantity < 1) {\n return { ok: false, error: 'Each quantity must be a whole number of at least 1.' }\n }\n quantities.set(sku, (quantities.get(sku) ?? 0) + quantity)\n }\n let units = 0\n const lines: PosHardwareLine[] = []\n for (const [sku, quantity] of quantities) {\n if (quantity > POS_HARDWARE_MAX_PER_LINE) {\n return { ok: false, error: `Order at most ${POS_HARDWARE_MAX_PER_LINE} of each item.` }\n }\n units += quantity\n const offer = offers.get(sku) as PosHardwareOffer\n lines.push({\n sku: offer.sku,\n name: offer.name,\n quantity,\n unitCents: offer.unitCents,\n lineCents: offer.unitCents * quantity,\n })\n }\n if (units > POS_HARDWARE_MAX_UNITS) {\n return { ok: false, error: `One order holds at most ${POS_HARDWARE_MAX_UNITS} items.` }\n }\n // Accessories alone are allowed: a merchant may want a second dock.\n return { ok: true, lines, subtotalCents: lines.reduce((sum, line) => sum + line.lineCents, 0) }\n}\n\n/*==========================================\n * THE CHECKOUT SESSION'S METADATA.\n *\n * The webhook builds the order from the session Stripe hands back, so the\n * lines ride in its metadata in a compact form (`sku*qty*unit;…`, well inside\n * Stripe's 500-character value limit for the at most seven distinct items),\n * and the webhook re-reads every one against the catalog rather than trusting\n * a name from the wire.\n *=========================================*/\n\nexport const POS_HARDWARE_METADATA_TYPE = 'pos-hardware-order'\n\nexport function encodePosHardwareLines(lines: readonly PosHardwareLine[]): string {\n return lines.map((line) => `${line.sku}*${line.quantity}*${line.unitCents}`).join(';')\n}\n\nexport function decodePosHardwareLines(encoded: unknown): PosHardwareLine[] | null {\n const text = String(encoded ?? '').trim()\n if (!text) return null\n const lines: PosHardwareLine[] = []\n for (const part of text.split(';')) {\n const [skuId, quantityText, unitText] = part.split('*')\n const sku = posHardwareSku(String(skuId ?? ''))\n const quantity = Number(quantityText)\n const unitCents = Number(unitText)\n if (!sku) return null\n if (!Number.isInteger(quantity) || quantity < 1 || quantity > POS_HARDWARE_MAX_PER_LINE) return null\n if (!Number.isSafeInteger(unitCents) || unitCents < 1) return null\n lines.push({ sku: sku.id, name: sku.displayName, quantity, unitCents, lineCents: quantity * unitCents })\n }\n return lines\n}\n\n/*==========================================\n * THE ORDER AND ITS STATUSES.\n *=========================================*/\n\nexport type PosHardwareOrderStatus =\n | 'paid'\n | 'ordered'\n | 'shipped'\n | 'delivered'\n | 'registered'\n | 'canceled'\n | 'refunded'\n\nexport const POS_HARDWARE_ORDER_STATUSES: readonly PosHardwareOrderStatus[] = [\n 'paid',\n 'ordered',\n 'shipped',\n 'delivered',\n 'registered',\n 'canceled',\n 'refunded',\n]\n\nexport function isPosHardwareOrderStatus(value: unknown): value is PosHardwareOrderStatus {\n return typeof value === 'string' && (POS_HARDWARE_ORDER_STATUSES as readonly string[]).includes(value)\n}\n\n/** What the merchant reads on the Hardware card. */\nexport const POS_HARDWARE_STATUS_LABELS: Record<PosHardwareOrderStatus, string> = {\n paid: 'Order received',\n ordered: 'Being prepared',\n shipped: 'Shipped',\n delivered: 'Delivered',\n registered: 'Set up',\n canceled: 'Canceled',\n refunded: 'Refunded',\n}\n\n/** What staff read on the orders page: where the order is in fulfillment. */\nexport const POS_HARDWARE_STAFF_STATUS_LABELS: Record<PosHardwareOrderStatus, string> = {\n paid: 'Paid',\n ordered: 'Ordered from Stripe',\n shipped: 'Shipped',\n delivered: 'Delivered',\n registered: 'Registered',\n canceled: 'Canceled',\n refunded: 'Refunded',\n}\n\n/**\n * The forward steps staff may move an order through. Canceling and refunding\n * are separate acts with their own rule ({@link posHardwareRefundable}),\n * because each returns money.\n */\nconst NEXT_STATUS: Partial<Record<PosHardwareOrderStatus, PosHardwareOrderStatus>> = {\n paid: 'ordered',\n ordered: 'shipped',\n shipped: 'delivered',\n delivered: 'registered',\n}\n\nexport function nextPosHardwareStatus(\n status: PosHardwareOrderStatus,\n): PosHardwareOrderStatus | null {\n return NEXT_STATUS[status] ?? null\n}\n\n/**\n * Whether the order may still be canceled or refunded in full: only before it\n * ships. Once a parcel is on its way the money comes back through a return of\n * the hardware, which is a separate process this phase does not automate.\n */\nexport function posHardwareRefundable(status: PosHardwareOrderStatus): boolean {\n return status === 'paid' || status === 'ordered'\n}\n\nexport interface PosHardwareAddress {\n line1: string\n line2?: string\n city: string\n state?: string\n postalCode: string\n country: string\n}\n\n/**\n * What sales tax is sourced by: where the order shipped, without the street\n * or the person. An erasure keeps this much of the address (see\n * {@link posHardwareBuyerErasedPatch}) and the retained sale record holds\n * nothing finer ({@link PosHardwareSaleRecord}).\n */\nexport interface PosHardwareTaxRegion {\n country: string\n state: string | null\n postalCode: string | null\n}\n\nexport interface PosHardwareTracking {\n carrier: string\n number: string\n url: string | null\n}\n\n/** `orgs/{orgId}/hardwareOrders/{orderId}`, written only by the server. */\nexport interface PosHardwareOrder {\n orgId: string\n /** The site whose POS settings the order was placed from. */\n hostId: string\n /** The member who started the checkout; `null` once their account is erased. */\n createdBy: string | null\n /** Where the order emails go: the address entered at checkout. */\n contactEmail: string | null\n lines: PosHardwareLine[]\n subtotalCents: number\n shipping: {\n method: PosHardwareShippingMethod\n label: string\n cents: number\n name: string | null\n phone: string | null\n address: PosHardwareAddress | null\n /**\n * Set when the buyer's account is erased, in place of `address`: the part\n * of it sales tax is sourced by.\n */\n taxRegion?: PosHardwareTaxRegion | null\n }\n taxCents: number\n totalCents: number\n currency: string\n stripeCheckoutSessionId: string\n stripePaymentIntentId: string | null\n /** Stripe's hardware order id, recorded by staff (phase 2 sets it itself). */\n stripeHardwareOrderId: string | null\n tracking: PosHardwareTracking | null\n status: PosHardwareOrderStatus\n livemode: boolean\n createdAtMs: number\n updatedAtMs: number\n paidAtMs: number\n orderedAtMs?: number\n shippedAtMs?: number\n deliveredAtMs?: number\n registeredAtMs?: number\n canceledAtMs?: number\n refundedAtMs?: number\n refundId?: string | null\n /** The cents Stripe reported refunding. */\n refundedCents?: number\n /** When the buyer's account was erased and their details taken off the order. */\n buyerErasedAtMs?: number\n}\n\n/** The timestamp field each status stamps when it is reached. */\nexport const POS_HARDWARE_STATUS_STAMP: Record<PosHardwareOrderStatus, keyof PosHardwareOrder> = {\n paid: 'paidAtMs',\n ordered: 'orderedAtMs',\n shipped: 'shippedAtMs',\n delivered: 'deliveredAtMs',\n registered: 'registeredAtMs',\n canceled: 'canceledAtMs',\n refunded: 'refundedAtMs',\n}\n\n/** The Hardware card's anchor: Checkout returns to it and the emails link at it. */\nexport const POS_HARDWARE_ANCHOR = 'pos-hardware'\n\n/** The collection under `orgs/{orgId}`, named once. */\nexport const POS_HARDWARE_ORDERS_COLLECTION = 'hardwareOrders'\n\n/*==========================================\n * THE SALE RECORD THE PLATFORM KEEPS (AGL-4038).\n *\n * A hardware order is the PLATFORM's own sale: the platform is the seller of\n * record and owes the sales tax on it. So beside every order the server keeps\n * `posHardwareSales/{orderId}`, a TOP-LEVEL record of what tax and accounting\n * need — the lines, the amounts, the tax, Stripe's ids, the status dates and\n * the region it shipped to — and nothing about the person who bought it.\n *\n * It is the `platformRevenue` / `storefrontTaxCollected` shape on purpose\n * (AGL-1811, AGL-1904): keyed to the workspace by FIELD, outside\n * `orgs/{orgId}`, so the workspace erasure's `recursiveDelete(orgs/{orgId})`\n * cannot see it, and never in any org-keyed sweep. GDPR Art. 17(3)(b) exempts\n * records kept to comply with a legal obligation. The order under the\n * workspace, with the buyer's name, street, phone, email and tracking, is\n * erased with the workspace as before.\n *\n * Written in the same transaction as every write to the order, from the\n * order as it stands after that write, so the two never disagree and a\n * missing record heals on the next write.\n *=========================================*/\n\n/** The top-level collection of retained hardware sale records. */\nexport const POS_HARDWARE_SALES_COLLECTION = 'posHardwareSales'\n\n/** `posHardwareSales/{orderId}`: an order with every personal detail left out. */\nexport interface PosHardwareSaleRecord {\n orderId: string\n /** The workspace that bought it, by id; the record outlives the workspace. */\n orgId: string\n lines: Array<Pick<PosHardwareLine, 'sku' | 'name' | 'quantity' | 'unitCents' | 'lineCents'>>\n subtotalCents: number\n shippingMethod: PosHardwareShippingMethod\n shippingCents: number\n taxCents: number\n totalCents: number\n currency: string\n /** The sales tax sourcing input: country, state and postal code, never the street. */\n shipTo: PosHardwareTaxRegion | null\n stripeCheckoutSessionId: string | null\n stripePaymentIntentId: string | null\n stripeHardwareOrderId: string | null\n refundId: string | null\n refundedCents: number | null\n status: PosHardwareOrderStatus\n livemode: boolean\n createdAtMs: number\n updatedAtMs: number\n paidAtMs: number | null\n orderedAtMs: number | null\n shippedAtMs: number | null\n deliveredAtMs: number | null\n registeredAtMs: number | null\n canceledAtMs: number | null\n refundedAtMs: number | null\n}\n\nconst stampOrNull = (value: unknown): number | null =>\n typeof value === 'number' && Number.isFinite(value) ? value : null\n\nconst textOrNull = (value: unknown): string | null =>\n typeof value === 'string' && value.trim() ? value.trim() : null\n\n/** The tax region of an order: its address cut down, or what an erasure left. */\nexport function posHardwareTaxRegion(order: Pick<PosHardwareOrder, 'shipping'>): PosHardwareTaxRegion | null {\n const address = order.shipping?.address\n if (address?.country) {\n return {\n country: address.country,\n state: textOrNull(address.state),\n postalCode: textOrNull(address.postalCode),\n }\n }\n const kept = order.shipping?.taxRegion\n return kept?.country\n ? { country: kept.country, state: textOrNull(kept.state), postalCode: textOrNull(kept.postalCode) }\n : null\n}\n\n/**\n * The retained sale record of an order, as it stands. Every field is picked\n * by name, so a field added to the order later reaches the record only when\n * someone decides it should.\n */\nexport function posHardwareSaleRecord(orderId: string, order: PosHardwareOrder): PosHardwareSaleRecord {\n return {\n orderId,\n orgId: order.orgId,\n lines: (order.lines ?? []).map((line) => ({\n sku: line.sku,\n name: line.name,\n quantity: line.quantity,\n unitCents: line.unitCents,\n lineCents: line.lineCents,\n })),\n subtotalCents: order.subtotalCents ?? 0,\n shippingMethod: order.shipping?.method ?? 'standard',\n shippingCents: order.shipping?.cents ?? 0,\n taxCents: order.taxCents ?? 0,\n totalCents: order.totalCents ?? 0,\n currency: order.currency ?? POS_HARDWARE_CURRENCY,\n shipTo: posHardwareTaxRegion(order),\n stripeCheckoutSessionId: textOrNull(order.stripeCheckoutSessionId),\n stripePaymentIntentId: textOrNull(order.stripePaymentIntentId),\n stripeHardwareOrderId: textOrNull(order.stripeHardwareOrderId),\n refundId: textOrNull(order.refundId),\n refundedCents: stampOrNull(order.refundedCents),\n status: order.status,\n livemode: order.livemode === true,\n createdAtMs: order.createdAtMs ?? 0,\n updatedAtMs: order.updatedAtMs ?? order.createdAtMs ?? 0,\n paidAtMs: stampOrNull(order.paidAtMs),\n orderedAtMs: stampOrNull(order.orderedAtMs),\n shippedAtMs: stampOrNull(order.shippedAtMs),\n deliveredAtMs: stampOrNull(order.deliveredAtMs),\n registeredAtMs: stampOrNull(order.registeredAtMs),\n canceledAtMs: stampOrNull(order.canceledAtMs),\n refundedAtMs: stampOrNull(order.refundedAtMs),\n }\n}\n\n/**\n * What an account erasure does to an order the person placed (AGL-4038): the\n * workspace keeps the order and every amount on it; the buyer comes off it —\n * who started it, the email, the name, the phone, the street and the\n * tracking, which names the parcel to a carrier who would say where it went.\n * The country, state and postal code stay, because sales tax is sourced by\n * them. Deterministic but for the stamp, and an order already erased no\n * longer answers to the person's uid, so a second run finds nothing.\n */\nexport function posHardwareBuyerErasedPatch(\n order: Pick<PosHardwareOrder, 'shipping'>,\n now: number,\n): Record<string, unknown> {\n // The whole map, read in the same transaction: a dotted-path update would\n // leave a field added to `shipping` later standing with the person in it.\n const shipping = order.shipping\n return {\n createdBy: null,\n contactEmail: null,\n shipping: {\n method: shipping?.method ?? 'standard',\n label: shipping?.label ?? POS_HARDWARE_SHIPPING_LABELS[shipping?.method ?? 'standard'],\n cents: shipping?.cents ?? 0,\n name: null,\n phone: null,\n address: null,\n taxRegion: posHardwareTaxRegion(order),\n },\n tracking: null,\n buyerErasedAtMs: now,\n updatedAtMs: now,\n }\n}\n\n/** An order id: `hw_` and twenty hex characters. */\nexport const POS_HARDWARE_ORDER_ID = /^hw_[a-f0-9]{20}$/\n\n/** Dollars and cents for the card and the emails. */\nexport function formatPosHardwareCents(cents: number): string {\n const value = Number.isFinite(cents) ? cents : 0\n return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(value / 100)\n}\n"],"names":["PLATFORM_BRAND_NAME","POS_HARDWARE_CURRENCY","POS_HARDWARE_SHIP_COUNTRIES","POS_HARDWARE_MAX_PER_LINE","POS_HARDWARE_MAX_UNITS","POS_HARDWARE_ENV","enabled","prices","markupBps","shipping","automaticTax","POS_HARDWARE_DEFAULT_MARKUP_BPS","POS_HARDWARE_SKUS","id","kind","stripeProductName","displayName","use","posHardwareSku","find","sku","POS_HARDWARE_SHIPPING_LABELS","standard","express","priority","SHIPPING_METHODS","Object","keys","isPosHardwareShippingMethod","value","includes","MAX_FIGURE_CENTS","MAX_MARKUP_BPS","isCents","min","Number","isSafeInteger","parseJsonObject","raw","text","String","trim","parsed","JSON","parse","Array","isArray","parsePosHardwareConfig","env","toLowerCase","markupText","test","costs","cost","anyReader","some","undefined","shippingRaw","flatMap","method","cents","length","posHardwareUnitCents","costCents","Math","floor","posHardwareOffers","config","name","unitCents","pricePosHardwareCart","requested","ok","error","offers","Map","map","offer","quantities","entry","quantity","has","isInteger","set","get","units","lines","push","lineCents","subtotalCents","reduce","sum","line","POS_HARDWARE_METADATA_TYPE","encodePosHardwareLines","join","decodePosHardwareLines","encoded","part","split","skuId","quantityText","unitText","POS_HARDWARE_ORDER_STATUSES","isPosHardwareOrderStatus","POS_HARDWARE_STATUS_LABELS","paid","ordered","shipped","delivered","registered","canceled","refunded","POS_HARDWARE_STAFF_STATUS_LABELS","NEXT_STATUS","nextPosHardwareStatus","status","posHardwareRefundable","POS_HARDWARE_STATUS_STAMP","POS_HARDWARE_ANCHOR","POS_HARDWARE_ORDERS_COLLECTION","POS_HARDWARE_SALES_COLLECTION","stampOrNull","isFinite","textOrNull","posHardwareTaxRegion","order","address","country","state","postalCode","kept","taxRegion","posHardwareSaleRecord","orderId","orgId","shippingMethod","shippingCents","taxCents","totalCents","currency","shipTo","stripeCheckoutSessionId","stripePaymentIntentId","stripeHardwareOrderId","refundId","refundedCents","livemode","createdAtMs","updatedAtMs","paidAtMs","orderedAtMs","shippedAtMs","deliveredAtMs","registeredAtMs","canceledAtMs","refundedAtMs","posHardwareBuyerErasedPatch","now","createdBy","contactEmail","label","phone","tracking","buyerErasedAtMs","POS_HARDWARE_ORDER_ID","formatPosHardwareCents","Intl","NumberFormat","style","format"],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /** How long cards may wait on a device before the register asks, more firmly, to reconnect. */
18
+ export declare const POS_OFFLINE_CARD_RECONNECT_WITHIN_MS: number;
19
+ /** Stripe's own ceiling for one payment collected offline (10,000 USD). */
20
+ export declare const POS_OFFLINE_CARD_MAX_SALE_CENTS_LIMIT = 1000000;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ /*
17
+ * The ceilings of cards taken offline on the app's own reader (AGL-4039), in
18
+ * a module of their own with no imports: the commerce settings read them at
19
+ * load, and pulling the offline register's model into the eager bundle for
20
+ * two numbers would cost every console page.
21
+ *
22
+ * There is no small default: a store's own limit per sale and daily limit
23
+ * per device are empty until it sets them, so offline cards run up to
24
+ * Stripe's own ceiling per sale with no daily limit.
25
+ */ /** How long cards may wait on a device before the register asks, more firmly, to reconnect. */ export const POS_OFFLINE_CARD_RECONNECT_WITHIN_MS = 24 * 60 * 60 * 1000;
26
+ /** Stripe's own ceiling for one payment collected offline (10,000 USD). */ export const POS_OFFLINE_CARD_MAX_SALE_CENTS_LIMIT = 1000000;
27
+
28
+ //# sourceMappingURL=pos-offline-card-limits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/pos-offline-card-limits.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\n/*\n * The ceilings of cards taken offline on the app's own reader (AGL-4039), in\n * a module of their own with no imports: the commerce settings read them at\n * load, and pulling the offline register's model into the eager bundle for\n * two numbers would cost every console page.\n *\n * There is no small default: a store's own limit per sale and daily limit\n * per device are empty until it sets them, so offline cards run up to\n * Stripe's own ceiling per sale with no daily limit.\n */\n\n/** How long cards may wait on a device before the register asks, more firmly, to reconnect. */\nexport const POS_OFFLINE_CARD_RECONNECT_WITHIN_MS = 24 * 60 * 60 * 1000\n\n/** Stripe's own ceiling for one payment collected offline (10,000 USD). */\nexport const POS_OFFLINE_CARD_MAX_SALE_CENTS_LIMIT = 1_000_000\n"],"names":["POS_OFFLINE_CARD_RECONNECT_WITHIN_MS","POS_OFFLINE_CARD_MAX_SALE_CENTS_LIMIT"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;CASC,GAED,6FAA6F,GAC7F,OAAO,MAAMA,uCAAuC,KAAK,KAAK,KAAK,KAAI;AAEvE,yEAAyE,GACzE,OAAO,MAAMC,wCAAwC,QAAS"}