@owlmeans/server-payment 0.1.18-rc.2 → 0.1.18-rc.21

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 (222) hide show
  1. package/README.md +109 -25
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/server-payment/SKILL.md +475 -52
  4. package/build/actions/index.d.ts +1 -0
  5. package/build/actions/index.d.ts.map +1 -1
  6. package/build/actions/index.js +1 -0
  7. package/build/actions/index.js.map +1 -1
  8. package/build/actions/resync-subscriptions.d.ts +3 -0
  9. package/build/actions/resync-subscriptions.d.ts.map +1 -0
  10. package/build/actions/resync-subscriptions.js +7 -0
  11. package/build/actions/resync-subscriptions.js.map +1 -0
  12. package/build/actions/resync.d.ts +1 -0
  13. package/build/actions/resync.d.ts.map +1 -1
  14. package/build/actions/resync.js +7 -3
  15. package/build/actions/resync.js.map +1 -1
  16. package/build/actions/webhook.d.ts.map +1 -1
  17. package/build/actions/webhook.js +6 -3
  18. package/build/actions/webhook.js.map +1 -1
  19. package/build/config.d.ts +60 -5
  20. package/build/config.d.ts.map +1 -1
  21. package/build/config.js +237 -5
  22. package/build/config.js.map +1 -1
  23. package/build/consts.d.ts +75 -3
  24. package/build/consts.d.ts.map +1 -1
  25. package/build/consts.js +94 -5
  26. package/build/consts.js.map +1 -1
  27. package/build/consumer/capture.d.ts +74 -0
  28. package/build/consumer/capture.d.ts.map +1 -0
  29. package/build/consumer/capture.js +291 -0
  30. package/build/consumer/capture.js.map +1 -0
  31. package/build/consumer/format.d.ts +27 -0
  32. package/build/consumer/format.d.ts.map +1 -0
  33. package/build/consumer/format.js +81 -0
  34. package/build/consumer/format.js.map +1 -0
  35. package/build/consumer/handlers.d.ts +28 -0
  36. package/build/consumer/handlers.d.ts.map +1 -0
  37. package/build/consumer/handlers.js +173 -0
  38. package/build/consumer/handlers.js.map +1 -0
  39. package/build/consumer/index.d.ts +7 -0
  40. package/build/consumer/index.d.ts.map +1 -0
  41. package/build/consumer/index.js +6 -0
  42. package/build/consumer/index.js.map +1 -0
  43. package/build/consumer/mail.d.ts +27 -0
  44. package/build/consumer/mail.d.ts.map +1 -0
  45. package/build/consumer/mail.js +314 -0
  46. package/build/consumer/mail.js.map +1 -0
  47. package/build/consumer/origin.d.ts +14 -0
  48. package/build/consumer/origin.d.ts.map +1 -0
  49. package/build/consumer/origin.js +47 -0
  50. package/build/consumer/origin.js.map +1 -0
  51. package/build/consumer/reconcile.d.ts +12 -0
  52. package/build/consumer/reconcile.d.ts.map +1 -0
  53. package/build/consumer/reconcile.js +317 -0
  54. package/build/consumer/reconcile.js.map +1 -0
  55. package/build/consumer/records.d.ts +78 -0
  56. package/build/consumer/records.d.ts.map +1 -0
  57. package/build/consumer/records.js +296 -0
  58. package/build/consumer/records.js.map +1 -0
  59. package/build/consumer/service.d.ts +51 -0
  60. package/build/consumer/service.d.ts.map +1 -0
  61. package/build/consumer/service.js +760 -0
  62. package/build/consumer/service.js.map +1 -0
  63. package/build/consumer/withdrawal.d.ts +57 -0
  64. package/build/consumer/withdrawal.d.ts.map +1 -0
  65. package/build/consumer/withdrawal.js +247 -0
  66. package/build/consumer/withdrawal.js.map +1 -0
  67. package/build/entitlement.d.ts +10 -0
  68. package/build/entitlement.d.ts.map +1 -0
  69. package/build/entitlement.js +69 -0
  70. package/build/entitlement.js.map +1 -0
  71. package/build/entrypoints.d.ts +1 -1
  72. package/build/entrypoints.d.ts.map +1 -1
  73. package/build/entrypoints.js +2 -1
  74. package/build/entrypoints.js.map +1 -1
  75. package/build/gate.d.ts +32 -4
  76. package/build/gate.d.ts.map +1 -1
  77. package/build/gate.js +59 -19
  78. package/build/gate.js.map +1 -1
  79. package/build/index.d.ts +18 -3
  80. package/build/index.d.ts.map +1 -1
  81. package/build/index.js +15 -3
  82. package/build/index.js.map +1 -1
  83. package/build/limit.d.ts +13 -0
  84. package/build/limit.d.ts.map +1 -0
  85. package/build/limit.js +47 -0
  86. package/build/limit.js.map +1 -0
  87. package/build/model.d.ts +10 -1
  88. package/build/model.d.ts.map +1 -1
  89. package/build/model.js +215 -17
  90. package/build/model.js.map +1 -1
  91. package/build/observer.d.ts +9 -0
  92. package/build/observer.d.ts.map +1 -1
  93. package/build/observer.js +38 -12
  94. package/build/observer.js.map +1 -1
  95. package/build/plan.d.ts +22 -0
  96. package/build/plan.d.ts.map +1 -0
  97. package/build/plan.js +72 -0
  98. package/build/plan.js.map +1 -0
  99. package/build/plugins/checkout-plugins.d.ts +49 -0
  100. package/build/plugins/checkout-plugins.d.ts.map +1 -0
  101. package/build/plugins/checkout-plugins.js +124 -0
  102. package/build/plugins/checkout-plugins.js.map +1 -0
  103. package/build/plugins/estimate.d.ts +43 -0
  104. package/build/plugins/estimate.d.ts.map +1 -0
  105. package/build/plugins/estimate.js +268 -0
  106. package/build/plugins/estimate.js.map +1 -0
  107. package/build/plugins/events.d.ts +45 -10
  108. package/build/plugins/events.d.ts.map +1 -1
  109. package/build/plugins/events.js +605 -136
  110. package/build/plugins/events.js.map +1 -1
  111. package/build/plugins/fx.d.ts +31 -0
  112. package/build/plugins/fx.d.ts.map +1 -0
  113. package/build/plugins/fx.js +81 -0
  114. package/build/plugins/fx.js.map +1 -0
  115. package/build/plugins/portal.d.ts +37 -0
  116. package/build/plugins/portal.d.ts.map +1 -0
  117. package/build/plugins/portal.js +265 -0
  118. package/build/plugins/portal.js.map +1 -0
  119. package/build/plugins/refunds.d.ts +33 -0
  120. package/build/plugins/refunds.d.ts.map +1 -0
  121. package/build/plugins/refunds.js +80 -0
  122. package/build/plugins/refunds.js.map +1 -0
  123. package/build/plugins/stripe.d.ts +36 -4
  124. package/build/plugins/stripe.d.ts.map +1 -1
  125. package/build/plugins/stripe.js +492 -97
  126. package/build/plugins/stripe.js.map +1 -1
  127. package/build/plugins/webhook-manager.d.ts +46 -0
  128. package/build/plugins/webhook-manager.d.ts.map +1 -0
  129. package/build/plugins/webhook-manager.js +231 -0
  130. package/build/plugins/webhook-manager.js.map +1 -0
  131. package/build/reconcile.d.ts +15 -0
  132. package/build/reconcile.d.ts.map +1 -0
  133. package/build/reconcile.js +88 -0
  134. package/build/reconcile.js.map +1 -0
  135. package/build/resource.d.ts +12 -1
  136. package/build/resource.d.ts.map +1 -1
  137. package/build/resource.js +97 -6
  138. package/build/resource.js.map +1 -1
  139. package/build/service.d.ts +28 -3
  140. package/build/service.d.ts.map +1 -1
  141. package/build/service.js +146 -19
  142. package/build/service.js.map +1 -1
  143. package/build/subscription.d.ts +48 -0
  144. package/build/subscription.d.ts.map +1 -0
  145. package/build/subscription.js +176 -0
  146. package/build/subscription.js.map +1 -0
  147. package/build/sync.d.ts +28 -1
  148. package/build/sync.d.ts.map +1 -1
  149. package/build/sync.js +208 -31
  150. package/build/sync.js.map +1 -1
  151. package/build/types.d.ts +1115 -36
  152. package/build/types.d.ts.map +1 -1
  153. package/build/usage.d.ts +63 -0
  154. package/build/usage.d.ts.map +1 -0
  155. package/build/usage.js +363 -0
  156. package/build/usage.js.map +1 -0
  157. package/build/utils.d.ts +52 -1
  158. package/build/utils.d.ts.map +1 -1
  159. package/build/utils.js +69 -7
  160. package/build/utils.js.map +1 -1
  161. package/package.json +17 -13
  162. package/src/actions/index.ts +1 -0
  163. package/src/actions/resync-subscriptions.ts +10 -0
  164. package/src/actions/resync.ts +6 -3
  165. package/src/actions/webhook.ts +5 -3
  166. package/src/config.ts +264 -8
  167. package/src/consts.ts +114 -6
  168. package/src/consumer/capture.ts +362 -0
  169. package/src/consumer/format.ts +90 -0
  170. package/src/consumer/handlers.ts +211 -0
  171. package/src/consumer/index.ts +6 -0
  172. package/src/consumer/mail.ts +368 -0
  173. package/src/consumer/origin.ts +63 -0
  174. package/src/consumer/reconcile.ts +329 -0
  175. package/src/consumer/records.ts +374 -0
  176. package/src/consumer/service.ts +868 -0
  177. package/src/consumer/withdrawal.ts +302 -0
  178. package/src/entitlement.ts +84 -0
  179. package/src/entrypoints.ts +2 -1
  180. package/src/gate.ts +88 -21
  181. package/src/index.ts +24 -3
  182. package/src/limit.ts +57 -0
  183. package/src/model.ts +237 -18
  184. package/src/observer.ts +44 -11
  185. package/src/plan.ts +89 -0
  186. package/src/plugins/checkout-plugins.ts +155 -0
  187. package/src/plugins/estimate.ts +339 -0
  188. package/src/plugins/events.ts +677 -121
  189. package/src/plugins/fx.ts +122 -0
  190. package/src/plugins/portal.ts +306 -0
  191. package/src/plugins/refunds.ts +108 -0
  192. package/src/plugins/stripe.ts +581 -96
  193. package/src/plugins/webhook-manager.ts +270 -0
  194. package/src/reconcile.ts +103 -0
  195. package/src/resource.ts +152 -7
  196. package/src/service.ts +174 -18
  197. package/src/subscription.ts +231 -0
  198. package/src/sync.ts +249 -29
  199. package/src/types.ts +1227 -32
  200. package/src/usage.ts +453 -0
  201. package/src/utils.ts +127 -10
  202. package/tests/checkout-consumer.spec.ts +348 -0
  203. package/tests/checkout-plugins.spec.ts +164 -0
  204. package/tests/checkout.spec.ts +184 -83
  205. package/tests/consumer-events.spec.ts +218 -0
  206. package/tests/consumer-fixtures.ts +132 -0
  207. package/tests/consumer-ops.spec.ts +351 -0
  208. package/tests/consumer-rights.integration.spec.ts +150 -0
  209. package/tests/consumer-rights.spec.ts +501 -0
  210. package/tests/context.ts +109 -0
  211. package/tests/entitlement.spec.ts +103 -0
  212. package/tests/estimate.spec.ts +240 -0
  213. package/tests/events.spec.ts +356 -0
  214. package/tests/fake-stripe.ts +972 -0
  215. package/tests/gate.spec.ts +62 -72
  216. package/tests/limit-gate.spec.ts +68 -0
  217. package/tests/portal.spec.ts +200 -0
  218. package/tests/protocol.spec.ts +23 -6
  219. package/tests/sync.spec.ts +114 -0
  220. package/tests/usage.integration.spec.ts +101 -0
  221. package/tests/usage.spec.ts +171 -0
  222. package/tests/webhook-manager.spec.ts +152 -0
package/build/observer.js CHANGED
@@ -1,19 +1,45 @@
1
1
  import { createLazyService } from '@owlmeans/context';
2
2
  import { PAYMENT_OBSERVER } from './consts.js';
3
+ /**
4
+ * The application's side of payment completion. Callbacks run sequentially and are awaited; a
5
+ * throw escapes to the webhook so the paygate delivers the event again — every callback must be
6
+ * idempotent by the `eventKey` (`externalId` for a top-up) it is handed.
7
+ *
8
+ * The consumer-rights callbacks (`onConsent`, `onWithdrawal`, `onCancellation`) run after the
9
+ * records and the paygate steps of their act; a throw is recorded as an `observers` event and
10
+ * retried by the consumer-rights `reconcile()` — they, too, are idempotent by `eventKey`.
11
+ */
3
12
  export const makeCompletionObserverService = (alias = PAYMENT_OBSERVER) => {
4
- const topUpCallbacks = [];
5
- const subscriptionCallbacks = [];
13
+ const topUp = [];
14
+ const subscription = [];
15
+ const refund = [];
16
+ const dispute = [];
17
+ const paymentFailed = [];
18
+ const consent = [];
19
+ const withdrawal = [];
20
+ const cancellation = [];
21
+ const run = async (callbacks, event, ctx) => {
22
+ for (const callback of callbacks) {
23
+ await callback(event, ctx);
24
+ }
25
+ };
6
26
  return createLazyService(alias, {
7
- onTopUp: cb => { topUpCallbacks.push(cb); },
8
- onSubscription: cb => { subscriptionCallbacks.push(cb); },
9
- propagateTopUp: async (completion, ctx) => {
10
- for (const callback of topUpCallbacks)
11
- await callback(completion, ctx);
12
- },
13
- propagateSubscription: async (completion, ctx) => {
14
- for (const callback of subscriptionCallbacks)
15
- await callback(completion, ctx);
16
- },
27
+ onTopUp: cb => { topUp.push(cb); },
28
+ onSubscription: cb => { subscription.push(cb); },
29
+ onRefund: cb => { refund.push(cb); },
30
+ onDispute: cb => { dispute.push(cb); },
31
+ onPaymentFailed: cb => { paymentFailed.push(cb); },
32
+ onConsent: cb => { consent.push(cb); },
33
+ onWithdrawal: cb => { withdrawal.push(cb); },
34
+ onCancellation: cb => { cancellation.push(cb); },
35
+ propagateTopUp: async (completion, ctx) => { await run(topUp, completion, ctx); },
36
+ propagateSubscription: async (event, ctx) => { await run(subscription, event, ctx); },
37
+ propagateRefund: async (event, ctx) => { await run(refund, event, ctx); },
38
+ propagateDispute: async (event, ctx) => { await run(dispute, event, ctx); },
39
+ propagatePaymentFailed: async (event, ctx) => { await run(paymentFailed, event, ctx); },
40
+ propagateConsent: async (event, ctx) => { await run(consent, event, ctx); },
41
+ propagateWithdrawal: async (event, ctx) => { await run(withdrawal, event, ctx); },
42
+ propagateCancellation: async (event, ctx) => { await run(cancellation, event, ctx); },
17
43
  }, service => async () => { service.initialized = true; });
18
44
  };
19
45
  export const appendCompletionObserver = (ctx, alias = PAYMENT_OBSERVER) => {
@@ -1 +1 @@
1
- {"version":3,"file":"observer.js","sourceRoot":"","sources":["../src/observer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAG9C,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAC3C,KAAK,GAAW,gBAAgB,EACZ,EAAE;IACtB,MAAM,cAAc,GAAoB,EAAE,CAAA;IAC1C,MAAM,qBAAqB,GAA2B,EAAE,CAAA;IACxD,OAAO,iBAAiB,CAAqB,KAAK,EAAE;QAClD,OAAO,EAAE,EAAE,CAAC,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA,CAAC,CAAC;QAC1C,cAAc,EAAE,EAAE,CAAC,EAAE,GAAG,qBAAqB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA,CAAC,CAAC;QACxD,cAAc,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE;YACxC,KAAK,MAAM,QAAQ,IAAI,cAAc;gBAAE,MAAM,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC,CAAA;QACxE,CAAC;QACD,qBAAqB,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE;YAC/C,KAAK,MAAM,QAAQ,IAAI,qBAAqB;gBAAE,MAAM,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC,CAAA;QAC/E,CAAC;KACF,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAA,CAAC,CAAC,CAAC,CAAA;AAC3D,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACtC,GAAM,EAAE,KAAK,GAAW,gBAAgB,EACrC,EAAE;IACL,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,GAAG,CAAC,eAAe,CAAC,6BAA6B,CAAC,KAAK,CAAC,CAAC,CAAA;IACrF,OAAO,GAAG,CAAA;AACZ,CAAC,CAAA"}
1
+ {"version":3,"file":"observer.js","sourceRoot":"","sources":["../src/observer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { ProductPlan } from '@owlmeans/payment';
2
+ import type { Context as ApiContext } from '@owlmeans/server-api';
3
+ import type { EffectivePlan, PaymentPlan, PaymentProduct } from './types.js';
4
+ /** A plan's rank; an absent rank reads as `0`. */
5
+ export declare const planRank: (plan: Pick<ProductPlan, 'rank'>) => number;
6
+ /** A plan by sku, or `null` when the catalogue no longer declares it. */
7
+ export declare const findPlan: (ctx: ApiContext, planSku: string) => Promise<PaymentPlan | null>;
8
+ /** A product by sku, or `null`. */
9
+ export declare const findProduct: (ctx: ApiContext, productSku: string) => Promise<PaymentProduct | null>;
10
+ /** Every plan of every declared product. */
11
+ export declare const catalogPlans: (ctx: ApiContext) => Promise<PaymentPlan[]>;
12
+ /** The declared free plan an entity falls back to: the lowest rank, then the sku. */
13
+ export declare const freePlanOf: (ctx: ApiContext) => Promise<PaymentPlan | null>;
14
+ /**
15
+ * The plan an entity holds: its highest-ranked subscription in an entitling status (the catalogue
16
+ * rank, the stored one for a plan the catalogue no longer declares; the newest first on a tie),
17
+ * else the declared free plan.
18
+ *
19
+ * @throws PlanRequired when there is neither.
20
+ */
21
+ export declare const resolveEffectivePlan: (ctx: ApiContext, entityId: string, at?: Date) => Promise<EffectivePlan>;
22
+ //# sourceMappingURL=plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAEjE,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAA6B,MAAM,YAAY,CAAA;AAEvG,kDAAkD;AAClD,eAAO,MAAM,QAAQ,SAAU,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,KAAG,MAAwB,CAAA;AAEnF,yEAAyE;AACzE,eAAO,MAAM,QAAQ,QAAe,UAAU,WAAW,MAAM,KAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAM3F,CAAA;AAED,mCAAmC;AACnC,eAAO,MAAM,WAAW,QAAe,UAAU,cAAc,MAAM,KAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAMpG,CAAA;AAED,4CAA4C;AAC5C,eAAO,MAAM,YAAY,QAAe,UAAU,KAAG,OAAO,CAAC,WAAW,EAAE,CAQzE,CAAA;AAED,qFAAqF;AACrF,eAAO,MAAM,UAAU,QAAe,UAAU,KAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAGU,CAAA;AAQvF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,QAC1B,UAAU,YAAY,MAAM,OAAM,IAAI,KAC1C,OAAO,CAAC,aAAa,CA6BvB,CAAA"}
package/build/plan.js ADDED
@@ -0,0 +1,72 @@
1
+ import { ENTITLING_STATUSES, INTERNAL_PAYGATE, PlanRequired } from '@owlmeans/payment';
2
+ import { payment, subscriptions } from './utils.js';
3
+ /** A plan's rank; an absent rank reads as `0`. */
4
+ export const planRank = (plan) => plan.rank ?? 0;
5
+ /** A plan by sku, or `null` when the catalogue no longer declares it. */
6
+ export const findPlan = async (ctx, planSku) => {
7
+ try {
8
+ return await payment(ctx).plan(planSku);
9
+ }
10
+ catch {
11
+ return null;
12
+ }
13
+ };
14
+ /** A product by sku, or `null`. */
15
+ export const findProduct = async (ctx, productSku) => {
16
+ try {
17
+ return await payment(ctx).product(productSku);
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ };
23
+ /** Every plan of every declared product. */
24
+ export const catalogPlans = async (ctx) => {
25
+ const products = await payment(ctx).products();
26
+ const plans = [];
27
+ for (const product of products) {
28
+ plans.push(...await payment(ctx).allPlans(product.sku));
29
+ }
30
+ return plans;
31
+ };
32
+ /** The declared free plan an entity falls back to: the lowest rank, then the sku. */
33
+ export const freePlanOf = async (ctx) => (await catalogPlans(ctx))
34
+ .filter(plan => plan.free === true)
35
+ .sort((a, b) => planRank(a) - planRank(b) || a.sku.localeCompare(b.sku))[0] ?? null;
36
+ /** An internal grant with a period end stops entitling at that instant; a paygate row never does. */
37
+ const stillEntitles = (row, at) => row.paygate !== INTERNAL_PAYGATE || row.periodEnd == null || new Date(row.periodEnd).getTime() > at.getTime();
38
+ const unknownPlans = new Set();
39
+ /**
40
+ * The plan an entity holds: its highest-ranked subscription in an entitling status (the catalogue
41
+ * rank, the stored one for a plan the catalogue no longer declares; the newest first on a tie),
42
+ * else the declared free plan.
43
+ *
44
+ * @throws PlanRequired when there is neither.
45
+ */
46
+ export const resolveEffectivePlan = async (ctx, entityId, at = new Date()) => {
47
+ const fallback = await freePlanOf(ctx);
48
+ const { items } = await subscriptions(ctx).list({ entityId, status: [...ENTITLING_STATUSES] }, { size: 0 });
49
+ const candidates = [];
50
+ for (const row of items.filter(item => stillEntitles(item, at))) {
51
+ const plan = await findPlan(ctx, row.planSku);
52
+ if (plan == null) {
53
+ if (!unknownPlans.has(row.planSku)) {
54
+ unknownPlans.add(row.planSku);
55
+ console.warn(`[payment] subscription "${row.externalId}" names unknown plan "${row.planSku}"; ignored`);
56
+ }
57
+ continue;
58
+ }
59
+ candidates.push({ plan, row, rank: planRank(plan) });
60
+ }
61
+ candidates.sort((a, b) => b.rank - a.rank
62
+ || new Date(b.row.createdAt).getTime() - new Date(a.row.createdAt).getTime());
63
+ const best = candidates[0];
64
+ if (best != null) {
65
+ return { plan: best.plan, subscription: best.row, fallback };
66
+ }
67
+ if (fallback != null) {
68
+ return { plan: fallback, subscription: null, fallback };
69
+ }
70
+ throw new PlanRequired(entityId);
71
+ };
72
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import type { AmountPolicyView } from '@owlmeans/payment';
2
+ import type { Context as ApiContext } from '@owlmeans/server-api';
3
+ import type { CheckoutAttempt, CheckoutNarrowInput, CheckoutPlugin, CheckoutSettled } from '../types.js';
4
+ export interface CheckoutPluginRegistry {
5
+ use: (plugin: CheckoutPlugin) => void;
6
+ list: () => readonly CheckoutPlugin[];
7
+ }
8
+ /**
9
+ * The gateway's checkout plugins, the way `ExecutionService.use` seats its own: a plugin with an
10
+ * alias already registered replaces it in place (a layer wired twice must not answer twice), one
11
+ * without an alias is appended.
12
+ */
13
+ export declare const makeCheckoutPluginRegistry: () => CheckoutPluginRegistry;
14
+ /**
15
+ * An entity's amount policy as every plugin narrows it now — the ONE computation both
16
+ * `gateway.amountPolicy` and checkout enforcement use, so a control and a refusal cannot disagree.
17
+ * A plugin's error propagates: a narrowing that cannot be computed fails closed.
18
+ */
19
+ export declare const narrowAmountFor: (ctx: ApiContext, plugins: readonly CheckoutPlugin[], input: CheckoutNarrowInput) => Promise<AmountPolicyView>;
20
+ /**
21
+ * Refuse an amount the narrowed view does not allow: any amount while `blocked`, else one above
22
+ * the narrowed maximum.
23
+ *
24
+ * @throws CheckoutLimitExceeded (409)
25
+ */
26
+ export declare const assertAmountAllowed: (view: AmountPolicyView, amountMinor: number) => void;
27
+ /**
28
+ * The session lifetime: the smallest `sessionTtlSeconds` any plugin declares, clamped to Stripe's
29
+ * 30 minutes – 24 hours; `undefined` when none declares one (Stripe's own default).
30
+ */
31
+ export declare const sessionTtlOf: (plugins: readonly CheckoutPlugin[]) => number | undefined;
32
+ /** A plugin that admitted an attempt, with the reservation it holds. */
33
+ export interface Admitted {
34
+ plugin: CheckoutPlugin;
35
+ reservationId?: string;
36
+ }
37
+ /**
38
+ * Tell plugins how a checkout ended. A plugin's error is logged, never raised: a hold carries its
39
+ * own TTL, and the webhook that reports the outcome must not be redelivered for it.
40
+ */
41
+ export declare const settleCheckout: (ctx: ApiContext, plugins: readonly CheckoutPlugin[], settled: CheckoutSettled) => Promise<void>;
42
+ /** Release what admitted plugins hold for a checkout that never became usable. */
43
+ export declare const releaseAdmitted: (ctx: ApiContext, admitted: readonly Admitted[], attempt: CheckoutAttempt, sessionId?: string) => Promise<void>;
44
+ /**
45
+ * Ask every plugin to admit the attempt, in order. A veto releases what the earlier plugins
46
+ * admitted and propagates.
47
+ */
48
+ export declare const admitCheckout: (ctx: ApiContext, plugins: readonly CheckoutPlugin[], attempt: CheckoutAttempt) => Promise<Admitted[]>;
49
+ //# sourceMappingURL=checkout-plugins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkout-plugins.d.ts","sourceRoot":"","sources":["../../src/plugins/checkout-plugins.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAmB,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAC1E,OAAO,KAAK,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAEjE,OAAO,KAAK,EACV,eAAe,EAAE,mBAAmB,EAAE,cAAc,EAAE,eAAe,EACtE,MAAM,aAAa,CAAA;AAEpB,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAA;IACrC,IAAI,EAAE,MAAM,SAAS,cAAc,EAAE,CAAA;CACtC;AAED;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,QAAO,sBAc7C,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,QACrB,UAAU,WAAW,SAAS,cAAc,EAAE,SAAS,mBAAmB,KAC9E,OAAO,CAAC,gBAAgB,CAe1B,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,SAAU,gBAAgB,eAAe,MAAM,KAAG,IASjF,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,YAAa,SAAS,cAAc,EAAE,KAAG,MAAM,GAAG,SAS1E,CAAA;AAED,wEAAwE;AACxE,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,cAAc,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,QACpB,UAAU,WAAW,SAAS,cAAc,EAAE,WAAW,eAAe,KAC5E,OAAO,CAAC,IAAI,CAWd,CAAA;AAED,kFAAkF;AAClF,eAAO,MAAM,eAAe,QACrB,UAAU,YAAY,SAAS,QAAQ,EAAE,WAAW,eAAe,cAAc,MAAM,KAC3F,OAAO,CAAC,IAAI,CAWd,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,QACnB,UAAU,WAAW,SAAS,cAAc,EAAE,WAAW,eAAe,KAC5E,OAAO,CAAC,QAAQ,EAAE,CAiBpB,CAAA"}
@@ -0,0 +1,124 @@
1
+ import { CheckoutLimitExceeded, narrowAmountPolicy } from '@owlmeans/payment';
2
+ import { STRIPE_SESSION_TTL_MAX_SECONDS, STRIPE_SESSION_TTL_MIN_SECONDS } from '../consts.js';
3
+ /**
4
+ * The gateway's checkout plugins, the way `ExecutionService.use` seats its own: a plugin with an
5
+ * alias already registered replaces it in place (a layer wired twice must not answer twice), one
6
+ * without an alias is appended.
7
+ */
8
+ export const makeCheckoutPluginRegistry = () => {
9
+ const plugins = [];
10
+ return {
11
+ use: plugin => {
12
+ const at = plugin.alias != null ? plugins.findIndex(entry => entry.alias === plugin.alias) : -1;
13
+ if (at < 0) {
14
+ plugins.push(plugin);
15
+ }
16
+ else {
17
+ plugins[at] = plugin;
18
+ }
19
+ },
20
+ list: () => [...plugins],
21
+ };
22
+ };
23
+ /**
24
+ * An entity's amount policy as every plugin narrows it now — the ONE computation both
25
+ * `gateway.amountPolicy` and checkout enforcement use, so a control and a refusal cannot disagree.
26
+ * A plugin's error propagates: a narrowing that cannot be computed fails closed.
27
+ */
28
+ export const narrowAmountFor = async (ctx, plugins, input) => {
29
+ const narrowings = [];
30
+ for (const plugin of plugins) {
31
+ if (plugin.narrow == null) {
32
+ continue;
33
+ }
34
+ const narrowing = await plugin.narrow(ctx, input);
35
+ if (narrowing != null) {
36
+ narrowings.push(narrowing);
37
+ }
38
+ }
39
+ return narrowAmountPolicy(input.base, narrowings, {
40
+ productSku: input.productSku, ...(input.planSku != null ? { planSku: input.planSku } : {}),
41
+ });
42
+ };
43
+ /**
44
+ * Refuse an amount the narrowed view does not allow: any amount while `blocked`, else one above
45
+ * the narrowed maximum.
46
+ *
47
+ * @throws CheckoutLimitExceeded (409)
48
+ */
49
+ export const assertAmountAllowed = (view, amountMinor) => {
50
+ const limit = view.limit;
51
+ if (limit == null || (!limit.blocked && amountMinor <= limit.maximumMinor)) {
52
+ return;
53
+ }
54
+ throw new CheckoutLimitExceeded({
55
+ reason: limit.reason ?? 'limit', maximumMinor: limit.maximumMinor, currency: limit.currency,
56
+ ...(limit.resetsAt != null ? { resetsAt: limit.resetsAt } : {}),
57
+ });
58
+ };
59
+ /**
60
+ * The session lifetime: the smallest `sessionTtlSeconds` any plugin declares, clamped to Stripe's
61
+ * 30 minutes – 24 hours; `undefined` when none declares one (Stripe's own default).
62
+ */
63
+ export const sessionTtlOf = (plugins) => {
64
+ const declared = plugins
65
+ .map(plugin => plugin.sessionTtlSeconds)
66
+ .filter((ttl) => typeof ttl === 'number' && Number.isFinite(ttl));
67
+ if (declared.length === 0) {
68
+ return undefined;
69
+ }
70
+ return Math.min(STRIPE_SESSION_TTL_MAX_SECONDS, Math.max(STRIPE_SESSION_TTL_MIN_SECONDS, Math.floor(Math.min(...declared))));
71
+ };
72
+ /**
73
+ * Tell plugins how a checkout ended. A plugin's error is logged, never raised: a hold carries its
74
+ * own TTL, and the webhook that reports the outcome must not be redelivered for it.
75
+ */
76
+ export const settleCheckout = async (ctx, plugins, settled) => {
77
+ for (const plugin of plugins) {
78
+ if (plugin.settled == null) {
79
+ continue;
80
+ }
81
+ try {
82
+ await plugin.settled(ctx, settled);
83
+ }
84
+ catch (error) {
85
+ console.error(`[payment] checkout plugin "${plugin.alias ?? 'anonymous'}" failed to settle`, error);
86
+ }
87
+ }
88
+ };
89
+ /** Release what admitted plugins hold for a checkout that never became usable. */
90
+ export const releaseAdmitted = async (ctx, admitted, attempt, sessionId) => {
91
+ for (const { plugin, reservationId } of admitted) {
92
+ await settleCheckout(ctx, [plugin], {
93
+ entityId: attempt.entityId, productSku: attempt.productSku,
94
+ ...(attempt.planSku != null ? { planSku: attempt.planSku } : {}),
95
+ ...(sessionId != null ? { sessionId } : {}),
96
+ ...(reservationId != null ? { reservationId } : {}),
97
+ ...(attempt.amountMinor != null ? { amountMinor: attempt.amountMinor } : {}),
98
+ outcome: 'failed', at: new Date(),
99
+ });
100
+ }
101
+ };
102
+ /**
103
+ * Ask every plugin to admit the attempt, in order. A veto releases what the earlier plugins
104
+ * admitted and propagates.
105
+ */
106
+ export const admitCheckout = async (ctx, plugins, attempt) => {
107
+ const admitted = [];
108
+ for (const plugin of plugins) {
109
+ if (plugin.admit == null) {
110
+ admitted.push({ plugin });
111
+ continue;
112
+ }
113
+ try {
114
+ const admission = await plugin.admit(ctx, attempt);
115
+ admitted.push({ plugin, ...(admission?.reservationId != null ? { reservationId: admission.reservationId } : {}) });
116
+ }
117
+ catch (error) {
118
+ await releaseAdmitted(ctx, admitted.filter(entry => entry.plugin.admit != null), attempt);
119
+ throw error;
120
+ }
121
+ }
122
+ return admitted;
123
+ };
124
+ //# sourceMappingURL=checkout-plugins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkout-plugins.js","sourceRoot":"","sources":["../../src/plugins/checkout-plugins.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import type Stripe from 'stripe';
2
+ import type { PriceEstimate } from '@owlmeans/payment';
3
+ import type { Context as ApiContext } from '@owlmeans/server-api';
4
+ import type { PriceEstimateParams } from '../types.js';
5
+ interface CacheEntry<T> {
6
+ value: T;
7
+ expiresAt: number;
8
+ }
9
+ export interface EstimateCache {
10
+ taxTtlMs: number;
11
+ fxTtlMs: number;
12
+ customerTtlMs: number;
13
+ tax: Map<string, CacheEntry<TaxOutcome>>;
14
+ fx: Map<string, CacheEntry<FxOutcome>>;
15
+ customer: Map<string, CacheEntry<Stripe.Customer | null>>;
16
+ inflight: Map<string, Promise<unknown>>;
17
+ }
18
+ /** @param taxTtlMs default 24h; @param fxTtlMs default 1h; @param customerTtlMs default 5min. */
19
+ export declare const makeEstimateCache: (taxTtlMs?: number, fxTtlMs?: number, customerTtlMs?: number) => EstimateCache;
20
+ type TaxOutcome = {
21
+ ok: true;
22
+ calculation: Stripe.Tax.Calculation;
23
+ } | {
24
+ ok: false;
25
+ code?: string;
26
+ };
27
+ type FxOutcome = {
28
+ currency: string;
29
+ exchangeRate: number;
30
+ fxFeeRate?: number;
31
+ } | null;
32
+ /**
33
+ * A Stripe Tax estimate (and, when the pricing policy asks for it, a local-currency conversion) for
34
+ * one product/plan, at a billing country the request names or the entity's paygate customer does.
35
+ *
36
+ * Costs one Tax Calculation API call ($0.05) per distinct (currency, country, amount, tax code,
37
+ * behavior, matching tax ids) within `cache`'s TTL, and one free FX Quotes call per distinct
38
+ * (currency, local currency) — never more, and never for a rate-limit or connection error, which is
39
+ * never cached.
40
+ */
41
+ export declare const estimateStripePrice: (ctx: ApiContext, stripe: Stripe, params: PriceEstimateParams, cache: EstimateCache) => Promise<PriceEstimate>;
42
+ export {};
43
+ //# sourceMappingURL=estimate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"estimate.d.ts","sourceRoot":"","sources":["../../src/plugins/estimate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAA;AAKhC,OAAO,KAAK,EAAE,aAAa,EAAgC,MAAM,mBAAmB,CAAA;AACpF,OAAO,KAAK,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAOjE,OAAO,KAAK,EAA+B,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAQnF,UAAU,UAAU,CAAC,CAAC;IAAI,KAAK,EAAE,CAAC,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE;AAEvD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,aAAa,EAAE,MAAM,CAAA;IACrB,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,CAAA;IACxC,EAAE,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,CAAA;IACtC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAA;IACzD,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAA;CACxC;AAED,iGAAiG;AACjG,eAAO,MAAM,iBAAiB,cAClB,MAAM,YAAkC,MAAM,kBACzC,MAAM,KACpB,aAED,CAAA;AAwDF,KAAK,UAAU,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC,GAAG,CAAC,WAAW,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAoGlG,KAAK,SAAS,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAA;AAwBtF;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,QACzB,UAAU,UAAU,MAAM,UAAU,mBAAmB,SAAS,aAAa,KACjF,OAAO,CAAC,aAAa,CA4GvB,CAAA"}