@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,295 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ /**
8
+ * Orders.SpawnRenewals — place the renewal orders that are due (plan D55).
9
+ *
10
+ * The last piece of the subscription lifecycle. `AutoRenew` and `RenewalLeadDays` were columns with
11
+ * no consumer: a subscription would reach the end of its term and simply stop, with nothing to
12
+ * continue it.
13
+ *
14
+ * THE DESIGN QUESTION THIS ANSWERS
15
+ * "Is a renewal an order the customer approves, or one the system places for them?" — BOTH, and
16
+ * `AutoRenew` is the switch. `AutoRenew = true` means the customer has already consented to
17
+ * recurring billing, so the system places a CONFIRMED order: it books, it invoices, and coverage
18
+ * continues without a gap. `AutoRenew = false` means it does not renew, full stop — this operation
19
+ * skips it and the term simply ends. Reminder-and-approve for the second case is a communication
20
+ * flow, not a booking one, and does not belong here.
21
+ *
22
+ * The order is placed at LEAD TIME, not on the expiry date, which is how subscription billing
23
+ * actually works: the invoice goes out before the period it covers. Revenue is not affected — the
24
+ * booking entry credits Deferred Revenue and the recognition entries are dated into the new term's
25
+ * own window (D14).
26
+ *
27
+ * IDEMPOTENCY, which a scheduled job makes non-negotiable
28
+ * Two independent guards, because this runs unattended and a double-spawn double-bills a customer:
29
+ * 1. the SELECTION only finds subscriptions whose LATEST term ends inside the window — once a
30
+ * renewal is booked, term N+1 exists and the subscription no longer qualifies;
31
+ * 2. an explicit check for an existing order with `RenewsSubscriptionID` covering that term,
32
+ * which catches the case where a prior pass booked the order but its term write failed.
33
+ * Running the operation twice in a row is a no-op, and that is asserted by the check suite.
34
+ *
35
+ * CONNECTS TO:
36
+ * BOOKING: OrderEntityServer.Save — the renewal order goes through the ordinary confirm path,
37
+ * so extension, term creation, GL resolution and recognition are all the SAME code
38
+ * POLICY: SubscriptionBehavior (IsRenewal bypasses ConcurrencyMode — a renewal is not a second
39
+ * concurrent subscription, it is this one continuing)
40
+ * TABLES: __mj_BizAppsOrders.{Subscription,SubscriptionTerm,SubscriptionType,OrderHeader,OrderLine}
41
+ */
42
+ import { BaseRemotableOperation, LogError, RunView, } from '@memberjunction/core';
43
+ import { RegisterClass } from '@memberjunction/global';
44
+ import { RequireOptionalUUID } from './sql-guards.js';
45
+ const SUBSCRIPTION_ENTITY = 'MJ_BizApps_Orders: Subscriptions';
46
+ const SUBSCRIPTION_TERM_ENTITY = 'MJ_BizApps_Orders: Subscription Terms';
47
+ const SUBSCRIPTION_TYPE_ENTITY = 'MJ_BizApps_Orders: Subscription Types';
48
+ const SUBSCRIPTION_EVENT_ENTITY = 'MJ_BizApps_Orders: Subscription Events';
49
+ const ORDER_HEADER_ENTITY = 'MJ_BizApps_Orders: Order Headers';
50
+ const ORDER_LINE_ENTITY = 'MJ_BizApps_Orders: Order Lines';
51
+ let SpawnRenewalsOperation = class SpawnRenewalsOperation extends BaseRemotableOperation {
52
+ constructor() {
53
+ super(...arguments);
54
+ this.OperationKey = 'Orders.SpawnRenewals';
55
+ }
56
+ async InternalExecute(input, provider, user) {
57
+ // Caller-supplied ids reach SQL filter text downstream. Validated here,
58
+ // at the boundary, so every frame below this one can trust them.
59
+ RequireOptionalUUID(input.SubscriptionID, 'SubscriptionID');
60
+ const asOf = input.AsOfDate ? new Date(input.AsOfDate) : new Date();
61
+ const candidates = await this.findDue(provider, user, asOf, input.SubscriptionID);
62
+ const out = { Success: true, Candidates: [], Placed: 0, Skipped: 0 };
63
+ const limit = input.MaxCount ?? Number.MAX_SAFE_INTEGER;
64
+ for (const due of candidates) {
65
+ if (out.Placed >= limit)
66
+ break;
67
+ const candidate = {
68
+ SubscriptionID: due.SubscriptionID,
69
+ SubscriptionNumber: due.SubscriptionNumber,
70
+ ProductID: due.ProductID,
71
+ CurrentTermEnd: due.TermEndDate,
72
+ LeadDays: due.SubscriptionRenewalLeadDays ?? due.TypeRenewalLeadDays ?? 0,
73
+ };
74
+ out.Candidates.push(candidate);
75
+ // Second idempotency guard — see the header. Cheap, and it is the one that saves a
76
+ // customer from being billed twice if a prior pass half-completed.
77
+ if (await this.alreadyRenewed(provider, user, due)) {
78
+ candidate.SkippedReason = 'a renewal order already exists for this term';
79
+ out.Skipped++;
80
+ continue;
81
+ }
82
+ if (input.Preview) {
83
+ out.Skipped++;
84
+ continue;
85
+ }
86
+ try {
87
+ const order = await this.placeRenewal(provider, user, due);
88
+ candidate.OrderID = order.ID;
89
+ candidate.OrderNumber = order.Number;
90
+ out.Placed++;
91
+ }
92
+ catch (err) {
93
+ // One subscription's failure must not stop the batch — an unattended job that
94
+ // aborts on the first bad row silently stops renewing everyone behind it.
95
+ const message = err instanceof Error ? err.message : String(err);
96
+ LogError(`Orders.SpawnRenewals: ${due.SubscriptionNumber} failed: ${message}`);
97
+ candidate.SkippedReason = message;
98
+ out.Skipped++;
99
+ }
100
+ }
101
+ out.Message =
102
+ `${out.Candidates.length} subscription(s) due as of ${asOf.toISOString().slice(0, 10)}; ` +
103
+ `${out.Placed} renewal order(s) placed, ${out.Skipped} skipped.`;
104
+ return out;
105
+ }
106
+ /**
107
+ * Subscriptions whose latest term expires within their effective lead window.
108
+ *
109
+ * Raw SQL rather than RunView because the selection is inherently a JOIN across four tables with
110
+ * a "latest term per subscription" window — expressible in one statement and awkward as several
111
+ * round trips. `RenewalLeadDays` falls back from the subscription to its type, which is the
112
+ * inheritance rule the schema documents.
113
+ */
114
+ async findDue(provider, user, asOf, subscriptionID) {
115
+ const db = provider;
116
+ const asOfDate = asOf.toISOString().slice(0, 10);
117
+ const only = subscriptionID ? `AND s.ID = '${subscriptionID}'` : '';
118
+ const rows = (await db.ExecuteSQL(`
119
+ WITH latest AS (
120
+ SELECT st.*, ROW_NUMBER() OVER (PARTITION BY st.SubscriptionID ORDER BY st.TermNumber DESC) AS rn
121
+ FROM __mj_BizAppsOrders.SubscriptionTerm st
122
+ )
123
+ SELECT
124
+ s.ID AS SubscriptionID,
125
+ s.SubscriptionNumber,
126
+ s.CompanyID,
127
+ s.ProductID,
128
+ s.HolderOrganizationID,
129
+ s.BeneficiaryPersonID,
130
+ s.RenewalLeadDays AS SubscriptionRenewalLeadDays,
131
+ t.RenewalLeadDays AS TypeRenewalLeadDays,
132
+ l.ID AS TermID,
133
+ l.TermNumber,
134
+ l.EndDate AS TermEndDate,
135
+ l.OrderLineID
136
+ FROM __mj_BizAppsOrders.Subscription s
137
+ JOIN __mj_BizAppsOrders.SubscriptionType t ON t.ID = s.SubscriptionTypeID
138
+ JOIN latest l ON l.SubscriptionID = s.ID AND l.rn = 1
139
+ WHERE s.AutoRenew = 1
140
+ AND s.Status IN ('Active','Trialing')
141
+ AND l.Status IN ('Scheduled','Active')
142
+ -- Due when the expiry falls inside the lead window. The lower bound keeps a long-
143
+ -- lapsed subscription from being silently revived months later by a routine pass.
144
+ AND l.EndDate >= DATEADD(DAY, -1, '${asOfDate}')
145
+ AND l.EndDate <= DATEADD(DAY, COALESCE(s.RenewalLeadDays, t.RenewalLeadDays, 0), '${asOfDate}')
146
+ ${only}
147
+ ORDER BY l.EndDate, s.SubscriptionNumber
148
+ `));
149
+ return Array.isArray(rows) ? rows : [];
150
+ }
151
+ /** True when an order already exists that renews this subscription past the expiring term. */
152
+ async alreadyRenewed(provider, user, due) {
153
+ const rv = new RunView(provider);
154
+ const result = await rv.RunView({
155
+ EntityName: ORDER_LINE_ENTITY,
156
+ ExtraFilter: `RenewsSubscriptionID='${due.SubscriptionID}'`,
157
+ Fields: ['ID'],
158
+ ResultType: 'simple',
159
+ BypassCache: true,
160
+ }, user);
161
+ const orders = result?.Results ?? [];
162
+ if (orders.length === 0)
163
+ return false;
164
+ // An order exists, but a subscription renews many times over its life — only a renewal that
165
+ // produced a term BEYOND the expiring one counts as this cycle's.
166
+ const db = provider;
167
+ const beyond = (await db.ExecuteSQL(`
168
+ SELECT TOP 1 st.ID
169
+ FROM __mj_BizAppsOrders.SubscriptionTerm st
170
+ WHERE st.SubscriptionID = '${due.SubscriptionID}' AND st.TermNumber > ${due.TermNumber}
171
+ `));
172
+ if (Array.isArray(beyond) && beyond.length > 0)
173
+ return true;
174
+ // No later term, but a renewal order exists — a prior pass booked the order and then failed
175
+ // before the term landed. Report rather than silently re-billing.
176
+ return true;
177
+ }
178
+ /**
179
+ * Place the renewal as an ordinary confirmed order.
180
+ *
181
+ * Deliberately NOT a special write path: routing it through `OrderEntityServer.Save` means the
182
+ * extension, the new term, GL resolution, the recognition schedule and the all-or-none
183
+ * guarantee are the same code that handles a customer purchase. A bespoke renewal writer would
184
+ * be a second implementation of booking, drifting from the first.
185
+ */
186
+ async placeRenewal(provider, user, due) {
187
+ const source = await this.loadSourceLine(provider, user, due.OrderLineID);
188
+ if (!source) {
189
+ throw new Error(`The order line that bought term ${due.TermNumber} (${due.OrderLineID}) no longer exists, ` +
190
+ `so there is no price to renew at.`);
191
+ }
192
+ const dbProvider = provider;
193
+ await dbProvider.BeginTransaction();
194
+ try {
195
+ const order = await provider.GetEntityObject(ORDER_HEADER_ENTITY, user);
196
+ order.NewRecord();
197
+ order.OrderType = 'Sale';
198
+ // Dated the day AFTER the expiring term, so the new term starts where the old one ends
199
+ // and the ledger shows the sale in the period it belongs to — not on the day the job
200
+ // happened to run.
201
+ order.OrderDate = this.dayAfter(due.TermEndDate);
202
+ order.CompanyID = due.CompanyID;
203
+ order.BillToOrganizationID = due.HolderOrganizationID;
204
+ order.BillToPersonID = due.BeneficiaryPersonID;
205
+ order.Notes = `Automatic renewal of ${due.SubscriptionNumber} (term ${due.TermNumber + 1})`;
206
+ const line = await provider.GetEntityObject(ORDER_LINE_ENTITY, user);
207
+ line.NewRecord();
208
+ line.ProductID = due.ProductID;
209
+ line.LineNumber = 1;
210
+ // Per-LINE (D61): renewal is a line-level act, so one order could renew several
211
+ // subscriptions. Naming the target also removes the guesswork from resolution — the
212
+ // engine renews exactly this one rather than searching by subscriber and product.
213
+ line.RenewsSubscriptionID = due.SubscriptionID;
214
+ // The subscription's own subscriber, carried onto the line's ship-to so the renewal
215
+ // lands on the same holder even when the order's customer differs.
216
+ line.ShipToOrganizationID = due.HolderOrganizationID;
217
+ line.ShipToPersonID = due.BeneficiaryPersonID;
218
+ // Renew at what they last paid. Re-pricing from the current ProductPrice is a policy
219
+ // decision (grandfathering, notice periods) that nobody has made yet — carrying the
220
+ // price forward is the choice that cannot surprise a customer.
221
+ line.Quantity = this.renewalQuantity(source);
222
+ line.UnitPrice = source.UnitPrice;
223
+ line.DiscountPct = source.DiscountPct ?? 0;
224
+ // Attached rather than assigned — see the note in CancelSubscriptionOperation.
225
+ order.Lines.Add(line);
226
+ order.Status = 'Confirmed';
227
+ if (!(await order.Save())) {
228
+ throw new Error(`Failed to book the renewal order: ${order.LatestResult?.CompleteMessage ?? 'unknown error'}`);
229
+ }
230
+ await this.logEvent(provider, user, due, order.ID);
231
+ await dbProvider.CommitTransaction();
232
+ return { ID: order.ID, Number: order.OrderNumber };
233
+ }
234
+ catch (err) {
235
+ try {
236
+ await dbProvider.RollbackTransaction();
237
+ }
238
+ catch (rollbackErr) {
239
+ LogError(`Rollback failed after renewal spawn error: ${rollbackErr}`);
240
+ }
241
+ throw err;
242
+ }
243
+ }
244
+ /**
245
+ * Quantity for the renewal.
246
+ *
247
+ * A PRORATED source line carries a fractional quantity — the short first period into a calendar
248
+ * anchor (D54). Renewing at that fraction would bill half a year forever. A renewal is always a
249
+ * FULL period, so the fraction is dropped and the quantity rounds up to whole units.
250
+ */
251
+ renewalQuantity(source) {
252
+ const quantity = Number(source.Quantity);
253
+ return quantity > 0 && quantity < 1 ? 1 : Math.round(quantity);
254
+ }
255
+ dayAfter(date) {
256
+ const d = new Date(date);
257
+ return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() + 1));
258
+ }
259
+ async loadSourceLine(provider, user, orderLineID) {
260
+ const rv = new RunView(provider);
261
+ const result = await rv.RunView({
262
+ EntityName: ORDER_LINE_ENTITY,
263
+ ExtraFilter: `ID='${orderLineID}'`,
264
+ Fields: ['ID', 'Quantity', 'UnitPrice', 'DiscountPct'],
265
+ ResultType: 'simple',
266
+ BypassCache: true,
267
+ }, user);
268
+ return result?.Results?.[0] ?? null;
269
+ }
270
+ async logEvent(provider, user, due, orderID) {
271
+ const event = await provider.GetEntityObject(SUBSCRIPTION_EVENT_ENTITY, user);
272
+ event.NewRecord();
273
+ event.SubscriptionID = due.SubscriptionID;
274
+ event.EventType = 'RenewalOrderSpawned';
275
+ event.OccurredAt = new Date();
276
+ event.RelatedOrderHeaderID = orderID;
277
+ event.Set('EventData', JSON.stringify({
278
+ RenewedTermNumber: due.TermNumber,
279
+ ExpiringTermEnd: due.TermEndDate,
280
+ LeadDays: due.SubscriptionRenewalLeadDays ?? due.TypeRenewalLeadDays ?? 0,
281
+ }));
282
+ if (!(await event.Save())) {
283
+ throw new Error(`Failed to log the renewal event: ${event.LatestResult?.CompleteMessage ?? 'unknown error'}`);
284
+ }
285
+ }
286
+ };
287
+ SpawnRenewalsOperation = __decorate([
288
+ RegisterClass(BaseRemotableOperation, 'Orders.SpawnRenewals')
289
+ ], SpawnRenewalsOperation);
290
+ export { SpawnRenewalsOperation };
291
+ /** Tree-shaking anchor — called from the server bootstrap so the registration is retained. */
292
+ export function LoadSpawnRenewalsOperation() {
293
+ // intentionally empty
294
+ }
295
+ //# sourceMappingURL=SpawnRenewalsOperation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpawnRenewalsOperation.js","sourceRoot":"","sources":["../src/SpawnRenewalsOperation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import { BasePaymentProvider, type CaptureRequest, type CaptureResult, type CreateIntentRequest, type CreateIntentResult, type RefundRequest, type RefundResult } from './BasePaymentProvider.js';
2
+ /**
3
+ * Which internal balance is being drawn down. Set by the caller from the `PaymentDetail` it is acting
4
+ * on, because that row is what already distinguishes the two.
5
+ */
6
+ export interface StoredValueTarget {
7
+ /** A gift card, by its account id. */
8
+ StoredValueAccountID?: string | null;
9
+ /** An over-paid order whose negative balance is the credit. */
10
+ SourceOrderHeaderID?: string | null;
11
+ }
12
+ export declare class StoredValuePaymentProvider extends BasePaymentProvider {
13
+ /** Internal money. Nothing external tells us about it. */
14
+ get HandledEventKinds(): readonly string[];
15
+ /**
16
+ * Confirm there is enough, before anything is spent.
17
+ *
18
+ * The balance check happens HERE rather than at capture because refusing early is the difference
19
+ * between "this card cannot cover it" and a half-applied payment. It is deliberately NOT a
20
+ * reservation: nothing is locked between this call and the capture, so a genuinely concurrent spend
21
+ * of the same card could still overdraw it. The guard against that is the capture's own re-read plus
22
+ * the balance's own constraint — the same shape as the order counter, where the authoritative check
23
+ * is the one inside the writing transaction.
24
+ */
25
+ CreateIntent(request: CreateIntentRequest & StoredValueTarget): Promise<CreateIntentResult>;
26
+ /**
27
+ * Spend it.
28
+ *
29
+ * RE-READS THE BALANCE. The check in `CreateIntent` is a courtesy that produces a good error
30
+ * message; this one is the one that protects the money, because it runs inside the caller's
31
+ * transaction alongside the write that spends it.
32
+ */
33
+ Capture(request: CaptureRequest & StoredValueTarget): Promise<CaptureResult>;
34
+ /**
35
+ * Refunding TO an internal balance puts the money back where it came from.
36
+ *
37
+ * Always succeeds at this layer for the same reason the manual driver's does: nothing external has
38
+ * to agree. The row that actually restores the balance is written by the caller, in its transaction.
39
+ */
40
+ Refund(request: RefundRequest): Promise<RefundResult>;
41
+ /**
42
+ * What is actually available, from whichever of the two shapes this is.
43
+ *
44
+ * Returns a LABEL as well as a number so the refusals above can name the thing that was short —
45
+ * "gift card ABC-123" or "order ORD-000042" — rather than saying "the balance", which tells a
46
+ * person nothing about where to look.
47
+ */
48
+ private available;
49
+ }
50
+ /** Tree-shaking anchor — call from the server bootstrap so @RegisterClass is retained. */
51
+ export declare function LoadStoredValuePaymentProvider(): void;
52
+ //# sourceMappingURL=StoredValuePaymentProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StoredValuePaymentProvider.d.ts","sourceRoot":"","sources":["../src/StoredValuePaymentProvider.ts"],"names":[],"mappings":"AAsCA,OAAO,EACH,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,YAAY,EACpB,MAAM,0BAA0B,CAAC;AAKlC;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAC9B,sCAAsC;IACtC,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,+DAA+D;IAC/D,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED,qBACa,0BAA2B,SAAQ,mBAAmB;IAC/D,0DAA0D;IAC1D,IAAoB,iBAAiB,IAAI,SAAS,MAAM,EAAE,CAEzD;IAED;;;;;;;;;OASG;IACmB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAyBjH;;;;;;OAMG;IACmB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,iBAAiB,GAAG,OAAO,CAAC,aAAa,CAAC;IA2BlG;;;;;OAKG;IACmB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAQ3E;;;;;;OAMG;YACW,SAAS;CAgF1B;AAED,0FAA0F;AAC1F,wBAAgB,8BAA8B,IAAI,IAAI,CAErD"}
@@ -0,0 +1,205 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ /**
8
+ * StoredValuePaymentProvider — money we already hold, in the two forms it takes.
9
+ *
10
+ * ONE DRIVER, TWO INSTRUMENT SHAPES, and that was a deliberate call (Amith 2026-07-30). A gift card and
11
+ * an over-payment are genuinely different things:
12
+ *
13
+ * GIFT CARD a `StoredValueAccount` with a redeemable `Code`, a stored balance and a
14
+ * transaction ledger. A BEARER instrument — transferable, possibly to somebody who
15
+ * was never a customer — which is exactly why it cannot be modelled as an order's
16
+ * negative balance: an order belongs to the party who placed it. Selling one is
17
+ * DEFERRED REVENUE; you took cash and owe goods.
18
+ *
19
+ * ACCOUNT CREDIT an over-paid order's negative balance (D68). No separate record, because the
20
+ * balance already has a home and a second copy could disagree with it. Not revenue
21
+ * at all — a customer deposit.
22
+ *
23
+ * They differ in ownership, transferability, expiry and accounting treatment. What they do NOT differ in
24
+ * is the MECHANICS of spending them: check a balance we control, decrement it, record the movement, no
25
+ * network. That shared half is why they are one driver rather than two — and why the split lives in
26
+ * `PaymentDetail` (`StoredValueAccountID` versus `SourceOrderHeaderID`), which is where an instrument's
27
+ * identity belongs.
28
+ *
29
+ * THIS DRIVER REFUSES MORE THAN IT MOVES, and every refusal is a case where the alternative silently
30
+ * creates money: spending an expired card, spending past a balance, spending a credit that belongs to
31
+ * somebody else. A gateway can afford to be optimistic because a real institution will decline; here we
32
+ * ARE the institution.
33
+ *
34
+ * WHAT IT DOES NOT DO. It never writes the journal entry — `PaymentHeaderEntityServer` owns that, as for
35
+ * every other tender. Nor does it ISSUE a gift card; that happens when one is sold, on the ordinary
36
+ * order path, because a gift card is a product.
37
+ *
38
+ * CONNECTS TO:
39
+ * BASE: ./BasePaymentProvider.ts
40
+ * AKIN: ./ApplyAccountCreditOperation.ts — the operation that spends a credit across orders (D68)
41
+ * DOC: plans/archive/bizapps-orders-master.md D19, D38, D68
42
+ */
43
+ import { RegisterClass } from '@memberjunction/global';
44
+ import { RunView } from '@memberjunction/core';
45
+ import { BasePaymentProvider, } from './BasePaymentProvider.js';
46
+ const STORED_VALUE_ACCOUNT_ENTITY = 'MJ_BizApps_Orders: Stored Value Accounts';
47
+ const ORDER_ENTITY = 'MJ_BizApps_Orders: Order Headers';
48
+ let StoredValuePaymentProvider = class StoredValuePaymentProvider extends BasePaymentProvider {
49
+ /** Internal money. Nothing external tells us about it. */
50
+ get HandledEventKinds() {
51
+ return [];
52
+ }
53
+ /**
54
+ * Confirm there is enough, before anything is spent.
55
+ *
56
+ * The balance check happens HERE rather than at capture because refusing early is the difference
57
+ * between "this card cannot cover it" and a half-applied payment. It is deliberately NOT a
58
+ * reservation: nothing is locked between this call and the capture, so a genuinely concurrent spend
59
+ * of the same card could still overdraw it. The guard against that is the capture's own re-read plus
60
+ * the balance's own constraint — the same shape as the order counter, where the authoritative check
61
+ * is the one inside the writing transaction.
62
+ */
63
+ async CreateIntent(request) {
64
+ if (request.Amount <= 0) {
65
+ return { Success: false, Reason: `Cannot draw ${request.Amount} from an internal balance.` };
66
+ }
67
+ const available = await this.available(request);
68
+ if (!available.Found)
69
+ return { Success: false, Reason: available.Reason };
70
+ if (available.Balance < request.Amount) {
71
+ return {
72
+ Success: false,
73
+ Reason: `${available.Label} holds ${available.Balance.toFixed(2)}, which does not cover ` +
74
+ `${request.Amount.toFixed(2)}. Apply what is there and settle the remainder with ` +
75
+ `another tender rather than over-drawing it.`,
76
+ };
77
+ }
78
+ return {
79
+ Success: true,
80
+ ProviderIntentID: `sv_${crypto.randomUUID()}`,
81
+ // Immediately payable: the money is ours to move, and there is no third party to wait for.
82
+ Status: 'RequiresPayment',
83
+ };
84
+ }
85
+ /**
86
+ * Spend it.
87
+ *
88
+ * RE-READS THE BALANCE. The check in `CreateIntent` is a courtesy that produces a good error
89
+ * message; this one is the one that protects the money, because it runs inside the caller's
90
+ * transaction alongside the write that spends it.
91
+ */
92
+ async Capture(request) {
93
+ const amount = request.Amount ?? 0;
94
+ if (amount <= 0) {
95
+ return { Success: false, Reason: 'An internal-tender capture needs the amount being drawn.' };
96
+ }
97
+ const available = await this.available(request);
98
+ if (!available.Found)
99
+ return { Success: false, Reason: available.Reason };
100
+ if (available.Balance < amount) {
101
+ return {
102
+ Success: false,
103
+ Reason: `${available.Label} now holds only ${available.Balance.toFixed(2)} against a ` +
104
+ `${amount.toFixed(2)} draw — it changed after the intent was opened. Nothing was spent.`,
105
+ };
106
+ }
107
+ return {
108
+ Success: true,
109
+ Amount: amount,
110
+ // We are the institution, so there is no cut. Genuinely zero, not unknown.
111
+ FeeAmount: 0,
112
+ ProviderChargeID: request.ProviderIntentID,
113
+ Status: 'Succeeded',
114
+ };
115
+ }
116
+ /**
117
+ * Refunding TO an internal balance puts the money back where it came from.
118
+ *
119
+ * Always succeeds at this layer for the same reason the manual driver's does: nothing external has
120
+ * to agree. The row that actually restores the balance is written by the caller, in its transaction.
121
+ */
122
+ async Refund(request) {
123
+ const amount = request.Amount;
124
+ if (amount == null || amount <= 0) {
125
+ return { Success: false, Reason: 'An internal-tender refund needs the amount being restored.' };
126
+ }
127
+ return { Success: true, Amount: amount, ProviderRefundID: `sv_refund_${crypto.randomUUID()}` };
128
+ }
129
+ /**
130
+ * What is actually available, from whichever of the two shapes this is.
131
+ *
132
+ * Returns a LABEL as well as a number so the refusals above can name the thing that was short —
133
+ * "gift card ABC-123" or "order ORD-000042" — rather than saying "the balance", which tells a
134
+ * person nothing about where to look.
135
+ */
136
+ async available(target) {
137
+ const none = { Found: false, Balance: 0, Label: '' };
138
+ if (!this.Provider || !this.User) {
139
+ return { ...none, Reason: 'The stored-value driver was not given a provider to read balances with.' };
140
+ }
141
+ const rv = new RunView(this.Provider);
142
+ if (target.StoredValueAccountID) {
143
+ const result = await rv.RunView({
144
+ EntityName: STORED_VALUE_ACCOUNT_ENTITY,
145
+ ExtraFilter: `ID = '${target.StoredValueAccountID}'`,
146
+ ResultType: 'simple',
147
+ }, this.User);
148
+ const account = result?.Results?.[0];
149
+ if (!account) {
150
+ return { ...none, Reason: `No stored-value account ${target.StoredValueAccountID} exists.` };
151
+ }
152
+ const label = `Gift card ${account.Code}`;
153
+ if (account.Status !== 'Active') {
154
+ return { ...none, Reason: `${label} is ${account.Status}, so it cannot be spent.` };
155
+ }
156
+ // Expiry is checked HERE rather than left to a nightly sweep, because a card that expired
157
+ // this morning must not be spendable this afternoon just because nothing has run yet.
158
+ if (account.ExpiresAt && new Date(account.ExpiresAt).getTime() < Date.now()) {
159
+ return {
160
+ ...none,
161
+ Reason: `${label} expired on ${new Date(account.ExpiresAt).toISOString().slice(0, 10)}.`,
162
+ };
163
+ }
164
+ return { Found: true, Balance: Number(account.CurrentBalance ?? 0), Label: label };
165
+ }
166
+ if (target.SourceOrderHeaderID) {
167
+ const result = await rv.RunView({
168
+ EntityName: ORDER_ENTITY,
169
+ ExtraFilter: `ID = '${target.SourceOrderHeaderID}'`,
170
+ ResultType: 'simple',
171
+ }, this.User);
172
+ const order = result?.Results?.[0];
173
+ if (!order) {
174
+ return { ...none, Reason: `No order ${target.SourceOrderHeaderID} exists to draw credit from.` };
175
+ }
176
+ const balance = Number(order.Balance ?? 0);
177
+ const label = `Order ${order.OrderNumber}`;
178
+ if (balance >= 0) {
179
+ // A credit IS a negative balance (D68). A zero or positive balance means there is
180
+ // nothing owed to the customer — and saying so beats reporting "0.00 available", which
181
+ // reads as an empty wallet rather than as the wrong order.
182
+ return {
183
+ ...none,
184
+ Reason: `${label} has a balance of ${balance.toFixed(2)}, so it holds no credit. ` +
185
+ `Account credit is an OVER-payment — a negative balance.`,
186
+ };
187
+ }
188
+ return { Found: true, Balance: Math.abs(balance), Label: `${label}'s credit` };
189
+ }
190
+ return {
191
+ ...none,
192
+ Reason: 'An internal-tender payment must name either a stored-value account or the over-paid ' +
193
+ 'order whose credit is being spent.',
194
+ };
195
+ }
196
+ };
197
+ StoredValuePaymentProvider = __decorate([
198
+ RegisterClass(BasePaymentProvider, 'StoredValue')
199
+ ], StoredValuePaymentProvider);
200
+ export { StoredValuePaymentProvider };
201
+ /** Tree-shaking anchor — call from the server bootstrap so @RegisterClass is retained. */
202
+ export function LoadStoredValuePaymentProvider() {
203
+ // intentionally empty
204
+ }
205
+ //# sourceMappingURL=StoredValuePaymentProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StoredValuePaymentProvider.js","sourceRoot":"","sources":["../src/StoredValuePaymentProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import { type CaptureRequest, type CaptureResult, type CreateIntentRequest, type WebhookEvent } from './BasePaymentProvider.js';
2
+ import { StripePaymentProvider } from './StripePaymentProvider.js';
3
+ import { type IntentStatus } from './PaymentProviderBehavior.js';
4
+ export declare class StripeACHPaymentProvider extends StripePaymentProvider {
5
+ /**
6
+ * The declaration that changes who books the cash. See `BasePaymentProvider.SettlesAsynchronously` —
7
+ * with this true, a caller leaves the payment `Pending` and the webhook promotes it.
8
+ */
9
+ get SettlesAsynchronously(): boolean;
10
+ get HandledEventKinds(): readonly string[];
11
+ /** A submitted debit is already in flight; it never waits for an instrument the way a card does. */
12
+ protected get StubIntentStatus(): IntentStatus;
13
+ /** 0.8% capped at $5 — see `AchFeeEstimate` for why the stub reports a fee at all. */
14
+ protected StubFeeFor(amount: number, currencyCode: string): number;
15
+ /**
16
+ * Pin the intent to the bank-debit rail.
17
+ *
18
+ * EXPLICIT RATHER THAN `automatic_payment_methods`. Letting Stripe choose would make the rail a
19
+ * property of the account's dashboard settings, so the same code could open a card intent on one
20
+ * deployment and a bank debit on another — with a fee, a settlement delay and a reversal risk that
21
+ * differ by a factor of four and several days. The provider row says which rail this is; the
22
+ * request should say the same thing.
23
+ */
24
+ protected DecorateIntentBody(body: Record<string, string>, _request: CreateIntentRequest): void;
25
+ /**
26
+ * Ask the bank what happened. Nothing is captured here — see the header.
27
+ *
28
+ * REFUSES ON ANYTHING BUT `succeeded`, and the two refusals say different things on purpose.
29
+ * "Still processing" is the ordinary case — somebody captured too early, and they should wait for
30
+ * the webhook — while a failure means the debit will not clear and waiting is pointless. Both come
31
+ * back as refusals rather than exceptions, because neither is a fault: the gateway answered.
32
+ */
33
+ Capture(request: CaptureRequest): Promise<CaptureResult>;
34
+ /**
35
+ * Read a `charge.failed`, which the base driver has no reason to know about.
36
+ *
37
+ * THIS IS THE RETURN, and it is the event this whole rail exists to survive. It can arrive after
38
+ * the payment has been booked, in which case `charge.failed` is the only notice that money which
39
+ * appeared to arrive has gone back out. The base handles `payment_intent.*` and `charge.refunded`
40
+ * correctly, so everything else defers to it.
41
+ *
42
+ * The failure MESSAGE is carried through verbatim. Stripe writes it for a human ("insufficient
43
+ * funds", "no account"), and it ends up on the reversing payment's reason — which is the only place
44
+ * anyone reconciling next month will look.
45
+ */
46
+ ParseWebhookEvent(rawBody: string): WebhookEvent | null;
47
+ }
48
+ /** Tree-shaking anchor — call from the server bootstrap so @RegisterClass is retained. */
49
+ export declare function LoadStripeACHPaymentProvider(): void;
50
+ //# sourceMappingURL=StripeACHPaymentProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StripeACHPaymentProvider.d.ts","sourceRoot":"","sources":["../src/StripeACHPaymentProvider.ts"],"names":[],"mappings":"AAgDA,OAAO,EAAuB,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,KAAK,mBAAmB,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACrJ,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAkC,KAAK,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAiCjG,qBACa,wBAAyB,SAAQ,qBAAqB;IAC/D;;;OAGG;IACH,IAAoB,qBAAqB,IAAI,OAAO,CAEnD;IAED,IAAoB,iBAAiB,IAAI,SAAS,MAAM,EAAE,CAEzD;IAED,oGAAoG;IACpG,cAAuB,gBAAgB,IAAI,YAAY,CAEtD;IAED,sFAAsF;cACnE,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;IAI3E;;;;;;;;OAQG;cACgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IAIxG;;;;;;;OAOG;IACmB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IA6C9E;;;;;;;;;;;OAWG;IACa,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;CAyB1E;AAED,0FAA0F;AAC1F,wBAAgB,4BAA4B,IAAI,IAAI,CAEnD"}