@classytic/revenue 2.1.0 → 2.1.3
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/CHANGELOG.md +66 -0
- package/README.md +33 -10
- package/dist/{bank-feed-DJtLvz_7.mjs → bank-feed-BlQeq2rK.mjs} +1 -1
- package/dist/core/state-machines.mjs +2 -2
- package/dist/{engine-types-txFXOiQS.d.mts → engine-types-Jctrbasz.d.mts} +192 -80
- package/dist/enums/index.mjs +1 -1
- package/dist/{escrow.schema-9yh4Q-aQ.d.mts → escrow.schema-YuBgjL-I.d.mts} +11 -11
- package/dist/events/index.mjs +2 -2
- package/dist/index.d.mts +14 -5
- package/dist/index.mjs +30 -8
- package/dist/providers/index.mjs +1 -1
- package/dist/repositories/create-repositories.d.mts +1 -1
- package/dist/repositories/create-repositories.mjs +1 -1
- package/dist/{revenue-event-catalog-CgZ57M-f.mjs → revenue-event-catalog-BvjNVnPd.mjs} +1 -1
- package/dist/{settlement.repository-Ba2U17zY.mjs → settlement.repository-BAdc9qGl.mjs} +252 -121
- package/dist/shared/index.mjs +1 -1
- package/dist/validators/index.d.mts +1 -1
- package/package.json +5 -5
- /package/dist/{errors-Dt46UZL_.mjs → errors-LYYg9wcs.mjs} +0 -0
- /package/dist/{event-constants-CTiDNWzc.mjs → event-constants-Dn1TKahe.mjs} +0 -0
- /package/dist/{splits-D8XkNWgX.mjs → splits-CNfQj92L.mjs} +0 -0
- /package/dist/{subscription.enums-DoIr56O6.mjs → subscription.enums-95othr0i.mjs} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,72 @@
|
|
|
3
3
|
Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
4
4
|
adhering to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
5
|
|
|
6
|
+
## [2.1.1] — multi-tenant scope correctness across all repos
|
|
7
|
+
|
|
8
|
+
**Fix.** `SubscriptionRepository` and `SettlementRepository` lifecycle verbs
|
|
9
|
+
were calling internal `getById` / `update` / `getAll` without threading
|
|
10
|
+
`ctx.organizationId` into the mongokit options bag. The moment a host
|
|
11
|
+
enabled `multiTenantPlugin` (the recommended default — see PACKAGE_RULES
|
|
12
|
+
§9), every verb threw `Missing 'organizationId' in context for 'getById'`
|
|
13
|
+
mid-flow and the lifecycle was unusable.
|
|
14
|
+
|
|
15
|
+
Affected verbs (all now threaded correctly):
|
|
16
|
+
|
|
17
|
+
- `SubscriptionRepository.{activate,cancel,pause,resume}` — every internal
|
|
18
|
+
`getById` and `update` now forwards `ctx`.
|
|
19
|
+
- `SettlementRepository.{schedule,processPending,complete,fail}` — every
|
|
20
|
+
internal `getById`, `getAll`, `update` now forwards `ctx`.
|
|
21
|
+
|
|
22
|
+
**Refactor.** Introduces `RevenueRepositoryBase<TDoc, TDeps>` (abstract;
|
|
23
|
+
internal — not exported) consolidating the two cross-cutting concerns
|
|
24
|
+
that were previously hand-rolled in three places:
|
|
25
|
+
|
|
26
|
+
- `protected optsFromCtx(ctx, extra?)` — thin adapter over mongokit's
|
|
27
|
+
canonical `repoOptionsFromCtx` extractor, plus revenue's `_bypassTenant`
|
|
28
|
+
flag for platform-admin cross-org reads. **Adding a new canonical context
|
|
29
|
+
field is now a single edit in `repo-options.ts` upstream, not three.**
|
|
30
|
+
- `protected dispatch(event, ctx)` — outbox-save (session-bound when
|
|
31
|
+
`ctx.session` is present) → transport-publish, with isolated try/catch
|
|
32
|
+
on each step (PACKAGE_RULES P8 / §5.5).
|
|
33
|
+
|
|
34
|
+
`TransactionRepository`, `SubscriptionRepository`, `SettlementRepository`
|
|
35
|
+
all extend the base. `BaseRevenueRepoDeps` (the shared `events` / `outbox`
|
|
36
|
+
/ `logger` trio) is now the canonical superset every per-repo `Deps`
|
|
37
|
+
interface extends.
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- **`tests/scenarios/subscription-tenancy.scenario.test.ts`** — 6 tests
|
|
42
|
+
proving each lifecycle verb works under `scope: { enabled, required }`,
|
|
43
|
+
cross-tenant access is rejected with `SubscriptionNotFoundError`, and
|
|
44
|
+
`multiTenantPlugin` is wired (canary: omitting ctx throws
|
|
45
|
+
`Missing organizationId`).
|
|
46
|
+
- **`tests/scenarios/settlement-tenancy.scenario.test.ts`** — 5 tests for
|
|
47
|
+
the same matrix on settlements: schedule, processPending, complete, fail,
|
|
48
|
+
cross-tenant rejection.
|
|
49
|
+
|
|
50
|
+
### Internal
|
|
51
|
+
|
|
52
|
+
- `RevenueRepositoryBase` is unexported on purpose — kept private to
|
|
53
|
+
the package. Adding a new repo means subclassing it; consumers stay
|
|
54
|
+
on the existing engine factory surface (`createRevenue(...)`).
|
|
55
|
+
- No public API change. Engine factory, repo method signatures, and
|
|
56
|
+
exported types are all byte-stable.
|
|
57
|
+
|
|
58
|
+
### Migration
|
|
59
|
+
|
|
60
|
+
None — this is a behavioural fix. If you were running 2.1.0 with
|
|
61
|
+
`scope: false` as a workaround for the lifecycle bugs, you can now turn
|
|
62
|
+
scope back on. Recommended config:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
await createRevenue({
|
|
66
|
+
connection: mongoose.connection,
|
|
67
|
+
scope: { enabled: true, fieldType: 'objectId', required: true },
|
|
68
|
+
// ...
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
6
72
|
## [2.0.0] — major rewrite
|
|
7
73
|
|
|
8
74
|
Payment lifecycle engine refactored around unified transactions, an
|
package/README.md
CHANGED
|
@@ -53,25 +53,48 @@ const refundTxn = await revenue.repositories.transaction.refund(
|
|
|
53
53
|
```
|
|
54
54
|
createRevenue(config) --> RevenueEngine
|
|
55
55
|
|
|
|
56
|
-
|-- repositories.transaction
|
|
57
|
-
|
|
|
56
|
+
|-- repositories.transaction extends RevenueRepositoryBase
|
|
57
|
+
| CRUD inherited (mongokit Repository)
|
|
58
58
|
| createPaymentIntent, verify, refund, handleWebhook (domain verbs)
|
|
59
59
|
| hold, release, split (escrow verbs)
|
|
60
|
+
| import, match, unmatch, journalize, reject, removeByFeed (bank-feed verbs)
|
|
60
61
|
|
|
|
61
|
-
|-- repositories.subscription
|
|
62
|
-
|
|
|
62
|
+
|-- repositories.subscription extends RevenueRepositoryBase
|
|
63
|
+
| CRUD inherited
|
|
63
64
|
| activate, cancel, pause, resume (domain verbs)
|
|
64
65
|
|
|
|
65
|
-
|-- repositories.settlement
|
|
66
|
-
|
|
|
66
|
+
|-- repositories.settlement extends RevenueRepositoryBase
|
|
67
|
+
| CRUD inherited
|
|
67
68
|
| schedule, processPending, complete, fail (domain verbs)
|
|
68
69
|
|
|
|
69
|
-
|-- providers
|
|
70
|
-
|-- events
|
|
71
|
-
|-- models
|
|
70
|
+
|-- providers ProviderRegistry
|
|
71
|
+
|-- events RevenueEventTransport (Arc-compatible)
|
|
72
|
+
|-- models Mongoose models (for Arc adapter)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
RevenueRepositoryBase (internal)
|
|
76
|
+
|
|
|
77
|
+
|-- extends mongokit Repository<TDoc>
|
|
78
|
+
|-- protected optsFromCtx(ctx, extra?) threads RevenueContext into mongokit
|
|
79
|
+
| options bag (uses repoOptionsFromCtx;
|
|
80
|
+
| forwards organizationId, userId,
|
|
81
|
+
| session, requestId + _bypassTenant)
|
|
82
|
+
|-- protected dispatch(event, ctx) outbox.save (session-bound) →
|
|
83
|
+
| events.publish (PACKAGE_RULES P8)
|
|
84
|
+
\-- protected deps: BaseRevenueRepoDeps events / outbox? / logger?
|
|
72
85
|
```
|
|
73
86
|
|
|
74
|
-
|
|
87
|
+
**Three repos. One scope-threading helper. One dispatch helper.** Every
|
|
88
|
+
domain verb routes its mongokit calls through `optsFromCtx(ctx)` so
|
|
89
|
+
multi-tenant scope, audit attribution, and transaction sessions land
|
|
90
|
+
on every read/write without per-method boilerplate. Every domain event
|
|
91
|
+
goes through `dispatch(event, ctx)` so outbox and transport semantics
|
|
92
|
+
stay consistent across the package.
|
|
93
|
+
|
|
94
|
+
CRUD, pagination, querying, and policy hooks come from
|
|
95
|
+
[`@classytic/mongokit`](https://www.npmjs.com/package/@classytic/mongokit).
|
|
96
|
+
Domain verbs contain real business logic (state machine transitions,
|
|
97
|
+
provider calls, event emission). No service layer. No proxy methods.
|
|
75
98
|
|
|
76
99
|
## RevenueConfig
|
|
77
100
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _ as TRANSACTION_STATUS, a as TRANSACTION_KIND } from "../bank-feed.enums-kYTLTTbe.mjs";
|
|
2
|
-
import { g as SETTLEMENT_STATUS, l as SPLIT_STATUS, r as SUBSCRIPTION_STATUS, w as HOLD_STATUS } from "../subscription.enums-
|
|
3
|
-
import { a as InvalidStateTransitionError } from "../errors-
|
|
2
|
+
import { g as SETTLEMENT_STATUS, l as SPLIT_STATUS, r as SUBSCRIPTION_STATUS, w as HOLD_STATUS } from "../subscription.enums-95othr0i.mjs";
|
|
3
|
+
import { a as InvalidStateTransitionError } from "../errors-LYYg9wcs.mjs";
|
|
4
4
|
import { defineStateMachine } from "@classytic/primitives/state-machine";
|
|
5
5
|
|
|
6
6
|
//#region src/core/state-machines.ts
|
|
@@ -6,7 +6,7 @@ import { RepositoryPluginBundle, RevenueRepositories } from "./repositories/crea
|
|
|
6
6
|
import { PluginType, Repository } from "@classytic/mongokit";
|
|
7
7
|
import { TenantConfig } from "@classytic/repo-core/tenant";
|
|
8
8
|
import mongoose, { Connection, Model, Schema } from "mongoose";
|
|
9
|
-
import { EventTransport } from "@classytic/primitives/events";
|
|
9
|
+
import { DomainEvent, EventTransport } from "@classytic/primitives/events";
|
|
10
10
|
import { OutboxStore } from "@classytic/primitives/outbox";
|
|
11
11
|
import { BankImportReport, BankImportRowError, BankTransaction } from "@classytic/primitives/bank-transaction";
|
|
12
12
|
import { ApprovalChain } from "@classytic/primitives/approval";
|
|
@@ -325,17 +325,125 @@ interface RevenueSchemaOptions {
|
|
|
325
325
|
};
|
|
326
326
|
}
|
|
327
327
|
//#endregion
|
|
328
|
-
//#region src/repositories/
|
|
329
|
-
|
|
328
|
+
//#region src/repositories/base.repository.d.ts
|
|
329
|
+
/**
|
|
330
|
+
* Cross-cutting deps that every revenue repository needs.
|
|
331
|
+
*
|
|
332
|
+
* Subclasses extend this with their own bridges, providers, configs:
|
|
333
|
+
*
|
|
334
|
+
* ```ts
|
|
335
|
+
* export interface SettlementRepoDeps extends BaseRevenueRepoDeps {
|
|
336
|
+
* bridges: RevenueBridges;
|
|
337
|
+
* }
|
|
338
|
+
* ```
|
|
339
|
+
*/
|
|
340
|
+
interface BaseRevenueRepoDeps {
|
|
341
|
+
/**
|
|
342
|
+
* Domain-event transport (in-process or arc-compatible). All four
|
|
343
|
+
* `revenue:*` event families (`payment.*`, `subscription.*`,
|
|
344
|
+
* `settlement.*`, `escrow.*`) flow through this single channel; hosts
|
|
345
|
+
* subscribe glob-style.
|
|
346
|
+
*/
|
|
330
347
|
events: EventTransport;
|
|
331
348
|
/**
|
|
332
|
-
* Optional host-owned outbox
|
|
333
|
-
* every
|
|
334
|
-
*
|
|
335
|
-
*
|
|
349
|
+
* Optional host-owned outbox (PACKAGE_RULES §5.5 + P8). When wired,
|
|
350
|
+
* every dispatched event is persisted via `outbox.save(event)` BEFORE
|
|
351
|
+
* `events.publish(event)` so a host relay (arc's EventOutbox, a Postgres
|
|
352
|
+
* LISTEN/NOTIFY pump, Kafka Connect, …) can replay on transport
|
|
336
353
|
* failure. When absent, events fire through `events.publish` only.
|
|
337
354
|
*/
|
|
338
355
|
outbox?: OutboxStore | undefined;
|
|
356
|
+
/**
|
|
357
|
+
* Optional structured logger. Outbox/transport failures are logged
|
|
358
|
+
* here rather than thrown — a downstream subscriber error never
|
|
359
|
+
* cancels the upstream business write.
|
|
360
|
+
*/
|
|
361
|
+
logger?: {
|
|
362
|
+
error(...args: unknown[]): void;
|
|
363
|
+
} | undefined;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Abstract base for `TransactionRepository`, `SubscriptionRepository`,
|
|
367
|
+
* `SettlementRepository`.
|
|
368
|
+
*
|
|
369
|
+
* Concrete subclasses MUST:
|
|
370
|
+
* - declare a `Deps` interface that extends {@link BaseRevenueRepoDeps}
|
|
371
|
+
* - call `super(model, plugins)` from the constructor
|
|
372
|
+
* - call `inject(deps)` once during engine boot
|
|
373
|
+
*
|
|
374
|
+
* Subclasses SHOULD use {@link optsFromCtx} for every mongokit call
|
|
375
|
+
* that takes an options bag, and {@link dispatch} for every event
|
|
376
|
+
* publish.
|
|
377
|
+
*/
|
|
378
|
+
declare abstract class RevenueRepositoryBase<TDoc, TDeps extends BaseRevenueRepoDeps> extends Repository<TDoc> {
|
|
379
|
+
/**
|
|
380
|
+
* Subclass-specific deps. `!` because the engine wires this once via
|
|
381
|
+
* `inject(deps)` immediately after construction; calling any domain
|
|
382
|
+
* verb before injection is a programming error and the runtime
|
|
383
|
+
* will fail-loud with `Cannot read properties of undefined`.
|
|
384
|
+
*/
|
|
385
|
+
protected deps: TDeps;
|
|
386
|
+
constructor(model: Model<TDoc>, plugins?: PluginType[]);
|
|
387
|
+
/**
|
|
388
|
+
* Wire engine-managed deps. Called exactly once per repository
|
|
389
|
+
* instance during {@link createRevenue} bootstrap. Subclasses with
|
|
390
|
+
* extra steps (caching, prebuilding state machine maps) override and
|
|
391
|
+
* call `super.inject(deps)`.
|
|
392
|
+
*/
|
|
393
|
+
inject(deps: TDeps): void;
|
|
394
|
+
/**
|
|
395
|
+
* Translate {@link RevenueContext} into a mongokit options bag.
|
|
396
|
+
*
|
|
397
|
+
* Forwards every canonical field mongokit's bundled plugins read —
|
|
398
|
+
* `organizationId` (multiTenant), `userId` / `user` (audit),
|
|
399
|
+
* `session` (transactions), `requestId` (observability) — plus
|
|
400
|
+
* the revenue-specific `_bypassTenant` flag for platform-admin
|
|
401
|
+
* cross-org reads.
|
|
402
|
+
*
|
|
403
|
+
* Pass `extra` for caller-specific options like `throwOnNotFound`,
|
|
404
|
+
* `lean`, `populate`, `select` — the spread is `extra`-first so
|
|
405
|
+
* ctx wins on a key collision (intentional: callers shouldn't
|
|
406
|
+
* be smuggling tenant fields through `extra`).
|
|
407
|
+
*
|
|
408
|
+
* @param ctx - The request-scoped revenue context.
|
|
409
|
+
* @param extra - Additional mongokit options merged in.
|
|
410
|
+
*/
|
|
411
|
+
protected optsFromCtx(ctx?: RevenueContext, extra?: Record<string, unknown>): Record<string, unknown>;
|
|
412
|
+
/**
|
|
413
|
+
* Persist an event to the host outbox, then publish to the in-process
|
|
414
|
+
* transport. The two sides have asymmetric failure handling — see
|
|
415
|
+
* PACKAGE_RULES §P8.
|
|
416
|
+
*
|
|
417
|
+
* When `ctx.session` is set, the outbox `save` runs inside the same
|
|
418
|
+
* Mongoose transaction (true P8 session-bound write); when absent, the
|
|
419
|
+
* outbox row lands after commit — still durable via the host's relay,
|
|
420
|
+
* with only a small at-most-once window on process crash.
|
|
421
|
+
*
|
|
422
|
+
* 1. **`outbox.save` failures PROPAGATE.** If we can't durably record
|
|
423
|
+
* the event, the caller's transaction MUST roll back so the
|
|
424
|
+
* business doc and the event row land atomically (or neither
|
|
425
|
+
* lands). Swallowing a save failure breaks the transactional-
|
|
426
|
+
* outbox correctness argument — the parent doc would land while
|
|
427
|
+
* the event vanishes.
|
|
428
|
+
*
|
|
429
|
+
* 2. **`events.publish` failures are SWALLOWED.** The host's outbox
|
|
430
|
+
* relay re-publishes from the durable row on its next poll. Even
|
|
431
|
+
* without an outbox, in-process subscribers shouldn't be able to
|
|
432
|
+
* break the business operation — they're best-effort consumers.
|
|
433
|
+
*
|
|
434
|
+
* @param event - Pre-built domain event (use `createEvent(REVENUE_EVENTS.X, payload, ctx, meta)`).
|
|
435
|
+
* @param ctx - The same context that produced the business write.
|
|
436
|
+
*/
|
|
437
|
+
protected dispatch(event: DomainEvent, ctx?: RevenueContext): Promise<void>;
|
|
438
|
+
}
|
|
439
|
+
//#endregion
|
|
440
|
+
//#region src/repositories/transaction.repository.d.ts
|
|
441
|
+
/**
|
|
442
|
+
* Deps for {@link TransactionRepository}. Extends {@link BaseRevenueRepoDeps}
|
|
443
|
+
* (events / outbox? / logger?) with provider/bridge/config wiring specific
|
|
444
|
+
* to the payment-flow + bank-feed lifecycles.
|
|
445
|
+
*/
|
|
446
|
+
interface TransactionRepoDeps extends BaseRevenueRepoDeps {
|
|
339
447
|
providers: ProviderRegistry;
|
|
340
448
|
/**
|
|
341
449
|
* Bank-feed provider registry (3.0). Optional — when omitted, the
|
|
@@ -346,47 +454,30 @@ interface TransactionRepoDeps {
|
|
|
346
454
|
bridges: RevenueBridges;
|
|
347
455
|
commission?: CommissionConfig;
|
|
348
456
|
defaultCurrency: string;
|
|
349
|
-
logger?: {
|
|
350
|
-
error(...args: unknown[]): void;
|
|
351
|
-
} | undefined;
|
|
352
457
|
}
|
|
353
|
-
declare class TransactionRepository extends
|
|
354
|
-
private deps;
|
|
458
|
+
declare class TransactionRepository extends RevenueRepositoryBase<TransactionDocument, TransactionRepoDeps> {
|
|
355
459
|
constructor(model: Model<TransactionDocument>, plugins?: PluginType[]);
|
|
356
|
-
inject(deps: TransactionRepoDeps): void;
|
|
357
|
-
/**
|
|
358
|
-
* Thread `ctx.organizationId` (and future ctx fields) into mongokit
|
|
359
|
-
* options so the `multiTenantPlugin` can auto-scope filters, queries,
|
|
360
|
-
* and inserts. Merges any caller-supplied extras. Centralizing this
|
|
361
|
-
* here means every domain verb participates in scope isolation without
|
|
362
|
-
* per-call boilerplate.
|
|
363
|
-
*/
|
|
364
|
-
private optsFromCtx;
|
|
365
460
|
/**
|
|
366
461
|
* Save an event to the host-owned outbox, session-bound when available.
|
|
367
462
|
*
|
|
368
|
-
*
|
|
369
|
-
* business write (P8 true session-bound write)
|
|
370
|
-
*
|
|
371
|
-
*
|
|
463
|
+
* Called INSIDE a `withTransaction` body. The outbox row commits
|
|
464
|
+
* atomically with the business write (P8 true session-bound write) —
|
|
465
|
+
* if outbox.save fails, this method re-throws so `withTransaction`
|
|
466
|
+
* rolls the parent write back. Without propagation, the parent doc
|
|
467
|
+
* would commit while the event row vanishes, defeating the whole
|
|
468
|
+
* transactional-outbox correctness argument.
|
|
372
469
|
*
|
|
373
|
-
*
|
|
374
|
-
*
|
|
470
|
+
* Logging happens before the re-throw so the failure surfaces in
|
|
471
|
+
* observability without losing the original stack trace.
|
|
375
472
|
*/
|
|
376
|
-
|
|
473
|
+
protected saveToOutbox(event: DomainEvent, session?: unknown): Promise<void>;
|
|
377
474
|
/**
|
|
378
475
|
* Publish an event to the in-process `EventTransport` after commit.
|
|
379
|
-
* Transport failure is logged — the host relay
|
|
380
|
-
* the outbox, so in-process
|
|
476
|
+
* Transport failure is logged and swallowed — the host relay re-delivers
|
|
477
|
+
* from the durable outbox row on the next poll, so in-process
|
|
478
|
+
* subscribers missing an event is recoverable. Best-effort by design.
|
|
381
479
|
*/
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* Non-transactional dispatch (used by verbs that don't open their own
|
|
385
|
-
* `withTransaction` block): outbox.save (session-bound when ctx provides
|
|
386
|
-
* one) → transport.publish. Matches arc's EventOutbox + MemoryEventTransport
|
|
387
|
-
* wiring bit-for-bit.
|
|
388
|
-
*/
|
|
389
|
-
private dispatch;
|
|
480
|
+
protected publishToTransport(event: DomainEvent): Promise<void>;
|
|
390
481
|
/** Creates transaction + calls provider. Returns the created transaction doc. */
|
|
391
482
|
createPaymentIntent(params: {
|
|
392
483
|
data?: Record<string, unknown>;
|
|
@@ -677,33 +768,53 @@ declare class TransactionRepository extends Repository<TransactionDocument> {
|
|
|
677
768
|
}
|
|
678
769
|
//#endregion
|
|
679
770
|
//#region src/repositories/subscription.repository.d.ts
|
|
680
|
-
interface SubscriptionRepoDeps {
|
|
681
|
-
events: EventTransport;
|
|
682
|
-
/** Host-owned outbox (PACKAGE_RULES §5.5 + P8). See TransactionRepoDeps. */
|
|
683
|
-
outbox?: OutboxStore | undefined;
|
|
684
|
-
logger?: {
|
|
685
|
-
error(...args: unknown[]): void;
|
|
686
|
-
} | undefined;
|
|
687
|
-
}
|
|
688
771
|
/**
|
|
689
|
-
*
|
|
772
|
+
* Deps for {@link SubscriptionRepository}. Currently identical to
|
|
773
|
+
* {@link BaseRevenueRepoDeps} (events / outbox? / logger?). Kept as its
|
|
774
|
+
* own type alias so future subscription-specific deps (e.g. a billing
|
|
775
|
+
* engine handle) can land without touching every callsite — and so the
|
|
776
|
+
* `inject(deps)` signature reads as `SubscriptionRepoDeps` at the
|
|
777
|
+
* engine factory.
|
|
778
|
+
*/
|
|
779
|
+
type SubscriptionRepoDeps = BaseRevenueRepoDeps;
|
|
780
|
+
/**
|
|
781
|
+
* SubscriptionRepository — data layer + domain verbs for the recurring-
|
|
782
|
+
* billing lifecycle.
|
|
783
|
+
*
|
|
784
|
+
* **CRUD inherited** from mongokit (via {@link RevenueRepositoryBase}):
|
|
785
|
+
* `getById`, `getByQuery`, `getAll`, `create`, `update`, `delete`,
|
|
786
|
+
* `findOneAndUpdate`, `count`, `exists`, `claim`, `cursor`, `updateMany`,
|
|
787
|
+
* `deleteMany`. All participate in `multiTenantPlugin` scope filtering
|
|
788
|
+
* when wired.
|
|
789
|
+
*
|
|
790
|
+
* **Domain verbs (state transitions):** `activate`, `cancel`, `pause`,
|
|
791
|
+
* `resume`. Each runs the state-machine guard (`SUBSCRIPTION_STATE_MACHINE`
|
|
792
|
+
* — invalid transitions throw, never silently no-op), persists the
|
|
793
|
+
* resulting writes through {@link RevenueRepositoryBase.optsFromCtx} so
|
|
794
|
+
* tenant scope is preserved end-to-end, then dispatches its
|
|
795
|
+
* `revenue:subscription.*` event via {@link RevenueRepositoryBase.dispatch}.
|
|
690
796
|
*
|
|
691
|
-
*
|
|
797
|
+
* **Multi-tenant correctness.** Every internal `getById`/`update` call
|
|
798
|
+
* threads `ctx.organizationId` through `optsFromCtx(ctx)`. Without this
|
|
799
|
+
* threading the inner read would either throw
|
|
800
|
+
* `Missing 'organizationId' in context` (when `multiTenantPlugin` is
|
|
801
|
+
* required) or — worse — return another tenant's subscription matching
|
|
802
|
+
* the same `_id` shape (when `required: false`). 2.1.0 had this bug; 2.1.1+
|
|
803
|
+
* is correct.
|
|
692
804
|
*
|
|
693
|
-
*
|
|
694
|
-
*
|
|
695
|
-
*
|
|
696
|
-
*
|
|
805
|
+
* @example Activate a pending sub
|
|
806
|
+
* ```ts
|
|
807
|
+
* const ctx = { organizationId: 'org_42', actorId: 'user_99' };
|
|
808
|
+
* const sub = await subRepo.create(
|
|
809
|
+
* { customerId: 'cust_1', planKey: 'monthly', amount: 999, currency: 'USD',
|
|
810
|
+
* status: SUBSCRIPTION_STATUS.PENDING, isActive: false },
|
|
811
|
+
* ctx,
|
|
812
|
+
* );
|
|
813
|
+
* await subRepo.activate(String(sub._id), {}, ctx);
|
|
814
|
+
* ```
|
|
697
815
|
*/
|
|
698
|
-
declare class SubscriptionRepository extends
|
|
699
|
-
private deps;
|
|
816
|
+
declare class SubscriptionRepository extends RevenueRepositoryBase<SubscriptionDocument, SubscriptionRepoDeps> {
|
|
700
817
|
constructor(model: Model<SubscriptionDocument>, plugins?: PluginType[]);
|
|
701
|
-
inject(deps: SubscriptionRepoDeps): void;
|
|
702
|
-
/**
|
|
703
|
-
* Host-owned outbox save → in-process transport publish (PACKAGE_RULES P8).
|
|
704
|
-
* Session-bound when `ctx.session` is present (atomic outbox row write).
|
|
705
|
-
*/
|
|
706
|
-
private dispatch;
|
|
707
818
|
activate(subscriptionId: string, options?: {
|
|
708
819
|
timestamp?: Date;
|
|
709
820
|
}, ctx?: RevenueContext): Promise<SubscriptionDocument>;
|
|
@@ -720,37 +831,38 @@ declare class SubscriptionRepository extends Repository<SubscriptionDocument> {
|
|
|
720
831
|
}
|
|
721
832
|
//#endregion
|
|
722
833
|
//#region src/repositories/settlement.repository.d.ts
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
834
|
+
/**
|
|
835
|
+
* Deps for {@link SettlementRepository}. Adds the optional
|
|
836
|
+
* `RevenueBridges` (ledger / notification ports) on top of
|
|
837
|
+
* {@link BaseRevenueRepoDeps}.
|
|
838
|
+
*/
|
|
839
|
+
interface SettlementRepoDeps extends BaseRevenueRepoDeps {
|
|
727
840
|
bridges: RevenueBridges;
|
|
728
|
-
logger?: {
|
|
729
|
-
error(...args: unknown[]): void;
|
|
730
|
-
} | undefined;
|
|
731
841
|
}
|
|
732
842
|
interface SettlementProcessingError {
|
|
733
843
|
settlementId: SettlementDocument['_id'];
|
|
734
844
|
error: unknown;
|
|
735
845
|
}
|
|
736
846
|
/**
|
|
737
|
-
* SettlementRepository —
|
|
847
|
+
* SettlementRepository — payouts to recipients (organizations, vendors,
|
|
848
|
+
* affiliates).
|
|
849
|
+
*
|
|
850
|
+
* **CRUD inherited** via {@link RevenueRepositoryBase}. **Domain verbs:**
|
|
851
|
+
* `schedule`, `processPending`, `complete`, `fail`. State machine:
|
|
852
|
+
* `pending → processing → completed | failed`; `failed` with
|
|
853
|
+
* `retry: true` resets to `pending` with a delayed `scheduledAt`.
|
|
738
854
|
*
|
|
739
|
-
*
|
|
855
|
+
* **Multi-tenant correctness.** Every read/write threads `ctx` through
|
|
856
|
+
* {@link RevenueRepositoryBase.optsFromCtx} so `multiTenantPlugin`
|
|
857
|
+
* scope filters apply. 2.1.0 had several `getById`/`update` calls that
|
|
858
|
+
* dropped ctx — fixed in 2.1.1+.
|
|
740
859
|
*
|
|
741
|
-
*
|
|
742
|
-
*
|
|
743
|
-
*
|
|
860
|
+
* Bridges (`ledger`, `notification`) fire on `complete()` so a host
|
|
861
|
+
* can pin double-entry book-keeping or push a "you got paid" email
|
|
862
|
+
* without the repo knowing about either subsystem.
|
|
744
863
|
*/
|
|
745
|
-
declare class SettlementRepository extends
|
|
746
|
-
private deps;
|
|
864
|
+
declare class SettlementRepository extends RevenueRepositoryBase<SettlementDocument, SettlementRepoDeps> {
|
|
747
865
|
constructor(model: Model<SettlementDocument>, plugins?: PluginType[]);
|
|
748
|
-
inject(deps: SettlementRepoDeps): void;
|
|
749
|
-
/**
|
|
750
|
-
* Host-owned outbox save → in-process transport publish (PACKAGE_RULES P8).
|
|
751
|
-
* Session-bound when `ctx.session` is present (atomic outbox row write).
|
|
752
|
-
*/
|
|
753
|
-
private dispatch;
|
|
754
866
|
schedule(params: {
|
|
755
867
|
organizationId: string;
|
|
756
868
|
recipientId: string;
|
package/dist/enums/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { _ as TRANSACTION_STATUS, a as TRANSACTION_KIND, b as isTransactionFlow, c as isBankFeedSource, d as isTransactionKind, f as statusesForKind, g as TRANSACTION_FLOW_VALUES, h as TRANSACTION_FLOW, i as BANK_FEED_STATUS_VALUES, l as isBankFeedStatus, m as LIBRARY_CATEGORY_VALUES, n as BANK_FEED_SOURCE_VALUES, o as TRANSACTION_KIND_VALUES, p as LIBRARY_CATEGORIES, r as BANK_FEED_STATUS, s as initialStatusFor, t as BANK_FEED_SOURCE, u as isStatusValidForKind, v as TRANSACTION_STATUS_VALUES, x as isTransactionStatus, y as isLibraryCategory } from "../bank-feed.enums-kYTLTTbe.mjs";
|
|
2
|
-
import { A as isReleaseReason, C as HOLD_REASON_VALUES, D as RELEASE_REASON_VALUES, E as RELEASE_REASON, O as isHoldReason, S as HOLD_REASON, T as HOLD_STATUS_VALUES, _ as SETTLEMENT_STATUS_VALUES, a as isPlanKey, b as isSettlementStatus, c as PAYOUT_METHOD_VALUES, d as SPLIT_TYPE, f as SPLIT_TYPE_VALUES, g as SETTLEMENT_STATUS, h as isSplitType, i as SUBSCRIPTION_STATUS_VALUES, k as isHoldStatus, l as SPLIT_STATUS, m as isSplitStatus, n as PLAN_KEY_VALUES, o as isSubscriptionStatus, p as isPayoutMethod, r as SUBSCRIPTION_STATUS, s as PAYOUT_METHOD, t as PLAN_KEYS, u as SPLIT_STATUS_VALUES, v as SETTLEMENT_TYPE, w as HOLD_STATUS, x as isSettlementType, y as SETTLEMENT_TYPE_VALUES } from "../subscription.enums-
|
|
2
|
+
import { A as isReleaseReason, C as HOLD_REASON_VALUES, D as RELEASE_REASON_VALUES, E as RELEASE_REASON, O as isHoldReason, S as HOLD_REASON, T as HOLD_STATUS_VALUES, _ as SETTLEMENT_STATUS_VALUES, a as isPlanKey, b as isSettlementStatus, c as PAYOUT_METHOD_VALUES, d as SPLIT_TYPE, f as SPLIT_TYPE_VALUES, g as SETTLEMENT_STATUS, h as isSplitType, i as SUBSCRIPTION_STATUS_VALUES, k as isHoldStatus, l as SPLIT_STATUS, m as isSplitStatus, n as PLAN_KEY_VALUES, o as isSubscriptionStatus, p as isPayoutMethod, r as SUBSCRIPTION_STATUS, s as PAYOUT_METHOD, t as PLAN_KEYS, u as SPLIT_STATUS_VALUES, v as SETTLEMENT_TYPE, w as HOLD_STATUS, x as isSettlementType, y as SETTLEMENT_TYPE_VALUES } from "../subscription.enums-95othr0i.mjs";
|
|
3
3
|
import { a as PAYMENT_GATEWAY_TYPE_VALUES, c as isPaymentGatewayType, i as PAYMENT_GATEWAY_TYPE, l as isPaymentStatus, n as MONETIZATION_TYPE_VALUES, o as PAYMENT_STATUS, r as isMonetizationType, s as PAYMENT_STATUS_VALUES, t as MONETIZATION_TYPES } from "../monetization.enums-B9HBOecd.mjs";
|
|
4
4
|
|
|
5
5
|
export { BANK_FEED_SOURCE, BANK_FEED_SOURCE_VALUES, BANK_FEED_STATUS, BANK_FEED_STATUS_VALUES, HOLD_REASON, HOLD_REASON_VALUES, HOLD_STATUS, HOLD_STATUS_VALUES, LIBRARY_CATEGORIES, LIBRARY_CATEGORY_VALUES, MONETIZATION_TYPES, MONETIZATION_TYPE_VALUES, PAYMENT_GATEWAY_TYPE, PAYMENT_GATEWAY_TYPE_VALUES, PAYMENT_STATUS, PAYMENT_STATUS_VALUES, PAYOUT_METHOD, PAYOUT_METHOD_VALUES, PLAN_KEYS, PLAN_KEY_VALUES, RELEASE_REASON, RELEASE_REASON_VALUES, SETTLEMENT_STATUS, SETTLEMENT_STATUS_VALUES, SETTLEMENT_TYPE, SETTLEMENT_TYPE_VALUES, SPLIT_STATUS, SPLIT_STATUS_VALUES, SPLIT_TYPE, SPLIT_TYPE_VALUES, SUBSCRIPTION_STATUS, SUBSCRIPTION_STATUS_VALUES, TRANSACTION_FLOW, TRANSACTION_FLOW_VALUES, TRANSACTION_KIND, TRANSACTION_KIND_VALUES, TRANSACTION_STATUS, TRANSACTION_STATUS_VALUES, initialStatusFor, isBankFeedSource, isBankFeedStatus, isHoldReason, isHoldStatus, isLibraryCategory, isMonetizationType, isPaymentGatewayType, isPaymentStatus, isPayoutMethod, isPlanKey, isReleaseReason, isSettlementStatus, isSettlementType, isSplitStatus, isSplitType, isStatusValidForKind, isSubscriptionStatus, isTransactionFlow, isTransactionKind, isTransactionStatus, statusesForKind };
|
|
@@ -95,18 +95,16 @@ declare const transactionBaseSchema: z.ZodObject<{
|
|
|
95
95
|
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
96
96
|
}, z.core.$strip>;
|
|
97
97
|
declare const transactionCreateSchema: z.ZodObject<{
|
|
98
|
-
organizationId: z.ZodOptional<z.ZodString>;
|
|
99
|
-
customerId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
100
98
|
type: z.ZodString;
|
|
101
99
|
amount: z.ZodNumber;
|
|
102
100
|
currency: z.ZodString;
|
|
103
|
-
status: z.ZodDefault<z.ZodString>;
|
|
104
|
-
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
105
101
|
relatedTransactionId: z.ZodOptional<z.ZodString>;
|
|
106
102
|
flow: z.ZodEnum<{
|
|
107
103
|
inflow: "inflow";
|
|
108
104
|
outflow: "outflow";
|
|
109
105
|
}>;
|
|
106
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
107
|
+
status: z.ZodDefault<z.ZodString>;
|
|
110
108
|
gateway: z.ZodOptional<z.ZodObject<{
|
|
111
109
|
type: z.ZodString;
|
|
112
110
|
sessionId: z.ZodOptional<z.ZodString>;
|
|
@@ -115,9 +113,11 @@ declare const transactionCreateSchema: z.ZodObject<{
|
|
|
115
113
|
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
116
114
|
verificationData: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
117
115
|
}, z.core.$strip>>;
|
|
116
|
+
customerId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
118
117
|
sourceId: z.ZodOptional<z.ZodString>;
|
|
119
118
|
sourceModel: z.ZodOptional<z.ZodString>;
|
|
120
119
|
idempotencyKey: z.ZodOptional<z.ZodString>;
|
|
120
|
+
organizationId: z.ZodOptional<z.ZodString>;
|
|
121
121
|
tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
122
122
|
fee: z.ZodDefault<z.ZodNumber>;
|
|
123
123
|
tax: z.ZodDefault<z.ZodNumber>;
|
|
@@ -315,20 +315,20 @@ declare const subscriptionBaseSchema: z.ZodObject<{
|
|
|
315
315
|
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
316
316
|
}, z.core.$strip>;
|
|
317
317
|
declare const subscriptionCreateSchema: z.ZodObject<{
|
|
318
|
-
organizationId: z.ZodOptional<z.ZodString>;
|
|
319
|
-
customerId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
320
|
-
planKey: z.ZodString;
|
|
321
318
|
amount: z.ZodNumber;
|
|
322
319
|
currency: z.ZodOptional<z.ZodString>;
|
|
323
|
-
|
|
320
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
321
|
+
customerId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
322
|
+
planKey: z.ZodString;
|
|
324
323
|
paymentIntentId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
324
|
+
transactionId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
325
|
+
organizationId: z.ZodOptional<z.ZodString>;
|
|
325
326
|
startDate: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
326
327
|
endDate: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
327
328
|
pauseReason: z.ZodOptional<z.ZodString>;
|
|
328
329
|
cancelAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
329
330
|
cancellationReason: z.ZodOptional<z.ZodString>;
|
|
330
331
|
renewalTransactionId: z.ZodOptional<z.ZodString>;
|
|
331
|
-
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
332
332
|
}, z.core.$strip>;
|
|
333
333
|
declare const subscriptionUpdateSchema: z.ZodObject<{
|
|
334
334
|
publicId: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
@@ -433,7 +433,6 @@ declare const settlementBaseSchema: z.ZodObject<{
|
|
|
433
433
|
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
434
434
|
}, z.core.$strip>;
|
|
435
435
|
declare const settlementCreateSchema: z.ZodObject<{
|
|
436
|
-
organizationId: z.ZodString;
|
|
437
436
|
type: z.ZodEnum<{
|
|
438
437
|
split_payout: "split_payout";
|
|
439
438
|
platform_withdrawal: "platform_withdrawal";
|
|
@@ -442,8 +441,8 @@ declare const settlementCreateSchema: z.ZodObject<{
|
|
|
442
441
|
}>;
|
|
443
442
|
amount: z.ZodNumber;
|
|
444
443
|
currency: z.ZodString;
|
|
445
|
-
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
446
444
|
notes: z.ZodOptional<z.ZodString>;
|
|
445
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
447
446
|
recipientId: z.ZodString;
|
|
448
447
|
recipientType: z.ZodEnum<{
|
|
449
448
|
platform: "platform";
|
|
@@ -452,6 +451,7 @@ declare const settlementCreateSchema: z.ZodObject<{
|
|
|
452
451
|
affiliate: "affiliate";
|
|
453
452
|
partner: "partner";
|
|
454
453
|
}>;
|
|
454
|
+
organizationId: z.ZodString;
|
|
455
455
|
payoutMethod: z.ZodEnum<{
|
|
456
456
|
manual: "manual";
|
|
457
457
|
bank_transfer: "bank_transfer";
|
package/dist/events/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as createEvent, t as REVENUE_EVENTS } from "../event-constants-
|
|
2
|
-
import { A as TransactionUpdated, C as SubscriptionResumed, D as TransactionRejected, E as TransactionMatched, M as revenueEventDefinitions, N as InProcessRevenueBus, O as TransactionRemovedByFeed, S as SubscriptionRenewed, T as TransactionJournalized, _ as SettlementScheduled, a as FreeCreated, b as SubscriptionCreated, c as PaymentProcessing, d as PaymentVerified, f as PurchaseCreated, g as SettlementProcessing, h as SettlementFailed, i as EscrowSplit, j as WebhookProcessed, k as TransactionUnmatched, l as PaymentRefunded, m as SettlementCreated, n as EscrowHeld, o as MonetizationCreated, p as SettlementCompleted, r as EscrowReleased, s as PaymentFailed, t as EscrowCancelled, u as PaymentRequiresAction, v as SubscriptionActivated, w as TransactionImported, x as SubscriptionPaused, y as SubscriptionCancelled } from "../revenue-event-catalog-
|
|
1
|
+
import { n as createEvent, t as REVENUE_EVENTS } from "../event-constants-Dn1TKahe.mjs";
|
|
2
|
+
import { A as TransactionUpdated, C as SubscriptionResumed, D as TransactionRejected, E as TransactionMatched, M as revenueEventDefinitions, N as InProcessRevenueBus, O as TransactionRemovedByFeed, S as SubscriptionRenewed, T as TransactionJournalized, _ as SettlementScheduled, a as FreeCreated, b as SubscriptionCreated, c as PaymentProcessing, d as PaymentVerified, f as PurchaseCreated, g as SettlementProcessing, h as SettlementFailed, i as EscrowSplit, j as WebhookProcessed, k as TransactionUnmatched, l as PaymentRefunded, m as SettlementCreated, n as EscrowHeld, o as MonetizationCreated, p as SettlementCompleted, r as EscrowReleased, s as PaymentFailed, t as EscrowCancelled, u as PaymentRequiresAction, v as SubscriptionActivated, w as TransactionImported, x as SubscriptionPaused, y as SubscriptionCancelled } from "../revenue-event-catalog-BvjNVnPd.mjs";
|
|
3
3
|
|
|
4
4
|
export { EscrowCancelled, EscrowHeld, EscrowReleased, EscrowSplit, FreeCreated, InProcessRevenueBus, MonetizationCreated, PaymentFailed, PaymentProcessing, PaymentRefunded, PaymentRequiresAction, PaymentVerified, PurchaseCreated, REVENUE_EVENTS, SettlementCompleted, SettlementCreated, SettlementFailed, SettlementProcessing, SettlementScheduled, SubscriptionActivated, SubscriptionCancelled, SubscriptionCreated, SubscriptionPaused, SubscriptionRenewed, SubscriptionResumed, TransactionImported, TransactionJournalized, TransactionMatched, TransactionRejected, TransactionRemovedByFeed, TransactionUnmatched, TransactionUpdated, WebhookProcessed, createEvent, revenueEventDefinitions };
|