@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/resource.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { makeMongoResource } from '@owlmeans/mongo-resource';
2
- import { RES_PAYGATE_CUSTOMER, RES_PAYMENT_FINGERPRINT, RES_PAYMENT_SUBSCRIPTION } from './consts.js';
3
- import { FingerprintSchema, PaygateCustomerSchema, PaymentSubscriptionSchema } from './model.js';
2
+ import { RES_BILLING_PROFILE, RES_CONSUMER_CONSENT, RES_CONSUMER_DECLARATION, RES_CONSUMER_EVENT, RES_PAYGATE_CUSTOMER, RES_PAYMENT_FINGERPRINT, RES_PAYMENT_FULFILLMENT, RES_PAYMENT_PURCHASE, RES_PAYMENT_SUBSCRIPTION, RES_PAYMENT_USAGE, RES_PAYMENT_USAGE_COUNTER, RES_PAYMENT_WEBHOOK, } from './consts.js';
3
+ import { BillingProfileSchema, ConsumerConsentSchema, ConsumerDeclarationSchema, ConsumerEventSchema, FingerprintSchema, PaygateCustomerSchema, PaymentFulfillmentSchema, PaymentSubscriptionSchema, PaymentUsageCounterSchema, PaymentUsageSchema, PaymentWebhookSchema, PurchaseSchema, } from './model.js';
4
4
  export const makePaygateCustomerResource = (dbAlias, serviceAlias) => {
5
5
  const resource = makeMongoResource(RES_PAYGATE_CUSTOMER, dbAlias, serviceAlias);
6
6
  resource.loadByPgId = async (externalId, paygate) => await resource.load({ externalId, paygate });
@@ -14,11 +14,45 @@ export const makePaygateCustomerResource = (dbAlias, serviceAlias) => {
14
14
  export const makeSubscriptionResource = (dbAlias, serviceAlias) => {
15
15
  const resource = makeMongoResource(RES_PAYMENT_SUBSCRIPTION, dbAlias, serviceAlias);
16
16
  resource.byExternalId = async (externalId, paygate) => await resource.load({ externalId, paygate });
17
+ resource.byItemId = async (itemId, paygate) => await resource.load({ itemId, paygate });
17
18
  resource.schema = PaymentSubscriptionSchema;
18
- resource.index('entity', { entityId: 1 });
19
- resource.index('entitySku', { entityId: 1, sku: 1 });
20
- resource.index('external', { externalId: 1, paygate: 1 }, { unique: true });
21
- resource.index('status', { status: 1 });
19
+ resource.index('external', { paygate: 1, externalId: 1 }, { unique: true });
20
+ resource.index('entityStatusRank', { entityId: 1, status: 1, rank: -1 });
21
+ resource.index('entityPlan', { entityId: 1, planSku: 1 });
22
+ resource.index('customer', { paygate: 1, customerId: 1 });
23
+ resource.index('item', { paygate: 1, itemId: 1 }, { sparse: true });
24
+ resource.index('statusUpdated', { paygate: 1, status: 1, updatedAt: 1 });
25
+ return resource;
26
+ };
27
+ export const makeFulfillmentResource = (dbAlias, serviceAlias) => {
28
+ const resource = makeMongoResource(RES_PAYMENT_FULFILLMENT, dbAlias, serviceAlias);
29
+ resource.byExternalId = async (externalId, paygate) => await resource.load({ externalId, paygate });
30
+ resource.schema = PaymentFulfillmentSchema;
31
+ resource.index('external', { paygate: 1, externalId: 1 }, { unique: true });
32
+ resource.index('paymentIntent', { paygate: 1, paymentIntentId: 1 }, { sparse: true });
33
+ resource.index('charge', { paygate: 1, chargeId: 1 }, { sparse: true });
34
+ resource.index('entityCreated', { entityId: 1, createdAt: -1 });
35
+ return resource;
36
+ };
37
+ export const makeWebhookResource = (dbAlias, serviceAlias) => {
38
+ const resource = makeMongoResource(RES_PAYMENT_WEBHOOK, dbAlias, serviceAlias);
39
+ resource.schema = PaymentWebhookSchema;
40
+ resource.index('endpoint', { paygate: 1, service: 1, url: 1 }, { unique: true });
41
+ return resource;
42
+ };
43
+ export const makeUsageResource = (dbAlias, serviceAlias) => {
44
+ const resource = makeMongoResource(RES_PAYMENT_USAGE, dbAlias, serviceAlias);
45
+ resource.schema = PaymentUsageSchema;
46
+ resource.index('event', { entityId: 1, limitKey: 1, eventKey: 1 }, { unique: true });
47
+ resource.index('window', { entityId: 1, limitKey: 1, window: 1 });
48
+ resource.index('ref', { entityId: 1, limitKey: 1, ref: 1 }, { sparse: true });
49
+ resource.index('entityCreated', { entityId: 1, createdAt: -1 });
50
+ return resource;
51
+ };
52
+ export const makeUsageCounterResource = (dbAlias, serviceAlias) => {
53
+ const resource = makeMongoResource(RES_PAYMENT_USAGE_COUNTER, dbAlias, serviceAlias);
54
+ resource.schema = PaymentUsageCounterSchema;
55
+ resource.index('window', { entityId: 1, limitKey: 1, window: 1 }, { unique: true });
22
56
  return resource;
23
57
  };
24
58
  export const makeFingerprintResource = (dbAlias, serviceAlias) => {
@@ -29,4 +63,61 @@ export const makeFingerprintResource = (dbAlias, serviceAlias) => {
29
63
  resource.index('sku', { sku: 1 }, { unique: true });
30
64
  return resource;
31
65
  };
66
+ export const makeBillingProfileResource = (dbAlias, serviceAlias) => {
67
+ const resource = makeMongoResource(RES_BILLING_PROFILE, dbAlias, serviceAlias);
68
+ resource.byEntity = async (entityId) => await resource.load({ entityId });
69
+ resource.schema = BillingProfileSchema;
70
+ // One profile per organization: the unique index is what makes the first lock the only one.
71
+ resource.index('entity', { entityId: 1 }, { unique: true });
72
+ resource.index('customer', { paygate: 1, customerId: 1 }, { sparse: true });
73
+ return resource;
74
+ };
75
+ export const makePurchaseResource = (dbAlias, serviceAlias) => {
76
+ const resource = makeMongoResource(RES_PAYMENT_PURCHASE, dbAlias, serviceAlias);
77
+ resource.byPurchaseId = async (purchaseId) => await resource.load({ purchaseId });
78
+ resource.schema = PurchaseSchema;
79
+ resource.index('purchase', { purchaseId: 1 }, { unique: true });
80
+ resource.index('contract', { contractRef: 1 }, { unique: true });
81
+ resource.index('entityDeadline', { entityId: 1, deadline: -1 });
82
+ resource.index('entityPurchased', { entityId: 1, purchasedAt: -1 });
83
+ // Single-field on purpose: a compound sparse index still indexes rows missing only `sessionId`
84
+ // (subscription purchases before their checkout is captured), and unique would collide on them.
85
+ resource.index('session', { sessionId: 1 }, { unique: true, sparse: true });
86
+ resource.index('subscription', { paygate: 1, subscriptionId: 1 }, { sparse: true });
87
+ resource.index('invoice', { invoiceId: 1 }, { sparse: true });
88
+ resource.index('invoiceNumber', { invoiceNumber: 1 }, { sparse: true });
89
+ resource.index('paymentIntent', { paygate: 1, paymentIntentId: 1 }, { sparse: true });
90
+ return resource;
91
+ };
92
+ export const makeConsumerConsentResource = (dbAlias, serviceAlias) => {
93
+ const resource = makeMongoResource(RES_CONSUMER_CONSENT, dbAlias, serviceAlias);
94
+ resource.schema = ConsumerConsentSchema;
95
+ resource.index('entityDecided', { entityId: 1, decidedAt: -1 });
96
+ resource.index('kindPlan', { kind: 1, entityId: 1, planSku: 1, decidedAt: -1 });
97
+ return resource;
98
+ };
99
+ export const makeConsumerDeclarationResource = (dbAlias, serviceAlias) => {
100
+ const resource = makeMongoResource(RES_CONSUMER_DECLARATION, dbAlias, serviceAlias);
101
+ resource.schema = ConsumerDeclarationSchema;
102
+ resource.index('kindReceived', { kind: 1, receivedAt: -1 });
103
+ resource.index('entityReceived', { entityId: 1, receivedAt: -1 }, { sparse: true });
104
+ resource.index('purchase', { purchaseId: 1 }, { sparse: true });
105
+ return resource;
106
+ };
107
+ export const makeConsumerEventResource = (dbAlias, serviceAlias) => {
108
+ const resource = makeMongoResource(RES_CONSUMER_EVENT, dbAlias, serviceAlias);
109
+ resource.schema = ConsumerEventSchema;
110
+ resource.index('record', { recordId: 1, at: 1 });
111
+ resource.index('entityAt', { entityId: 1, at: -1 }, { sparse: true });
112
+ resource.index('actionOk', { action: 1, ok: 1, at: 1 });
113
+ return resource;
114
+ };
115
+ /** The consumer-rights resources, alias → maker — registered by the gateway, each only when absent. */
116
+ export const CONSUMER_RIGHTS_RESOURCE_MAKERS = [
117
+ [RES_BILLING_PROFILE, makeBillingProfileResource],
118
+ [RES_PAYMENT_PURCHASE, makePurchaseResource],
119
+ [RES_CONSUMER_CONSENT, makeConsumerConsentResource],
120
+ [RES_CONSUMER_DECLARATION, makeConsumerDeclarationResource],
121
+ [RES_CONSUMER_EVENT, makeConsumerEventResource],
122
+ ];
32
123
  //# sourceMappingURL=resource.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resource.js","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resource.js","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,29 @@
1
- import type { Config, Context, GatewayService, PaymentResourceOptions } from './types.js';
2
- export declare const makeGatewayService: (alias?: string) => GatewayService;
3
- export declare const appendPaymentGatewayService: <C extends Config, T extends Context<C>>(ctx: T, opts?: PaymentResourceOptions) => T;
1
+ import type Stripe from 'stripe';
2
+ import type { Context as ApiContext } from '@owlmeans/server-api';
3
+ import type { Config, Context, GatewayService, GrantInternalPlanOptions, PaymentGatewayOptions, PaymentSubscriptionRecord } from './types.js';
4
+ export interface StripeBootstrapOptions {
5
+ /** Re-verify what the stored fingerprints say is in place. */
6
+ force?: boolean;
7
+ }
8
+ /**
9
+ * Bring Stripe to what this deployment declares, each step on its own so one failing does not block
10
+ * the others: products and prices, the customer-portal configuration, the webhook endpoint.
11
+ */
12
+ export declare const bootstrapStripe: (ctx: ApiContext, stripe: Stripe, opts?: StripeBootstrapOptions) => Promise<void>;
13
+ /**
14
+ * Grant a plan without a paygate — the free plan every entity holds, or a complimentary one
15
+ * (`force`). One row per grant (`free:<entityId>` / `internal:<planSku>:<entityId>`), upserted: its
16
+ * `createdAt` survives a re-grant, and observers hear `created` once.
17
+ */
18
+ export declare const grantInternalPlan: (ctx: ApiContext, entityId: string, planSku: string, opts?: GrantInternalPlanOptions) => Promise<PaymentSubscriptionRecord>;
19
+ export declare const makeGatewayService: (alias?: string, opts?: Pick<PaymentGatewayOptions, 'manage'>) => GatewayService;
20
+ /**
21
+ * Register the payment resources, the catalogue service, the completion observer, the gateway, both
22
+ * gate services, the entitlement service and the consumer-rights records and service — each only
23
+ * when not registered yet.
24
+ *
25
+ * `manage: false` registers the same surface for a process that reads entitlements but never talks
26
+ * to Stripe.
27
+ */
28
+ export declare const appendPaymentGatewayService: <C extends Config, T extends Context<C>>(ctx: T, opts?: PaymentGatewayOptions) => T;
4
29
  //# sourceMappingURL=service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAEzF,eAAO,MAAM,kBAAkB,WAAW,MAAM,KAAqB,cAUpE,CAAA;AAED,eAAO,MAAM,2BAA2B,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,OAC3E,CAAC,SAAS,sBAAsB,KACpC,CASF,CAAA"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAA;AAMhC,OAAO,KAAK,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAyBjE,OAAO,KAAK,EACV,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,wBAAwB,EAAE,qBAAqB,EAChF,yBAAyB,EAC1B,MAAM,YAAY,CAAA;AAEnB,MAAM,WAAW,sBAAsB;IACrC,8DAA8D;IAC9D,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,QACrB,UAAU,UAAU,MAAM,SAAQ,sBAAsB,KAC5D,OAAO,CAAC,IAAI,CAad,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,QACvB,UAAU,YAAY,MAAM,WAAW,MAAM,SAAQ,wBAAwB,KACjF,OAAO,CAAC,yBAAyB,CA+BnC,CAAA;AAID,eAAO,MAAM,kBAAkB,WACtB,MAAM,SAA0B,IAAI,CAAC,qBAAqB,EAAE,QAAQ,CAAC,KAC3E,cA6CF,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,OAC3E,CAAC,SAAS,qBAAqB,KACnC,CA2BF,CAAA"}
package/build/service.js CHANGED
@@ -1,35 +1,162 @@
1
1
  import { createService } from '@owlmeans/context';
2
- import { DEFAULT_ALIAS as PAYMENT_SERVICE, ENTITLEMENT_GATE, appendPaymentService } from '@owlmeans/payment';
3
- import { GATEWAY_SERVICE, RES_PAYGATE_CUSTOMER, RES_PAYMENT_FINGERPRINT, RES_PAYMENT_SUBSCRIPTION } from './consts.js';
4
- import { makeEntitlementGate } from './gate.js';
2
+ import { appendPaymentService, DEFAULT_ALIAS as PAYMENT_SERVICE, ENTITLEMENT_GATE, INTERNAL_PAYGATE, LIMIT_GATE, PaygateError, ProductError, SubscriptionStatus, UnknownPlan, } from '@owlmeans/payment';
3
+ import { assertPlanDeclarations } from './config.js';
4
+ import { ENTITLEMENT_SERVICE, GATEWAY_SERVICE, RES_PAYGATE_CUSTOMER, RES_PAYMENT_FINGERPRINT, RES_PAYMENT_FULFILLMENT, RES_PAYMENT_SUBSCRIPTION, RES_PAYMENT_USAGE, RES_PAYMENT_USAGE_COUNTER, RES_PAYMENT_WEBHOOK, } from './consts.js';
5
+ import { registerConsumerRights } from './consumer/service.js';
6
+ import { makeEntitlementService } from './entitlement.js';
7
+ import { makeCapabilityGate } from './gate.js';
8
+ import { makeLimitGate } from './limit.js';
5
9
  import { appendCompletionObserver } from './observer.js';
6
- import { stripePlugin } from './plugins/stripe.js';
7
- import { makeFingerprintResource, makePaygateCustomerResource, makeSubscriptionResource } from './resource.js';
8
- import { initialize } from './sync.js';
9
- export const makeGatewayService = (alias = GATEWAY_SERVICE) => {
10
+ import { findPlan, findProduct, planRank } from './plan.js';
11
+ import { resyncStripeSubscription, resyncStripeSubscriptions } from './plugins/events.js';
12
+ import { makeEstimateCache, estimateStripePrice } from './plugins/estimate.js';
13
+ import { createPortalLink, ensurePortalConfiguration } from './plugins/portal.js';
14
+ import { makeCheckoutPluginRegistry, narrowAmountFor } from './plugins/checkout-plugins.js';
15
+ import { consumablePlanOf, createCheckoutLink } from './plugins/stripe.js';
16
+ import { ensureWebhookEndpoint } from './plugins/webhook-manager.js';
17
+ import { makeFingerprintResource, makeFulfillmentResource, makePaygateCustomerResource, makeSubscriptionResource, makeUsageCounterResource, makeUsageResource, makeWebhookResource, } from './resource.js';
18
+ import { commitSubscription } from './subscription.js';
19
+ import { syncedPlanPrices, syncStripeProducts } from './sync.js';
20
+ import { consumerRightsOf, stripeClient, subscriptions } from './utils.js';
21
+ /**
22
+ * Bring Stripe to what this deployment declares, each step on its own so one failing does not block
23
+ * the others: products and prices, the customer-portal configuration, the webhook endpoint.
24
+ */
25
+ export const bootstrapStripe = async (ctx, stripe, opts = {}) => {
26
+ const steps = [
27
+ ['products', async () => await syncStripeProducts(ctx, stripe)],
28
+ ['portal', async () => await ensurePortalConfiguration(ctx, stripe, opts)],
29
+ ['webhook', async () => await ensureWebhookEndpoint(ctx, stripe, opts)],
30
+ ];
31
+ for (const [name, step] of steps) {
32
+ try {
33
+ await step();
34
+ }
35
+ catch (error) {
36
+ console.error(`[payment] Stripe ${name} bootstrap failed`, error);
37
+ }
38
+ }
39
+ };
40
+ /**
41
+ * Grant a plan without a paygate — the free plan every entity holds, or a complimentary one
42
+ * (`force`). One row per grant (`free:<entityId>` / `internal:<planSku>:<entityId>`), upserted: its
43
+ * `createdAt` survives a re-grant, and observers hear `created` once.
44
+ */
45
+ export const grantInternalPlan = async (ctx, entityId, planSku, opts = {}) => {
46
+ const plan = await findPlan(ctx, planSku);
47
+ if (plan == null) {
48
+ throw new UnknownPlan(planSku);
49
+ }
50
+ if (plan.free !== true && opts.force !== true) {
51
+ throw new ProductError(`internal-grant:${planSku}`);
52
+ }
53
+ const product = await findProduct(ctx, plan.productSku);
54
+ const externalId = plan.free === true ? `free:${entityId}` : `internal:${planSku}:${entityId}`;
55
+ const previous = await subscriptions(ctx).byExternalId(externalId, INTERNAL_PAYGATE);
56
+ const now = new Date();
57
+ const { periodEnd: _periodEnd, endedAt: _endedAt, canceledAt: _canceledAt, ...kept } = previous ?? {};
58
+ const next = {
59
+ ...kept,
60
+ entityId,
61
+ planSku,
62
+ productSku: plan.productSku,
63
+ service: previous?.service ?? product?.services?.[0] ?? ctx.cfg.service,
64
+ paygate: INTERNAL_PAYGATE,
65
+ externalId,
66
+ status: SubscriptionStatus.Active,
67
+ rank: planRank(plan),
68
+ ...(opts.periodEnd != null ? { periodEnd: opts.periodEnd } : {}),
69
+ createdAt: previous?.createdAt ?? now,
70
+ updatedAt: now,
71
+ };
72
+ const { record } = await commitSubscription(ctx, previous, next);
73
+ return record ?? next;
74
+ };
75
+ const unmanaged = () => { throw new PaygateError('unmanaged'); };
76
+ export const makeGatewayService = (alias = GATEWAY_SERVICE, opts = {}) => {
77
+ const managed = opts.manage !== false;
78
+ // One estimate cache per gateway SERVICE instance, never module-level: several service
79
+ // instances (several tests, several deployments in one process) must never share hits.
80
+ const estimateCache = makeEstimateCache();
81
+ // Checkout plugins are seated per gateway instance, like the estimate cache.
82
+ const plugins = makeCheckoutPluginRegistry();
10
83
  const service = createService(alias, {
11
- createLink: async (ctx, params) => stripePlugin.createLink(ctx, params),
12
- manageSubscription: async (ctx, entityId, returnUrl) => stripePlugin.manageSubscription(ctx, entityId, returnUrl),
84
+ managed,
85
+ createLink: async (ctx, params) => managed
86
+ ? await createCheckoutLink(ctx, await stripeClient(ctx), params, plugins.list()) : unmanaged(),
87
+ use: plugin => { plugins.use(plugin); },
88
+ checkoutPlugins: () => plugins.list(),
89
+ amountPolicy: async (ctx, entityId, productSku, planSku) => {
90
+ const { plan } = await consumablePlanOf(ctx, productSku, planSku);
91
+ if (plan.amountPolicy == null)
92
+ throw new ProductError(`amount-policy:${plan.sku}`);
93
+ return await narrowAmountFor(ctx, plugins.list(), {
94
+ entityId, productSku, planSku: plan.sku, base: plan.amountPolicy, at: new Date(),
95
+ });
96
+ },
97
+ planPrices: async (ctx, productSku) => await syncedPlanPrices(ctx, productSku),
98
+ portalLink: async (ctx, entityId, link) => managed
99
+ ? await createPortalLink(ctx, await stripeClient(ctx), entityId, link) : unmanaged(),
100
+ grantInternalPlan: async (ctx, entityId, planSku, grant) => await grantInternalPlan(ctx, entityId, planSku, grant),
101
+ resyncSubscription: async (ctx, ref) => managed
102
+ ? await resyncStripeSubscription(ctx, await stripeClient(ctx), ref) : unmanaged(),
103
+ resyncAll: async (ctx) => managed
104
+ ? await resyncStripeSubscriptions(ctx, await stripeClient(ctx)) : unmanaged(),
105
+ estimatePrice: async (ctx, params) => managed
106
+ ? await estimateStripePrice(ctx, await stripeClient(ctx), params, estimateCache) : unmanaged(),
13
107
  }, service => async () => {
14
- service.initialized = true;
15
108
  const ctx = service.assertCtx();
16
- ctx.waitForInitialized().then(() => initialize(ctx).catch(console.error));
109
+ assertPlanDeclarations(ctx.cfg);
110
+ service.initialized = true;
111
+ // The consumer-rights service is lazy (reachable while the application is wired): initialize it
112
+ // with the gateway, so its boot checks run at boot.
113
+ consumerRightsOf(ctx);
114
+ if (managed) {
115
+ void ctx.waitForInitialized().then(async () => {
116
+ await bootstrapStripe(ctx, await stripeClient(ctx));
117
+ }).catch(error => { console.error('[payment] Stripe bootstrap failed', error); });
118
+ }
17
119
  });
18
120
  return service;
19
121
  };
122
+ /**
123
+ * Register the payment resources, the catalogue service, the completion observer, the gateway, both
124
+ * gate services, the entitlement service and the consumer-rights records and service — each only
125
+ * when not registered yet.
126
+ *
127
+ * `manage: false` registers the same surface for a process that reads entitlements but never talks
128
+ * to Stripe.
129
+ */
20
130
  export const appendPaymentGatewayService = (ctx, opts) => {
21
- if (!ctx.hasResource(RES_PAYGATE_CUSTOMER))
22
- ctx.registerResource(makePaygateCustomerResource(opts?.dbAlias, opts?.serviceAlias));
23
- if (!ctx.hasResource(RES_PAYMENT_SUBSCRIPTION))
24
- ctx.registerResource(makeSubscriptionResource(opts?.dbAlias, opts?.serviceAlias));
25
- if (!ctx.hasResource(RES_PAYMENT_FINGERPRINT))
26
- ctx.registerResource(makeFingerprintResource(opts?.dbAlias, opts?.serviceAlias));
131
+ const makers = [
132
+ [RES_PAYGATE_CUSTOMER, makePaygateCustomerResource],
133
+ [RES_PAYMENT_SUBSCRIPTION, makeSubscriptionResource],
134
+ [RES_PAYMENT_FULFILLMENT, makeFulfillmentResource],
135
+ [RES_PAYMENT_WEBHOOK, makeWebhookResource],
136
+ [RES_PAYMENT_USAGE, makeUsageResource],
137
+ [RES_PAYMENT_USAGE_COUNTER, makeUsageCounterResource],
138
+ [RES_PAYMENT_FINGERPRINT, makeFingerprintResource],
139
+ ];
140
+ for (const [alias, maker] of makers) {
141
+ if (!ctx.hasResource(alias)) {
142
+ ctx.registerResource(maker(opts?.dbAlias, opts?.serviceAlias));
143
+ }
144
+ }
27
145
  if (!ctx.hasService(PAYMENT_SERVICE))
28
146
  appendPaymentService(ctx);
29
147
  appendCompletionObserver(ctx);
30
- ctx.registerService(makeGatewayService());
148
+ if (!ctx.hasService(GATEWAY_SERVICE))
149
+ ctx.registerService(makeGatewayService(GATEWAY_SERVICE, { manage: opts?.manage }));
31
150
  if (!ctx.hasService(ENTITLEMENT_GATE))
32
- ctx.registerService(makeEntitlementGate());
151
+ ctx.registerService(makeCapabilityGate());
152
+ if (!ctx.hasService(LIMIT_GATE))
153
+ ctx.registerService(makeLimitGate());
154
+ if (!ctx.hasService(ENTITLEMENT_SERVICE))
155
+ ctx.registerService(makeEntitlementService());
156
+ // The consumer-rights records and service, with this gateway's `manage` unless the application
157
+ // gave its own (before or after this call): the webhook writes purchases and locks, and an
158
+ // unmanaged process still reads and asserts consent.
159
+ registerConsumerRights(ctx, { manage: opts?.manage, dbAlias: opts?.dbAlias, serviceAlias: opts?.serviceAlias }, 'gateway');
33
160
  return ctx;
34
161
  };
35
162
  //# sourceMappingURL=service.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAE,aAAa,IAAI,eAAe,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAE5G,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AACtH,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAC/C,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAA;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,uBAAuB,EAAE,2BAA2B,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAA;AAC9G,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAGtC,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,KAAK,GAAW,eAAe,EAAkB,EAAE;IACpF,MAAM,OAAO,GAAG,aAAa,CAAiB,KAAK,EAAE;QACnD,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;QACvE,kBAAkB,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,CAAC,YAAY,CAAC,kBAAkB,CAAC,GAAG,EAAE,QAAQ,EAAE,SAAS,CAAC;KAClH,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE;QACvB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAA;QAC1B,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,EAAgB,CAAA;QAC7C,GAAG,CAAC,kBAAkB,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;IAC3E,CAAC,CAAC,CAAA;IACF,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,GAAM,EAAE,IAA6B,EAClC,EAAE;IACL,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,oBAAoB,CAAC;QAAE,GAAG,CAAC,gBAAgB,CAAC,2BAA2B,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC,CAAA;IAChI,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,wBAAwB,CAAC;QAAE,GAAG,CAAC,gBAAgB,CAAC,wBAAwB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC,CAAA;IACjI,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,uBAAuB,CAAC;QAAE,GAAG,CAAC,gBAAgB,CAAC,uBAAuB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC,CAAA;IAC/H,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,eAAe,CAAC;QAAE,oBAAoB,CAAC,GAAY,CAAC,CAAA;IACxE,wBAAwB,CAAC,GAAG,CAAC,CAAA;IAC7B,GAAG,CAAC,eAAe,CAAC,kBAAkB,EAAE,CAAC,CAAA;IACzC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,gBAAgB,CAAC;QAAE,GAAG,CAAC,eAAe,CAAC,mBAAmB,EAAE,CAAC,CAAA;IACjF,OAAO,GAAG,CAAA;AACZ,CAAC,CAAA"}
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type { Context as ApiContext } from '@owlmeans/server-api';
2
+ import type { PaymentSubscriptionRecord, PropagatedState, SubscriptionChange, SubscriptionSnapshot } from './types.js';
3
+ export interface CommitOptions {
4
+ /** The paygate event being applied; a repeat of the last applied one is ignored. */
5
+ eventId?: string;
6
+ /** A paid renewal invoice. */
7
+ renewal?: boolean;
8
+ invoiceId?: string;
9
+ /** The paygate announced the trial ends soon. */
10
+ trialEnding?: boolean;
11
+ /**
12
+ * Runs after the new state is written and BEFORE observers hear the change — what must exist
13
+ * before an observer grants anything (a subscription's purchase row). A throw propagates: the
14
+ * observers are not told and the paygate retries.
15
+ */
16
+ beforePropagate?: (record: PaymentSubscriptionRecord, change: SubscriptionChange) => Promise<void>;
17
+ }
18
+ export interface CommitResult {
19
+ record: PaymentSubscriptionRecord | null;
20
+ change: SubscriptionChange | null;
21
+ /** The stored subscription state changed. */
22
+ updated: boolean;
23
+ }
24
+ /** The inputs classification compares. */
25
+ export declare const propagatedStateOf: (record: PaymentSubscriptionRecord, renewedInvoiceId?: string) => PropagatedState;
26
+ /**
27
+ * What one subscription change means — the first rule that matches:
28
+ * `created` (first entitling state ever propagated), `canceled`, `paused`, `resumed`,
29
+ * `upgraded`/`downgraded` (plan change by rank; equal rank reads as an upgrade),
30
+ * `cancel-scheduled`, `cancel-undone`, `renewed` (once per invoice), `past-due`, `suspended`,
31
+ * `trial-ending`; otherwise nothing an observer is told about.
32
+ */
33
+ export declare const classifySubscriptionChange: (previous: PropagatedState | null, current: PaymentSubscriptionRecord, opts?: CommitOptions) => SubscriptionChange | null;
34
+ /** The idempotency key a consumer keys the side effect of a change by. */
35
+ export declare const subscriptionEventKey: (record: PaymentSubscriptionRecord, change: SubscriptionChange, opts?: CommitOptions) => string;
36
+ /** A subscription as observers see it; `state` overlays what was last propagated. */
37
+ export declare const snapshotOf: (ctx: ApiContext, record: PaymentSubscriptionRecord, state?: PropagatedState) => Promise<SubscriptionSnapshot>;
38
+ export declare const materiallyDiffers: (previous: PaymentSubscriptionRecord, next: PaymentSubscriptionRecord) => boolean;
39
+ /**
40
+ * Store a subscription's new state, tell observers what changed, then record what was told.
41
+ *
42
+ * The state is written BEFORE observers run, so an observer reading entitlements sees the new plan.
43
+ * What classification compares against is the state last PROPAGATED (`propagated`), stamped only
44
+ * after every observer succeeded — so an observer that throws leaves the change to be classified
45
+ * again, identically, when the paygate retries. `lastEventId` is stamped with it.
46
+ */
47
+ export declare const commitSubscription: (ctx: ApiContext, previous: PaymentSubscriptionRecord | null, next: PaymentSubscriptionRecord, opts?: CommitOptions) => Promise<CommitResult>;
48
+ //# sourceMappingURL=subscription.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscription.d.ts","sourceRoot":"","sources":["../src/subscription.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAGjE,OAAO,KAAK,EACV,yBAAyB,EAAE,eAAe,EAAE,kBAAkB,EAAE,oBAAoB,EACrF,MAAM,YAAY,CAAA;AAEnB,MAAM,WAAW,aAAa;IAC5B,oFAAoF;IACpF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,iDAAiD;IACjD,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,yBAAyB,EAAE,MAAM,EAAE,kBAAkB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACnG;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,yBAAyB,GAAG,IAAI,CAAA;IACxC,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAAA;IACjC,6CAA6C;IAC7C,OAAO,EAAE,OAAO,CAAA;CACjB;AAMD,0CAA0C;AAC1C,eAAO,MAAM,iBAAiB,WAAY,yBAAyB,qBAAqB,MAAM,KAAG,eAO/F,CAAA;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,aAC3B,eAAe,GAAG,IAAI,WAAW,yBAAyB,SAAQ,aAAa,KACxF,kBAAkB,GAAG,IAsCvB,CAAA;AAED,0EAA0E;AAC1E,eAAO,MAAM,oBAAoB,WACvB,yBAAyB,UAAU,kBAAkB,SAAQ,aAAa,KACjF,MAeF,CAAA;AAED,qFAAqF;AACrF,eAAO,MAAM,UAAU,QAChB,UAAU,UAAU,yBAAyB,UAAU,eAAe,KAC1E,OAAO,CAAC,oBAAoB,CAsB9B,CAAA;AAmBD,eAAO,MAAM,iBAAiB,aAAc,yBAAyB,QAAQ,yBAAyB,KAAG,OACvC,CAAA;AAElE;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,QACxB,UAAU,YAAY,yBAAyB,GAAG,IAAI,QAAQ,yBAAyB,SACtF,aAAa,KAClB,OAAO,CAAC,YAAY,CAqDtB,CAAA"}
@@ -0,0 +1,176 @@
1
+ import { ENTITLING_STATUSES, SubscriptionStatus } from '@owlmeans/payment';
2
+ import { findPlan } from './plan.js';
3
+ import { compact, isDuplicateKey, observer, subscriptions } from './utils.js';
4
+ const ENDED = [SubscriptionStatus.Canceled, SubscriptionStatus.Ended];
5
+ const timeOf = (value) => value == null ? null : new Date(value).getTime();
6
+ /** The inputs classification compares. */
7
+ export const propagatedStateOf = (record, renewedInvoiceId) => compact({
8
+ planSku: record.planSku,
9
+ rank: record.rank,
10
+ status: record.status,
11
+ cancelAtPeriodEnd: record.cancelAtPeriodEnd === true,
12
+ pausedAt: record.pausedAt ?? undefined,
13
+ renewedInvoiceId: renewedInvoiceId ?? undefined,
14
+ });
15
+ /**
16
+ * What one subscription change means — the first rule that matches:
17
+ * `created` (first entitling state ever propagated), `canceled`, `paused`, `resumed`,
18
+ * `upgraded`/`downgraded` (plan change by rank; equal rank reads as an upgrade),
19
+ * `cancel-scheduled`, `cancel-undone`, `renewed` (once per invoice), `past-due`, `suspended`,
20
+ * `trial-ending`; otherwise nothing an observer is told about.
21
+ */
22
+ export const classifySubscriptionChange = (previous, current, opts = {}) => {
23
+ const active = ENTITLING_STATUSES.includes(current.status);
24
+ if (previous == null) {
25
+ return active ? 'created' : null;
26
+ }
27
+ if (ENDED.includes(current.status) && !ENDED.includes(previous.status)) {
28
+ return 'canceled';
29
+ }
30
+ if (current.pausedAt != null && previous.pausedAt == null) {
31
+ return 'paused';
32
+ }
33
+ if ((current.pausedAt == null && previous.pausedAt != null)
34
+ || (previous.status === SubscriptionStatus.Suspended && active)) {
35
+ return 'resumed';
36
+ }
37
+ if (current.planSku !== previous.planSku) {
38
+ return current.rank < previous.rank ? 'downgraded' : 'upgraded';
39
+ }
40
+ if (previous.cancelAtPeriodEnd !== true && current.cancelAtPeriodEnd === true) {
41
+ return 'cancel-scheduled';
42
+ }
43
+ if (previous.cancelAtPeriodEnd === true && current.cancelAtPeriodEnd !== true) {
44
+ return 'cancel-undone';
45
+ }
46
+ if (opts.renewal === true && opts.invoiceId != null && opts.invoiceId !== previous.renewedInvoiceId) {
47
+ return 'renewed';
48
+ }
49
+ if (current.status === SubscriptionStatus.PastDue && previous.status !== SubscriptionStatus.PastDue) {
50
+ return 'past-due';
51
+ }
52
+ if (current.status === SubscriptionStatus.Suspended && previous.status !== SubscriptionStatus.Suspended) {
53
+ return 'suspended';
54
+ }
55
+ if (opts.trialEnding === true) {
56
+ return 'trial-ending';
57
+ }
58
+ return null;
59
+ };
60
+ /** The idempotency key a consumer keys the side effect of a change by. */
61
+ export const subscriptionEventKey = (record, change, opts = {}) => {
62
+ const base = `subscription:${record.externalId}:${change}`;
63
+ switch (change) {
64
+ case 'created':
65
+ return `${base}:${new Date(record.createdAt).toISOString()}`;
66
+ case 'renewed':
67
+ return `${base}:${opts.invoiceId ?? record.latestInvoiceId ?? 'unknown'}`;
68
+ case 'upgraded':
69
+ case 'downgraded':
70
+ return `${base}:${record.planSku}`;
71
+ case 'trial-ending':
72
+ return `${base}:${record.trialEnd != null ? new Date(record.trialEnd).toISOString() : 'unknown'}`;
73
+ default:
74
+ return `${base}:${opts.eventId ?? `sync-${new Date(record.updatedAt ?? Date.now()).toISOString()}`}`;
75
+ }
76
+ };
77
+ /** A subscription as observers see it; `state` overlays what was last propagated. */
78
+ export const snapshotOf = async (ctx, record, state) => {
79
+ const planSku = state?.planSku ?? record.planSku;
80
+ const plan = await findPlan(ctx, planSku);
81
+ return compact({
82
+ entityId: record.entityId,
83
+ planSku,
84
+ productSku: plan?.productSku ?? record.productSku,
85
+ rank: state?.rank ?? record.rank,
86
+ status: state?.status ?? record.status,
87
+ paygate: record.paygate,
88
+ subscriptionId: record.externalId,
89
+ service: record.service,
90
+ periodStart: record.periodStart ?? undefined,
91
+ periodEnd: record.periodEnd ?? undefined,
92
+ cancelAtPeriodEnd: state != null ? state.cancelAtPeriodEnd : record.cancelAtPeriodEnd ?? undefined,
93
+ trialEnd: record.trialEnd ?? undefined,
94
+ pausedAt: state != null ? state.pausedAt : record.pausedAt ?? undefined,
95
+ createdAt: record.createdAt ?? undefined,
96
+ capabilities: plan?.capabilities,
97
+ limits: plan?.limits,
98
+ });
99
+ };
100
+ const MATERIAL = [
101
+ 'entityId', 'planSku', 'productSku', 'service', 'itemId', 'priceId', 'status', 'externalStatus', 'rank',
102
+ 'periodStart', 'periodEnd', 'cancelAtPeriodEnd', 'canceledAt', 'endedAt', 'pausedAt', 'trialEnd',
103
+ 'latestInvoiceId', 'customerId',
104
+ ];
105
+ const sameValue = (left, right) => {
106
+ if (left == null || right == null) {
107
+ return left == null && right == null;
108
+ }
109
+ if (left instanceof Date || right instanceof Date) {
110
+ return timeOf(left) === timeOf(right);
111
+ }
112
+ return left === right;
113
+ };
114
+ export const materiallyDiffers = (previous, next) => MATERIAL.some(field => !sameValue(previous[field], next[field]));
115
+ /**
116
+ * Store a subscription's new state, tell observers what changed, then record what was told.
117
+ *
118
+ * The state is written BEFORE observers run, so an observer reading entitlements sees the new plan.
119
+ * What classification compares against is the state last PROPAGATED (`propagated`), stamped only
120
+ * after every observer succeeded — so an observer that throws leaves the change to be classified
121
+ * again, identically, when the paygate retries. `lastEventId` is stamped with it.
122
+ */
123
+ export const commitSubscription = async (ctx, previous, next, opts = {}) => {
124
+ const resource = subscriptions(ctx);
125
+ const updated = previous == null || materiallyDiffers(previous, next);
126
+ const prior = previous?.propagated ?? null;
127
+ const change = classifySubscriptionChange(prior, next, opts);
128
+ const at = new Date();
129
+ const finalize = (record) => {
130
+ const tracked = prior != null || change === 'created';
131
+ return {
132
+ ...record,
133
+ ...(opts.eventId != null ? { lastEventId: opts.eventId } : {}),
134
+ ...(tracked ? {
135
+ propagated: propagatedStateOf(record, change === 'renewed' ? opts.invoiceId : prior?.renewedInvoiceId),
136
+ } : {}),
137
+ ...(change === 'created' && record.initialPropagatedAt == null ? { initialPropagatedAt: at } : {}),
138
+ };
139
+ };
140
+ const write = async (record) => {
141
+ if (record.id != null) {
142
+ return await resource.update(record);
143
+ }
144
+ try {
145
+ return await resource.create(record);
146
+ }
147
+ catch (error) {
148
+ if (!isDuplicateKey(error)) {
149
+ throw error;
150
+ }
151
+ const winner = await resource.byExternalId(record.externalId, record.paygate);
152
+ return await resource.update({ ...record, id: winner?.id });
153
+ }
154
+ };
155
+ if (change == null) {
156
+ const quiet = previous != null && !updated && (opts.eventId == null || previous.lastEventId === opts.eventId)
157
+ && (prior == null || !stateDiffers(prior, next));
158
+ return { record: quiet ? previous : await write(finalize(next)), change: null, updated };
159
+ }
160
+ const stored = await write(next);
161
+ await opts.beforePropagate?.(stored, change);
162
+ await observer(ctx).propagateSubscription({
163
+ change,
164
+ previous: prior != null && previous != null ? await snapshotOf(ctx, previous, prior) : null,
165
+ current: await snapshotOf(ctx, stored),
166
+ active: ENTITLING_STATUSES.includes(stored.status),
167
+ eventKey: subscriptionEventKey(stored, change, opts),
168
+ ...(opts.invoiceId != null ? { invoiceId: opts.invoiceId } : {}),
169
+ ...(opts.eventId != null ? { externalEventId: opts.eventId } : {}),
170
+ }, ctx);
171
+ return { record: await resource.update(finalize(stored)), change, updated };
172
+ };
173
+ const stateDiffers = (state, record) => state.planSku !== record.planSku || state.rank !== record.rank || state.status !== record.status
174
+ || (state.cancelAtPeriodEnd === true) !== (record.cancelAtPeriodEnd === true)
175
+ || timeOf(state.pausedAt) !== timeOf(record.pausedAt);
176
+ //# sourceMappingURL=subscription.js.map