@owlmeans/server-payment 0.1.18-rc.20 → 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 (144) hide show
  1. package/README.md +1 -1
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/server-payment/SKILL.md +402 -260
  4. package/build/config.d.ts +15 -1
  5. package/build/config.d.ts.map +1 -1
  6. package/build/config.js +99 -3
  7. package/build/config.js.map +1 -1
  8. package/build/consts.d.ts +27 -0
  9. package/build/consts.d.ts.map +1 -1
  10. package/build/consts.js +27 -0
  11. package/build/consts.js.map +1 -1
  12. package/build/consumer/capture.d.ts +74 -0
  13. package/build/consumer/capture.d.ts.map +1 -0
  14. package/build/consumer/capture.js +291 -0
  15. package/build/consumer/capture.js.map +1 -0
  16. package/build/consumer/format.d.ts +27 -0
  17. package/build/consumer/format.d.ts.map +1 -0
  18. package/build/consumer/format.js +81 -0
  19. package/build/consumer/format.js.map +1 -0
  20. package/build/consumer/handlers.d.ts +28 -0
  21. package/build/consumer/handlers.d.ts.map +1 -0
  22. package/build/consumer/handlers.js +173 -0
  23. package/build/consumer/handlers.js.map +1 -0
  24. package/build/consumer/index.d.ts +7 -0
  25. package/build/consumer/index.d.ts.map +1 -0
  26. package/build/consumer/index.js +6 -0
  27. package/build/consumer/index.js.map +1 -0
  28. package/build/consumer/mail.d.ts +27 -0
  29. package/build/consumer/mail.d.ts.map +1 -0
  30. package/build/consumer/mail.js +314 -0
  31. package/build/consumer/mail.js.map +1 -0
  32. package/build/consumer/origin.d.ts +14 -0
  33. package/build/consumer/origin.d.ts.map +1 -0
  34. package/build/consumer/origin.js +47 -0
  35. package/build/consumer/origin.js.map +1 -0
  36. package/build/consumer/reconcile.d.ts +12 -0
  37. package/build/consumer/reconcile.d.ts.map +1 -0
  38. package/build/consumer/reconcile.js +317 -0
  39. package/build/consumer/reconcile.js.map +1 -0
  40. package/build/consumer/records.d.ts +78 -0
  41. package/build/consumer/records.d.ts.map +1 -0
  42. package/build/consumer/records.js +296 -0
  43. package/build/consumer/records.js.map +1 -0
  44. package/build/consumer/service.d.ts +51 -0
  45. package/build/consumer/service.d.ts.map +1 -0
  46. package/build/consumer/service.js +760 -0
  47. package/build/consumer/service.js.map +1 -0
  48. package/build/consumer/withdrawal.d.ts +57 -0
  49. package/build/consumer/withdrawal.d.ts.map +1 -0
  50. package/build/consumer/withdrawal.js +247 -0
  51. package/build/consumer/withdrawal.js.map +1 -0
  52. package/build/index.d.ts +4 -2
  53. package/build/index.d.ts.map +1 -1
  54. package/build/index.js +4 -2
  55. package/build/index.js.map +1 -1
  56. package/build/model.d.ts +6 -1
  57. package/build/model.d.ts.map +1 -1
  58. package/build/model.js +134 -4
  59. package/build/model.js.map +1 -1
  60. package/build/observer.d.ts +4 -0
  61. package/build/observer.d.ts.map +1 -1
  62. package/build/observer.js +13 -0
  63. package/build/observer.js.map +1 -1
  64. package/build/plugins/checkout-plugins.d.ts +49 -0
  65. package/build/plugins/checkout-plugins.d.ts.map +1 -0
  66. package/build/plugins/checkout-plugins.js +124 -0
  67. package/build/plugins/checkout-plugins.js.map +1 -0
  68. package/build/plugins/estimate.d.ts.map +1 -1
  69. package/build/plugins/estimate.js +41 -8
  70. package/build/plugins/estimate.js.map +1 -1
  71. package/build/plugins/events.d.ts +2 -0
  72. package/build/plugins/events.d.ts.map +1 -1
  73. package/build/plugins/events.js +126 -9
  74. package/build/plugins/events.js.map +1 -1
  75. package/build/plugins/fx.d.ts +5 -0
  76. package/build/plugins/fx.d.ts.map +1 -1
  77. package/build/plugins/fx.js +25 -0
  78. package/build/plugins/fx.js.map +1 -1
  79. package/build/plugins/portal.d.ts.map +1 -1
  80. package/build/plugins/portal.js +11 -1
  81. package/build/plugins/portal.js.map +1 -1
  82. package/build/plugins/stripe.d.ts +21 -2
  83. package/build/plugins/stripe.d.ts.map +1 -1
  84. package/build/plugins/stripe.js +397 -61
  85. package/build/plugins/stripe.js.map +1 -1
  86. package/build/resource.d.ts +8 -1
  87. package/build/resource.d.ts.map +1 -1
  88. package/build/resource.js +59 -2
  89. package/build/resource.js.map +1 -1
  90. package/build/service.d.ts +2 -1
  91. package/build/service.d.ts.map +1 -1
  92. package/build/service.js +28 -5
  93. package/build/service.js.map +1 -1
  94. package/build/subscription.d.ts +6 -0
  95. package/build/subscription.d.ts.map +1 -1
  96. package/build/subscription.js +1 -0
  97. package/build/subscription.js.map +1 -1
  98. package/build/sync.d.ts +7 -0
  99. package/build/sync.d.ts.map +1 -1
  100. package/build/sync.js +108 -15
  101. package/build/sync.js.map +1 -1
  102. package/build/types.d.ts +707 -4
  103. package/build/types.d.ts.map +1 -1
  104. package/build/utils.d.ts +22 -1
  105. package/build/utils.d.ts.map +1 -1
  106. package/build/utils.js +27 -1
  107. package/build/utils.js.map +1 -1
  108. package/package.json +14 -13
  109. package/src/config.ts +111 -7
  110. package/src/consts.ts +34 -0
  111. package/src/consumer/capture.ts +362 -0
  112. package/src/consumer/format.ts +90 -0
  113. package/src/consumer/handlers.ts +211 -0
  114. package/src/consumer/index.ts +6 -0
  115. package/src/consumer/mail.ts +368 -0
  116. package/src/consumer/origin.ts +63 -0
  117. package/src/consumer/reconcile.ts +329 -0
  118. package/src/consumer/records.ts +374 -0
  119. package/src/consumer/service.ts +868 -0
  120. package/src/consumer/withdrawal.ts +302 -0
  121. package/src/index.ts +6 -4
  122. package/src/model.ts +148 -6
  123. package/src/observer.ts +15 -2
  124. package/src/plugins/checkout-plugins.ts +155 -0
  125. package/src/plugins/estimate.ts +49 -9
  126. package/src/plugins/events.ts +135 -11
  127. package/src/plugins/fx.ts +29 -0
  128. package/src/plugins/portal.ts +11 -1
  129. package/src/plugins/stripe.ts +476 -60
  130. package/src/resource.ts +87 -4
  131. package/src/service.ts +28 -6
  132. package/src/subscription.ts +7 -0
  133. package/src/sync.ts +124 -17
  134. package/src/types.ts +756 -6
  135. package/src/utils.ts +56 -7
  136. package/tests/checkout-consumer.spec.ts +348 -0
  137. package/tests/checkout-plugins.spec.ts +164 -0
  138. package/tests/consumer-events.spec.ts +218 -0
  139. package/tests/consumer-fixtures.ts +132 -0
  140. package/tests/consumer-ops.spec.ts +351 -0
  141. package/tests/consumer-rights.integration.spec.ts +150 -0
  142. package/tests/consumer-rights.spec.ts +501 -0
  143. package/tests/context.ts +20 -2
  144. package/tests/fake-stripe.ts +188 -18
package/src/types.ts CHANGED
@@ -1,13 +1,21 @@
1
+ import type Stripe from 'stripe'
1
2
  import type { InitializedService, LazyService } from '@owlmeans/context'
2
3
  import type { ResourceRecord } from '@owlmeans/resource'
3
4
  import type { MongoResource } from '@owlmeans/mongo-resource'
4
5
  import type { PluginConfig } from '@owlmeans/config'
5
6
  import type { PermissionSet } from '@owlmeans/auth'
7
+ import type { AbstractRequest } from '@owlmeans/entrypoint'
8
+ import type { MailMessage } from '@owlmeans/mailer'
6
9
  import type { Config as ApiConfig, Context as ApiContext } from '@owlmeans/server-api'
7
10
  import type {
8
- AmountCheckoutPolicy, CheckoutPricingMode, EntitlementView, LimitDeclaration, LimitView,
9
- PlanCapability, PlanDuration, PortalFlow, PriceEstimate, PricingPolicy, Product, ProductPlan,
10
- ProductType, QuantityCheckoutPolicy, SubscriptionStatus,
11
+ AmountCheckoutPolicy, AmountNarrowing, AmountPolicyView, BillingProfileView, CancellationBody,
12
+ CancellationKind, CancellationReceipt, CancellationStatus, CheckoutPricingMode, ConsentKind, ConsumerRegion,
13
+ ConsumerRightsDeclaration, ConsumerRightsLinks, ConsumerRightsPolicy, DeclarationChannel, DeclarationKind,
14
+ EntitlementView, LimitDeclaration, LimitView, PerformanceConsentBody, PerformanceConsentResponse,
15
+ PerformanceConsentView, PlanCapability, PlanDuration, PlanPriceView, PlanWithdrawalComponent, PortalFlow,
16
+ PriceEstimate, PricingPolicy, Product, ProductPlan, ProductType, PurchaseKind, PurchaseView,
17
+ QuantityCheckoutPolicy, SubscriptionStartBody, SubscriptionStartResponse, SubscriptionStartView,
18
+ SubscriptionStatus, WithdrawalBody, WithdrawalCandidateList, WithdrawalReceipt, WithdrawalStatus,
11
19
  } from '@owlmeans/payment'
12
20
 
13
21
  export interface Config extends ApiConfig {}
@@ -30,6 +38,11 @@ export interface PaymentPlan extends ProductPlan {
30
38
  pricingMode?: CheckoutPricingMode
31
39
  amountPolicy?: AmountCheckoutPolicy
32
40
  quantityPolicy?: QuantityCheckoutPolicy
41
+ /**
42
+ * Exact prices in further currencies, major units by lowercase ISO 4217 code (`{ usd: 20 }`).
43
+ * Synced as the reusable Price's `currency_options`, never converted.
44
+ */
45
+ currencyPrices?: Record<string, number>
33
46
  }
34
47
 
35
48
  export interface PaymentProductDef {
@@ -70,6 +83,17 @@ export interface PaymentPlanDef {
70
83
  minQuantity?: number
71
84
  maxQuantity?: number
72
85
  defaultQuantity?: number
86
+ /**
87
+ * Exact prices in further currencies, major units by ISO 4217 code (`{ usd: 20 }`): a recurring
88
+ * or quantity plan's synced Price carries each as a `currency_options` entry at exactly this
89
+ * amount, while its default currency (the settlement currency) is converted as before.
90
+ */
91
+ currencyPrices?: Record<string, number>
92
+ /**
93
+ * The separately priced parts of a subscription for a withdrawal (CJEU C-641/19): their
94
+ * `shareMinor` sum to `round(price × 100)`. Absent: the whole price is one `time` component.
95
+ */
96
+ withdrawal?: { components: PlanWithdrawalComponent[] }
73
97
  }
74
98
 
75
99
  /** File paths (or values) of the Stripe secrets. The webhook secret is an optional override. */
@@ -102,6 +126,38 @@ export interface PricingDef extends PricingPolicy {
102
126
  stripe?: StripePricingDef
103
127
  }
104
128
 
129
+ /**
130
+ * The business the consumer contracts with — backend only, never advertised. `name` is what the
131
+ * express statements say (`{{trader}}` in a consent or start request); the mails' identity line,
132
+ * the withdrawal information and the model form ("To: …") say `legalName`, `address` and `email`.
133
+ */
134
+ // A type alias, not an interface: it is stored inside a plugin config record, whose values must be
135
+ // index-signature compatible.
136
+ export type TraderDef = {
137
+ name: string
138
+ legalName: string
139
+ /** Postal address; without it (or `email`) the mails render without it and the boot warns. */
140
+ address?: string
141
+ email?: string
142
+ website?: string
143
+ }
144
+
145
+ /** The consumer-rights mail options — backend only, never advertised. */
146
+ export interface ConsumerMailDef {
147
+ /** The mailer service alias. Default `MAILER_SERVICE`. */
148
+ alias?: string
149
+ from?: string
150
+ replyTo?: string
151
+ /** Evidence archive addresses: each receives its own copy of every consumer-rights mail. */
152
+ bcc?: string[]
153
+ }
154
+ export interface ConsumerMailPluginConfig extends PluginConfig, ConsumerMailDef {
155
+ trader?: TraderDef
156
+ }
157
+
158
+ /** `declareConsumerRights`'s argument: the advertised policy plus the backend-only trader and mail options. */
159
+ export type ConsumerRightsDef = ConsumerRightsDeclaration & { trader?: TraderDef, mail?: ConsumerMailDef }
160
+
105
161
  /** What the managed customer portal configuration shows. */
106
162
  export interface PortalBrandingDef {
107
163
  headline?: string
@@ -126,10 +182,116 @@ export interface CreateLinkParams {
126
182
  amountMinor?: number
127
183
  /** Stripe Checkout and future paid-invoice language. Caller supplies a Stripe-supported locale. */
128
184
  locale?: string
129
- /** Trusted application copy shown beside the Checkout confirmation button. */
130
- submitText?: string
185
+ /**
186
+ * Trusted application copy shown beside the Checkout confirmation button — never caller-provided
187
+ * text. A function receives the charge currency and the price the session charges.
188
+ */
189
+ submitText?: string | ((context: CheckoutTextContext) => string)
131
190
  successUrl?: string
132
191
  cancelUrl?: string
192
+ /** The billing country the buyer declared (ISO 3166-1 alpha-2). A locked profile overrides it. */
193
+ country?: string
194
+ /** The subscription start request recorded right before a subscription checkout. */
195
+ startRequestId?: string
196
+ /** The language the buyer is shown legal copy in; default: the billing country's. */
197
+ consumerLanguage?: string
198
+ /** The request's geolocated country (`cf-ipcountry`) — second location evidence, stored at lock. */
199
+ ipCountry?: string
200
+ }
201
+
202
+ /** What a submit-text function is told about the session it labels. */
203
+ export interface CheckoutTextContext {
204
+ language: string
205
+ /** The charge currency, lowercase. */
206
+ currency: string
207
+ /** A subscription's unit price in the charge currency; an amount checkout's pre-tax charge. */
208
+ unitAmountMinor?: number
209
+ interval?: 'month' | 'year'
210
+ region: ConsumerRegion | null
211
+ country?: string
212
+ }
213
+
214
+ // ---------------------------------------------------------------------------------------------
215
+ // Checkout plugins
216
+ // ---------------------------------------------------------------------------------------------
217
+
218
+ /** What a plugin narrows: one entity's amount checkout of one plan, at one instant. */
219
+ export interface CheckoutNarrowInput {
220
+ entityId: string
221
+ productSku: string
222
+ planSku?: string
223
+ /** The plan's own amount policy — the same for everyone. */
224
+ base: AmountCheckoutPolicy
225
+ at: Date
226
+ }
227
+
228
+ /** A checkout about to be created — what `admit` may veto. */
229
+ export interface CheckoutAttempt {
230
+ entityId: string
231
+ productSku: string
232
+ planSku?: string
233
+ mode: 'amount' | 'quantity' | 'subscription'
234
+ /** Amount mode: the net value bought, in the amount policy's currency. */
235
+ amountMinor?: number
236
+ /** The amount policy's currency (amount mode), else the charge currency. */
237
+ amountCurrency?: string
238
+ /** Amount mode: the pre-tax charge in `currency`. */
239
+ chargeMinor?: number
240
+ currency: string
241
+ /** The session lifetime the gateway will set; absent: Stripe's default (24 h). */
242
+ sessionTtlSeconds?: number
243
+ expiresAt?: Date
244
+ at: Date
245
+ }
246
+
247
+ /** A plugin's admission: an optional reservation it binds to the session in `created`. */
248
+ export interface CheckoutAdmission {
249
+ reservationId?: string
250
+ }
251
+
252
+ /** The session a checkout became. */
253
+ export interface CheckoutCreated extends CheckoutAttempt {
254
+ sessionId: string
255
+ url: string
256
+ /** What this plugin's own `admit` answered. */
257
+ reservationId?: string
258
+ }
259
+
260
+ export type CheckoutOutcome = 'paid' | 'expired' | 'failed'
261
+
262
+ /**
263
+ * How a checkout ended: `paid` (completed and paid), `expired`, or `failed` — an asynchronous
264
+ * payment failure, or a session that never became usable (a later plugin vetoed, Stripe refused,
265
+ * a `created` threw; then `sessionId` may be absent and `reservationId` names the hold to release).
266
+ */
267
+ export interface CheckoutSettled {
268
+ entityId: string
269
+ productSku?: string
270
+ planSku?: string
271
+ sessionId?: string
272
+ reservationId?: string
273
+ outcome: CheckoutOutcome
274
+ amountMinor?: number
275
+ at: Date
276
+ }
277
+
278
+ /**
279
+ * A seam around amount and subscription checkout, registered with `gateway(ctx).use(plugin)` (a
280
+ * plugin with an `alias` registered twice replaces the first). Errors from `narrow` and `admit`
281
+ * propagate — a plugin fails closed; `settled` errors are logged, since holds carry their own TTL.
282
+ */
283
+ export interface CheckoutPlugin {
284
+ alias?: string
285
+ /** Lower what this entity may buy now; `null` for no opinion. */
286
+ narrow?: (ctx: ApiContext, input: CheckoutNarrowInput) => Promise<AmountNarrowing | null>
287
+ /** Veto (throw, e.g. `CheckoutLimitExceeded`) or hold a reservation for the session. */
288
+ admit?: (ctx: ApiContext, attempt: CheckoutAttempt) => Promise<CheckoutAdmission | void>
289
+ /** The session exists — bind the reservation. A throw expires the session and rethrows. */
290
+ created?: (ctx: ApiContext, created: CheckoutCreated) => Promise<void>
291
+ /** From the webhook (paid, expired, failed) or from a checkout that never became usable. */
292
+ settled?: (ctx: ApiContext, settled: CheckoutSettled) => Promise<void>
293
+ /** The longest a session may stay open; the gateway uses the smallest, clamped to 30 min–24 h. */
294
+ sessionTtlSeconds?: number
133
295
  }
134
296
 
135
297
  export interface PortalLinkOptions {
@@ -175,6 +337,17 @@ export interface GatewayService extends InitializedService {
175
337
  /** @returns how many subscription rows changed */
176
338
  resyncSubscription: (ctx: ApiContext, ref: SubscriptionRef) => Promise<number>
177
339
  resyncAll: (ctx: ApiContext) => Promise<{ scanned: number; updated: number }>
340
+ /** Register a checkout plugin; one with an `alias` already registered replaces it. */
341
+ use: (plugin: CheckoutPlugin) => void
342
+ /** The registered checkout plugins, in registration order. */
343
+ checkoutPlugins: () => readonly CheckoutPlugin[]
344
+ /**
345
+ * The entity's amount policy of an amount plan as narrowed NOW — the very computation
346
+ * `createLink` enforces. Mongo and plugins only; works unmanaged.
347
+ */
348
+ amountPolicy: (ctx: ApiContext, entityId: string, productSku: string, planSku?: string) => Promise<AmountPolicyView>
349
+ /** The synced prices of a product's plans, per currency — stored rows, no Stripe call. */
350
+ planPrices: (ctx: ApiContext, productSku: string) => Promise<PlanPriceView[]>
178
351
  }
179
352
 
180
353
  export interface PaymentGatewayOptions {
@@ -187,6 +360,9 @@ export interface PaymentGatewayOptions {
187
360
  manage?: boolean
188
361
  }
189
362
 
363
+ /** A Stripe client for one context — the default reads the configured secret. */
364
+ export type StripeFactory = (ctx: ApiContext) => Promise<Stripe>
365
+
190
366
  /** @deprecated use `PaymentGatewayOptions` */
191
367
  export type PaymentResourceOptions = PaymentGatewayOptions
192
368
 
@@ -283,6 +459,14 @@ export interface RefundEvent {
283
459
  netAmountMinor?: number
284
460
  /** Fulfillment targets: the pre-tax subtotal charged at checkout. */
285
461
  chargeAmountMinor?: number
462
+ /** The refund's own metadata. */
463
+ metadata?: Record<string, string>
464
+ /**
465
+ * Set when the refund executes a withdrawal (`refund.metadata.withdrawalId`): the `onWithdrawal`
466
+ * observer takes back exactly the unused units — an `onRefund` observer must skip its own
467
+ * claw-back for such a refund, or the units are taken back twice.
468
+ */
469
+ withdrawalId?: string
286
470
  }
287
471
 
288
472
  export type DisputePhase = 'opened' | 'funds-withdrawn' | 'funds-reinstated' | 'closed'
@@ -317,24 +501,94 @@ export interface PaymentFailedEvent {
317
501
  eventKey: string
318
502
  }
319
503
 
504
+ /** A consumer's express request was recorded (performance consent or subscription start). */
505
+ export interface ConsentEvent {
506
+ kind: ConsentKind
507
+ consentId: string
508
+ entityId: string
509
+ profileId?: string
510
+ /** The purchases the consent covers (none for a start request, which precedes its purchase). */
511
+ purchaseIds: string[]
512
+ planSku?: string
513
+ textVersion: string
514
+ language: string
515
+ decidedAt: Date
516
+ expiresAt?: Date
517
+ /** `consent:<consentId>`. */
518
+ eventKey: string
519
+ }
520
+
521
+ /** A withdrawal executed (`refunded`) or left to an operator (`review`). */
522
+ export interface WithdrawalEvent {
523
+ withdrawalId: string
524
+ /** `withdrawal:<withdrawalId>`. */
525
+ eventKey: string
526
+ entityId: string
527
+ channel: DeclarationChannel
528
+ purchase: PurchaseRef
529
+ declaredAt: Date
530
+ status: WithdrawalStatus
531
+ refund: {
532
+ amountMinor: number
533
+ currency: string
534
+ netMinor?: number
535
+ refundId?: string
536
+ creditNoteId?: string
537
+ }
538
+ /**
539
+ * The purchase's units as the usage meter read them: `returned` is what the application takes
540
+ * back (`granted − used`), `null` without a meter.
541
+ */
542
+ units: { granted: number, used: number, returned: number } | null
543
+ subscriptionCanceled: boolean
544
+ }
545
+
546
+ /** A cancellation was declared (matched to a subscription or not). */
547
+ export interface CancellationEvent {
548
+ cancellationId: string
549
+ /** `cancellation:<cancellationId>`. */
550
+ eventKey: string
551
+ entityId?: string
552
+ matched: boolean
553
+ channel: DeclarationChannel
554
+ kind: CancellationKind
555
+ status: CancellationStatus
556
+ subscriptionId?: string
557
+ effectiveAt?: Date
558
+ declaredAt: Date
559
+ }
560
+
320
561
  export interface TopUpCallback { (completion: TopUpCompletion, ctx: ApiContext): Promise<void> }
321
562
  export interface SubscriptionCallback { (event: SubscriptionEvent, ctx: ApiContext): Promise<void> }
322
563
  export interface RefundCallback { (event: RefundEvent, ctx: ApiContext): Promise<void> }
323
564
  export interface DisputeCallback { (event: DisputeEvent, ctx: ApiContext): Promise<void> }
324
565
  export interface PaymentFailedCallback { (event: PaymentFailedEvent, ctx: ApiContext): Promise<void> }
566
+ export interface ConsentCallback { (event: ConsentEvent, ctx: ApiContext): Promise<void> }
567
+ export interface WithdrawalCallback { (event: WithdrawalEvent, ctx: ApiContext): Promise<void> }
568
+ export interface CancellationCallback { (event: CancellationEvent, ctx: ApiContext): Promise<void> }
325
569
 
326
- /** Callbacks run sequentially and are awaited; a throw escapes so the paygate retries. */
570
+ /**
571
+ * Callbacks run sequentially and are awaited. A paygate callback's throw escapes so the paygate
572
+ * retries; a consumer-rights callback (`onConsent`, `onWithdrawal`, `onCancellation`) runs AFTER
573
+ * the records and the paygate steps, and its throw is recorded and retried by `reconcile()`.
574
+ */
327
575
  export interface CompletionObserver extends LazyService {
328
576
  onTopUp: (cb: TopUpCallback) => void
329
577
  onSubscription: (cb: SubscriptionCallback) => void
330
578
  onRefund: (cb: RefundCallback) => void
331
579
  onDispute: (cb: DisputeCallback) => void
332
580
  onPaymentFailed: (cb: PaymentFailedCallback) => void
581
+ onConsent: (cb: ConsentCallback) => void
582
+ onWithdrawal: (cb: WithdrawalCallback) => void
583
+ onCancellation: (cb: CancellationCallback) => void
333
584
  propagateTopUp: (completion: TopUpCompletion, ctx: ApiContext) => Promise<void>
334
585
  propagateSubscription: (event: SubscriptionEvent, ctx: ApiContext) => Promise<void>
335
586
  propagateRefund: (event: RefundEvent, ctx: ApiContext) => Promise<void>
336
587
  propagateDispute: (event: DisputeEvent, ctx: ApiContext) => Promise<void>
337
588
  propagatePaymentFailed: (event: PaymentFailedEvent, ctx: ApiContext) => Promise<void>
589
+ propagateConsent: (event: ConsentEvent, ctx: ApiContext) => Promise<void>
590
+ propagateWithdrawal: (event: WithdrawalEvent, ctx: ApiContext) => Promise<void>
591
+ propagateCancellation: (event: CancellationEvent, ctx: ApiContext) => Promise<void>
338
592
  }
339
593
 
340
594
  // ---------------------------------------------------------------------------------------------
@@ -349,6 +603,10 @@ export interface PaygateCustomerRecord extends ResourceRecord {
349
603
  email?: string
350
604
  name?: string
351
605
  taxId?: string
606
+ /** The paygate customer's billing address country, as its last webhook stated it. */
607
+ country?: string
608
+ /** The paygate customer's own currency (set by its first subscription or invoice). */
609
+ currency?: string
352
610
  deletedAt?: Date
353
611
  }
354
612
  export interface PaygateCustomerResource extends MongoResource<PaygateCustomerRecord> {
@@ -398,6 +656,19 @@ export interface PaymentSubscriptionRecord extends ResourceRecord {
398
656
  lastEventId?: string
399
657
  initialPropagatedAt?: Date
400
658
  propagated?: PropagatedState
659
+ /** The subscription's currency (lowercase). */
660
+ currency?: string
661
+ // Checkout evidence — written once the checkout that created the subscription completed.
662
+ checkoutSessionId?: string
663
+ purchaseId?: string
664
+ firstInvoiceId?: string
665
+ country?: string
666
+ email?: string
667
+ amountTotalMinor?: number
668
+ amountTaxMinor?: number
669
+ termsAccepted?: boolean
670
+ startRequestId?: string
671
+ withdrawnAt?: Date
401
672
  }
402
673
  export interface PaymentSubscriptionResource extends MongoResource<PaymentSubscriptionRecord> {
403
674
  byExternalId: (externalId: string, paygate: string) => Promise<PaymentSubscriptionRecord | null>
@@ -429,6 +700,14 @@ export interface PaymentFulfillmentRecord extends ResourceRecord {
429
700
  refundedAt?: Date
430
701
  disputedAt?: Date
431
702
  disputeStatus?: string
703
+ // Checkout evidence.
704
+ country?: string
705
+ email?: string
706
+ profileId?: string
707
+ amountTotalMinor?: number
708
+ amountTaxMinor?: number
709
+ termsAccepted?: boolean
710
+ purchaseId?: string
432
711
  }
433
712
  export interface PaymentFulfillmentResource extends MongoResource<PaymentFulfillmentRecord> {
434
713
  byExternalId: (externalId: string, paygate: string) => Promise<PaymentFulfillmentRecord | null>
@@ -480,12 +759,37 @@ export interface PaymentUsageCounterRecord extends ResourceRecord {
480
759
  }
481
760
  export interface PaymentUsageCounterResource extends MongoResource<PaymentUsageCounterRecord> {}
482
761
 
762
+ /** One currency option of a synced price. */
763
+ export interface SyncedPriceOption {
764
+ currency: string
765
+ unitAmount: number
766
+ }
767
+
768
+ /** A reusable price as the last sync left it — what `planPrices` and checkout read without Stripe. */
769
+ export interface SyncedPrice {
770
+ planSku: string
771
+ priceId: string
772
+ lookupKey: string
773
+ /** The default currency and its amount. */
774
+ currency: string
775
+ unitAmount: number
776
+ /** `currency_options` besides the default currency. */
777
+ options: SyncedPriceOption[]
778
+ taxBehavior?: string
779
+ interval?: 'month' | 'year'
780
+ sourceUnitAmount: number
781
+ sourceCurrency: string
782
+ syncedAt: Date
783
+ }
784
+
483
785
  export interface FingerprintRecord extends ResourceRecord {
484
786
  sku: string
485
787
  hash: string
486
788
  productId?: string
487
789
  /** The paygate object the fingerprint describes (a portal configuration id). */
488
790
  externalId?: string
791
+ /** A product's synced reusable prices. */
792
+ prices?: SyncedPrice[]
489
793
  updatedAt: Date
490
794
  }
491
795
  export interface FingerprintResource extends MongoResource<FingerprintRecord> {
@@ -600,3 +904,449 @@ export interface ReconcileAllResult {
600
904
  repaired: number
601
905
  failed: number
602
906
  }
907
+
908
+ // ---------------------------------------------------------------------------------------------
909
+ // Consumer rights — records
910
+ // ---------------------------------------------------------------------------------------------
911
+
912
+ /** Where a billing profile's country came from. */
913
+ export type BillingProfileSource = 'checkout' | 'customer' | 'manual'
914
+
915
+ /** One per organization: the billing country fixed at the first purchase (first write wins). */
916
+ export interface BillingProfileRecord extends ResourceRecord {
917
+ entityId: string
918
+ country: string
919
+ region: ConsumerRegion
920
+ /** The charge currency, fixed with the country. */
921
+ currency: string
922
+ /** The language of the legal copy. */
923
+ language: string
924
+ source: BillingProfileSource
925
+ paygate: string
926
+ customerId?: string
927
+ sessionId?: string
928
+ /** The geolocated country of the request that started the locking checkout — VAT evidence. */
929
+ ipCountry?: string
930
+ email?: string
931
+ name?: string
932
+ /** A business tax id was given at the locking checkout. */
933
+ business?: boolean
934
+ lockedAt: Date
935
+ createdAt: Date
936
+ updatedAt?: Date
937
+ }
938
+ export interface BillingProfileResource extends MongoResource<BillingProfileRecord> {
939
+ byEntity: (entityId: string) => Promise<BillingProfileRecord | null>
940
+ }
941
+
942
+ /**
943
+ * One purchase — a contract and its withdrawal window: a paid one-time checkout (`top-up`) or a
944
+ * subscription's first invoice (`subscription`). Renewals are not purchases.
945
+ */
946
+ export interface PurchaseRecord extends ResourceRecord {
947
+ /** `stripe:<session id>` (one-time) or `stripe:<subscription id>`. */
948
+ purchaseId: string
949
+ /** `CR-YYMMDD-XXXXXX` — what a consumer quotes. */
950
+ contractRef: string
951
+ entityId: string
952
+ kind: PurchaseKind
953
+ paygate: string
954
+ sessionId?: string
955
+ subscriptionId?: string
956
+ paymentIntentId?: string
957
+ invoiceId?: string
958
+ invoiceNumber?: string
959
+ invoiceLineId?: string
960
+ productSku: string
961
+ planSku?: string
962
+ profileId?: string
963
+ country?: string
964
+ region?: ConsumerRegion
965
+ ipCountry?: string
966
+ inScope: boolean
967
+ language: string
968
+ email?: string
969
+ name?: string
970
+ business?: boolean
971
+ /** The charge currency. */
972
+ currency: string
973
+ amountSubtotalMinor: number
974
+ amountTaxMinor: number
975
+ amountTotalMinor: number
976
+ presentmentCurrency?: string
977
+ presentmentAmountMinor?: number
978
+ /** Amount checkout: the net value credited, in `amountCurrency`. */
979
+ netAmountMinor?: number
980
+ amountCurrency?: string
981
+ /** Quantity checkout: the units bought. */
982
+ units?: number
983
+ taxBehavior?: string
984
+ /**
985
+ * Stripe's own `consent.terms_of_service` of the checkout — absent when the session did not
986
+ * collect it (the policy's `checkoutTerms` off, or the fallback of a Dashboard without a terms URL).
987
+ */
988
+ termsAccepted?: boolean
989
+ textVersion?: string
990
+ copyVersion?: string
991
+ startRequestId?: string
992
+ /** When the consumer expressly requested the services (the start request) — time deductions count from here. */
993
+ servicesStartedAt?: Date
994
+ /** When the confirmation mail was claimed — one claim per purchase, whatever the outcome of the send. */
995
+ confirmationMailAt?: Date
996
+ purchasedAt: Date
997
+ /** Exclusive end of the withdrawal window; absent outside the consumer-rights territories. */
998
+ deadline?: Date
999
+ consentId?: string
1000
+ consentedAt?: Date
1001
+ withdrawalId?: string
1002
+ withdrawnAt?: Date
1003
+ /** Refunded so far on this purchase's payment, tax included. */
1004
+ refundedMinor?: number
1005
+ /** Set once the whole payment is refunded — the window closes. */
1006
+ refundedAt?: Date
1007
+ cancellationId?: string
1008
+ cancelEffectiveAt?: Date
1009
+ createdAt: Date
1010
+ updatedAt?: Date
1011
+ }
1012
+ export interface PurchaseResource extends MongoResource<PurchaseRecord> {
1013
+ byPurchaseId: (purchaseId: string) => Promise<PurchaseRecord | null>
1014
+ }
1015
+
1016
+ /** The request evidence a consumer act is recorded with (`requestOriginOf`). */
1017
+ export interface RequestOrigin {
1018
+ /** `cf-connecting-ip`, else the LAST `x-forwarded-for` entry, else `x-real-ip`, else the socket. */
1019
+ ip?: string
1020
+ /** The raw `x-forwarded-for` header. */
1021
+ forwardedFor?: string
1022
+ /** `user-agent`, at most 512 characters. */
1023
+ userAgent?: string
1024
+ /** `cf-ipcountry`. */
1025
+ ipCountry?: string
1026
+ acceptLanguage?: string
1027
+ /** The channel the request came through (`http`, `mcp`, …), when the application knows. */
1028
+ via?: string
1029
+ }
1030
+
1031
+ /** Append-only: one express request — a performance consent or a subscription start request. */
1032
+ export interface ConsumerConsentRecord extends ResourceRecord, RequestOrigin {
1033
+ kind: ConsentKind
1034
+ entityId: string
1035
+ profileId?: string
1036
+ /** The person who gave it — the confirmation is mailed here. */
1037
+ name?: string
1038
+ email?: string
1039
+ purchaseIds: string[]
1040
+ planSku?: string
1041
+ /** The plan's name as the statement said it (`{{plan}}`). */
1042
+ planName?: string
1043
+ textVersion: string
1044
+ copyVersion: string
1045
+ /** The language the statement was shown in. */
1046
+ language: string
1047
+ uiLanguage?: string
1048
+ trader: string
1049
+ /** The statement exactly as rendered and recorded. */
1050
+ text: { request: string, acknowledgement: string, checkbox: string }
1051
+ links: ConsumerRightsLinks
1052
+ /** The latest deadline among the covered purchases. */
1053
+ deadline?: Date
1054
+ decidedAt: Date
1055
+ /** A start request stops being usable at this instant. */
1056
+ expiresAt?: Date
1057
+ }
1058
+ export interface ConsumerConsentResource extends MongoResource<ConsumerConsentRecord> {}
1059
+
1060
+ /** Append-only: one withdrawal or cancellation declaration, matched to a contract or not. */
1061
+ export interface ConsumerDeclarationRecord extends ResourceRecord, RequestOrigin {
1062
+ kind: DeclarationKind
1063
+ channel: DeclarationChannel
1064
+ entityId?: string
1065
+ purchaseId?: string
1066
+ subscriptionId?: string
1067
+ /** The contract as the consumer named it. */
1068
+ contractRef?: string
1069
+ name: string
1070
+ email: string
1071
+ cancellationKind?: CancellationKind
1072
+ reason?: string
1073
+ effective?: 'earliest' | 'date'
1074
+ requestedDate?: string
1075
+ language: string
1076
+ textVersion?: string
1077
+ copyVersion: string
1078
+ receivedAt: Date
1079
+ matched: boolean
1080
+ profileId?: string
1081
+ /** A repeated declaration of a contract already withdrawn from: the original's id. */
1082
+ duplicateOf?: string
1083
+ /** The status decided on receipt (`WithdrawalStatus` / `CancellationStatus`); execution steps are events. */
1084
+ status: string
1085
+ refundMinor?: number
1086
+ currency?: string
1087
+ effectiveAt?: Date
1088
+ }
1089
+ export interface ConsumerDeclarationResource extends MongoResource<ConsumerDeclarationRecord> {}
1090
+
1091
+ export type ConsumerEventAction =
1092
+ | 'mail' | 'computed' | 'meter' | 'refund' | 'credit-note' | 'subscription-cancel' | 'cancel-scheduled'
1093
+ | 'observers' | 'lock' | 'lock-mismatch' | 'relock' | 'unlock' | 'duplicate' | 'checkout-terms-fallback'
1094
+
1095
+ export type ConsumerRecordKind = 'purchase' | 'consent' | 'declaration' | 'profile' | 'checkout'
1096
+
1097
+ /** Append-only: one execution or audit step. */
1098
+ export interface ConsumerEventRecord extends ResourceRecord {
1099
+ /** The purchase id, consent id, declaration id, or the entity id of a profile or a checkout. */
1100
+ recordId: string
1101
+ recordKind: ConsumerRecordKind
1102
+ entityId?: string
1103
+ action: ConsumerEventAction
1104
+ /** The mail kind of a `mail` event; the observer family of an `observers` one. */
1105
+ step?: string
1106
+ ok: boolean
1107
+ /** A mail deliberately not sent (a reserved domain, a renderer that suppressed it). */
1108
+ skipped?: boolean
1109
+ externalId?: string
1110
+ amountMinor?: number
1111
+ currency?: string
1112
+ /** JSON. */
1113
+ detail?: string
1114
+ error?: string
1115
+ at: Date
1116
+ }
1117
+ export interface ConsumerEventResource extends MongoResource<ConsumerEventRecord> {}
1118
+
1119
+ // ---------------------------------------------------------------------------------------------
1120
+ // Consumer rights — service
1121
+ // ---------------------------------------------------------------------------------------------
1122
+
1123
+ /** What a usage meter is told about the purchase it reads. */
1124
+ export interface PurchaseRef {
1125
+ purchaseId: string
1126
+ kind: PurchaseKind
1127
+ entityId: string
1128
+ contractRef: string
1129
+ productSku: string
1130
+ planSku?: string
1131
+ /** One-time purchases: the checkout session id. */
1132
+ sessionId?: string
1133
+ /** Subscription purchases: the paygate subscription id. */
1134
+ subscriptionId?: string
1135
+ invoiceId?: string
1136
+ purchasedAt: Date
1137
+ }
1138
+
1139
+ export interface UsageQuery {
1140
+ entityId: string
1141
+ purchase: PurchaseRef
1142
+ /** The purchase's consent — before it the consumer bears no cost; absent: nothing is deductible. */
1143
+ after?: Date
1144
+ at: Date
1145
+ }
1146
+
1147
+ export interface UsageReading {
1148
+ /** Units the purchase granted. */
1149
+ granted: number
1150
+ /** Units of it used so far. */
1151
+ used: number
1152
+ /** Units of it used strictly after `after` (0 without `after`). */
1153
+ usedAfter: number
1154
+ /** Units of it that paid off an earlier overdraft when it was granted. */
1155
+ settled?: number
1156
+ /** Units of it already taken back by an earlier (money) refund. */
1157
+ clawed?: number
1158
+ remaining?: number
1159
+ }
1160
+
1161
+ /**
1162
+ * The application's reading of how much of one purchase was used — for a withdrawal's refund.
1163
+ * The deduction is `usedAfter + settled + clawed`.
1164
+ */
1165
+ export interface UsageMeter {
1166
+ used: (ctx: ApiContext, query: UsageQuery) => Promise<UsageReading>
1167
+ }
1168
+
1169
+ /** Who acts: the organization, and the person's prefill. */
1170
+ export interface ConsumerSubject {
1171
+ entityId: string
1172
+ profileId?: string
1173
+ name?: string
1174
+ email?: string
1175
+ /** `public` when an application matched a public declaration to this organization itself. */
1176
+ channel?: DeclarationChannel
1177
+ }
1178
+
1179
+ export type ConsumerMailKind = 'purchase' | 'consent' | 'start' | 'withdrawal' | 'cancellation'
1180
+
1181
+ /** What a consumer-rights mail is rendered from. */
1182
+ export interface ConsumerMailData {
1183
+ kind: ConsumerMailKind
1184
+ /** The purchase id, consent id or declaration id. */
1185
+ recordId: string
1186
+ entityId?: string
1187
+ language: string
1188
+ to: string
1189
+ name?: string
1190
+ trader: TraderDef
1191
+ links: ConsumerRightsLinks
1192
+ purchase?: PurchaseRecord
1193
+ purchases?: PurchaseRecord[]
1194
+ consent?: ConsumerConsentRecord
1195
+ declaration?: ConsumerDeclarationRecord
1196
+ }
1197
+
1198
+ /**
1199
+ * Replace or suppress a consumer-rights mail: answer a message to send it instead, `null` to send
1200
+ * nothing (recorded as skipped), `undefined` to send the rendered one.
1201
+ */
1202
+ export type ConsumerMailRenderer = (
1203
+ kind: ConsumerMailKind, data: ConsumerMailData, rendered: MailMessage,
1204
+ ) => MailMessage | null | undefined | Promise<MailMessage | null | undefined>
1205
+
1206
+ export interface LockOptions {
1207
+ customerId?: string
1208
+ sessionId?: string
1209
+ ipCountry?: string
1210
+ email?: string
1211
+ name?: string
1212
+ business?: boolean
1213
+ /** The charge currency; default: an active subscription's, else the region's. */
1214
+ currency?: string
1215
+ language?: string
1216
+ /** `manual` only: replace an existing lock (audited as a `relock` event). */
1217
+ force?: boolean
1218
+ by?: string
1219
+ reason?: string
1220
+ }
1221
+
1222
+ /** Who removes a lock, and why — kept on the `unlock` event. */
1223
+ export interface UnlockOptions {
1224
+ by?: string
1225
+ reason?: string
1226
+ }
1227
+
1228
+ export interface ConsumerReconcileOptions {
1229
+ /** How far back declarations, mails and observers are retried. Default 30 days. */
1230
+ since?: Date
1231
+ /** The most items per step. Default 50. */
1232
+ limit?: number
1233
+ }
1234
+
1235
+ export interface ConsumerReconcileResult {
1236
+ retried: number
1237
+ mailed: number
1238
+ observed: number
1239
+ backfilled: number
1240
+ locked: number
1241
+ failed: number
1242
+ }
1243
+
1244
+ /**
1245
+ * Lazy: reachable while the application is still being wired (`useMeter`, `useMailRenderer` before
1246
+ * the context initializes); the gateway's initialization initializes it at boot.
1247
+ */
1248
+ export interface ConsumerRightsService extends LazyService {
1249
+ /**
1250
+ * `false`: every paygate-calling method (`withdraw`, `cancel`, `reconcile`'s paygate steps) refuses.
1251
+ * An explicit `manage` of `appendConsumerRights` wins over the gateway's, whatever the order.
1252
+ */
1253
+ readonly managed: boolean
1254
+ policy: () => Promise<ConsumerRightsPolicy | null>
1255
+ /** The locked billing profile, or `null`. */
1256
+ profile: (entityId: string) => Promise<BillingProfileView | null>
1257
+ /** Lock the billing country; the first write wins unless a `manual` lock is `force`d. */
1258
+ lock: (entityId: string, country: string, source: BillingProfileSource, opts?: LockOptions) => Promise<BillingProfileView>
1259
+ /**
1260
+ * An operator removes the lock: the profile is deleted and an `unlock` event keeps it. No lazy lock
1261
+ * from the paygate customer follows (checkout, reconcile); the next completed purchase locks again.
1262
+ * @returns the profile as it was, or `null` when nothing was locked
1263
+ */
1264
+ unlock: (entityId: string, opts?: UnlockOptions) => Promise<BillingProfileView | null>
1265
+ purchases: (entityId: string, opts?: { open?: boolean, at?: Date }) => Promise<PurchaseView[]>
1266
+ consentView: (entityId: string, at?: Date) => Promise<PerformanceConsentView>
1267
+ /** @throws PerformanceConsentRequired (428) for a stale text version */
1268
+ recordConsent: (subject: ConsumerSubject, body: PerformanceConsentBody, origin?: RequestOrigin) => Promise<PerformanceConsentResponse>
1269
+ /** @throws PerformanceConsentRequired while an open in-scope window has no consent */
1270
+ assertConsent: (entityId: string, at?: Date) => Promise<void>
1271
+ startView: (entityId: string, planSku: string, opts?: { language?: string }) => Promise<SubscriptionStartView>
1272
+ /** @throws SubscriptionStartRequired (428) for a stale text version */
1273
+ /** `plan`: the plan's short name the statement says (default: its localized catalogue title). */
1274
+ recordStartRequest: (
1275
+ subject: ConsumerSubject, body: SubscriptionStartBody, origin?: RequestOrigin, opts?: { plan?: string },
1276
+ ) => Promise<SubscriptionStartResponse>
1277
+ /**
1278
+ * The fresh start request bound to this entity and plan, or `null` when none is required.
1279
+ * @throws SubscriptionStartRequired
1280
+ */
1281
+ assertStartRequest: (entityId: string, planSku: string, startRequestId?: string) => Promise<ConsumerConsentRecord | null>
1282
+ withdrawalCandidates: (entityId: string, subject?: Partial<ConsumerSubject>) => Promise<WithdrawalCandidateList>
1283
+ /** `subject: null` — the public function: matched by contract reference and e-mail, never disclosed. */
1284
+ withdraw: (subject: ConsumerSubject | null, body: WithdrawalBody, origin?: RequestOrigin) => Promise<WithdrawalReceipt>
1285
+ cancel: (subject: ConsumerSubject | null, body: CancellationBody, origin?: RequestOrigin) => Promise<CancellationReceipt>
1286
+ useMeter: (meter: UsageMeter | null) => void
1287
+ useMailRenderer: (renderer: ConsumerMailRenderer | null) => void
1288
+ usageMeter: () => UsageMeter | null
1289
+ mailRenderer: () => ConsumerMailRenderer | null
1290
+ /** Retry refunds, credit notes, cancellations, mails and observers; backfill purchases and legacy locks. */
1291
+ reconcile: (opts?: ConsumerReconcileOptions) => Promise<ConsumerReconcileResult>
1292
+ }
1293
+
1294
+ /**
1295
+ * `appendConsumerRights` options. `manage`, `usage` and `stripe` apply to the service whenever the
1296
+ * call comes — before or after the gateway registered it; the resources' `dbAlias` / `serviceAlias`
1297
+ * are those of the first registration.
1298
+ */
1299
+ export interface ConsumerRightsOptions {
1300
+ /**
1301
+ * `false` in a process that never talks to the paygate (reads and consent asserts only). Absent:
1302
+ * the gateway's `manage`, else managed.
1303
+ */
1304
+ manage?: boolean
1305
+ /** The application's usage meter; without one a withdrawal is left to an operator (`review`). */
1306
+ usage?: UsageMeter
1307
+ alias?: string
1308
+ /** The paygate client; default: the configured Stripe secret. */
1309
+ stripe?: StripeFactory
1310
+ dbAlias?: string
1311
+ serviceAlias?: string
1312
+ }
1313
+
1314
+ // ---------------------------------------------------------------------------------------------
1315
+ // Consumer rights — handlers
1316
+ // ---------------------------------------------------------------------------------------------
1317
+
1318
+ /** What a public declaration is throttled by. */
1319
+ export interface ConsumerThrottleKey {
1320
+ action: 'withdrawal' | 'cancellation'
1321
+ email: string
1322
+ ip?: string
1323
+ }
1324
+
1325
+ /**
1326
+ * The hooks of `consumerRightsEntrypoints`. Every hook gets the request's context last, so an
1327
+ * application keeps its counters and lookups on the context, never in module state.
1328
+ */
1329
+ export interface ConsumerRightsHandlerOptions {
1330
+ /** The organization a signed-in request acts for. Default: `req.entity.id`. */
1331
+ resolveEntity?: (req: AbstractRequest, ctx: ApiContext) => string | null | undefined
1332
+ /** The person's profile id and prefill. */
1333
+ subjectOf?: (req: AbstractRequest, ctx: ApiContext) => Partial<ConsumerSubject> | Promise<Partial<ConsumerSubject>>
1334
+ /** Refuse a money-moving act (consent, start, withdraw, cancel) — e.g. an API-key request. */
1335
+ guardMoney?: (req: AbstractRequest, action: string, ctx: ApiContext) => void | Promise<void>
1336
+ /** REQUIRED when the public subtree is bound: throw (e.g. 429) to refuse a public declaration. */
1337
+ throttle?: (req: AbstractRequest, key: ConsumerThrottleKey, ctx: ApiContext) => void | Promise<void>
1338
+ /** The request evidence; default `requestOriginOf`. */
1339
+ metaOf?: (req: AbstractRequest, ctx: ApiContext) => RequestOrigin
1340
+ /** The least time a public declaration takes to answer, matched or not. Default 1000 ms. */
1341
+ publicMinMs?: number
1342
+ /** The plan's short name a start request's statement says (default: its localized catalogue title). */
1343
+ planNameOf?: (
1344
+ planSku: string, language: string, req: AbstractRequest, ctx: ApiContext,
1345
+ ) => string | undefined | Promise<string | undefined>
1346
+ serviceAlias?: string
1347
+ }
1348
+
1349
+ export interface CheckoutReadHandlerOptions {
1350
+ resolveEntity?: (req: AbstractRequest, ctx: ApiContext) => string | null | undefined
1351
+ gatewayAlias?: string
1352
+ }