@mj-biz-apps/orders-core-entities-server 0.0.1 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. package/LICENSE +183 -0
  2. package/README.md +141 -2
  3. package/dist/AccountingBridge.d.ts +65 -0
  4. package/dist/AccountingBridge.d.ts.map +1 -0
  5. package/dist/AccountingBridge.js +102 -0
  6. package/dist/AccountingBridge.js.map +1 -0
  7. package/dist/AdvanceOrderStateOperation.d.ts +80 -0
  8. package/dist/AdvanceOrderStateOperation.d.ts.map +1 -0
  9. package/dist/AdvanceOrderStateOperation.js +267 -0
  10. package/dist/AdvanceOrderStateOperation.js.map +1 -0
  11. package/dist/ApplyAccountCreditOperation.d.ts +97 -0
  12. package/dist/ApplyAccountCreditOperation.d.ts.map +1 -0
  13. package/dist/ApplyAccountCreditOperation.js +275 -0
  14. package/dist/ApplyAccountCreditOperation.js.map +1 -0
  15. package/dist/BaseDeliveryChannel.d.ts +106 -0
  16. package/dist/BaseDeliveryChannel.d.ts.map +1 -0
  17. package/dist/BaseDeliveryChannel.js +36 -0
  18. package/dist/BaseDeliveryChannel.js.map +1 -0
  19. package/dist/BasePaymentProvider.d.ts +239 -0
  20. package/dist/BasePaymentProvider.d.ts.map +1 -0
  21. package/dist/BasePaymentProvider.js +91 -0
  22. package/dist/BasePaymentProvider.js.map +1 -0
  23. package/dist/BundleBehavior.d.ts +139 -0
  24. package/dist/BundleBehavior.d.ts.map +1 -0
  25. package/dist/BundleBehavior.js +173 -0
  26. package/dist/BundleBehavior.js.map +1 -0
  27. package/dist/BundleEngine.d.ts +60 -0
  28. package/dist/BundleEngine.d.ts.map +1 -0
  29. package/dist/BundleEngine.js +197 -0
  30. package/dist/BundleEngine.js.map +1 -0
  31. package/dist/CancelSubscriptionOperation.d.ts +98 -0
  32. package/dist/CancelSubscriptionOperation.d.ts.map +1 -0
  33. package/dist/CancelSubscriptionOperation.js +317 -0
  34. package/dist/CancelSubscriptionOperation.js.map +1 -0
  35. package/dist/CapturePaymentOperation.d.ts +62 -0
  36. package/dist/CapturePaymentOperation.d.ts.map +1 -0
  37. package/dist/CapturePaymentOperation.js +463 -0
  38. package/dist/CapturePaymentOperation.js.map +1 -0
  39. package/dist/CheckEntitlementOperation.d.ts +27 -0
  40. package/dist/CheckEntitlementOperation.d.ts.map +1 -0
  41. package/dist/CheckEntitlementOperation.js +45 -0
  42. package/dist/CheckEntitlementOperation.js.map +1 -0
  43. package/dist/CheckoutSessionService.d.ts +258 -0
  44. package/dist/CheckoutSessionService.d.ts.map +1 -0
  45. package/dist/CheckoutSessionService.js +1557 -0
  46. package/dist/CheckoutSessionService.js.map +1 -0
  47. package/dist/DeliveryBehavior.d.ts +121 -0
  48. package/dist/DeliveryBehavior.d.ts.map +1 -0
  49. package/dist/DeliveryBehavior.js +145 -0
  50. package/dist/DeliveryBehavior.js.map +1 -0
  51. package/dist/DeliveryRecipientResolver.d.ts +38 -0
  52. package/dist/DeliveryRecipientResolver.d.ts.map +1 -0
  53. package/dist/DeliveryRecipientResolver.js +93 -0
  54. package/dist/DeliveryRecipientResolver.js.map +1 -0
  55. package/dist/DeliveryResolver.d.ts +14 -0
  56. package/dist/DeliveryResolver.d.ts.map +1 -0
  57. package/dist/DeliveryResolver.js +45 -0
  58. package/dist/DeliveryResolver.js.map +1 -0
  59. package/dist/EmailDeliveryChannel.d.ts +31 -0
  60. package/dist/EmailDeliveryChannel.d.ts.map +1 -0
  61. package/dist/EmailDeliveryChannel.js +158 -0
  62. package/dist/EmailDeliveryChannel.js.map +1 -0
  63. package/dist/EntitlementBehavior.d.ts +235 -0
  64. package/dist/EntitlementBehavior.d.ts.map +1 -0
  65. package/dist/EntitlementBehavior.js +330 -0
  66. package/dist/EntitlementBehavior.js.map +1 -0
  67. package/dist/EntitlementEngine.d.ts +97 -0
  68. package/dist/EntitlementEngine.d.ts.map +1 -0
  69. package/dist/EntitlementEngine.js +338 -0
  70. package/dist/EntitlementEngine.js.map +1 -0
  71. package/dist/EntitlementGrantClaimDriver.d.ts +42 -0
  72. package/dist/EntitlementGrantClaimDriver.d.ts.map +1 -0
  73. package/dist/EntitlementGrantClaimDriver.js +158 -0
  74. package/dist/EntitlementGrantClaimDriver.js.map +1 -0
  75. package/dist/EntitlementRead.d.ts +82 -0
  76. package/dist/EntitlementRead.d.ts.map +1 -0
  77. package/dist/EntitlementRead.js +368 -0
  78. package/dist/EntitlementRead.js.map +1 -0
  79. package/dist/FulfillOrderLinesOperation.d.ts +34 -0
  80. package/dist/FulfillOrderLinesOperation.d.ts.map +1 -0
  81. package/dist/FulfillOrderLinesOperation.js +208 -0
  82. package/dist/FulfillOrderLinesOperation.js.map +1 -0
  83. package/dist/FulfillmentBehavior.d.ts +101 -0
  84. package/dist/FulfillmentBehavior.d.ts.map +1 -0
  85. package/dist/FulfillmentBehavior.js +145 -0
  86. package/dist/FulfillmentBehavior.js.map +1 -0
  87. package/dist/GLAccountResolver.d.ts +123 -0
  88. package/dist/GLAccountResolver.d.ts.map +1 -0
  89. package/dist/GLAccountResolver.js +175 -0
  90. package/dist/GLAccountResolver.js.map +1 -0
  91. package/dist/GetFulfillmentQueueOperation.d.ts +35 -0
  92. package/dist/GetFulfillmentQueueOperation.d.ts.map +1 -0
  93. package/dist/GetFulfillmentQueueOperation.js +221 -0
  94. package/dist/GetFulfillmentQueueOperation.js.map +1 -0
  95. package/dist/GetOverdueWorklistOperation.d.ts +41 -0
  96. package/dist/GetOverdueWorklistOperation.d.ts.map +1 -0
  97. package/dist/GetOverdueWorklistOperation.js +210 -0
  98. package/dist/GetOverdueWorklistOperation.js.map +1 -0
  99. package/dist/GiftCardBehavior.d.ts +97 -0
  100. package/dist/GiftCardBehavior.d.ts.map +1 -0
  101. package/dist/GiftCardBehavior.js +121 -0
  102. package/dist/GiftCardBehavior.js.map +1 -0
  103. package/dist/GiftCardEngine.d.ts +59 -0
  104. package/dist/GiftCardEngine.d.ts.map +1 -0
  105. package/dist/GiftCardEngine.js +197 -0
  106. package/dist/GiftCardEngine.js.map +1 -0
  107. package/dist/GuestOrderClaimDriver.d.ts +36 -0
  108. package/dist/GuestOrderClaimDriver.d.ts.map +1 -0
  109. package/dist/GuestOrderClaimDriver.js +161 -0
  110. package/dist/GuestOrderClaimDriver.js.map +1 -0
  111. package/dist/InvoiceBehavior.d.ts +394 -0
  112. package/dist/InvoiceBehavior.d.ts.map +1 -0
  113. package/dist/InvoiceBehavior.js +496 -0
  114. package/dist/InvoiceBehavior.js.map +1 -0
  115. package/dist/InvoiceBuilder.d.ts +48 -0
  116. package/dist/InvoiceBuilder.d.ts.map +1 -0
  117. package/dist/InvoiceBuilder.js +352 -0
  118. package/dist/InvoiceBuilder.js.map +1 -0
  119. package/dist/InvoiceDisplay.d.ts +96 -0
  120. package/dist/InvoiceDisplay.d.ts.map +1 -0
  121. package/dist/InvoiceDisplay.js +121 -0
  122. package/dist/InvoiceDisplay.js.map +1 -0
  123. package/dist/ListEntitlementsOperation.d.ts +21 -0
  124. package/dist/ListEntitlementsOperation.d.ts.map +1 -0
  125. package/dist/ListEntitlementsOperation.js +39 -0
  126. package/dist/ListEntitlementsOperation.js.map +1 -0
  127. package/dist/ManualPaymentProvider.d.ts +19 -0
  128. package/dist/ManualPaymentProvider.d.ts.map +1 -0
  129. package/dist/ManualPaymentProvider.js +92 -0
  130. package/dist/ManualPaymentProvider.js.map +1 -0
  131. package/dist/OrderEntityServer.d.ts +542 -0
  132. package/dist/OrderEntityServer.d.ts.map +1 -0
  133. package/dist/OrderEntityServer.js +2100 -0
  134. package/dist/OrderEntityServer.js.map +1 -0
  135. package/dist/OrderJournalEntryFactory.d.ts +140 -0
  136. package/dist/OrderJournalEntryFactory.d.ts.map +1 -0
  137. package/dist/OrderJournalEntryFactory.js +466 -0
  138. package/dist/OrderJournalEntryFactory.js.map +1 -0
  139. package/dist/OrderLineEntityServer.d.ts +90 -0
  140. package/dist/OrderLineEntityServer.d.ts.map +1 -0
  141. package/dist/OrderLineEntityServer.js +260 -0
  142. package/dist/OrderLineEntityServer.js.map +1 -0
  143. package/dist/OrdersSettings.d.ts +36 -0
  144. package/dist/OrdersSettings.d.ts.map +1 -0
  145. package/dist/OrdersSettings.js +147 -0
  146. package/dist/OrdersSettings.js.map +1 -0
  147. package/dist/PaymentAllocationFactory.d.ts +128 -0
  148. package/dist/PaymentAllocationFactory.d.ts.map +1 -0
  149. package/dist/PaymentAllocationFactory.js +235 -0
  150. package/dist/PaymentAllocationFactory.js.map +1 -0
  151. package/dist/PaymentHeaderEntityServer.d.ts +180 -0
  152. package/dist/PaymentHeaderEntityServer.d.ts.map +1 -0
  153. package/dist/PaymentHeaderEntityServer.js +656 -0
  154. package/dist/PaymentHeaderEntityServer.js.map +1 -0
  155. package/dist/PaymentIntentService.d.ts +101 -0
  156. package/dist/PaymentIntentService.d.ts.map +1 -0
  157. package/dist/PaymentIntentService.js +150 -0
  158. package/dist/PaymentIntentService.js.map +1 -0
  159. package/dist/PaymentJournalEntryFactory.d.ts +99 -0
  160. package/dist/PaymentJournalEntryFactory.d.ts.map +1 -0
  161. package/dist/PaymentJournalEntryFactory.js +127 -0
  162. package/dist/PaymentJournalEntryFactory.js.map +1 -0
  163. package/dist/PaymentLineEntityServer.d.ts +73 -0
  164. package/dist/PaymentLineEntityServer.d.ts.map +1 -0
  165. package/dist/PaymentLineEntityServer.js +303 -0
  166. package/dist/PaymentLineEntityServer.js.map +1 -0
  167. package/dist/PaymentProviderBehavior.d.ts +222 -0
  168. package/dist/PaymentProviderBehavior.d.ts.map +1 -0
  169. package/dist/PaymentProviderBehavior.js +368 -0
  170. package/dist/PaymentProviderBehavior.js.map +1 -0
  171. package/dist/PaymentProviderResolver.d.ts +84 -0
  172. package/dist/PaymentProviderResolver.d.ts.map +1 -0
  173. package/dist/PaymentProviderResolver.js +220 -0
  174. package/dist/PaymentProviderResolver.js.map +1 -0
  175. package/dist/PaymentReversalFactory.d.ts +107 -0
  176. package/dist/PaymentReversalFactory.d.ts.map +1 -0
  177. package/dist/PaymentReversalFactory.js +174 -0
  178. package/dist/PaymentReversalFactory.js.map +1 -0
  179. package/dist/PaymentSettlement.d.ts +56 -0
  180. package/dist/PaymentSettlement.d.ts.map +1 -0
  181. package/dist/PaymentSettlement.js +243 -0
  182. package/dist/PaymentSettlement.js.map +1 -0
  183. package/dist/PaymentTermsBehavior.d.ts +109 -0
  184. package/dist/PaymentTermsBehavior.d.ts.map +1 -0
  185. package/dist/PaymentTermsBehavior.js +172 -0
  186. package/dist/PaymentTermsBehavior.js.map +1 -0
  187. package/dist/PaymentWebhookHandler.d.ts +103 -0
  188. package/dist/PaymentWebhookHandler.d.ts.map +1 -0
  189. package/dist/PaymentWebhookHandler.js +246 -0
  190. package/dist/PaymentWebhookHandler.js.map +1 -0
  191. package/dist/PreviewPriceOperation.d.ts +62 -0
  192. package/dist/PreviewPriceOperation.d.ts.map +1 -0
  193. package/dist/PreviewPriceOperation.js +161 -0
  194. package/dist/PreviewPriceOperation.js.map +1 -0
  195. package/dist/PriceOrderOperation.d.ts +93 -0
  196. package/dist/PriceOrderOperation.d.ts.map +1 -0
  197. package/dist/PriceOrderOperation.js +146 -0
  198. package/dist/PriceOrderOperation.js.map +1 -0
  199. package/dist/ProductPriceEntityServer.d.ts +34 -0
  200. package/dist/ProductPriceEntityServer.d.ts.map +1 -0
  201. package/dist/ProductPriceEntityServer.js +97 -0
  202. package/dist/ProductPriceEntityServer.js.map +1 -0
  203. package/dist/RefundPaymentOperation.d.ts +61 -0
  204. package/dist/RefundPaymentOperation.d.ts.map +1 -0
  205. package/dist/RefundPaymentOperation.js +177 -0
  206. package/dist/RefundPaymentOperation.js.map +1 -0
  207. package/dist/RevenueRecognition.d.ts +76 -0
  208. package/dist/RevenueRecognition.d.ts.map +1 -0
  209. package/dist/RevenueRecognition.js +133 -0
  210. package/dist/RevenueRecognition.js.map +1 -0
  211. package/dist/ReversalBehavior.d.ts +82 -0
  212. package/dist/ReversalBehavior.d.ts.map +1 -0
  213. package/dist/ReversalBehavior.js +100 -0
  214. package/dist/ReversalBehavior.js.map +1 -0
  215. package/dist/ReversalResolver.d.ts +37 -0
  216. package/dist/ReversalResolver.d.ts.map +1 -0
  217. package/dist/ReversalResolver.js +96 -0
  218. package/dist/ReversalResolver.js.map +1 -0
  219. package/dist/SpawnRenewalsOperation.d.ts +109 -0
  220. package/dist/SpawnRenewalsOperation.d.ts.map +1 -0
  221. package/dist/SpawnRenewalsOperation.js +295 -0
  222. package/dist/SpawnRenewalsOperation.js.map +1 -0
  223. package/dist/StoredValuePaymentProvider.d.ts +52 -0
  224. package/dist/StoredValuePaymentProvider.d.ts.map +1 -0
  225. package/dist/StoredValuePaymentProvider.js +205 -0
  226. package/dist/StoredValuePaymentProvider.js.map +1 -0
  227. package/dist/StripeACHPaymentProvider.d.ts +50 -0
  228. package/dist/StripeACHPaymentProvider.d.ts.map +1 -0
  229. package/dist/StripeACHPaymentProvider.js +211 -0
  230. package/dist/StripeACHPaymentProvider.js.map +1 -0
  231. package/dist/StripePaymentProvider.d.ts +83 -0
  232. package/dist/StripePaymentProvider.d.ts.map +1 -0
  233. package/dist/StripePaymentProvider.js +442 -0
  234. package/dist/StripePaymentProvider.js.map +1 -0
  235. package/dist/SubscriptionBehavior.d.ts +197 -0
  236. package/dist/SubscriptionBehavior.d.ts.map +1 -0
  237. package/dist/SubscriptionBehavior.js +415 -0
  238. package/dist/SubscriptionBehavior.js.map +1 -0
  239. package/dist/checkoutCaptureAlert.d.ts +16 -0
  240. package/dist/checkoutCaptureAlert.d.ts.map +1 -0
  241. package/dist/checkoutCaptureAlert.js +55 -0
  242. package/dist/checkoutCaptureAlert.js.map +1 -0
  243. package/dist/checkoutCaptureRetry.d.ts +27 -0
  244. package/dist/checkoutCaptureRetry.d.ts.map +1 -0
  245. package/dist/checkoutCaptureRetry.js +50 -0
  246. package/dist/checkoutCaptureRetry.js.map +1 -0
  247. package/dist/claimDriverHelpers.d.ts +15 -0
  248. package/dist/claimDriverHelpers.d.ts.map +1 -0
  249. package/dist/claimDriverHelpers.js +34 -0
  250. package/dist/claimDriverHelpers.js.map +1 -0
  251. package/dist/entity-names.d.ts +15 -0
  252. package/dist/entity-names.d.ts.map +1 -0
  253. package/dist/entity-names.js +15 -0
  254. package/dist/entity-names.js.map +1 -0
  255. package/dist/identityClaimContracts.d.ts +118 -0
  256. package/dist/identityClaimContracts.d.ts.map +1 -0
  257. package/dist/identityClaimContracts.js +58 -0
  258. package/dist/identityClaimContracts.js.map +1 -0
  259. package/dist/index.d.ts +126 -0
  260. package/dist/index.d.ts.map +1 -0
  261. package/dist/index.js +115 -0
  262. package/dist/index.js.map +1 -0
  263. package/dist/sql-guards.d.ts +78 -0
  264. package/dist/sql-guards.d.ts.map +1 -0
  265. package/dist/sql-guards.js +115 -0
  266. package/dist/sql-guards.js.map +1 -0
  267. package/package.json +50 -5
@@ -0,0 +1,368 @@
1
+ /**
2
+ * The parts of talking to a payment gateway that can be got wrong without a network — and therefore
3
+ * the parts worth proving without one.
4
+ *
5
+ * Three things live here, and each is here because its failure mode is silent:
6
+ *
7
+ * 1. MINOR-UNIT CONVERSION. Stripe and most gateways transact in the currency's smallest unit, so
8
+ * $12.34 is `1234`. Getting that wrong is a factor-of-100 error that looks like a plausible
9
+ * amount at both ends — charge 12 cents for a $12 order, or $1,234 for a $12.34 one. Worse, JPY
10
+ * has NO minor unit, so the "obvious" `× 100` overcharges a Japanese customer a hundredfold.
11
+ *
12
+ * 2. WEBHOOK SIGNATURE VERIFICATION. The webhook route is unauthenticated by necessity (D19) — the
13
+ * signature IS the authentication. A verifier that accepts anything is a public endpoint that
14
+ * marks orders paid, and it passes every test that only ever feeds it valid input.
15
+ *
16
+ * 3. IDEMPOTENCY. Gateways retry, and they retry on success too — a webhook that times out on our
17
+ * side is redelivered. Processing one twice books the cash twice.
18
+ *
19
+ * CONNECTS TO:
20
+ * SERVER: ./BasePaymentProvider.ts and the drivers under it
21
+ * ROUTE: ./PaymentWebhookHandler.ts
22
+ * DOC: plans/archive/bizapps-orders-master.md D19, D37
23
+ */
24
+ // WEB CRYPTO, NOT `node:crypto`. The shared server tsconfig sets `"types": []`, so this package has no
25
+ // Node globals by design and no other file in it reaches for one. `globalThis.crypto` is typed by the
26
+ // `dom` lib that IS included, exists in Node 18+, and needs no dependency — the only cost is that HMAC
27
+ // becomes async, which the webhook path already is.
28
+ /**
29
+ * Currencies with NO minor unit, so an amount is already in its smallest unit.
30
+ *
31
+ * From Stripe's zero-decimal list. Kept explicit rather than derived from `Intl` because the gateway's
32
+ * opinion is what matters here, not the locale database's: we must send what Stripe expects, and a
33
+ * disagreement between the two would be a hundredfold error in whichever direction it fell.
34
+ */
35
+ const ZERO_DECIMAL = new Set([
36
+ 'BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF',
37
+ 'UGX', 'VND', 'VUV', 'XAF', 'XOF', 'XPF',
38
+ ]);
39
+ /** Currencies whose smallest unit is a THOUSANDTH. Rare, and wrong by 10x if treated as ordinary. */
40
+ const THREE_DECIMAL = new Set(['BHD', 'JOD', 'KWD', 'OMR', 'TND']);
41
+ /** How many minor units make one major unit. */
42
+ export function CurrencyExponent(currencyCode) {
43
+ const code = (currencyCode ?? 'USD').toUpperCase();
44
+ if (ZERO_DECIMAL.has(code))
45
+ return 0;
46
+ if (THREE_DECIMAL.has(code))
47
+ return 3;
48
+ return 2;
49
+ }
50
+ /**
51
+ * Turn an amount we hold as a decimal into the integer the gateway wants.
52
+ *
53
+ * Rounds rather than truncates, and rounds the SCALED value: `Math.round(12.345 * 100)` is 1235 while
54
+ * `Math.round(12.345) * 100` is 1200. Both look like money.
55
+ *
56
+ * Throws on a non-finite amount instead of sending `NaN` to a gateway, which some accept as zero.
57
+ */
58
+ export function ToMinorUnits(amount, currencyCode) {
59
+ if (!Number.isFinite(amount)) {
60
+ throw new Error(`Cannot convert ${amount} to minor units — it is not a finite amount.`);
61
+ }
62
+ const factor = 10 ** CurrencyExponent(currencyCode);
63
+ // `Number.EPSILON` nudge for the same reason `Money()` uses one: 1.005 * 100 is 100.49999… in
64
+ // binary floating point, and rounding that gives 100 rather than 101.
65
+ return Math.round((amount + Number.EPSILON * Math.sign(amount)) * factor);
66
+ }
67
+ /** And back, for reading what the gateway reported. */
68
+ export function FromMinorUnits(minor, currencyCode) {
69
+ if (!Number.isFinite(minor)) {
70
+ throw new Error(`Cannot convert ${minor} from minor units — it is not a finite amount.`);
71
+ }
72
+ const factor = 10 ** CurrencyExponent(currencyCode);
73
+ return Math.round((minor / factor) * 100) / 100;
74
+ }
75
+ /**
76
+ * Verify a Stripe-style `Stripe-Signature` header.
77
+ *
78
+ * The scheme: `t=<unix seconds>,v1=<hex hmac>[,v1=<another>]`, where the HMAC is SHA-256 over
79
+ * `${t}.${rawBody}` keyed with the endpoint secret. Multiple `v1` values appear during a secret
80
+ * rotation, and ANY match is a pass — a verifier that only reads the first one breaks silently
81
+ * halfway through a rotation, which is the worst possible moment.
82
+ *
83
+ * THE RAW BODY IS NOT NEGOTIABLE. The signature covers the exact bytes Stripe sent. Any JSON
84
+ * round-trip — key reordering, whitespace, unicode normalisation — changes them, and the signature
85
+ * then fails for a legitimate event. That is why the route captures the raw body before any parser
86
+ * touches it.
87
+ *
88
+ * TIMESTAMP TOLERANCE defends against replay: a valid old payload stays valid forever without it.
89
+ * Default five minutes, matching Stripe's own guidance. `nowSeconds` is a parameter so this is
90
+ * testable without waiting.
91
+ */
92
+ export async function VerifyWebhookSignature(rawBody, signatureHeader, secret, opts = {}) {
93
+ if (!secret) {
94
+ // A missing secret must FAIL rather than skip verification. "No secret configured, so accept
95
+ // everything" turns a misconfiguration into an open endpoint that marks orders paid.
96
+ return { Valid: false, Reason: 'no endpoint secret is configured for this provider' };
97
+ }
98
+ if (!signatureHeader)
99
+ return { Valid: false, Reason: 'the request carried no signature header' };
100
+ let timestamp = null;
101
+ const candidates = [];
102
+ for (const part of signatureHeader.split(',')) {
103
+ const [key, value] = part.split('=', 2).map((s) => s?.trim());
104
+ if (key === 't' && value)
105
+ timestamp = Number(value);
106
+ if (key === 'v1' && value)
107
+ candidates.push(value);
108
+ }
109
+ if (timestamp == null || !Number.isFinite(timestamp)) {
110
+ return { Valid: false, Reason: 'the signature header carried no usable timestamp' };
111
+ }
112
+ if (!candidates.length)
113
+ return { Valid: false, Reason: 'the signature header carried no v1 signature' };
114
+ const tolerance = opts.ToleranceSeconds ?? 300;
115
+ const now = opts.NowSeconds ?? Math.floor(Date.now() / 1000);
116
+ if (Math.abs(now - timestamp) > tolerance) {
117
+ return {
118
+ Valid: false,
119
+ Reason: `the signature timestamp is ${Math.abs(now - timestamp)}s away, outside the ${tolerance}s tolerance`,
120
+ };
121
+ }
122
+ const expected = await HmacSha256Hex(secret, `${timestamp}.${rawBody}`);
123
+ for (const candidate of candidates) {
124
+ if (SignaturesMatch(expected, candidate))
125
+ return { Valid: true };
126
+ }
127
+ return { Valid: false, Reason: 'no provided signature matched' };
128
+ }
129
+ /** HMAC-SHA256 as lowercase hex, via Web Crypto. */
130
+ export async function HmacSha256Hex(secret, payload) {
131
+ const encoder = new TextEncoder();
132
+ const key = await crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
133
+ const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(payload));
134
+ return [...new Uint8Array(signature)].map((b) => b.toString(16).padStart(2, '0')).join('');
135
+ }
136
+ /**
137
+ * Constant-time hex comparison.
138
+ *
139
+ * `===` on a signature leaks how many leading characters were right through how long the comparison
140
+ * took. That is a real attack against a public endpoint, and the fix costs nothing. `timingSafeEqual`
141
+ * throws on a length mismatch, so the lengths are checked first — and a mismatched length is itself
142
+ * not secret, since the algorithm fixes it.
143
+ */
144
+ export function SignaturesMatch(expected, provided) {
145
+ if (expected.length !== provided.length)
146
+ return false;
147
+ // XOR-accumulate every position rather than returning on the first difference. `Node`'s
148
+ // `timingSafeEqual` would do this for us but lives behind `node:crypto`; the loop is the same
149
+ // guarantee in six lines, and it never short-circuits.
150
+ let diff = 0;
151
+ for (let i = 0; i < expected.length; i++) {
152
+ diff |= expected.charCodeAt(i) ^ provided.charCodeAt(i);
153
+ }
154
+ return diff === 0;
155
+ }
156
+ /**
157
+ * Map a gateway's own status onto ours.
158
+ *
159
+ * UNKNOWN MAPS TO `Processing`, NOT to `Succeeded` or `Failed`. A status we do not recognise means the
160
+ * gateway has told us something this code was not written for, and both confident answers are
161
+ * dangerous: `Succeeded` books cash that may not exist, `Failed` abandons a payment that may be fine.
162
+ * `Processing` is the honest reading — something is happening and we do not yet know what — and it
163
+ * leaves the payment for a human or a later event to resolve.
164
+ */
165
+ export function MapStripeIntentStatus(providerStatus) {
166
+ switch ((providerStatus ?? '').toLowerCase()) {
167
+ case 'requires_payment_method':
168
+ case 'requires_confirmation':
169
+ case 'requires_action':
170
+ case 'requires_source':
171
+ case 'requires_source_action':
172
+ return 'RequiresPayment';
173
+ case 'processing':
174
+ return 'Processing';
175
+ case 'succeeded':
176
+ return 'Succeeded';
177
+ case 'canceled':
178
+ case 'cancelled':
179
+ return 'Canceled';
180
+ // Stripe has no `failed` intent status — a failure arrives as an EVENT against an intent that
181
+ // is back to requires_payment_method. `payment_intent.payment_failed` is mapped by the event
182
+ // reader, not here, and this case exists for gateways that do report it directly.
183
+ case 'failed':
184
+ return 'Failed';
185
+ default:
186
+ return 'Processing';
187
+ }
188
+ }
189
+ /**
190
+ * Decide what to do with an event, given what we have already seen.
191
+ *
192
+ * `AlreadyApplied` IS A SUCCESS, and that distinction is the whole point. A gateway that does not get
193
+ * a 2xx retries, so returning an error for a duplicate guarantees it comes back — forever. The
194
+ * response must say "yes, we have this" without doing the work twice.
195
+ *
196
+ * `Ignore` is for events we are not interested in. Gateways send far more than any one integration
197
+ * consumes, and treating an unrecognised event as an error means a Stripe dashboard full of red for
198
+ * events we were never going to read.
199
+ */
200
+ export function DecideWebhookAction(input) {
201
+ if (!input.EventID) {
202
+ return { Action: 'Reject', Reason: 'the event carried no id, so it cannot be de-duplicated' };
203
+ }
204
+ if (input.AlreadySeen) {
205
+ return { Action: 'AlreadyApplied', Reason: `event ${input.EventID} has already been applied` };
206
+ }
207
+ if (!input.EventKind || !input.HandledKinds.includes(input.EventKind)) {
208
+ return { Action: 'Ignore', Reason: `'${input.EventKind ?? 'unknown'}' is not an event this integration acts on` };
209
+ }
210
+ if (!input.IntentKnown) {
211
+ // NOT an error. A gateway account may serve more than this application, and an intent we did
212
+ // not create is simply not ours — rejecting it would mean retries forever for someone else's
213
+ // traffic.
214
+ return { Action: 'Ignore', Reason: 'the event names a payment intent this application did not create' };
215
+ }
216
+ return { Action: 'Apply', Reason: `event ${input.EventID} is new and actionable` };
217
+ }
218
+ /**
219
+ * Split a captured amount into the cash actually received and the gateway's cut.
220
+ *
221
+ * The capture entry is `Dr Cash (net) / Dr Processing Fee / Cr A/R (gross)` (D18), so these three
222
+ * numbers must reconcile exactly or the entry will not balance. Deriving `net` by subtraction rather
223
+ * than trusting a reported net is deliberate: a gateway that rounds its own net differently would
224
+ * leave a penny with no home in the ledger.
225
+ *
226
+ * ALL THREE ARE EXACT AT 2DP, BUT `Net + Fee` IN JAVASCRIPT IS NOT. Adding two correctly-rounded
227
+ * decimals drifts in binary floating point — 32.06 + 1.27 is 33.330000000000005 — so a caller
228
+ * reconciling them must round the SUM before comparing, exactly as `Money()` does everywhere else in
229
+ * this package. This is the same hazard that made a balanced journal entry fail an exact-equality check
230
+ * in accounting; the numbers were never wrong, the comparison was.
231
+ */
232
+ export function SplitCapturedAmount(grossAmount, feeAmount) {
233
+ const gross = Math.round(grossAmount * 100) / 100;
234
+ const fee = Math.round(Math.abs(feeAmount ?? 0) * 100) / 100;
235
+ if (fee > gross) {
236
+ throw new Error(`A processing fee of ${fee} cannot exceed the ${gross} captured. Booking this would credit ` +
237
+ `receivables less than the cash and fee together, and the entry would not balance.`);
238
+ }
239
+ return { Gross: gross, Fee: fee, Net: Math.round((gross - fee) * 100) / 100 };
240
+ }
241
+ // ─── Bank debits (ACH) ─────────────────────────────────────────────────────────────────────────
242
+ /**
243
+ * Stripe's published US bank-debit price: 0.8% of the amount, capped at $5.
244
+ *
245
+ * FOR THE STUB ONLY, and the name says so deliberately. The live path reads the fee off the charge's
246
+ * balance transaction like every other capture — what Stripe actually charged is a fact, and computing
247
+ * it here would be this file's opinion competing with the gateway's record. A stub that reported a
248
+ * ZERO fee would be worse still: it would make the fee leg of the capture entry (D18) unreachable in
249
+ * every test, so the arithmetic most worth exercising would be the arithmetic never exercised. That is
250
+ * the same reasoning behind the card stub's 2.9% + 30c, and the same reason this constant will drift
251
+ * from Stripe's price list one day without breaking anything real.
252
+ *
253
+ * The cap is a MAJOR-UNIT USD figure. A bank debit is a US rail, so the currency is USD in practice;
254
+ * a caller passing anything else gets the percentage without the cap rather than a cap converted at a
255
+ * rate this module has no business knowing.
256
+ */
257
+ export function AchFeeEstimate(grossAmount, currencyCode = 'USD') {
258
+ const gross = Math.round(Math.abs(grossAmount ?? 0) * 100) / 100;
259
+ if (!Number.isFinite(gross) || gross <= 0)
260
+ return 0;
261
+ const raw = gross * 0.008;
262
+ const capped = currencyCode.toUpperCase() === 'USD' ? Math.min(raw, 5) : raw;
263
+ return Math.round(capped * 100) / 100;
264
+ }
265
+ /**
266
+ * Decide what a bank-debit event does to its payment.
267
+ *
268
+ * THE ASYMMETRY IS THE WHOLE DESIGN. A card tells you at the till; a bank debit tells you up to four
269
+ * business days later, and it can tell you TWICE — "taken" and then, a week after that, "returned"
270
+ * (insufficient funds, account closed, unauthorised). So there is no single moment where the answer is
271
+ * known, and a payment has to be able to move forward AND backward:
272
+ *
273
+ * Pending + succeeded → `Promote` the payment becomes Captured, which is what books the cash
274
+ * Pending + failed → `Fail` nothing was ever booked, so nothing needs reversing
275
+ * Captured + failed → `Reverse` the money left again; a REVERSING payment says so in the ledger
276
+ *
277
+ * WHY `Reverse` AND NOT AN UPDATE. A returned debit is not "the payment never happened" — it happened,
278
+ * the ledger recorded it, a period may have closed over it. Editing the original would erase a true
279
+ * fact about a past date. A reversing payment is the same answer the rest of this application already
280
+ * gives (`ReversesPaymentHeaderID`, D53), and it leaves both facts standing.
281
+ *
282
+ * `None` covers the ordinary quiet cases — an event that changes nothing, a promotion that already
283
+ * happened — and is a SUCCESS. A gateway that does not get a 2xx retries forever.
284
+ */
285
+ export function DecideSettlement(input) {
286
+ const header = (input.HeaderStatus ?? '').trim();
287
+ if (!header) {
288
+ // NOT an error. An intent can exist without a payment — a checkout the customer abandoned, or
289
+ // one whose payment has not been captured yet. There is simply nothing to settle.
290
+ return { Action: 'None', Reason: 'the intent has no payment to settle' };
291
+ }
292
+ switch (input.EventStatus) {
293
+ case 'Succeeded':
294
+ if (header === 'Pending') {
295
+ return { Action: 'Promote', Reason: 'the bank confirmed the debit, so the payment becomes Captured' };
296
+ }
297
+ if (header === 'Captured') {
298
+ return { Action: 'None', Reason: 'the payment is already Captured' };
299
+ }
300
+ // Succeeded against Failed / Refunded / Disputed. A bank does not un-return a debit, so
301
+ // this is either a gateway we have misread or an event arriving wildly out of order.
302
+ return {
303
+ Action: 'Hold',
304
+ Reason: `the bank reported success against a payment that is ${header} — this needs a person`,
305
+ };
306
+ case 'Failed':
307
+ case 'Canceled':
308
+ if (header === 'Pending') {
309
+ return { Action: 'Fail', Reason: 'the debit did not clear and nothing was booked' };
310
+ }
311
+ if (header === 'Captured') {
312
+ if (input.AlreadyReversed) {
313
+ return { Action: 'None', Reason: 'this payment has already been reversed' };
314
+ }
315
+ if (!input.HeaderBooked) {
316
+ // Captured without a journal entry should be impossible — booking is part of the
317
+ // same transaction as the transition. If it happened, the two records disagree
318
+ // about whether cash exists, and guessing which is right is how the disagreement
319
+ // becomes permanent.
320
+ return {
321
+ Action: 'Hold',
322
+ Reason: 'the payment is Captured but carries no journal entry, so what to reverse is unclear',
323
+ };
324
+ }
325
+ return { Action: 'Reverse', Reason: 'the debit was returned after being booked, so the cash comes back out' };
326
+ }
327
+ if (header === 'Failed') {
328
+ return { Action: 'None', Reason: 'the payment is already Failed' };
329
+ }
330
+ return {
331
+ Action: 'Hold',
332
+ Reason: `the bank reported a failure against a payment that is ${header} — this needs a person`,
333
+ };
334
+ // Processing / RequiresPayment / unknown. The gateway is telling us it is still working, which
335
+ // is exactly the state a Pending payment already records.
336
+ default:
337
+ return { Action: 'None', Reason: `nothing to settle while the intent reads ${input.EventStatus ?? 'unknown'}` };
338
+ }
339
+ }
340
+ /**
341
+ * Whether a payment asking to be `Captured` must be held at `Pending` because its rail settles on
342
+ * somebody else's schedule.
343
+ *
344
+ * WHY THIS IS A FUNCTION AND NOT AN `IF` IN THE SAVE PATH. The rule used to live only in
345
+ * `Orders.CapturePayment`, which made it a rule that ONE CALLER followed rather than one the system
346
+ * enforced — a workflow, a UI form or a test builder writing `Status: 'Captured'` went straight past
347
+ * it and booked `Dr Cash` for a bank debit that had not cleared. Balanced, posted, and undetectable
348
+ * downstream. Stating it here means the entity server and the operation ask the same question of the
349
+ * same code instead of both remembering.
350
+ *
351
+ * THE PROMOTION MUST PASS. When the webhook moves a `Pending` payment to `Captured`, that IS the
352
+ * bank answering; holding it again would mean a bank debit could never book at all. The persisted
353
+ * status is the signal — already `Pending` means promotion, anything else means a caller declaring
354
+ * cash that has not moved.
355
+ */
356
+ export function ShouldHoldForLateSettlement(facts) {
357
+ if (facts.RequestedStatus !== 'Captured')
358
+ return false;
359
+ if (!facts.HasProvider)
360
+ return false;
361
+ if (!facts.SettlesAsynchronously)
362
+ return false;
363
+ // The webhook promoting a payment that was already waiting — let it book.
364
+ if (facts.IsSaved && facts.PersistedStatus === 'Pending')
365
+ return false;
366
+ return true;
367
+ }
368
+ //# sourceMappingURL=PaymentProviderBehavior.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PaymentProviderBehavior.js","sourceRoot":"","sources":["../src/PaymentProviderBehavior.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * PaymentProviderResolver — turns a `PaymentProvider` row into a working driver.
3
+ *
4
+ * Two jobs, and the second is the one with teeth.
5
+ *
6
+ * 1. FIND THE DRIVER. `PaymentProviderType.Code` is the `@RegisterClass` key (D37), so resolution is a
7
+ * ClassFactory lookup and adding a gateway needs no schema change. A missing registration REFUSES
8
+ * rather than falling back to the base class: the base's methods all decline, so a fallback would
9
+ * look like a gateway that cannot do anything rather than like a driver nobody registered — and the
10
+ * difference is a five-minute fix versus an afternoon.
11
+ *
12
+ * 2. FIND THE CREDENTIALS, WITHOUT EVER STORING THEM. `PaymentProvider.CredentialsRef` is a POINTER —
13
+ * the name of a place to look, not a secret. Nothing in this schema holds an API key, because a
14
+ * database that holds live payment credentials is a database whose backups do too.
15
+ *
16
+ * THE SECRET SEAM. `CredentialsRef` resolves through `BaseSecretResolver`, registered by ClassFactory
17
+ * like everything else here. The default reads environment variables, which is right for a single
18
+ * deployment and inadequate for a real one — so a customer using Vault, AWS Secrets Manager or Azure
19
+ * Key Vault registers a subclass and changes no other code. Naming the seam is the point; pretending
20
+ * environment variables are a secret store would be the mistake.
21
+ *
22
+ * CREDENTIALS ARE NEVER CACHED. They are fetched per operation and dropped. A cache would keep live
23
+ * keys in process memory for the lifetime of a server for the sake of avoiding a lookup that happens
24
+ * once per payment, which is not a trade worth making.
25
+ *
26
+ * CONNECTS TO:
27
+ * BASE: ./BasePaymentProvider.ts
28
+ * DRIVERS: ./StripePaymentProvider.ts · ./ManualPaymentProvider.ts · ./StoredValuePaymentProvider.ts
29
+ * DOC: plans/archive/bizapps-orders-master.md D19, D37
30
+ */
31
+ import { IMetadataProvider, UserInfo } from '@memberjunction/core';
32
+ import { BasePaymentProvider, type PaymentCredentials, type PaymentProviderConfig } from './BasePaymentProvider.js';
33
+ /**
34
+ * How a `CredentialsRef` becomes actual secrets.
35
+ *
36
+ * The default treats the ref as an environment-variable PREFIX, so `STRIPE_BLUECYPRESS` reads
37
+ * `STRIPE_BLUECYPRESS_API_KEY` and `STRIPE_BLUECYPRESS_WEBHOOK_SECRET`. Prefixed rather than a single
38
+ * variable because one deployment routinely holds several configured accounts — a live one and a test
39
+ * one at minimum — and they need separate keys.
40
+ */
41
+ export declare class BaseSecretResolver {
42
+ /**
43
+ * Returns whatever it can find. An EMPTY result is not an error here: a Manual provider has no
44
+ * credentials at all, and the driver that needs a key is the one that should complain about its
45
+ * absence, with the context to say what it was trying to do.
46
+ */
47
+ Resolve(_credentialsRef: string | null | undefined): Promise<PaymentCredentials>;
48
+ }
49
+ /**
50
+ * The default: `CredentialsRef` is an environment-variable PREFIX.
51
+ *
52
+ * `STRIPE_BLUECYPRESS` reads `STRIPE_BLUECYPRESS_API_KEY` and `STRIPE_BLUECYPRESS_WEBHOOK_SECRET`.
53
+ * Prefixed rather than a single variable because one deployment routinely holds several configured
54
+ * accounts — a live one and a test one at minimum — and they need separate keys.
55
+ *
56
+ * Registered with NO key, so it is the fallback a deployment replaces by registering its own against
57
+ * `BaseSecretResolver` — the same shape as `DefaultPriceResolver`.
58
+ */
59
+ export declare class EnvironmentSecretResolver extends BaseSecretResolver {
60
+ Resolve(credentialsRef: string | null | undefined): Promise<PaymentCredentials>;
61
+ }
62
+ /** Tree-shaking anchor for the secret seam. */
63
+ export declare function LoadEnvironmentSecretResolver(): void;
64
+ export declare class PaymentProviderNotConfiguredError extends Error {
65
+ }
66
+ /**
67
+ * Load a configured provider row and hand back a ready driver.
68
+ *
69
+ * Throws rather than returning null. Every caller of this is about to move money and cannot proceed
70
+ * without a driver, so there is no branch for a caller to take — and an exception carries the reason to
71
+ * a log, where a returned null would arrive as "cannot read property of undefined" somewhere else.
72
+ */
73
+ export declare function ResolvePaymentProvider(paymentProviderID: string, provider: IMetadataProvider, user: UserInfo): Promise<BasePaymentProvider>;
74
+ /** The configured account, with its type's code and capability flags flattened onto it. */
75
+ export declare function LoadPaymentProviderConfig(paymentProviderID: string, provider: IMetadataProvider, user: UserInfo): Promise<PaymentProviderConfig>;
76
+ /**
77
+ * Instantiate the driver for a config and attach everything it needs.
78
+ *
79
+ * Exposed separately from `ResolvePaymentProvider` so the webhook route can build a driver from a config
80
+ * it already loaded — the route resolves a provider from the URL before it has a payment in hand, and
81
+ * making it re-query would mean two lookups for one request.
82
+ */
83
+ export declare function BuildPaymentProvider(config: PaymentProviderConfig, provider: IMetadataProvider, user: UserInfo): Promise<BasePaymentProvider>;
84
+ //# sourceMappingURL=PaymentProviderResolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PaymentProviderResolver.d.ts","sourceRoot":"","sources":["../src/PaymentProviderResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,EAAE,iBAAiB,EAA6B,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAG9F,OAAO,EACH,mBAAmB,EACnB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC7B,MAAM,0BAA0B,CAAC;AAYlC;;;;;;;GAOG;AACH,qBAAa,kBAAkB;IAC3B;;;;OAIG;IACU,OAAO,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAGhG;AAED;;;;;;;;;GASG;AACH,qBACa,yBAA0B,SAAQ,kBAAkB;IACvC,OAAO,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAWxG;AAED,+CAA+C;AAC/C,wBAAgB,6BAA6B,IAAI,IAAI,CAEpD;AAED,qBAAa,iCAAkC,SAAQ,KAAK;CAAG;AAE/D;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CACxC,iBAAiB,EAAE,MAAM,EACzB,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,QAAQ,GACf,OAAO,CAAC,mBAAmB,CAAC,CAG9B;AAED,2FAA2F;AAC3F,wBAAsB,yBAAyB,CAC3C,iBAAiB,EAAE,MAAM,EACzB,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,QAAQ,GACf,OAAO,CAAC,qBAAqB,CAAC,CAyDhC;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACtC,MAAM,EAAE,qBAAqB,EAC7B,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,QAAQ,GACf,OAAO,CAAC,mBAAmB,CAAC,CAiC9B"}