@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.
- package/README.md +1 -1
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/server-payment/SKILL.md +402 -260
- package/build/config.d.ts +15 -1
- package/build/config.d.ts.map +1 -1
- package/build/config.js +99 -3
- package/build/config.js.map +1 -1
- package/build/consts.d.ts +27 -0
- package/build/consts.d.ts.map +1 -1
- package/build/consts.js +27 -0
- package/build/consts.js.map +1 -1
- package/build/consumer/capture.d.ts +74 -0
- package/build/consumer/capture.d.ts.map +1 -0
- package/build/consumer/capture.js +291 -0
- package/build/consumer/capture.js.map +1 -0
- package/build/consumer/format.d.ts +27 -0
- package/build/consumer/format.d.ts.map +1 -0
- package/build/consumer/format.js +81 -0
- package/build/consumer/format.js.map +1 -0
- package/build/consumer/handlers.d.ts +28 -0
- package/build/consumer/handlers.d.ts.map +1 -0
- package/build/consumer/handlers.js +173 -0
- package/build/consumer/handlers.js.map +1 -0
- package/build/consumer/index.d.ts +7 -0
- package/build/consumer/index.d.ts.map +1 -0
- package/build/consumer/index.js +6 -0
- package/build/consumer/index.js.map +1 -0
- package/build/consumer/mail.d.ts +27 -0
- package/build/consumer/mail.d.ts.map +1 -0
- package/build/consumer/mail.js +314 -0
- package/build/consumer/mail.js.map +1 -0
- package/build/consumer/origin.d.ts +14 -0
- package/build/consumer/origin.d.ts.map +1 -0
- package/build/consumer/origin.js +47 -0
- package/build/consumer/origin.js.map +1 -0
- package/build/consumer/reconcile.d.ts +12 -0
- package/build/consumer/reconcile.d.ts.map +1 -0
- package/build/consumer/reconcile.js +317 -0
- package/build/consumer/reconcile.js.map +1 -0
- package/build/consumer/records.d.ts +78 -0
- package/build/consumer/records.d.ts.map +1 -0
- package/build/consumer/records.js +296 -0
- package/build/consumer/records.js.map +1 -0
- package/build/consumer/service.d.ts +51 -0
- package/build/consumer/service.d.ts.map +1 -0
- package/build/consumer/service.js +760 -0
- package/build/consumer/service.js.map +1 -0
- package/build/consumer/withdrawal.d.ts +57 -0
- package/build/consumer/withdrawal.d.ts.map +1 -0
- package/build/consumer/withdrawal.js +247 -0
- package/build/consumer/withdrawal.js.map +1 -0
- package/build/index.d.ts +4 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +4 -2
- package/build/index.js.map +1 -1
- package/build/model.d.ts +6 -1
- package/build/model.d.ts.map +1 -1
- package/build/model.js +134 -4
- package/build/model.js.map +1 -1
- package/build/observer.d.ts +4 -0
- package/build/observer.d.ts.map +1 -1
- package/build/observer.js +13 -0
- package/build/observer.js.map +1 -1
- package/build/plugins/checkout-plugins.d.ts +49 -0
- package/build/plugins/checkout-plugins.d.ts.map +1 -0
- package/build/plugins/checkout-plugins.js +124 -0
- package/build/plugins/checkout-plugins.js.map +1 -0
- package/build/plugins/estimate.d.ts.map +1 -1
- package/build/plugins/estimate.js +41 -8
- package/build/plugins/estimate.js.map +1 -1
- package/build/plugins/events.d.ts +2 -0
- package/build/plugins/events.d.ts.map +1 -1
- package/build/plugins/events.js +126 -9
- package/build/plugins/events.js.map +1 -1
- package/build/plugins/fx.d.ts +5 -0
- package/build/plugins/fx.d.ts.map +1 -1
- package/build/plugins/fx.js +25 -0
- package/build/plugins/fx.js.map +1 -1
- package/build/plugins/portal.d.ts.map +1 -1
- package/build/plugins/portal.js +11 -1
- package/build/plugins/portal.js.map +1 -1
- package/build/plugins/stripe.d.ts +21 -2
- package/build/plugins/stripe.d.ts.map +1 -1
- package/build/plugins/stripe.js +397 -61
- package/build/plugins/stripe.js.map +1 -1
- package/build/resource.d.ts +8 -1
- package/build/resource.d.ts.map +1 -1
- package/build/resource.js +59 -2
- package/build/resource.js.map +1 -1
- package/build/service.d.ts +2 -1
- package/build/service.d.ts.map +1 -1
- package/build/service.js +28 -5
- package/build/service.js.map +1 -1
- package/build/subscription.d.ts +6 -0
- package/build/subscription.d.ts.map +1 -1
- package/build/subscription.js +1 -0
- package/build/subscription.js.map +1 -1
- package/build/sync.d.ts +7 -0
- package/build/sync.d.ts.map +1 -1
- package/build/sync.js +108 -15
- package/build/sync.js.map +1 -1
- package/build/types.d.ts +707 -4
- package/build/types.d.ts.map +1 -1
- package/build/utils.d.ts +22 -1
- package/build/utils.d.ts.map +1 -1
- package/build/utils.js +27 -1
- package/build/utils.js.map +1 -1
- package/package.json +14 -13
- package/src/config.ts +111 -7
- package/src/consts.ts +34 -0
- package/src/consumer/capture.ts +362 -0
- package/src/consumer/format.ts +90 -0
- package/src/consumer/handlers.ts +211 -0
- package/src/consumer/index.ts +6 -0
- package/src/consumer/mail.ts +368 -0
- package/src/consumer/origin.ts +63 -0
- package/src/consumer/reconcile.ts +329 -0
- package/src/consumer/records.ts +374 -0
- package/src/consumer/service.ts +868 -0
- package/src/consumer/withdrawal.ts +302 -0
- package/src/index.ts +6 -4
- package/src/model.ts +148 -6
- package/src/observer.ts +15 -2
- package/src/plugins/checkout-plugins.ts +155 -0
- package/src/plugins/estimate.ts +49 -9
- package/src/plugins/events.ts +135 -11
- package/src/plugins/fx.ts +29 -0
- package/src/plugins/portal.ts +11 -1
- package/src/plugins/stripe.ts +476 -60
- package/src/resource.ts +87 -4
- package/src/service.ts +28 -6
- package/src/subscription.ts +7 -0
- package/src/sync.ts +124 -17
- package/src/types.ts +756 -6
- package/src/utils.ts +56 -7
- package/tests/checkout-consumer.spec.ts +348 -0
- package/tests/checkout-plugins.spec.ts +164 -0
- package/tests/consumer-events.spec.ts +218 -0
- package/tests/consumer-fixtures.ts +132 -0
- package/tests/consumer-ops.spec.ts +351 -0
- package/tests/consumer-rights.integration.spec.ts +150 -0
- package/tests/consumer-rights.spec.ts +501 -0
- package/tests/context.ts +20 -2
- 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,
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
/**
|
|
130
|
-
|
|
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
|
-
/**
|
|
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
|
+
}
|