@classytic/revenue 2.0.1 → 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-BlQeq2rK.mjs +133 -0
- package/dist/bank-feed.enums-BadqNJTC.d.mts +118 -0
- package/dist/bank-feed.enums-kYTLTTbe.mjs +165 -0
- package/dist/bridges/index.d.mts +1 -1
- package/dist/core/state-machines.d.mts +25 -2
- package/dist/core/state-machines.mjs +43 -3
- package/dist/engine-types-Jctrbasz.d.mts +1160 -0
- package/dist/enums/index.d.mts +4 -3
- package/dist/enums/index.mjs +4 -3
- package/dist/{errors-DHa8JVQ-.mjs → errors-LYYg9wcs.mjs} +23 -1
- package/dist/{escrow.schema-D5X32LwX.d.mts → escrow.schema-YuBgjL-I.d.mts} +27 -27
- package/dist/{event-constants-CEMitnIV.mjs → event-constants-Dn1TKahe.mjs} +6 -0
- package/dist/events/index.d.mts +2 -2
- package/dist/events/index.mjs +3 -3
- package/dist/index.d.mts +32 -13
- package/dist/index.mjs +142 -19
- package/dist/providers/index.d.mts +2 -2
- package/dist/providers/index.mjs +2 -2
- package/dist/registry-h8sasoLh.d.mts +145 -0
- package/dist/repositories/create-repositories.d.mts +1 -1
- package/dist/repositories/create-repositories.mjs +1 -1
- package/dist/{revenue-bridges-sdlrR85c.d.mts → revenue-bridges-BtkWFsJu.d.mts} +107 -1
- package/dist/{revenue-event-catalog-LqxPnsU_.mjs → revenue-event-catalog-BvjNVnPd.mjs} +77 -3
- package/dist/{revenue-event-catalog-BX3g7RUi.d.mts → revenue-event-catalog-JpJcyK1E.d.mts} +198 -2
- package/dist/settlement.repository-BAdc9qGl.mjs +1444 -0
- package/dist/shared/index.d.mts +1 -1
- package/dist/shared/index.mjs +2 -2
- package/dist/{subscription.enums-tfoAgsTv.mjs → subscription.enums-95othr0i.mjs} +1 -40
- package/dist/{transaction.enums-u4MshXcL.d.mts → subscription.enums-k24kLpF7.d.mts} +1 -36
- package/dist/validators/index.d.mts +158 -2
- package/dist/validators/index.mjs +95 -2
- package/package.json +7 -7
- package/dist/engine-types-CcjIb4Fy.d.mts +0 -611
- package/dist/registry-DhFMsSn5.mjs +0 -150
- package/dist/registry-SvIGPAx_.d.mts +0 -143
- package/dist/settlement.repository-DHIPx5S4.mjs +0 -771
- /package/dist/{audit-B39B0Sdq.mjs → audit-Ba2XB2C4.mjs} +0 -0
- /package/dist/{audit-DZ0eTr9g.d.mts → audit-DRKuLBFO.d.mts} +0 -0
- /package/dist/{context-DRqSeTPM.d.mts → context-pjP1QeE3.d.mts} +0 -0
- /package/dist/{escrow.schema-BBv9oVEW.mjs → escrow.schema-C-b41z_G.mjs} +0 -0
- /package/dist/{monetization.enums-BtiU3t8o.mjs → monetization.enums-B9HBOecd.mjs} +0 -0
- /package/dist/{monetization.enums-D2xbxXJM.d.mts → monetization.enums-DzAI4sT7.d.mts} +0 -0
- /package/dist/{splits-BAfY-a9P.mjs → splits-CNfQj92L.mjs} +0 -0
|
@@ -0,0 +1,1160 @@
|
|
|
1
|
+
import { t as RevenueContext } from "./context-pjP1QeE3.mjs";
|
|
2
|
+
import { t as RevenueBridges } from "./revenue-bridges-BtkWFsJu.mjs";
|
|
3
|
+
import { u as TransactionKindValue } from "./bank-feed.enums-BadqNJTC.mjs";
|
|
4
|
+
import { a as BankFeedProviderRegistry, d as PaymentProvider, o as FetchTransactionsParams, r as BankFeedProvider, t as ProviderRegistry } from "./registry-h8sasoLh.mjs";
|
|
5
|
+
import { RepositoryPluginBundle, RevenueRepositories } from "./repositories/create-repositories.mjs";
|
|
6
|
+
import { PluginType, Repository } from "@classytic/mongokit";
|
|
7
|
+
import { TenantConfig } from "@classytic/repo-core/tenant";
|
|
8
|
+
import mongoose, { Connection, Model, Schema } from "mongoose";
|
|
9
|
+
import { DomainEvent, EventTransport } from "@classytic/primitives/events";
|
|
10
|
+
import { OutboxStore } from "@classytic/primitives/outbox";
|
|
11
|
+
import { BankImportReport, BankImportRowError, BankTransaction } from "@classytic/primitives/bank-transaction";
|
|
12
|
+
import { ApprovalChain } from "@classytic/primitives/approval";
|
|
13
|
+
|
|
14
|
+
//#region src/models/transaction.schema.d.ts
|
|
15
|
+
/**
|
|
16
|
+
* Counterparty on a bank-feed entry. Untyped Mixed at the Mongoose level
|
|
17
|
+
* (banks populate different fields), but typed here so consumers and
|
|
18
|
+
* validators can introspect.
|
|
19
|
+
*/
|
|
20
|
+
interface TransactionCounterparty {
|
|
21
|
+
name?: string;
|
|
22
|
+
identifier?: string;
|
|
23
|
+
iban?: string;
|
|
24
|
+
accountNumber?: string;
|
|
25
|
+
bic?: string;
|
|
26
|
+
routingNumber?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Polymorphic ledger journal-entry reference. String-typed (PACKAGE_RULES
|
|
30
|
+
* §7) so it works for ledger documents stored in another connection,
|
|
31
|
+
* Postgres ledgers, or external accounting systems (QBO Journal IDs).
|
|
32
|
+
*/
|
|
33
|
+
interface JournalEntryRef {
|
|
34
|
+
type: string;
|
|
35
|
+
id: string;
|
|
36
|
+
}
|
|
37
|
+
interface TransactionDocument {
|
|
38
|
+
_id: mongoose.Types.ObjectId;
|
|
39
|
+
publicId: string;
|
|
40
|
+
organizationId?: string;
|
|
41
|
+
customerId?: string | null;
|
|
42
|
+
/**
|
|
43
|
+
* Selects the state machine that governs this row's `status` field.
|
|
44
|
+
* Defaults to `'payment_flow'` so existing data reads identically.
|
|
45
|
+
* See `core/state-machines.ts:smFor()`.
|
|
46
|
+
*/
|
|
47
|
+
kind: TransactionKindValue;
|
|
48
|
+
type: string;
|
|
49
|
+
flow: 'inflow' | 'outflow';
|
|
50
|
+
tags: string[];
|
|
51
|
+
amount: number;
|
|
52
|
+
currency: string;
|
|
53
|
+
fee: number;
|
|
54
|
+
tax: number;
|
|
55
|
+
net: number;
|
|
56
|
+
taxDetails?: {
|
|
57
|
+
type?: string;
|
|
58
|
+
rate?: number;
|
|
59
|
+
isInclusive?: boolean;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Multi-currency reconciliation (3.0). When the bank deposit clears
|
|
63
|
+
* in a currency different from the originating charge, store the
|
|
64
|
+
* other side here. Cross-currency `findMatchCandidates` reads these
|
|
65
|
+
* to compare same-currency-equivalent amounts.
|
|
66
|
+
*/
|
|
67
|
+
fxRate?: number;
|
|
68
|
+
originalAmount?: number;
|
|
69
|
+
originalCurrency?: string;
|
|
70
|
+
method: string;
|
|
71
|
+
status: string;
|
|
72
|
+
/**
|
|
73
|
+
* Optional embedded approval chain — P7. Hosts that gate manual /
|
|
74
|
+
* non-gateway transactions on a maker-checker review attach a chain via
|
|
75
|
+
* `createChain()` from `@classytic/primitives/approval`; the host's
|
|
76
|
+
* approval action checks `isApproved(doc.approvals)` before flipping
|
|
77
|
+
* status to `succeeded`. Auto-gateway transactions (Stripe, SSLCommerz,
|
|
78
|
+
* etc.) bypass this entirely — synchronous gateway success is the gate.
|
|
79
|
+
*
|
|
80
|
+
* Use cases:
|
|
81
|
+
* - Manual cash receipts (front desk → bookkeeper verify)
|
|
82
|
+
* - Manual bank transfers (supplier-paid → finance verify)
|
|
83
|
+
* - Cheque deposits (queued until cleared)
|
|
84
|
+
* - **Refunds especially** — the audit-defining moment
|
|
85
|
+
*/
|
|
86
|
+
approvals?: ApprovalChain;
|
|
87
|
+
gateway?: {
|
|
88
|
+
type: string;
|
|
89
|
+
sessionId?: string;
|
|
90
|
+
paymentIntentId?: string;
|
|
91
|
+
chargeId?: string;
|
|
92
|
+
metadata?: Record<string, unknown>;
|
|
93
|
+
verificationData?: Record<string, unknown>;
|
|
94
|
+
};
|
|
95
|
+
paymentDetails?: Record<string, unknown>;
|
|
96
|
+
commission?: {
|
|
97
|
+
rate: number;
|
|
98
|
+
grossAmount: number;
|
|
99
|
+
gatewayFeeRate: number;
|
|
100
|
+
gatewayFeeAmount: number;
|
|
101
|
+
netAmount: number;
|
|
102
|
+
status: string;
|
|
103
|
+
};
|
|
104
|
+
splits?: Array<{
|
|
105
|
+
type: string;
|
|
106
|
+
recipientId: string;
|
|
107
|
+
recipientType: string;
|
|
108
|
+
rate: number;
|
|
109
|
+
grossAmount: number;
|
|
110
|
+
gatewayFeeRate: number;
|
|
111
|
+
gatewayFeeAmount: number;
|
|
112
|
+
netAmount: number;
|
|
113
|
+
status: string;
|
|
114
|
+
}>;
|
|
115
|
+
hold?: {
|
|
116
|
+
status: string;
|
|
117
|
+
heldAmount: number;
|
|
118
|
+
releasedAmount: number;
|
|
119
|
+
reason: string;
|
|
120
|
+
heldAt: Date;
|
|
121
|
+
holdUntil?: Date;
|
|
122
|
+
releasedAt?: Date;
|
|
123
|
+
cancelledAt?: Date;
|
|
124
|
+
releases: Array<{
|
|
125
|
+
amount: number;
|
|
126
|
+
recipientId: string;
|
|
127
|
+
recipientType: string;
|
|
128
|
+
releasedAt: Date;
|
|
129
|
+
releasedBy?: string;
|
|
130
|
+
reason?: string;
|
|
131
|
+
metadata?: Record<string, unknown>;
|
|
132
|
+
}>;
|
|
133
|
+
metadata?: Record<string, unknown>;
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Vendor-stable id from the upstream feed (FITID, NtryRef, qbo Id,
|
|
137
|
+
* Plaid transaction_id). Used to enforce idempotent re-import via the
|
|
138
|
+
* `(orgId, bankAccountId, externalId)` partial unique index.
|
|
139
|
+
*
|
|
140
|
+
* Distinct from `idempotencyKey` (host-chosen, request-scoped). See
|
|
141
|
+
* `@classytic/primitives/bank-transaction` and PACKAGE_RULES §8.
|
|
142
|
+
*/
|
|
143
|
+
externalId?: string;
|
|
144
|
+
/** When the bank booked the entry. */
|
|
145
|
+
postedDate?: Date;
|
|
146
|
+
/** When funds clear / become available. */
|
|
147
|
+
valueDate?: Date;
|
|
148
|
+
/** Free-text bank description. */
|
|
149
|
+
description?: string;
|
|
150
|
+
counterparty?: TransactionCounterparty;
|
|
151
|
+
/** Check number, payment reference, end-to-end ID. */
|
|
152
|
+
reference?: string;
|
|
153
|
+
/** Running balance on the account after this entry, if the format provides it. */
|
|
154
|
+
balanceAfter?: number;
|
|
155
|
+
/** Bank's own category (rare in OFX, common in Plaid / Mint exports). */
|
|
156
|
+
vendorCategory?: string;
|
|
157
|
+
/**
|
|
158
|
+
* Polymorphic external ref to the bank account this row belongs to.
|
|
159
|
+
* String per PACKAGE_RULES §7 — accepts ObjectId hex, UUID, external
|
|
160
|
+
* IDs (Plaid `account_id`, QBO Account Id). Distinct from
|
|
161
|
+
* `customerId`; the `bankAccount` resource lives on the host side.
|
|
162
|
+
*/
|
|
163
|
+
bankAccountId?: string;
|
|
164
|
+
/**
|
|
165
|
+
* Provenance — which feed produced this row. One of the values from
|
|
166
|
+
* `BANK_FEED_SOURCE` (`'ofx'`, `'plaid'`, `'qbo'`, …). Drives admin UI
|
|
167
|
+
* filtering and duplicate-detection rules.
|
|
168
|
+
*/
|
|
169
|
+
source?: string;
|
|
170
|
+
/**
|
|
171
|
+
* Bidirectional link to the journal entry that posted this row to the
|
|
172
|
+
* GL. Stamped by `journalize()` after the host's LedgerBridge confirms
|
|
173
|
+
* the JE was created. Polymorphic — `type` names the foreign model
|
|
174
|
+
* (`'JournalEntry'`, `'QboJournalEntry'`, …).
|
|
175
|
+
*/
|
|
176
|
+
journalEntryRef?: JournalEntryRef;
|
|
177
|
+
/**
|
|
178
|
+
* Operator-supplied mapping from bank line → GL accounts. Stored at
|
|
179
|
+
* match time so re-running the journalize step is deterministic.
|
|
180
|
+
*/
|
|
181
|
+
matching?: {
|
|
182
|
+
debitAccount?: string;
|
|
183
|
+
creditAccount?: string;
|
|
184
|
+
notes?: string;
|
|
185
|
+
matchedAt?: Date;
|
|
186
|
+
matchedBy?: string;
|
|
187
|
+
};
|
|
188
|
+
sourceId?: string;
|
|
189
|
+
sourceModel?: string;
|
|
190
|
+
relatedTransactionId?: mongoose.Types.ObjectId;
|
|
191
|
+
refundedAmount?: number;
|
|
192
|
+
refundedAt?: Date;
|
|
193
|
+
failureReason?: string;
|
|
194
|
+
failedAt?: Date;
|
|
195
|
+
verifiedAt?: Date;
|
|
196
|
+
verifiedBy?: string;
|
|
197
|
+
webhook?: {
|
|
198
|
+
eventId: string;
|
|
199
|
+
eventType: string;
|
|
200
|
+
receivedAt: Date;
|
|
201
|
+
processedAt: Date;
|
|
202
|
+
data: Record<string, unknown>;
|
|
203
|
+
};
|
|
204
|
+
idempotencyKey?: string;
|
|
205
|
+
metadata?: Record<string, unknown>;
|
|
206
|
+
deletedAt?: Date | null;
|
|
207
|
+
createdAt: Date;
|
|
208
|
+
updatedAt: Date;
|
|
209
|
+
}
|
|
210
|
+
//#endregion
|
|
211
|
+
//#region src/models/subscription.schema.d.ts
|
|
212
|
+
interface SubscriptionDocument {
|
|
213
|
+
_id: mongoose.Types.ObjectId;
|
|
214
|
+
publicId: string;
|
|
215
|
+
organizationId?: string;
|
|
216
|
+
customerId?: string | null;
|
|
217
|
+
planKey: string;
|
|
218
|
+
amount: number;
|
|
219
|
+
currency?: string;
|
|
220
|
+
status: string;
|
|
221
|
+
isActive: boolean;
|
|
222
|
+
transactionId?: mongoose.Types.ObjectId | null;
|
|
223
|
+
paymentIntentId?: string | null;
|
|
224
|
+
startDate?: Date;
|
|
225
|
+
endDate?: Date;
|
|
226
|
+
activatedAt?: Date;
|
|
227
|
+
pausedAt?: Date;
|
|
228
|
+
pauseReason?: string;
|
|
229
|
+
canceledAt?: Date;
|
|
230
|
+
cancelAt?: Date;
|
|
231
|
+
cancellationReason?: string;
|
|
232
|
+
/**
|
|
233
|
+
* Optional embedded approval chain — P7. Hosts that gate high-value
|
|
234
|
+
* subscription transitions on a maker-checker review attach a chain via
|
|
235
|
+
* `createChain()` from `@classytic/primitives/approval`; the host's
|
|
236
|
+
* approval action checks `isApproved(doc.approvals)` before applying
|
|
237
|
+
* the change. Routine renewals/auto-pause flows leave it undefined.
|
|
238
|
+
*
|
|
239
|
+
* Use cases:
|
|
240
|
+
* - Cancel-with-refund (manager sign-off before issuing credit)
|
|
241
|
+
* - Plan change with credit note (finance verifies proration)
|
|
242
|
+
* - Manual reactivation after dunning failure
|
|
243
|
+
*/
|
|
244
|
+
approvals?: ApprovalChain;
|
|
245
|
+
renewalTransactionId?: mongoose.Types.ObjectId;
|
|
246
|
+
renewalCount: number;
|
|
247
|
+
metadata?: Record<string, unknown>;
|
|
248
|
+
deletedAt?: Date | null;
|
|
249
|
+
createdAt: Date;
|
|
250
|
+
updatedAt: Date;
|
|
251
|
+
}
|
|
252
|
+
//#endregion
|
|
253
|
+
//#region src/models/settlement.schema.d.ts
|
|
254
|
+
interface SettlementDocument {
|
|
255
|
+
_id: mongoose.Types.ObjectId;
|
|
256
|
+
publicId: string;
|
|
257
|
+
organizationId: string;
|
|
258
|
+
recipientId: string;
|
|
259
|
+
recipientType: string;
|
|
260
|
+
type: string;
|
|
261
|
+
status: string;
|
|
262
|
+
/**
|
|
263
|
+
* Optional embedded approval chain — P7. Hosts that gate payout release
|
|
264
|
+
* on a maker-checker review attach a chain via `createChain()` from
|
|
265
|
+
* `@classytic/primitives/approval`; the host's approval action checks
|
|
266
|
+
* `isApproved(doc.approvals)` before transitioning the settlement to
|
|
267
|
+
* `processed`/`completed`. Auto-disbursement flows leave it undefined.
|
|
268
|
+
*
|
|
269
|
+
* Use cases:
|
|
270
|
+
* - Vendor payout sign-off (finance verifies before funds release)
|
|
271
|
+
* - High-value mobile-wallet / crypto disbursement review
|
|
272
|
+
* - Manual bank-transfer payouts to recipients
|
|
273
|
+
*/
|
|
274
|
+
approvals?: ApprovalChain;
|
|
275
|
+
payoutMethod: string;
|
|
276
|
+
amount: number;
|
|
277
|
+
currency: string;
|
|
278
|
+
sourceTransactionIds: mongoose.Types.ObjectId[];
|
|
279
|
+
sourceSplitIds: string[];
|
|
280
|
+
bankTransferDetails?: Record<string, unknown>;
|
|
281
|
+
mobileWalletDetails?: Record<string, unknown>;
|
|
282
|
+
cryptoDetails?: Record<string, unknown>;
|
|
283
|
+
scheduledAt: Date;
|
|
284
|
+
processedAt?: Date;
|
|
285
|
+
completedAt?: Date;
|
|
286
|
+
failedAt?: Date;
|
|
287
|
+
cancelledAt?: Date;
|
|
288
|
+
failureReason?: string;
|
|
289
|
+
failureCode?: string;
|
|
290
|
+
retryCount: number;
|
|
291
|
+
notes?: string;
|
|
292
|
+
metadata?: Record<string, unknown>;
|
|
293
|
+
deletedAt?: Date | null;
|
|
294
|
+
createdAt: Date;
|
|
295
|
+
updatedAt: Date;
|
|
296
|
+
}
|
|
297
|
+
//#endregion
|
|
298
|
+
//#region src/models/create-models.d.ts
|
|
299
|
+
interface RevenueModels {
|
|
300
|
+
Transaction: Model<TransactionDocument>;
|
|
301
|
+
Subscription?: Model<SubscriptionDocument>;
|
|
302
|
+
Settlement?: Model<SettlementDocument>;
|
|
303
|
+
}
|
|
304
|
+
interface RevenueSchemaOptions {
|
|
305
|
+
transaction?: {
|
|
306
|
+
extraFields?: Record<string, unknown>;
|
|
307
|
+
extraIndexes?: Array<{
|
|
308
|
+
fields: Record<string, 1 | -1>;
|
|
309
|
+
options?: Record<string, unknown>;
|
|
310
|
+
}>;
|
|
311
|
+
};
|
|
312
|
+
subscription?: {
|
|
313
|
+
extraFields?: Record<string, unknown>;
|
|
314
|
+
extraIndexes?: Array<{
|
|
315
|
+
fields: Record<string, 1 | -1>;
|
|
316
|
+
options?: Record<string, unknown>;
|
|
317
|
+
}>;
|
|
318
|
+
};
|
|
319
|
+
settlement?: {
|
|
320
|
+
extraFields?: Record<string, unknown>;
|
|
321
|
+
extraIndexes?: Array<{
|
|
322
|
+
fields: Record<string, 1 | -1>;
|
|
323
|
+
options?: Record<string, unknown>;
|
|
324
|
+
}>;
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
//#endregion
|
|
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
|
+
*/
|
|
347
|
+
events: EventTransport;
|
|
348
|
+
/**
|
|
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
|
|
353
|
+
* failure. When absent, events fire through `events.publish` only.
|
|
354
|
+
*/
|
|
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 {
|
|
447
|
+
providers: ProviderRegistry;
|
|
448
|
+
/**
|
|
449
|
+
* Bank-feed provider registry (3.0). Optional — when omitted, the
|
|
450
|
+
* `drainSync` and `parseAndImport` verbs throw on use. The host typically
|
|
451
|
+
* wires Plaid / fin-io / a custom CSV provider here.
|
|
452
|
+
*/
|
|
453
|
+
bankFeedProviders?: BankFeedProviderRegistry | undefined;
|
|
454
|
+
bridges: RevenueBridges;
|
|
455
|
+
commission?: CommissionConfig;
|
|
456
|
+
defaultCurrency: string;
|
|
457
|
+
}
|
|
458
|
+
declare class TransactionRepository extends RevenueRepositoryBase<TransactionDocument, TransactionRepoDeps> {
|
|
459
|
+
constructor(model: Model<TransactionDocument>, plugins?: PluginType[]);
|
|
460
|
+
/**
|
|
461
|
+
* Save an event to the host-owned outbox, session-bound when available.
|
|
462
|
+
*
|
|
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.
|
|
469
|
+
*
|
|
470
|
+
* Logging happens before the re-throw so the failure surfaces in
|
|
471
|
+
* observability without losing the original stack trace.
|
|
472
|
+
*/
|
|
473
|
+
protected saveToOutbox(event: DomainEvent, session?: unknown): Promise<void>;
|
|
474
|
+
/**
|
|
475
|
+
* Publish an event to the in-process `EventTransport` after commit.
|
|
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.
|
|
479
|
+
*/
|
|
480
|
+
protected publishToTransport(event: DomainEvent): Promise<void>;
|
|
481
|
+
/** Creates transaction + calls provider. Returns the created transaction doc. */
|
|
482
|
+
createPaymentIntent(params: {
|
|
483
|
+
data?: Record<string, unknown>;
|
|
484
|
+
planKey?: string;
|
|
485
|
+
monetizationType?: string;
|
|
486
|
+
amount: number;
|
|
487
|
+
currency?: string;
|
|
488
|
+
gateway: string;
|
|
489
|
+
paymentData?: Record<string, unknown>;
|
|
490
|
+
metadata?: Record<string, unknown>;
|
|
491
|
+
idempotencyKey?: string;
|
|
492
|
+
}, ctx?: RevenueContext): Promise<TransactionDocument>;
|
|
493
|
+
/** Verifies payment via provider, updates status. Returns the updated doc. */
|
|
494
|
+
verify(paymentIntentId: string, options?: {
|
|
495
|
+
verifiedBy?: string;
|
|
496
|
+
}, ctx?: RevenueContext): Promise<TransactionDocument>;
|
|
497
|
+
/**
|
|
498
|
+
* Creates refund transaction, updates original. Returns the refund transaction doc.
|
|
499
|
+
*
|
|
500
|
+
* The provider call happens OUTSIDE the transaction — it's a non-idempotent external
|
|
501
|
+
* side effect we can't roll back. The two Mongo writes (create refund + update original)
|
|
502
|
+
* run inside `withTransaction` so they commit atomically or both abort. Bridges and
|
|
503
|
+
* event emission run AFTER commit because they're independent side effects; rolling
|
|
504
|
+
* them back would not undo external state anyway.
|
|
505
|
+
*
|
|
506
|
+
* Powered by mongokit 3.6's module-level `withTransaction` helper. Automatically
|
|
507
|
+
* retries on `TransientTransactionError` / `UnknownTransactionCommitResult`.
|
|
508
|
+
*/
|
|
509
|
+
refund(transactionId: string, amount?: number | null, options?: {
|
|
510
|
+
reason?: string;
|
|
511
|
+
}, ctx?: RevenueContext): Promise<TransactionDocument>;
|
|
512
|
+
/** Handles provider webhook. Returns the updated transaction doc (or null if not found). */
|
|
513
|
+
handleWebhook(providerName: string, payload: unknown, headers?: Record<string, string>, ctx?: RevenueContext): Promise<TransactionDocument | null>;
|
|
514
|
+
/** Places hold on verified transaction. Returns the updated doc. */
|
|
515
|
+
hold(transactionId: string, options?: {
|
|
516
|
+
amount?: number;
|
|
517
|
+
reason?: string;
|
|
518
|
+
holdUntil?: Date;
|
|
519
|
+
metadata?: Record<string, unknown>;
|
|
520
|
+
}, ctx?: RevenueContext): Promise<TransactionDocument>;
|
|
521
|
+
/**
|
|
522
|
+
* Releases held funds. Returns the updated transaction doc.
|
|
523
|
+
*
|
|
524
|
+
* The hold update and the escrow_release transaction create happen inside
|
|
525
|
+
* `withTransaction` — a mid-flow crash can't leave the hold marked released
|
|
526
|
+
* without the corresponding outflow record (or vice versa).
|
|
527
|
+
*/
|
|
528
|
+
release(transactionId: string, options: {
|
|
529
|
+
amount?: number;
|
|
530
|
+
recipientId: string;
|
|
531
|
+
recipientType: string;
|
|
532
|
+
reason?: string;
|
|
533
|
+
releasedBy?: string;
|
|
534
|
+
createTransaction?: boolean;
|
|
535
|
+
metadata?: Record<string, unknown>;
|
|
536
|
+
}, ctx?: RevenueContext): Promise<TransactionDocument>;
|
|
537
|
+
/**
|
|
538
|
+
* Splits payment among recipients. Returns the updated transaction doc.
|
|
539
|
+
*
|
|
540
|
+
* N + 2 writes (one create per recipient, one update on the parent, one
|
|
541
|
+
* platform_revenue create) all commit atomically. Partial splits are the
|
|
542
|
+
* worst class of bug in a payments system — this is exactly what
|
|
543
|
+
* `withTransaction` is for.
|
|
544
|
+
*/
|
|
545
|
+
split(transactionId: string, rules: Array<{
|
|
546
|
+
type: string;
|
|
547
|
+
recipientId: string;
|
|
548
|
+
recipientType: string;
|
|
549
|
+
rate: number;
|
|
550
|
+
}>, ctx?: RevenueContext): Promise<TransactionDocument>;
|
|
551
|
+
/**
|
|
552
|
+
* Idempotent bulk import of bank-feed rows.
|
|
553
|
+
*
|
|
554
|
+
* Each row is upserted by `(orgId, bankAccountId, externalId)` — the
|
|
555
|
+
* partial unique index declared in `create-models.ts`. Re-running the
|
|
556
|
+
* same Plaid sync, OFX upload, or QBO CDC drain produces zero new
|
|
557
|
+
* inserts on the second call (modified counts may rise as
|
|
558
|
+
* descriptions/categories evolve upstream).
|
|
559
|
+
*
|
|
560
|
+
* Signed bank `amount` is normalized into the (`amount` >= 0, `flow`)
|
|
561
|
+
* shape revenue uses internally so downstream queries (`flow:
|
|
562
|
+
* 'inflow'`) work uniformly across kinds.
|
|
563
|
+
*
|
|
564
|
+
* Emits one `revenue:transaction.imported` event per **inserted** row
|
|
565
|
+
* (not per row in `rows` — re-imports do not re-fire). Hosts wanting
|
|
566
|
+
* batch-level signal subscribe to the per-doc events and aggregate.
|
|
567
|
+
*
|
|
568
|
+
* Per-row failures (validation, hash collisions on a non-unique
|
|
569
|
+
* `externalId`) collect into `errors[]` instead of aborting the whole
|
|
570
|
+
* batch — the typical Plaid drain pulls thousands of rows; one bad
|
|
571
|
+
* row should not block the rest.
|
|
572
|
+
*
|
|
573
|
+
* @param rows Canonical bank transactions, structurally compatible
|
|
574
|
+
* with `@classytic/fin-io` parsers' output.
|
|
575
|
+
* @param opts `bankAccountId` (required, polymorphic ID) and
|
|
576
|
+
* `source` (provenance — `'plaid'`, `'ofx'`, …).
|
|
577
|
+
*/
|
|
578
|
+
import(rows: BankTransaction[], opts: {
|
|
579
|
+
bankAccountId: string;
|
|
580
|
+
source: string;
|
|
581
|
+
method?: string;
|
|
582
|
+
}, ctx?: RevenueContext): Promise<BankImportReport>;
|
|
583
|
+
/**
|
|
584
|
+
* Drain a bank-feed provider into the collection.
|
|
585
|
+
*
|
|
586
|
+
* Pulls pages from `provider.fetchTransactions()` (Plaid cursor, QBO
|
|
587
|
+
* CDC) and feeds each batch through `import()`. Yields the running
|
|
588
|
+
* report so a host cron can stream-progress-report to logs / metrics.
|
|
589
|
+
*
|
|
590
|
+
* Stops when the provider returns no new rows AND no removals AND no
|
|
591
|
+
* `nextCursor`. Caller is responsible for persisting the final cursor
|
|
592
|
+
* in their own checkpoint table — `result.nextCursor` is returned so
|
|
593
|
+
* the host can write it after a successful drain.
|
|
594
|
+
*
|
|
595
|
+
* Plaid `removed[]` rows (and any provider that retracts entries) are
|
|
596
|
+
* routed through `removeByFeed` so the host's LedgerBridge can void
|
|
597
|
+
* any JE that was already posted.
|
|
598
|
+
*/
|
|
599
|
+
drainSync(providerName: string, params: FetchTransactionsParams & {
|
|
600
|
+
bankAccountId: string;
|
|
601
|
+
}, ctx?: RevenueContext): Promise<{
|
|
602
|
+
totalImported: number;
|
|
603
|
+
totalUpdated: number;
|
|
604
|
+
totalRemoved: number;
|
|
605
|
+
nextCursor?: string;
|
|
606
|
+
errors: BankImportRowError[];
|
|
607
|
+
}>;
|
|
608
|
+
/**
|
|
609
|
+
* Parse an upload (OFX / CAMT.053 / MT940 / CSV) via a registered
|
|
610
|
+
* bank-feed provider, then `import()` the result.
|
|
611
|
+
*
|
|
612
|
+
* Convenience over manually calling `provider.parseUpload()` and
|
|
613
|
+
* threading the canonical rows into `import()` — the file-upload
|
|
614
|
+
* route handler is one line.
|
|
615
|
+
*/
|
|
616
|
+
parseAndImport(providerName: string, upload: {
|
|
617
|
+
buffer: Buffer | string | Uint8Array;
|
|
618
|
+
format?: string;
|
|
619
|
+
bankAccountId: string;
|
|
620
|
+
}, ctx?: RevenueContext): Promise<BankImportReport>;
|
|
621
|
+
/**
|
|
622
|
+
* Hand-keyed entry — treasurer logs a cash deposit, owner injects
|
|
623
|
+
* capital, refund correction. Created in `pending` (manual SM); host
|
|
624
|
+
* proceeds with `match()` → `journalize()` to post it to the ledger.
|
|
625
|
+
*
|
|
626
|
+
* `kind: 'manual'` is enforced — calls passing other kinds throw.
|
|
627
|
+
*/
|
|
628
|
+
createManual(data: {
|
|
629
|
+
amount: number;
|
|
630
|
+
currency: string;
|
|
631
|
+
flow: 'inflow' | 'outflow';
|
|
632
|
+
type: string;
|
|
633
|
+
description?: string;
|
|
634
|
+
counterparty?: TransactionDocument['counterparty'];
|
|
635
|
+
reference?: string;
|
|
636
|
+
postedDate?: Date;
|
|
637
|
+
valueDate?: Date;
|
|
638
|
+
bankAccountId?: string;
|
|
639
|
+
sourceId?: string;
|
|
640
|
+
sourceModel?: string;
|
|
641
|
+
metadata?: Record<string, unknown>;
|
|
642
|
+
}, ctx?: RevenueContext): Promise<TransactionDocument>;
|
|
643
|
+
/**
|
|
644
|
+
* Match a bank-feed / manual transaction to GL accounts, optionally
|
|
645
|
+
* cross-linking to an upstream payment-flow transaction.
|
|
646
|
+
*
|
|
647
|
+
* Atomic state CAS via `claim()` — the `where: { kind: { $in: [...] } }`
|
|
648
|
+
* predicate prevents a payment-flow row from being matched through this
|
|
649
|
+
* verb. Multi-source `from` (`['imported', 'matched']`) supports
|
|
650
|
+
* re-match after `unmatch()` (`matched → imported → matched`) without
|
|
651
|
+
* losing the prior mapping if the host wants to overwrite it.
|
|
652
|
+
*
|
|
653
|
+
* After a successful claim, `LedgerBridge.onTransactionMatched` runs
|
|
654
|
+
* — the canonical implementation creates a journal entry and chains
|
|
655
|
+
* `journalize()` to record the JE ref. The bridge call is OUTSIDE the
|
|
656
|
+
* claim's CAS window because JE posting is a side effect that may
|
|
657
|
+
* take seconds (cross-process call to ledger).
|
|
658
|
+
*/
|
|
659
|
+
match(id: string, data: {
|
|
660
|
+
mapping: {
|
|
661
|
+
debitAccount?: string;
|
|
662
|
+
creditAccount?: string;
|
|
663
|
+
notes?: string;
|
|
664
|
+
};
|
|
665
|
+
relatedTransactionId?: string;
|
|
666
|
+
matchedBy?: string;
|
|
667
|
+
}, ctx?: RevenueContext): Promise<TransactionDocument>;
|
|
668
|
+
/**
|
|
669
|
+
* Revert a matched transaction back to `imported`. Clears the
|
|
670
|
+
* `matching` block and `relatedTransactionId`. Notifies the
|
|
671
|
+
* LedgerBridge (which typically voids the journal entry) AFTER the
|
|
672
|
+
* state CAS lands.
|
|
673
|
+
*
|
|
674
|
+
* Only legal for `kind: 'bank_feed'` — manual entries don't allow
|
|
675
|
+
* un-match (the manual SM has no `matched → pending` edge).
|
|
676
|
+
*/
|
|
677
|
+
unmatch(id: string, options?: {
|
|
678
|
+
unmatchedBy?: string;
|
|
679
|
+
}, ctx?: RevenueContext): Promise<TransactionDocument>;
|
|
680
|
+
/**
|
|
681
|
+
* Stamp the journal entry reference and transition `matched →
|
|
682
|
+
* journalized`. Typical caller is the `LedgerBridge.onTransactionMatched`
|
|
683
|
+
* implementation — after creating a JE, it calls this verb so the row
|
|
684
|
+
* carries the back-reference.
|
|
685
|
+
*/
|
|
686
|
+
journalize(id: string, data: {
|
|
687
|
+
journalEntryRef: {
|
|
688
|
+
type: string;
|
|
689
|
+
id: string;
|
|
690
|
+
};
|
|
691
|
+
journalizedBy?: string;
|
|
692
|
+
}, ctx?: RevenueContext): Promise<TransactionDocument>;
|
|
693
|
+
/**
|
|
694
|
+
* Operator skip — marks an imported / matched / pending row as
|
|
695
|
+
* rejected (terminal). Use cases: duplicate of an already-imported
|
|
696
|
+
* row, non-cash entry the host doesn't want in the ledger, manual
|
|
697
|
+
* correction overrides.
|
|
698
|
+
*
|
|
699
|
+
* `relatedTransactionId` is preserved; reversal is the host's call.
|
|
700
|
+
*/
|
|
701
|
+
reject(id: string, data: {
|
|
702
|
+
reason: string;
|
|
703
|
+
rejectedBy?: string;
|
|
704
|
+
}, ctx?: RevenueContext): Promise<TransactionDocument>;
|
|
705
|
+
/**
|
|
706
|
+
* Soft-delete bank-feed rows that the upstream feed has retracted
|
|
707
|
+
* (Plaid `removed[]`, OFX correction).
|
|
708
|
+
*
|
|
709
|
+
* Each row is matched by `(orgId, bankAccountId, externalId)`; rows
|
|
710
|
+
* already journalized are NOT silently kept — they're surfaced in
|
|
711
|
+
* `retainedJournalized` so the caller can surface them in the UI
|
|
712
|
+
* ("the feed retracted these N rows but they're posted; reverse
|
|
713
|
+
* manually"). The host's `LedgerBridge` should post a reversing JE
|
|
714
|
+
* for those before any subsequent `delete()` can succeed.
|
|
715
|
+
*
|
|
716
|
+
* @returns `removed` (count soft-deleted), `retainedJournalized`
|
|
717
|
+
* (rows kept because they're already in the GL).
|
|
718
|
+
*/
|
|
719
|
+
removeByFeed(externalIds: string[], opts: {
|
|
720
|
+
bankAccountId: string;
|
|
721
|
+
source: string;
|
|
722
|
+
}, ctx?: RevenueContext): Promise<{
|
|
723
|
+
removed: number;
|
|
724
|
+
retainedJournalized: TransactionDocument[];
|
|
725
|
+
}>;
|
|
726
|
+
/**
|
|
727
|
+
* Find candidate matches for cross-referencing a payment-flow row to
|
|
728
|
+
* its bank deposit (or vice-versa).
|
|
729
|
+
*
|
|
730
|
+
* Heuristic:
|
|
731
|
+
* - same currency by default; cross-currency requires `fxRate` on
|
|
732
|
+
* the candidate row (multi-currency reconciliation).
|
|
733
|
+
* - amount within `amountTolerancePct` (default 1%) — accounts for
|
|
734
|
+
* gateway fees / FX rounding.
|
|
735
|
+
* - posted/created within `toleranceDays` of the target date
|
|
736
|
+
* (default 3 days — covers ACH delays, weekend settlement).
|
|
737
|
+
* - terminal verified states only (`verified` / `completed` for
|
|
738
|
+
* payment_flow, `imported` / `matched` for bank_feed).
|
|
739
|
+
*
|
|
740
|
+
* Returned candidates are unsorted; callers rank by their own
|
|
741
|
+
* confidence model (counterparty fuzzy match, currency identity,
|
|
742
|
+
* exact-amount preference, …).
|
|
743
|
+
*/
|
|
744
|
+
findMatchCandidates(filter: {
|
|
745
|
+
amount: number;
|
|
746
|
+
currency?: string;
|
|
747
|
+
postedDate: Date;
|
|
748
|
+
toleranceDays?: number;
|
|
749
|
+
amountTolerancePct?: number;
|
|
750
|
+
counterpartyName?: string;
|
|
751
|
+
kind?: TransactionKindValue;
|
|
752
|
+
}, ctx?: RevenueContext): Promise<TransactionDocument[]>;
|
|
753
|
+
/**
|
|
754
|
+
* Running balance for a bank account as of `asOf` (defaults to now).
|
|
755
|
+
*
|
|
756
|
+
* Uses mongokit's tenant-scoped read via `findAll` — inflows minus
|
|
757
|
+
* outflows over `kind: 'bank_feed'`, terminal states only. For audit
|
|
758
|
+
* pages where exact-to-the-cent reconciliation is required, prefer
|
|
759
|
+
* the most recent row's `balanceAfter` (banks ship that field on
|
|
760
|
+
* every entry).
|
|
761
|
+
*/
|
|
762
|
+
getRunningBalance(bankAccountId: string, asOf?: Date, ctx?: RevenueContext): Promise<{
|
|
763
|
+
balance: number;
|
|
764
|
+
currency: string | null;
|
|
765
|
+
rowCount: number;
|
|
766
|
+
asOf: Date;
|
|
767
|
+
}>;
|
|
768
|
+
}
|
|
769
|
+
//#endregion
|
|
770
|
+
//#region src/repositories/subscription.repository.d.ts
|
|
771
|
+
/**
|
|
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}.
|
|
796
|
+
*
|
|
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.
|
|
804
|
+
*
|
|
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
|
+
* ```
|
|
815
|
+
*/
|
|
816
|
+
declare class SubscriptionRepository extends RevenueRepositoryBase<SubscriptionDocument, SubscriptionRepoDeps> {
|
|
817
|
+
constructor(model: Model<SubscriptionDocument>, plugins?: PluginType[]);
|
|
818
|
+
activate(subscriptionId: string, options?: {
|
|
819
|
+
timestamp?: Date;
|
|
820
|
+
}, ctx?: RevenueContext): Promise<SubscriptionDocument>;
|
|
821
|
+
cancel(subscriptionId: string, options?: {
|
|
822
|
+
immediate?: boolean;
|
|
823
|
+
reason?: string;
|
|
824
|
+
}, ctx?: RevenueContext): Promise<SubscriptionDocument>;
|
|
825
|
+
pause(subscriptionId: string, options?: {
|
|
826
|
+
reason?: string;
|
|
827
|
+
}, ctx?: RevenueContext): Promise<SubscriptionDocument>;
|
|
828
|
+
resume(subscriptionId: string, options?: {
|
|
829
|
+
extendPeriod?: boolean;
|
|
830
|
+
}, ctx?: RevenueContext): Promise<SubscriptionDocument>;
|
|
831
|
+
}
|
|
832
|
+
//#endregion
|
|
833
|
+
//#region src/repositories/settlement.repository.d.ts
|
|
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 {
|
|
840
|
+
bridges: RevenueBridges;
|
|
841
|
+
}
|
|
842
|
+
interface SettlementProcessingError {
|
|
843
|
+
settlementId: SettlementDocument['_id'];
|
|
844
|
+
error: unknown;
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
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`.
|
|
854
|
+
*
|
|
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+.
|
|
859
|
+
*
|
|
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.
|
|
863
|
+
*/
|
|
864
|
+
declare class SettlementRepository extends RevenueRepositoryBase<SettlementDocument, SettlementRepoDeps> {
|
|
865
|
+
constructor(model: Model<SettlementDocument>, plugins?: PluginType[]);
|
|
866
|
+
schedule(params: {
|
|
867
|
+
organizationId: string;
|
|
868
|
+
recipientId: string;
|
|
869
|
+
recipientType: string;
|
|
870
|
+
type: string;
|
|
871
|
+
amount: number;
|
|
872
|
+
currency: string;
|
|
873
|
+
payoutMethod: string;
|
|
874
|
+
sourceTransactionIds?: string[];
|
|
875
|
+
sourceSplitIds?: string[];
|
|
876
|
+
scheduledAt?: Date;
|
|
877
|
+
bankTransferDetails?: Record<string, unknown>;
|
|
878
|
+
mobileWalletDetails?: Record<string, unknown>;
|
|
879
|
+
cryptoDetails?: Record<string, unknown>;
|
|
880
|
+
notes?: string;
|
|
881
|
+
metadata?: Record<string, unknown>;
|
|
882
|
+
}, ctx?: RevenueContext): Promise<SettlementDocument>;
|
|
883
|
+
processPending(options?: {
|
|
884
|
+
limit?: number;
|
|
885
|
+
organizationId?: string;
|
|
886
|
+
payoutMethod?: string;
|
|
887
|
+
dryRun?: boolean;
|
|
888
|
+
}, ctx?: RevenueContext): Promise<{
|
|
889
|
+
processed: number;
|
|
890
|
+
succeeded: number;
|
|
891
|
+
failed: number;
|
|
892
|
+
settlements: SettlementDocument[];
|
|
893
|
+
errors: SettlementProcessingError[];
|
|
894
|
+
}>;
|
|
895
|
+
complete(settlementId: string, details?: {
|
|
896
|
+
transferReference?: string;
|
|
897
|
+
transferredAt?: Date;
|
|
898
|
+
transactionHash?: string;
|
|
899
|
+
notes?: string;
|
|
900
|
+
metadata?: Record<string, unknown>;
|
|
901
|
+
}, ctx?: RevenueContext): Promise<SettlementDocument>;
|
|
902
|
+
fail(settlementId: string, reason: string, options?: {
|
|
903
|
+
code?: string;
|
|
904
|
+
retry?: boolean;
|
|
905
|
+
}, ctx?: RevenueContext): Promise<SettlementDocument>;
|
|
906
|
+
}
|
|
907
|
+
//#endregion
|
|
908
|
+
//#region src/engine/engine-types.d.ts
|
|
909
|
+
interface CommissionConfig {
|
|
910
|
+
defaultRate: number;
|
|
911
|
+
gatewayFeeRate?: number | undefined;
|
|
912
|
+
categoryRates?: Record<string, number> | undefined;
|
|
913
|
+
gatewayRates?: Record<string, number> | undefined;
|
|
914
|
+
}
|
|
915
|
+
interface RetryConfig {
|
|
916
|
+
maxAttempts?: number | undefined;
|
|
917
|
+
baseDelay?: number | undefined;
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* Per-index opt-in for the bank-feed lifecycle. Each flag controls one
|
|
921
|
+
* MongoDB index on the `Transaction` collection. Defaults are tuned for
|
|
922
|
+
* "moderate use" — required indexes are on, dashboard indexes are on,
|
|
923
|
+
* heavy reconciliation indexes are off.
|
|
924
|
+
*
|
|
925
|
+
* Toggle these to match real usage. Index builds are non-trivial on a
|
|
926
|
+
* collection with millions of rows; turning unused ones off saves disk +
|
|
927
|
+
* write amplification.
|
|
928
|
+
*/
|
|
929
|
+
interface BankFeedIndexConfig {
|
|
930
|
+
/**
|
|
931
|
+
* `(orgId, bankAccountId, externalId)` partial unique index. Required
|
|
932
|
+
* for `import()` to enforce idempotent re-import — turning this off
|
|
933
|
+
* means a Plaid drain that retries can produce duplicate rows.
|
|
934
|
+
*
|
|
935
|
+
* Default: `true`. Disable only if you don't use `import()` /
|
|
936
|
+
* `drainSync()` / `parseAndImport()`.
|
|
937
|
+
*/
|
|
938
|
+
idempotentImport?: boolean | undefined;
|
|
939
|
+
/**
|
|
940
|
+
* `(bankAccountId, postedDate -1)` partial — drives the treasurer
|
|
941
|
+
* dashboard ("show me last 30 days of transactions for account X").
|
|
942
|
+
* Cheap; on by default.
|
|
943
|
+
*/
|
|
944
|
+
byAccount?: boolean | undefined;
|
|
945
|
+
/**
|
|
946
|
+
* `(kind, amount, postedDate)` and `(kind, amount, createdAt)` —
|
|
947
|
+
* back the cross-reference query in `findMatchCandidates`. Two
|
|
948
|
+
* compound indexes; turn on when you actively reconcile bank
|
|
949
|
+
* deposits to gateway charges.
|
|
950
|
+
*
|
|
951
|
+
* Default: `false`. Enable when running a recon dashboard.
|
|
952
|
+
*/
|
|
953
|
+
matchCandidates?: boolean | undefined;
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* Bank-feed module configuration. Pass `true` for defaults, `false` to
|
|
957
|
+
* disable the module (skips the bulkWrite plugin + every bank-feed
|
|
958
|
+
* index), or an object to fine-tune indexes.
|
|
959
|
+
*/
|
|
960
|
+
interface BankFeedModuleConfig {
|
|
961
|
+
enabled?: boolean | undefined;
|
|
962
|
+
indexes?: BankFeedIndexConfig | undefined;
|
|
963
|
+
}
|
|
964
|
+
interface RevenueConfig {
|
|
965
|
+
connection: Connection;
|
|
966
|
+
defaultCurrency: string;
|
|
967
|
+
/**
|
|
968
|
+
* Event transport — structurally compatible with `@classytic/arc`'s
|
|
969
|
+
* `EventTransport`. Drop in any arc transport (Memory/Redis/Kafka) and it
|
|
970
|
+
* works without an adapter. When omitted, the engine uses
|
|
971
|
+
* `InProcessRevenueBus` (a ~50-line match of arc's `MemoryEventTransport`).
|
|
972
|
+
*/
|
|
973
|
+
eventTransport?: EventTransport | undefined;
|
|
974
|
+
/**
|
|
975
|
+
* Host-owned transactional outbox store (PACKAGE_RULES §5.5 + P8).
|
|
976
|
+
* Structurally identical to `@classytic/arc`'s `OutboxStore` by design —
|
|
977
|
+
* primitives is the source of truth for the contract and arc mirrors it.
|
|
978
|
+
*
|
|
979
|
+
* **Host responsibility.** Revenue does NOT ship a durable store. Arc 2.10
|
|
980
|
+
* only ships `MemoryOutboxStore` (dev) + the `OutboxStore` interface — the
|
|
981
|
+
* host wires durability. The canonical production wiring uses arc's
|
|
982
|
+
* `repositoryAsOutboxStore` adapter over a mongokit `Repository`:
|
|
983
|
+
*
|
|
984
|
+
* ```ts
|
|
985
|
+
* import mongoose, { Schema } from 'mongoose';
|
|
986
|
+
* import {
|
|
987
|
+
* Repository,
|
|
988
|
+
* methodRegistryPlugin,
|
|
989
|
+
* batchOperationsPlugin,
|
|
990
|
+
* } from '@classytic/mongokit';
|
|
991
|
+
* import { EventOutbox, repositoryAsOutboxStore } from '@classytic/arc/events';
|
|
992
|
+
* import { createRevenue } from '@classytic/revenue';
|
|
993
|
+
*
|
|
994
|
+
* // Arc owns the on-disk doc shape — strict:false forwards every field
|
|
995
|
+
* // (see arc's events.mdx "Why strict: false").
|
|
996
|
+
* const OutboxModel = mongoose.model(
|
|
997
|
+
* 'ArcOutbox',
|
|
998
|
+
* new Schema({}, { strict: false, timestamps: false, _id: false }),
|
|
999
|
+
* 'event_outbox',
|
|
1000
|
+
* );
|
|
1001
|
+
*
|
|
1002
|
+
* // Required plugins: the adapter calls `create` / `findAll` /
|
|
1003
|
+
* // `findOneAndUpdate` (base Repository) + `deleteMany` (batchOperations).
|
|
1004
|
+
* const outboxRepo = new Repository(OutboxModel, [
|
|
1005
|
+
* methodRegistryPlugin(),
|
|
1006
|
+
* batchOperationsPlugin(),
|
|
1007
|
+
* ]);
|
|
1008
|
+
* const outbox = repositoryAsOutboxStore(outboxRepo);
|
|
1009
|
+
*
|
|
1010
|
+
* const engine = await createRevenue({
|
|
1011
|
+
* connection: mongoose.connection,
|
|
1012
|
+
* defaultCurrency: 'USD',
|
|
1013
|
+
* outbox, // revenue's dispatch saves here
|
|
1014
|
+
* eventTransport: app.events, // arc transport for in-process subscribers
|
|
1015
|
+
* // ...
|
|
1016
|
+
* });
|
|
1017
|
+
*
|
|
1018
|
+
* // Relay + DLQ live in the host, not the package:
|
|
1019
|
+
* const relay = new EventOutbox({ store: outbox, transport: app.events });
|
|
1020
|
+
* setInterval(() => relay.relay(), 1_000);
|
|
1021
|
+
* ```
|
|
1022
|
+
*
|
|
1023
|
+
* **Session-bound atomicity.** Revenue's transactional verbs (`refund`,
|
|
1024
|
+
* `release`, `split`) open a mongokit `withTransaction` and pass the
|
|
1025
|
+
* mongoose `ClientSession` into `outbox.save(event, { session })`, so the
|
|
1026
|
+
* outbox row commits atomically with the business writes. Non-transactional
|
|
1027
|
+
* verbs (`createPaymentIntent`, `verify`, `handleWebhook`, `hold`) forward
|
|
1028
|
+
* `ctx.session` when the host is coordinating its own transaction — pass
|
|
1029
|
+
* `{ session }` in `RevenueContext` to participate.
|
|
1030
|
+
*
|
|
1031
|
+
* **Non-arc hosts.** Any `OutboxStore` works — implement the three-method
|
|
1032
|
+
* floor (`save` / `getPending` / `acknowledge`) over Postgres / Redis /
|
|
1033
|
+
* Kafka / SQS. When omitted, events flow to `eventTransport` only
|
|
1034
|
+
* (durability becomes transport-level, not at-least-once).
|
|
1035
|
+
*/
|
|
1036
|
+
outbox?: OutboxStore | undefined;
|
|
1037
|
+
modules?: {
|
|
1038
|
+
subscription?: boolean | undefined;
|
|
1039
|
+
escrow?: boolean | undefined;
|
|
1040
|
+
settlement?: boolean | undefined;
|
|
1041
|
+
commission?: CommissionConfig | boolean | undefined;
|
|
1042
|
+
/**
|
|
1043
|
+
* Bank-feed / accounting-feed module (3.0). Default: enabled (the
|
|
1044
|
+
* schema fields are always present so the discriminator works
|
|
1045
|
+
* uniformly). Disabling this suppresses the auto-wiring of
|
|
1046
|
+
* `bankFeedProviders`, the bulk-write plugin, AND every bank-feed
|
|
1047
|
+
* index — set to `false` for hosts that purely use the payment-
|
|
1048
|
+
* flow lifecycle and want to omit those costs.
|
|
1049
|
+
*
|
|
1050
|
+
* Pass an object to fine-tune which bank-feed indexes are built.
|
|
1051
|
+
* Examples:
|
|
1052
|
+
* `{ bankFeed: { indexes: { matchCandidates: true } } }`
|
|
1053
|
+
* — turn on cross-ref indexes for an active recon dashboard.
|
|
1054
|
+
* `{ bankFeed: { indexes: { idempotentImport: false, byAccount: false } } }`
|
|
1055
|
+
* — host doesn't use `import()` and doesn't need treasurer dashboards.
|
|
1056
|
+
*/
|
|
1057
|
+
bankFeed?: boolean | BankFeedModuleConfig | undefined;
|
|
1058
|
+
} | undefined;
|
|
1059
|
+
providers?: Record<string, PaymentProvider> | undefined;
|
|
1060
|
+
/**
|
|
1061
|
+
* Bank-feed providers — Plaid, fin-io OFX/CAMT/MT940/CSV, QBO/Xero CDC
|
|
1062
|
+
* adapters. Wired into `engine.bankFeedProviders` and consumed by
|
|
1063
|
+
* `transactionRepository.drainSync()` and `parseAndImport()`.
|
|
1064
|
+
*
|
|
1065
|
+
* @example
|
|
1066
|
+
* ```ts
|
|
1067
|
+
* import { PlaidBankFeedProvider } from '@classytic/revenue-plaid';
|
|
1068
|
+
* import { FinIoBankFeedProvider } from '@classytic/revenue-fin-io';
|
|
1069
|
+
*
|
|
1070
|
+
* const engine = await createRevenue({
|
|
1071
|
+
* ...,
|
|
1072
|
+
* bankFeedProviders: {
|
|
1073
|
+
* plaid: new PlaidBankFeedProvider({ clientId, secret }),
|
|
1074
|
+
* ofx: new FinIoBankFeedProvider(),
|
|
1075
|
+
* },
|
|
1076
|
+
* });
|
|
1077
|
+
* ```
|
|
1078
|
+
*/
|
|
1079
|
+
bankFeedProviders?: Record<string, BankFeedProvider> | undefined;
|
|
1080
|
+
bridges?: RevenueBridges | undefined;
|
|
1081
|
+
repositoryPlugins?: RepositoryPluginBundle | undefined;
|
|
1082
|
+
schemaOptions?: RevenueSchemaOptions | undefined;
|
|
1083
|
+
/**
|
|
1084
|
+
* Tenant scope configuration. Delegates to `@classytic/primitives`'
|
|
1085
|
+
* `TenantConfig`. Field names match mongokit's `MultiTenantOptions` so
|
|
1086
|
+
* the resolved config forwards directly into `multiTenantPlugin(...)`.
|
|
1087
|
+
*
|
|
1088
|
+
* - `undefined` / `true` → default field strategy, ObjectId storage.
|
|
1089
|
+
* - `false` → single-tenant (no plugin, field still present, not required).
|
|
1090
|
+
* - `{ fieldType: 'string' }` → string orgIds (UUID/slug hosts).
|
|
1091
|
+
* - `{ strategy: 'custom', resolve: ... }` → composite / derived scope.
|
|
1092
|
+
*
|
|
1093
|
+
* See PACKAGE_RULES.md §9.
|
|
1094
|
+
*/
|
|
1095
|
+
scope?: TenantConfig | boolean | undefined;
|
|
1096
|
+
commission?: CommissionConfig | undefined;
|
|
1097
|
+
retry?: RetryConfig | undefined;
|
|
1098
|
+
circuitBreaker?: boolean | undefined;
|
|
1099
|
+
/**
|
|
1100
|
+
* Set `false` to disable Mongoose auto-index on boot. Indexes are then
|
|
1101
|
+
* managed explicitly via `engine.syncIndexes()` or a deploy-time script.
|
|
1102
|
+
*/
|
|
1103
|
+
autoIndex?: boolean | Partial<Record<'Transaction' | 'Subscription' | 'Settlement', boolean>> | undefined;
|
|
1104
|
+
/**
|
|
1105
|
+
* Optional prefix prepended to every physical collection this package
|
|
1106
|
+
* creates (see PACKAGE_RULES.md §20.1). Unset → default names
|
|
1107
|
+
* (`revenue_transactions`, `revenue_subscriptions`, `revenue_settlements`).
|
|
1108
|
+
* Model names and `ref:` populate are unaffected.
|
|
1109
|
+
*/
|
|
1110
|
+
collectionPrefix?: string | undefined;
|
|
1111
|
+
/**
|
|
1112
|
+
* When true, existing Mongoose models with revenue's names are deleted
|
|
1113
|
+
* from the connection before re-registering. Hot-reload / test fixtures
|
|
1114
|
+
* only. Default `false` — collision throws `RevenueModelCollisionError`.
|
|
1115
|
+
* Hosts that need two revenue engines should use two Mongoose connections
|
|
1116
|
+
* (`mongoose.createConnection(...)`). See PACKAGE_RULES.md §21.
|
|
1117
|
+
*/
|
|
1118
|
+
forceRecreate?: boolean | undefined;
|
|
1119
|
+
logger?: {
|
|
1120
|
+
info: (...args: unknown[]) => void;
|
|
1121
|
+
error: (...args: unknown[]) => void;
|
|
1122
|
+
warn: (...args: unknown[]) => void;
|
|
1123
|
+
debug: (...args: unknown[]) => void;
|
|
1124
|
+
} | undefined;
|
|
1125
|
+
}
|
|
1126
|
+
/**
|
|
1127
|
+
* RevenueEngine — no service facade.
|
|
1128
|
+
*
|
|
1129
|
+
* Repositories ARE the domain layer. CRUD is inherited from mongokit.
|
|
1130
|
+
* Domain verbs (verify, refund, hold, activate, etc.) live on repositories.
|
|
1131
|
+
* Arc's BaseController/adapter plugs into repositories directly.
|
|
1132
|
+
*
|
|
1133
|
+
* `events` is structurally compatible with `@classytic/arc`'s
|
|
1134
|
+
* `EventTransport`. Hosts subscribe glob-style:
|
|
1135
|
+
*
|
|
1136
|
+
* await revenue.events.subscribe('revenue:payment.*', handler);
|
|
1137
|
+
*
|
|
1138
|
+
* and the same transport can be wired into the outbox relay for durable
|
|
1139
|
+
* delivery (see mongokit's `outbox-recipe.ts`). See PACKAGE_RULES §13.
|
|
1140
|
+
*/
|
|
1141
|
+
interface RevenueEngine {
|
|
1142
|
+
config: Readonly<RevenueConfig>;
|
|
1143
|
+
models: RevenueModels;
|
|
1144
|
+
repositories: RevenueRepositories;
|
|
1145
|
+
providers: ProviderRegistry;
|
|
1146
|
+
/**
|
|
1147
|
+
* Bank-feed providers registry (3.0). Populated when `bankFeed`
|
|
1148
|
+
* module is enabled and `bankFeedProviders` config is non-empty.
|
|
1149
|
+
* Used by `transactionRepository.drainSync()` and
|
|
1150
|
+
* `parseAndImport()`. Hosts can `register` providers at runtime too
|
|
1151
|
+
* (e.g. after the engine boots, to add a per-tenant Plaid client).
|
|
1152
|
+
*/
|
|
1153
|
+
bankFeedProviders: BankFeedProviderRegistry;
|
|
1154
|
+
events: EventTransport;
|
|
1155
|
+
/** Explicitly build all schema-declared indexes. Non-destructive. */
|
|
1156
|
+
syncIndexes(): Promise<void>;
|
|
1157
|
+
destroy(): Promise<void>;
|
|
1158
|
+
}
|
|
1159
|
+
//#endregion
|
|
1160
|
+
export { RevenueConfig as a, SubscriptionRepository as c, RevenueSchemaOptions as d, SettlementDocument as f, RetryConfig as i, TransactionRepository as l, TransactionDocument as m, BankFeedModuleConfig as n, RevenueEngine as o, SubscriptionDocument as p, CommissionConfig as r, SettlementRepository as s, BankFeedIndexConfig as t, RevenueModels as u };
|