@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/README.md CHANGED
@@ -1,42 +1,126 @@
1
1
  # @owlmeans/server-payment
2
2
 
3
- Public server-side payment primitives for OwlMeans applications. The package registers payment
4
- resources, the entitlement gate, protocol-bound webhook/resync entrypoints, and a Stripe gateway
5
- supporting amount-priced consumables, quantity-priced consumables, and subscriptions.
3
+ Server-side payments for OwlMeans applications: a Stripe gateway embedded in the backend
4
+ (amount and quantity checkout, subscriptions, the customer portal), a subscription store that
5
+ understands the whole Stripe lifecycle, a usage ledger for counted plan limits, the entitlement
6
+ service that resolves what an organization entity may do, and the two gate services that refuse a
7
+ request before its handler runs. The contracts — plans, limits, promos, the entitlement view, the
8
+ refusal errors — live in `@owlmeans/payment`.
6
9
 
7
- Applications declare products and plans during configuration, mount `paymentGateEntrypoints`, and
8
- register completion callbacks on the payment observer. Amount policies are validated at declaration
9
- time. Stripe Checkout is always authoritative: completion is idempotent by session id and an
10
- amount checkout is fulfilled only after its paid currency and subtotal match its signed-in metadata.
10
+ ## Declare the catalogue
11
11
 
12
12
  ```typescript
13
- import { CheckoutPricingMode, PlanDuration, ProductType } from '@owlmeans/payment'
13
+ import { CheckoutPricingMode, LimitKind, LimitWindow, PlanDuration, ProductType } from '@owlmeans/payment'
14
14
  import {
15
- appendPaymentGatewayService, declarePaymentPlan, declarePaymentProduct, observer,
15
+ declarePaymentPlan, declarePaymentProduct, portalBranding, stripeSecrets,
16
16
  } from '@owlmeans/server-payment'
17
17
 
18
- declarePaymentProduct(cfg, {
19
- sku: 'app-credits', type: ProductType.Consumable, services: ['app'], gateways: ['stripe'],
20
- name: 'Credits',
18
+ stripeSecrets(cfg, { api: '/secrets/stripe-key' }) // `webhook` is an optional override
19
+ portalBranding(cfg, { returnUrl: 'https://app.example.com/billing', headline: 'Example' })
20
+
21
+ declarePaymentProduct(cfg, { sku: 'app-plans', type: ProductType.Service, services: ['app'], name: 'Plans' })
22
+ declarePaymentPlan(cfg, {
23
+ productSku: 'app-plans', sku: 'free', duration: PlanDuration.Monthly, rank: 0, free: true, price: 0,
24
+ capabilities: [{ scope: 'feature', permissions: { basic: true } }],
25
+ limits: { seats: { kind: LimitKind.Occupancy, limit: 1 }, exports: { kind: LimitKind.Window, window: LimitWindow.Month, limit: 3 } },
21
26
  })
22
27
  declarePaymentPlan(cfg, {
23
- productSku: 'app-credits', sku: 'app-credit-unit', duration: PlanDuration.Consumable,
24
- price: 0.02, pricingMode: CheckoutPricingMode.Amount,
25
- amountPolicy: {
26
- currency: 'usd', minimumMinor: 500, maximumMinor: 50_000, defaultMinor: 1_000,
27
- presetsMinor: [1_000, 2_000, 5_000, 10_000], fixedMinor: 0, rateBps: 200,
28
- },
28
+ productSku: 'app-plans', sku: 'pro-monthly', duration: PlanDuration.Monthly, rank: 10, price: 20,
29
+ recurring: { interval: 'month' },
30
+ capabilities: [{ scope: 'feature', permissions: { basic: true, whitelabel: true } }],
31
+ limits: { seats: { kind: LimitKind.Occupancy, limit: 5 }, exports: { kind: LimitKind.Window, window: LimitWindow.Day, limit: 20 } },
29
32
  })
30
33
 
31
- appendPaymentGatewayService(context)
32
- observer(context).onTopUp(async completion => {
33
- if (completion.mode === 'amount') await credit(completion.entityId, completion.amountMinor)
34
+ declarePaymentProduct(cfg, { sku: 'app-credits', type: ProductType.Consumable, services: ['app'], name: 'Credits' })
35
+ declarePaymentPlan(cfg, {
36
+ productSku: 'app-credits', sku: 'app-credit-unit', duration: PlanDuration.Consumable, price: 0.02,
37
+ pricingMode: CheckoutPricingMode.Amount,
38
+ amountPolicy: { currency: 'usd', minimumMinor: 500, maximumMinor: 50_000, defaultMinor: 1_000,
39
+ presetsMinor: [1_000, 5_000], fixedMinor: 0, rateBps: 200 },
34
40
  })
35
41
  ```
36
42
 
37
- Mount `paymentGateEntrypoints` in the server entrypoint list. `gateway(context).createLink(...)`
38
- accepts the stable organization `entityId` only after the application has resolved it at its
39
- authenticated boundary; public protocol bodies use `entitySlug`.
43
+ `declarePaymentPlan` validates each declaration before recording it; `assertPlanDeclarations` checks
44
+ the catalogue as a whole (one free plan per rank, no two paid plans of a product at one rank) and runs
45
+ when the gateway initializes. A limit key may use a different kind on different plans — each kind keeps
46
+ its own counter window, so a lifetime count still sticks to the entity when it moves to a monthly plan.
47
+
48
+ ## Wire the services
49
+
50
+ ```typescript
51
+ import {
52
+ appendPaymentGatewayService, entitlements, gateway, observer, paymentGateEntrypoints,
53
+ } from '@owlmeans/server-payment'
54
+
55
+ appendPaymentGatewayService(context) // the process that owns Stripe
56
+ appendPaymentGatewayService(context, { manage: false }) // a process that only reads entitlements
57
+ export const serverBindings = [...paymentGateEntrypoints]
58
+
59
+ observer(context).onTopUp(async completion => { /* credit, idempotent by completion.externalId */ })
60
+ observer(context).onSubscription(async event => { /* idempotent by event.eventKey */ })
61
+ observer(context).onRefund(async event => { /* claw back, idempotent by event.eventKey */ })
62
+ observer(context).onDispute(async event => { /* … */ })
63
+ observer(context).onPaymentFailed(async event => { /* notify */ })
64
+ ```
65
+
66
+ A managed gateway brings Stripe to the declared state at boot — products and prices, one
67
+ customer-portal configuration, one webhook endpoint at this deployment's public URL (its signing
68
+ secret stored in `payment-webhook`) — and each step is fingerprinted, so an unchanged deployment
69
+ makes no Stripe call.
70
+
71
+ Several deployments of one service may share a Stripe account (typically every test-mode
72
+ deployment), each with its own database and URL — and a deployment's identity is its webhook URL,
73
+ even an undeliverable local one.
74
+
75
+ - **Webhook endpoint.** A deployment owns exactly the endpoint at its own URL: it deletes an
76
+ endpoint only when its own `payment-webhook` rows name it — the endpoint of a URL it has moved away
77
+ from — and never one it merely finds at another URL, whatever its metadata says. The endpoint of a
78
+ retired deployment is removed by hand in the Stripe dashboard; one deleted from outside comes back
79
+ on the next forced `resync`.
80
+ - **Portal configuration.** Each deployment has its own, tagged
81
+ `{ owlmeans: 'payment', service, deployment: <webhook URL> }`. It updates the configuration its
82
+ `portal:<service>` fingerprint row names, unless that one is tagged for another deployment, and
83
+ without a row adopts only a configuration carrying exactly its own tag. Stripe cannot delete a
84
+ portal configuration, so one a deployment can no longer identify stays in the account and a new
85
+ one is created.
86
+
87
+ ## Use it
88
+
89
+ ```typescript
90
+ // Checkout and the portal (in-process; resolve the stable entityId at your authenticated boundary)
91
+ const url = await gateway(ctx).createLink(ctx, { productSku: 'app-plans', planSku: 'pro-monthly', entityId, service: 'app', successUrl })
92
+ const portal = await gateway(ctx).portalLink(ctx, entityId, { flow: PortalFlow.Change, planSku: 'pro-monthly', returnUrl })
93
+ await gateway(ctx).grantInternalPlan(ctx, entityId, 'free') // when the organization is created
94
+
95
+ // What the entity may do
96
+ const view = await entitlements(ctx).entitlements(entityId) // the EntitlementView a UI renders
97
+ await entitlements(ctx).hasCapability(entityId, 'feature:whitelabel')
98
+
99
+ // Spend a counted allowance — key it by the record it pays for
100
+ await entitlements(ctx).consume({ entityId, limitKey: 'exports', eventKey: `export:${reportId}`, ref: reportId })
101
+ await entitlements(ctx).release({ entityId, limitKey: 'exports', eventKey: `export:${reportId}` })
102
+
103
+ // Periodic repair
104
+ await reconcileAll(ctx, { freePlanSku: 'free' })
105
+ await entitlements(ctx).reconcileOccupancy(entityId, 'seats', liveSeatCount)
106
+ ```
107
+
108
+ Routes declare what they need; the gates refuse before the handler:
109
+
110
+ ```typescript
111
+ protocol(route(...), contract(...), { gate: { alias: ENTITLEMENT_GATE, params: ['feature:whitelabel'] } })
112
+ protocol(route(...), contract(...), { gate: { alias: LIMIT_GATE, params: [formatLimitParam('seats')] } })
113
+ ```
114
+
115
+ `CapabilityRequired` and `LimitExhausted` extend `AuthForbidden`, so an HTTP boundary answers 403.
116
+ The limit gate never consumes; the handler does.
117
+
118
+ ## Routes
119
+
120
+ `paymentGateEntrypoints` binds the immutable `paymentGate` tree: the public Stripe webhook
121
+ (`POST /payment-gate/webhook/:paygate`, verified by signature over the raw body), and two
122
+ Ed25519-guarded repairs — `resync` (products, portal, webhook endpoint) and `resyncSubscriptions`
123
+ (re-reads every live Stripe subscription, answering `{ scanned, updated }`).
40
124
 
41
125
  <!-- owlmeans:agent-guidance:start -->
42
126
  ## Agent guidance
@@ -46,7 +130,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
46
130
  your project's skill store (`.agents/skills/`):
47
131
 
48
132
  ```sh
49
- npx @owlmeans/agent-skills@^0.1.18-rc.17
133
+ npx @owlmeans/agent-skills@^0.1.18-rc.38
50
134
  ```
51
135
 
52
136
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/server-payment",
4
- "version": "0.1.18-rc.2",
5
- "generatedAt": "2026-09-12T12:59:24.916Z",
4
+ "version": "0.1.18-rc.21",
5
+ "generatedAt": "2026-09-23T19:57:40.693Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {