@classytic/revenue 1.1.4 → 2.0.0
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 +90 -0
- package/README.md +638 -632
- package/dist/audit-B39B0Sdq.mjs +53 -0
- package/dist/audit-DZ0eTr9g.d.mts +89 -0
- package/dist/bridges/index.d.mts +2 -0
- package/dist/bridges/index.mjs +1 -0
- package/dist/context-DRqSeTPM.d.mts +35 -0
- package/dist/core/state-machines.d.mts +35 -0
- package/dist/core/state-machines.mjs +134 -0
- package/dist/engine-types-CcjIb4Fy.d.mts +611 -0
- package/dist/enums/index.d.mts +3 -157
- package/dist/enums/index.mjs +3 -55
- package/dist/errors-DHa8JVQ-.mjs +92 -0
- package/dist/escrow.schema-BBv9oVEW.mjs +322 -0
- package/dist/escrow.schema-CC8XuD46.d.mts +629 -0
- package/dist/event-constants-CEMitnIV.mjs +53 -0
- package/dist/events/index.d.mts +3 -0
- package/dist/events/index.mjs +4 -0
- package/dist/index.d.mts +77 -9
- package/dist/index.mjs +465 -29
- package/dist/monetization.enums-BtiU3t8o.mjs +39 -0
- package/dist/monetization.enums-D2xbxXJM.d.mts +34 -0
- package/dist/plugins/plugin.interface.d.mts +28 -0
- package/dist/plugins/plugin.interface.mjs +26 -0
- package/dist/providers/index.d.mts +2 -3
- package/dist/providers/index.mjs +2 -2
- package/dist/{base-DCoyIUj6.mjs → registry-DhFMsSn5.mjs} +34 -36
- package/dist/{base-CsTlVQJe.d.mts → registry-SvIGPAx_.d.mts} +73 -66
- package/dist/repositories/create-repositories.d.mts +21 -0
- package/dist/repositories/create-repositories.mjs +12 -0
- package/dist/revenue-bridges-sdlrR85c.d.mts +145 -0
- package/dist/revenue-event-catalog-BX3g7RUi.d.mts +823 -0
- package/dist/revenue-event-catalog-LqxPnsU_.mjs +388 -0
- package/dist/settlement.repository-Cy3mMWGH.mjs +771 -0
- package/dist/shared/index.d.mts +2 -0
- package/dist/shared/index.mjs +4 -0
- package/dist/split.enums-CQE3ekH1.mjs +172 -0
- package/dist/split.enums-Dw4zCrcZ.d.mts +154 -0
- package/dist/splits-BAfY-a9P.mjs +123 -0
- package/dist/validators/index.d.mts +2 -0
- package/dist/validators/index.mjs +3 -0
- package/package.json +33 -37
- package/dist/application/services/index.d.mts +0 -4
- package/dist/application/services/index.mjs +0 -3
- package/dist/category-resolver-DV83N8ok.mjs +0 -284
- package/dist/commission-split-BzB8cd39.mjs +0 -485
- package/dist/core/events.d.mts +0 -294
- package/dist/core/events.mjs +0 -100
- package/dist/core/index.d.mts +0 -9
- package/dist/core/index.mjs +0 -8
- package/dist/errors-rRdOqnWx.d.mts +0 -787
- package/dist/escrow.enums-CZGrrdg7.mjs +0 -101
- package/dist/escrow.enums-DwdLuuve.d.mts +0 -78
- package/dist/idempotency-DaYcUGY1.mjs +0 -172
- package/dist/index-Dsp7H5Wb.d.mts +0 -471
- package/dist/infrastructure/plugins/index.d.mts +0 -239
- package/dist/infrastructure/plugins/index.mjs +0 -345
- package/dist/money-CvrDOijQ.mjs +0 -271
- package/dist/money-DPG8AtJ8.d.mts +0 -112
- package/dist/payment.enums-HAuAS9Pp.d.mts +0 -70
- package/dist/payment.enums-tEFVa-Xp.mjs +0 -69
- package/dist/plugin-BbK0OVHy.d.mts +0 -327
- package/dist/plugin-Cd_V04Em.mjs +0 -210
- package/dist/reconciliation/index.d.mts +0 -193
- package/dist/reconciliation/index.mjs +0 -192
- package/dist/retry-HHCOXYdn.d.mts +0 -186
- package/dist/revenue-BhdS7nXh.mjs +0 -553
- package/dist/schemas/index.d.mts +0 -2665
- package/dist/schemas/index.mjs +0 -717
- package/dist/schemas/validation.d.mts +0 -375
- package/dist/schemas/validation.mjs +0 -325
- package/dist/settlement.enums-DFhkqZEY.d.mts +0 -132
- package/dist/settlement.schema-DnNSFpGd.d.mts +0 -344
- package/dist/settlement.service-DjzAjezU.d.mts +0 -594
- package/dist/settlement.service-DmdKv0Zu.mjs +0 -2511
- package/dist/split.enums-BrjabxIX.mjs +0 -86
- package/dist/split.enums-DmskfLOM.d.mts +0 -43
- package/dist/tax-BoCt5cEd.d.mts +0 -61
- package/dist/tax-EQ15DO81.mjs +0 -162
- package/dist/transaction.enums-pCyMFT4Z.mjs +0 -96
- package/dist/utils/index.d.mts +0 -428
- package/dist/utils/index.mjs +0 -346
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
import { t as RevenueContext } from "./context-DRqSeTPM.mjs";
|
|
2
|
+
import { t as RevenueBridges } from "./revenue-bridges-sdlrR85c.mjs";
|
|
3
|
+
import { o as PaymentProvider, t as ProviderRegistry } from "./registry-SvIGPAx_.mjs";
|
|
4
|
+
import { RepositoryPluginBundle, RevenueRepositories } from "./repositories/create-repositories.mjs";
|
|
5
|
+
import { PluginType, Repository } from "@classytic/mongokit";
|
|
6
|
+
import { TenantConfig } from "@classytic/primitives/tenant";
|
|
7
|
+
import mongoose, { Connection, Model, Schema } from "mongoose";
|
|
8
|
+
import { EventTransport } from "@classytic/primitives/events";
|
|
9
|
+
import { OutboxStore } from "@classytic/primitives/outbox";
|
|
10
|
+
|
|
11
|
+
//#region src/models/transaction.schema.d.ts
|
|
12
|
+
interface TransactionDocument {
|
|
13
|
+
_id: mongoose.Types.ObjectId;
|
|
14
|
+
publicId: string;
|
|
15
|
+
organizationId?: string;
|
|
16
|
+
customerId?: string | null;
|
|
17
|
+
type: string;
|
|
18
|
+
flow: 'inflow' | 'outflow';
|
|
19
|
+
tags: string[];
|
|
20
|
+
amount: number;
|
|
21
|
+
currency: string;
|
|
22
|
+
fee: number;
|
|
23
|
+
tax: number;
|
|
24
|
+
net: number;
|
|
25
|
+
taxDetails?: {
|
|
26
|
+
type?: string;
|
|
27
|
+
rate?: number;
|
|
28
|
+
isInclusive?: boolean;
|
|
29
|
+
};
|
|
30
|
+
method: string;
|
|
31
|
+
status: string;
|
|
32
|
+
gateway?: {
|
|
33
|
+
type: string;
|
|
34
|
+
sessionId?: string;
|
|
35
|
+
paymentIntentId?: string;
|
|
36
|
+
chargeId?: string;
|
|
37
|
+
metadata?: Record<string, unknown>;
|
|
38
|
+
verificationData?: Record<string, unknown>;
|
|
39
|
+
};
|
|
40
|
+
paymentDetails?: Record<string, unknown>;
|
|
41
|
+
commission?: {
|
|
42
|
+
rate: number;
|
|
43
|
+
grossAmount: number;
|
|
44
|
+
gatewayFeeRate: number;
|
|
45
|
+
gatewayFeeAmount: number;
|
|
46
|
+
netAmount: number;
|
|
47
|
+
status: string;
|
|
48
|
+
};
|
|
49
|
+
splits?: Array<{
|
|
50
|
+
type: string;
|
|
51
|
+
recipientId: string;
|
|
52
|
+
recipientType: string;
|
|
53
|
+
rate: number;
|
|
54
|
+
grossAmount: number;
|
|
55
|
+
gatewayFeeRate: number;
|
|
56
|
+
gatewayFeeAmount: number;
|
|
57
|
+
netAmount: number;
|
|
58
|
+
status: string;
|
|
59
|
+
}>;
|
|
60
|
+
hold?: {
|
|
61
|
+
status: string;
|
|
62
|
+
heldAmount: number;
|
|
63
|
+
releasedAmount: number;
|
|
64
|
+
reason: string;
|
|
65
|
+
heldAt: Date;
|
|
66
|
+
holdUntil?: Date;
|
|
67
|
+
releasedAt?: Date;
|
|
68
|
+
cancelledAt?: Date;
|
|
69
|
+
releases: Array<{
|
|
70
|
+
amount: number;
|
|
71
|
+
recipientId: string;
|
|
72
|
+
recipientType: string;
|
|
73
|
+
releasedAt: Date;
|
|
74
|
+
releasedBy?: string;
|
|
75
|
+
reason?: string;
|
|
76
|
+
metadata?: Record<string, unknown>;
|
|
77
|
+
}>;
|
|
78
|
+
metadata?: Record<string, unknown>;
|
|
79
|
+
};
|
|
80
|
+
sourceId?: string;
|
|
81
|
+
sourceModel?: string;
|
|
82
|
+
relatedTransactionId?: mongoose.Types.ObjectId;
|
|
83
|
+
refundedAmount?: number;
|
|
84
|
+
refundedAt?: Date;
|
|
85
|
+
failureReason?: string;
|
|
86
|
+
failedAt?: Date;
|
|
87
|
+
verifiedAt?: Date;
|
|
88
|
+
verifiedBy?: string;
|
|
89
|
+
webhook?: {
|
|
90
|
+
eventId: string;
|
|
91
|
+
eventType: string;
|
|
92
|
+
receivedAt: Date;
|
|
93
|
+
processedAt: Date;
|
|
94
|
+
data: Record<string, unknown>;
|
|
95
|
+
};
|
|
96
|
+
idempotencyKey?: string;
|
|
97
|
+
metadata?: Record<string, unknown>;
|
|
98
|
+
deletedAt?: Date | null;
|
|
99
|
+
createdAt: Date;
|
|
100
|
+
updatedAt: Date;
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/models/subscription.schema.d.ts
|
|
104
|
+
interface SubscriptionDocument {
|
|
105
|
+
_id: mongoose.Types.ObjectId;
|
|
106
|
+
publicId: string;
|
|
107
|
+
organizationId?: string;
|
|
108
|
+
customerId?: string | null;
|
|
109
|
+
planKey: string;
|
|
110
|
+
amount: number;
|
|
111
|
+
currency?: string;
|
|
112
|
+
status: string;
|
|
113
|
+
isActive: boolean;
|
|
114
|
+
transactionId?: mongoose.Types.ObjectId | null;
|
|
115
|
+
paymentIntentId?: string | null;
|
|
116
|
+
startDate?: Date;
|
|
117
|
+
endDate?: Date;
|
|
118
|
+
activatedAt?: Date;
|
|
119
|
+
pausedAt?: Date;
|
|
120
|
+
pauseReason?: string;
|
|
121
|
+
canceledAt?: Date;
|
|
122
|
+
cancelAt?: Date;
|
|
123
|
+
cancellationReason?: string;
|
|
124
|
+
renewalTransactionId?: mongoose.Types.ObjectId;
|
|
125
|
+
renewalCount: number;
|
|
126
|
+
metadata?: Record<string, unknown>;
|
|
127
|
+
deletedAt?: Date | null;
|
|
128
|
+
createdAt: Date;
|
|
129
|
+
updatedAt: Date;
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/models/settlement.schema.d.ts
|
|
133
|
+
interface SettlementDocument {
|
|
134
|
+
_id: mongoose.Types.ObjectId;
|
|
135
|
+
publicId: string;
|
|
136
|
+
organizationId: string;
|
|
137
|
+
recipientId: string;
|
|
138
|
+
recipientType: string;
|
|
139
|
+
type: string;
|
|
140
|
+
status: string;
|
|
141
|
+
payoutMethod: string;
|
|
142
|
+
amount: number;
|
|
143
|
+
currency: string;
|
|
144
|
+
sourceTransactionIds: mongoose.Types.ObjectId[];
|
|
145
|
+
sourceSplitIds: string[];
|
|
146
|
+
bankTransferDetails?: Record<string, unknown>;
|
|
147
|
+
mobileWalletDetails?: Record<string, unknown>;
|
|
148
|
+
cryptoDetails?: Record<string, unknown>;
|
|
149
|
+
scheduledAt: Date;
|
|
150
|
+
processedAt?: Date;
|
|
151
|
+
completedAt?: Date;
|
|
152
|
+
failedAt?: Date;
|
|
153
|
+
cancelledAt?: Date;
|
|
154
|
+
failureReason?: string;
|
|
155
|
+
failureCode?: string;
|
|
156
|
+
retryCount: number;
|
|
157
|
+
notes?: string;
|
|
158
|
+
metadata?: Record<string, unknown>;
|
|
159
|
+
deletedAt?: Date | null;
|
|
160
|
+
createdAt: Date;
|
|
161
|
+
updatedAt: Date;
|
|
162
|
+
}
|
|
163
|
+
//#endregion
|
|
164
|
+
//#region src/models/create-models.d.ts
|
|
165
|
+
interface RevenueModels {
|
|
166
|
+
Transaction: Model<TransactionDocument>;
|
|
167
|
+
Subscription?: Model<SubscriptionDocument>;
|
|
168
|
+
Settlement?: Model<SettlementDocument>;
|
|
169
|
+
}
|
|
170
|
+
interface RevenueSchemaOptions {
|
|
171
|
+
transaction?: {
|
|
172
|
+
extraFields?: Record<string, unknown>;
|
|
173
|
+
extraIndexes?: Array<{
|
|
174
|
+
fields: Record<string, 1 | -1>;
|
|
175
|
+
options?: Record<string, unknown>;
|
|
176
|
+
}>;
|
|
177
|
+
};
|
|
178
|
+
subscription?: {
|
|
179
|
+
extraFields?: Record<string, unknown>;
|
|
180
|
+
extraIndexes?: Array<{
|
|
181
|
+
fields: Record<string, 1 | -1>;
|
|
182
|
+
options?: Record<string, unknown>;
|
|
183
|
+
}>;
|
|
184
|
+
};
|
|
185
|
+
settlement?: {
|
|
186
|
+
extraFields?: Record<string, unknown>;
|
|
187
|
+
extraIndexes?: Array<{
|
|
188
|
+
fields: Record<string, 1 | -1>;
|
|
189
|
+
options?: Record<string, unknown>;
|
|
190
|
+
}>;
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
//#endregion
|
|
194
|
+
//#region src/repositories/transaction.repository.d.ts
|
|
195
|
+
interface TransactionRepoDeps {
|
|
196
|
+
events: EventTransport;
|
|
197
|
+
/**
|
|
198
|
+
* Optional host-owned outbox store (PACKAGE_RULES §5.5 + P8). When present,
|
|
199
|
+
* every domain event is persisted via `outbox.save(event)` before the
|
|
200
|
+
* in-process `events.publish(event)` so the host's relay (arc's EventOutbox,
|
|
201
|
+
* a Postgres LISTEN/NOTIFY pump, Kafka Connect, …) can replay on transport
|
|
202
|
+
* failure. When absent, events fire through `events.publish` only.
|
|
203
|
+
*/
|
|
204
|
+
outbox?: OutboxStore | undefined;
|
|
205
|
+
providers: ProviderRegistry;
|
|
206
|
+
bridges: RevenueBridges;
|
|
207
|
+
commission?: CommissionConfig;
|
|
208
|
+
defaultCurrency: string;
|
|
209
|
+
logger?: {
|
|
210
|
+
error(...args: unknown[]): void;
|
|
211
|
+
} | undefined;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* TransactionRepository — extends mongokit Repository.
|
|
215
|
+
*
|
|
216
|
+
* CRUD inherited: getAll, getById, getByQuery, create, update, delete, count, exists.
|
|
217
|
+
* Domain verbs: createPaymentIntent, verify, refund, handleWebhook, hold, release, split.
|
|
218
|
+
*
|
|
219
|
+
* All domain verbs return raw mongokit docs — no custom envelopes.
|
|
220
|
+
* Composite results (refund creates a new doc) are stored in metadata on the primary doc.
|
|
221
|
+
*/
|
|
222
|
+
declare class TransactionRepository extends Repository<TransactionDocument> {
|
|
223
|
+
private deps;
|
|
224
|
+
constructor(model: Model<TransactionDocument>, plugins?: PluginType[]);
|
|
225
|
+
inject(deps: TransactionRepoDeps): void;
|
|
226
|
+
/**
|
|
227
|
+
* Thread `ctx.organizationId` (and future ctx fields) into mongokit
|
|
228
|
+
* options so the `multiTenantPlugin` can auto-scope filters, queries,
|
|
229
|
+
* and inserts. Merges any caller-supplied extras. Centralizing this
|
|
230
|
+
* here means every domain verb participates in scope isolation without
|
|
231
|
+
* per-call boilerplate.
|
|
232
|
+
*/
|
|
233
|
+
private optsFromCtx;
|
|
234
|
+
/**
|
|
235
|
+
* Save an event to the host-owned outbox, session-bound when available.
|
|
236
|
+
*
|
|
237
|
+
* When `session` is passed, the outbox row commits atomically with the
|
|
238
|
+
* business write (P8 true session-bound write). When absent, the save
|
|
239
|
+
* happens after commit — still durable via the host's relay, but with a
|
|
240
|
+
* small at-most-once window on process crash.
|
|
241
|
+
*
|
|
242
|
+
* Isolated try/catch: an outbox failure never throws out of this helper;
|
|
243
|
+
* the caller still issues a transport.publish.
|
|
244
|
+
*/
|
|
245
|
+
private saveToOutbox;
|
|
246
|
+
/**
|
|
247
|
+
* Publish an event to the in-process `EventTransport` after commit.
|
|
248
|
+
* Transport failure is logged — the host relay will still redeliver from
|
|
249
|
+
* the outbox, so in-process subscribers missing an event is recoverable.
|
|
250
|
+
*/
|
|
251
|
+
private publishToTransport;
|
|
252
|
+
/**
|
|
253
|
+
* Non-transactional dispatch (used by verbs that don't open their own
|
|
254
|
+
* `withTransaction` block): outbox.save (session-bound when ctx provides
|
|
255
|
+
* one) → transport.publish. Matches arc's EventOutbox + MemoryEventTransport
|
|
256
|
+
* wiring bit-for-bit.
|
|
257
|
+
*/
|
|
258
|
+
private dispatch;
|
|
259
|
+
/** Creates transaction + calls provider. Returns the created transaction doc. */
|
|
260
|
+
createPaymentIntent(params: {
|
|
261
|
+
data?: Record<string, unknown>;
|
|
262
|
+
planKey?: string;
|
|
263
|
+
monetizationType?: string;
|
|
264
|
+
amount: number;
|
|
265
|
+
currency?: string;
|
|
266
|
+
gateway: string;
|
|
267
|
+
paymentData?: Record<string, unknown>;
|
|
268
|
+
metadata?: Record<string, unknown>;
|
|
269
|
+
idempotencyKey?: string;
|
|
270
|
+
}, ctx?: RevenueContext): Promise<TransactionDocument>;
|
|
271
|
+
/** Verifies payment via provider, updates status. Returns the updated doc. */
|
|
272
|
+
verify(paymentIntentId: string, options?: {
|
|
273
|
+
verifiedBy?: string;
|
|
274
|
+
}, ctx?: RevenueContext): Promise<TransactionDocument>;
|
|
275
|
+
/**
|
|
276
|
+
* Creates refund transaction, updates original. Returns the refund transaction doc.
|
|
277
|
+
*
|
|
278
|
+
* The provider call happens OUTSIDE the transaction — it's a non-idempotent external
|
|
279
|
+
* side effect we can't roll back. The two Mongo writes (create refund + update original)
|
|
280
|
+
* run inside `withTransaction` so they commit atomically or both abort. Bridges and
|
|
281
|
+
* event emission run AFTER commit because they're independent side effects; rolling
|
|
282
|
+
* them back would not undo external state anyway.
|
|
283
|
+
*
|
|
284
|
+
* Powered by mongokit 3.6's module-level `withTransaction` helper. Automatically
|
|
285
|
+
* retries on `TransientTransactionError` / `UnknownTransactionCommitResult`.
|
|
286
|
+
*/
|
|
287
|
+
refund(transactionId: string, amount?: number | null, options?: {
|
|
288
|
+
reason?: string;
|
|
289
|
+
}, ctx?: RevenueContext): Promise<TransactionDocument>;
|
|
290
|
+
/** Handles provider webhook. Returns the updated transaction doc (or null if not found). */
|
|
291
|
+
handleWebhook(providerName: string, payload: unknown, headers?: Record<string, string>, ctx?: RevenueContext): Promise<TransactionDocument | null>;
|
|
292
|
+
/** Places hold on verified transaction. Returns the updated doc. */
|
|
293
|
+
hold(transactionId: string, options?: {
|
|
294
|
+
amount?: number;
|
|
295
|
+
reason?: string;
|
|
296
|
+
holdUntil?: Date;
|
|
297
|
+
metadata?: Record<string, unknown>;
|
|
298
|
+
}, ctx?: RevenueContext): Promise<TransactionDocument>;
|
|
299
|
+
/**
|
|
300
|
+
* Releases held funds. Returns the updated transaction doc.
|
|
301
|
+
*
|
|
302
|
+
* The hold update and the escrow_release transaction create happen inside
|
|
303
|
+
* `withTransaction` — a mid-flow crash can't leave the hold marked released
|
|
304
|
+
* without the corresponding outflow record (or vice versa).
|
|
305
|
+
*/
|
|
306
|
+
release(transactionId: string, options: {
|
|
307
|
+
amount?: number;
|
|
308
|
+
recipientId: string;
|
|
309
|
+
recipientType: string;
|
|
310
|
+
reason?: string;
|
|
311
|
+
releasedBy?: string;
|
|
312
|
+
createTransaction?: boolean;
|
|
313
|
+
metadata?: Record<string, unknown>;
|
|
314
|
+
}, ctx?: RevenueContext): Promise<TransactionDocument>;
|
|
315
|
+
/**
|
|
316
|
+
* Splits payment among recipients. Returns the updated transaction doc.
|
|
317
|
+
*
|
|
318
|
+
* N + 2 writes (one create per recipient, one update on the parent, one
|
|
319
|
+
* platform_revenue create) all commit atomically. Partial splits are the
|
|
320
|
+
* worst class of bug in a payments system — this is exactly what
|
|
321
|
+
* `withTransaction` is for.
|
|
322
|
+
*/
|
|
323
|
+
split(transactionId: string, rules: Array<{
|
|
324
|
+
type: string;
|
|
325
|
+
recipientId: string;
|
|
326
|
+
recipientType: string;
|
|
327
|
+
rate: number;
|
|
328
|
+
}>, ctx?: RevenueContext): Promise<TransactionDocument>;
|
|
329
|
+
}
|
|
330
|
+
//#endregion
|
|
331
|
+
//#region src/repositories/subscription.repository.d.ts
|
|
332
|
+
interface SubscriptionRepoDeps {
|
|
333
|
+
events: EventTransport;
|
|
334
|
+
/** Host-owned outbox (PACKAGE_RULES §5.5 + P8). See TransactionRepoDeps. */
|
|
335
|
+
outbox?: OutboxStore | undefined;
|
|
336
|
+
logger?: {
|
|
337
|
+
error(...args: unknown[]): void;
|
|
338
|
+
} | undefined;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* SubscriptionRepository — data layer + domain verbs.
|
|
342
|
+
*
|
|
343
|
+
* CRUD inherited from mongokit. Domain verbs: activate, cancel, pause, resume.
|
|
344
|
+
*
|
|
345
|
+
* Events: each domain verb calls `this.deps.events.publish(createEvent(...))`
|
|
346
|
+
* with a fully-qualified `REVENUE_EVENTS.*` name. Hosts can subscribe glob-style
|
|
347
|
+
* via `revenue.events.subscribe('revenue:subscription.*', handler)` — the
|
|
348
|
+
* injected transport is arc-compatible (PACKAGE_RULES §13–§14).
|
|
349
|
+
*/
|
|
350
|
+
declare class SubscriptionRepository extends Repository<SubscriptionDocument> {
|
|
351
|
+
private deps;
|
|
352
|
+
constructor(model: Model<SubscriptionDocument>, plugins?: PluginType[]);
|
|
353
|
+
inject(deps: SubscriptionRepoDeps): void;
|
|
354
|
+
/**
|
|
355
|
+
* Host-owned outbox save → in-process transport publish (PACKAGE_RULES P8).
|
|
356
|
+
* Session-bound when `ctx.session` is present (atomic outbox row write).
|
|
357
|
+
*/
|
|
358
|
+
private dispatch;
|
|
359
|
+
activate(subscriptionId: string, options?: {
|
|
360
|
+
timestamp?: Date;
|
|
361
|
+
}, ctx?: RevenueContext): Promise<SubscriptionDocument>;
|
|
362
|
+
cancel(subscriptionId: string, options?: {
|
|
363
|
+
immediate?: boolean;
|
|
364
|
+
reason?: string;
|
|
365
|
+
}, ctx?: RevenueContext): Promise<SubscriptionDocument>;
|
|
366
|
+
pause(subscriptionId: string, options?: {
|
|
367
|
+
reason?: string;
|
|
368
|
+
}, ctx?: RevenueContext): Promise<SubscriptionDocument>;
|
|
369
|
+
resume(subscriptionId: string, options?: {
|
|
370
|
+
extendPeriod?: boolean;
|
|
371
|
+
}, ctx?: RevenueContext): Promise<SubscriptionDocument>;
|
|
372
|
+
}
|
|
373
|
+
//#endregion
|
|
374
|
+
//#region src/repositories/settlement.repository.d.ts
|
|
375
|
+
interface SettlementRepoDeps {
|
|
376
|
+
events: EventTransport;
|
|
377
|
+
/** Host-owned outbox (PACKAGE_RULES §5.5 + P8). See TransactionRepoDeps. */
|
|
378
|
+
outbox?: OutboxStore | undefined;
|
|
379
|
+
bridges: RevenueBridges;
|
|
380
|
+
logger?: {
|
|
381
|
+
error(...args: unknown[]): void;
|
|
382
|
+
} | undefined;
|
|
383
|
+
}
|
|
384
|
+
interface SettlementProcessingError {
|
|
385
|
+
settlementId: SettlementDocument['_id'];
|
|
386
|
+
error: unknown;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* SettlementRepository — data layer + domain verbs.
|
|
390
|
+
*
|
|
391
|
+
* CRUD inherited from mongokit. Domain verbs: schedule, processPending, complete, fail.
|
|
392
|
+
*
|
|
393
|
+
* Events are published via the injected `events` transport (arc-compatible).
|
|
394
|
+
* Hosts subscribe glob-style via `revenue.events.subscribe('revenue:settlement.*', h)`.
|
|
395
|
+
* See PACKAGE_RULES §13–§14.
|
|
396
|
+
*/
|
|
397
|
+
declare class SettlementRepository extends Repository<SettlementDocument> {
|
|
398
|
+
private deps;
|
|
399
|
+
constructor(model: Model<SettlementDocument>, plugins?: PluginType[]);
|
|
400
|
+
inject(deps: SettlementRepoDeps): void;
|
|
401
|
+
/**
|
|
402
|
+
* Host-owned outbox save → in-process transport publish (PACKAGE_RULES P8).
|
|
403
|
+
* Session-bound when `ctx.session` is present (atomic outbox row write).
|
|
404
|
+
*/
|
|
405
|
+
private dispatch;
|
|
406
|
+
schedule(params: {
|
|
407
|
+
organizationId: string;
|
|
408
|
+
recipientId: string;
|
|
409
|
+
recipientType: string;
|
|
410
|
+
type: string;
|
|
411
|
+
amount: number;
|
|
412
|
+
currency: string;
|
|
413
|
+
payoutMethod: string;
|
|
414
|
+
sourceTransactionIds?: string[];
|
|
415
|
+
sourceSplitIds?: string[];
|
|
416
|
+
scheduledAt?: Date;
|
|
417
|
+
bankTransferDetails?: Record<string, unknown>;
|
|
418
|
+
mobileWalletDetails?: Record<string, unknown>;
|
|
419
|
+
cryptoDetails?: Record<string, unknown>;
|
|
420
|
+
notes?: string;
|
|
421
|
+
metadata?: Record<string, unknown>;
|
|
422
|
+
}, ctx?: RevenueContext): Promise<SettlementDocument>;
|
|
423
|
+
processPending(options?: {
|
|
424
|
+
limit?: number;
|
|
425
|
+
organizationId?: string;
|
|
426
|
+
payoutMethod?: string;
|
|
427
|
+
dryRun?: boolean;
|
|
428
|
+
}, ctx?: RevenueContext): Promise<{
|
|
429
|
+
processed: number;
|
|
430
|
+
succeeded: number;
|
|
431
|
+
failed: number;
|
|
432
|
+
settlements: SettlementDocument[];
|
|
433
|
+
errors: SettlementProcessingError[];
|
|
434
|
+
}>;
|
|
435
|
+
complete(settlementId: string, details?: {
|
|
436
|
+
transferReference?: string;
|
|
437
|
+
transferredAt?: Date;
|
|
438
|
+
transactionHash?: string;
|
|
439
|
+
notes?: string;
|
|
440
|
+
metadata?: Record<string, unknown>;
|
|
441
|
+
}, ctx?: RevenueContext): Promise<SettlementDocument>;
|
|
442
|
+
fail(settlementId: string, reason: string, options?: {
|
|
443
|
+
code?: string;
|
|
444
|
+
retry?: boolean;
|
|
445
|
+
}, ctx?: RevenueContext): Promise<SettlementDocument>;
|
|
446
|
+
}
|
|
447
|
+
//#endregion
|
|
448
|
+
//#region src/engine/engine-types.d.ts
|
|
449
|
+
interface CommissionConfig {
|
|
450
|
+
defaultRate: number;
|
|
451
|
+
gatewayFeeRate?: number | undefined;
|
|
452
|
+
categoryRates?: Record<string, number> | undefined;
|
|
453
|
+
gatewayRates?: Record<string, number> | undefined;
|
|
454
|
+
}
|
|
455
|
+
interface RetryConfig {
|
|
456
|
+
maxAttempts?: number | undefined;
|
|
457
|
+
baseDelay?: number | undefined;
|
|
458
|
+
}
|
|
459
|
+
interface RevenueConfig {
|
|
460
|
+
connection: Connection;
|
|
461
|
+
defaultCurrency: string;
|
|
462
|
+
/**
|
|
463
|
+
* Event transport — structurally compatible with `@classytic/arc`'s
|
|
464
|
+
* `EventTransport`. Drop in any arc transport (Memory/Redis/Kafka) and it
|
|
465
|
+
* works without an adapter. When omitted, the engine uses
|
|
466
|
+
* `InProcessRevenueBus` (a ~50-line match of arc's `MemoryEventTransport`).
|
|
467
|
+
*/
|
|
468
|
+
eventTransport?: EventTransport | undefined;
|
|
469
|
+
/**
|
|
470
|
+
* Host-owned transactional outbox store (PACKAGE_RULES §5.5 + P8).
|
|
471
|
+
* Structurally identical to `@classytic/arc`'s `OutboxStore` by design —
|
|
472
|
+
* primitives is the source of truth for the contract and arc mirrors it.
|
|
473
|
+
*
|
|
474
|
+
* **Host responsibility.** Revenue does NOT ship a durable store. Arc 2.10
|
|
475
|
+
* only ships `MemoryOutboxStore` (dev) + the `OutboxStore` interface — the
|
|
476
|
+
* host wires durability. The canonical production wiring uses arc's
|
|
477
|
+
* `repositoryAsOutboxStore` adapter over a mongokit `Repository`:
|
|
478
|
+
*
|
|
479
|
+
* ```ts
|
|
480
|
+
* import mongoose, { Schema } from 'mongoose';
|
|
481
|
+
* import {
|
|
482
|
+
* Repository,
|
|
483
|
+
* methodRegistryPlugin,
|
|
484
|
+
* batchOperationsPlugin,
|
|
485
|
+
* } from '@classytic/mongokit';
|
|
486
|
+
* import { EventOutbox, repositoryAsOutboxStore } from '@classytic/arc/events';
|
|
487
|
+
* import { createRevenue } from '@classytic/revenue';
|
|
488
|
+
*
|
|
489
|
+
* // Arc owns the on-disk doc shape — strict:false forwards every field
|
|
490
|
+
* // (see arc's events.mdx "Why strict: false").
|
|
491
|
+
* const OutboxModel = mongoose.model(
|
|
492
|
+
* 'ArcOutbox',
|
|
493
|
+
* new Schema({}, { strict: false, timestamps: false, _id: false }),
|
|
494
|
+
* 'event_outbox',
|
|
495
|
+
* );
|
|
496
|
+
*
|
|
497
|
+
* // Required plugins: the adapter calls `create` / `findAll` /
|
|
498
|
+
* // `findOneAndUpdate` (base Repository) + `deleteMany` (batchOperations).
|
|
499
|
+
* const outboxRepo = new Repository(OutboxModel, [
|
|
500
|
+
* methodRegistryPlugin(),
|
|
501
|
+
* batchOperationsPlugin(),
|
|
502
|
+
* ]);
|
|
503
|
+
* const outbox = repositoryAsOutboxStore(outboxRepo);
|
|
504
|
+
*
|
|
505
|
+
* const engine = await createRevenue({
|
|
506
|
+
* connection: mongoose.connection,
|
|
507
|
+
* defaultCurrency: 'USD',
|
|
508
|
+
* outbox, // revenue's dispatch saves here
|
|
509
|
+
* eventTransport: app.events, // arc transport for in-process subscribers
|
|
510
|
+
* // ...
|
|
511
|
+
* });
|
|
512
|
+
*
|
|
513
|
+
* // Relay + DLQ live in the host, not the package:
|
|
514
|
+
* const relay = new EventOutbox({ store: outbox, transport: app.events });
|
|
515
|
+
* setInterval(() => relay.relay(), 1_000);
|
|
516
|
+
* ```
|
|
517
|
+
*
|
|
518
|
+
* **Session-bound atomicity.** Revenue's transactional verbs (`refund`,
|
|
519
|
+
* `release`, `split`) open a mongokit `withTransaction` and pass the
|
|
520
|
+
* mongoose `ClientSession` into `outbox.save(event, { session })`, so the
|
|
521
|
+
* outbox row commits atomically with the business writes. Non-transactional
|
|
522
|
+
* verbs (`createPaymentIntent`, `verify`, `handleWebhook`, `hold`) forward
|
|
523
|
+
* `ctx.session` when the host is coordinating its own transaction — pass
|
|
524
|
+
* `{ session }` in `RevenueContext` to participate.
|
|
525
|
+
*
|
|
526
|
+
* **Non-arc hosts.** Any `OutboxStore` works — implement the three-method
|
|
527
|
+
* floor (`save` / `getPending` / `acknowledge`) over Postgres / Redis /
|
|
528
|
+
* Kafka / SQS. When omitted, events flow to `eventTransport` only
|
|
529
|
+
* (durability becomes transport-level, not at-least-once).
|
|
530
|
+
*/
|
|
531
|
+
outbox?: OutboxStore | undefined;
|
|
532
|
+
modules?: {
|
|
533
|
+
subscription?: boolean | undefined;
|
|
534
|
+
escrow?: boolean | undefined;
|
|
535
|
+
settlement?: boolean | undefined;
|
|
536
|
+
commission?: CommissionConfig | boolean | undefined;
|
|
537
|
+
} | undefined;
|
|
538
|
+
providers?: Record<string, PaymentProvider> | undefined;
|
|
539
|
+
bridges?: RevenueBridges | undefined;
|
|
540
|
+
repositoryPlugins?: RepositoryPluginBundle | undefined;
|
|
541
|
+
schemaOptions?: RevenueSchemaOptions | undefined;
|
|
542
|
+
/**
|
|
543
|
+
* Tenant scope configuration. Delegates to `@classytic/primitives`'
|
|
544
|
+
* `TenantConfig`. Field names match mongokit's `MultiTenantOptions` so
|
|
545
|
+
* the resolved config forwards directly into `multiTenantPlugin(...)`.
|
|
546
|
+
*
|
|
547
|
+
* - `undefined` / `true` → default field strategy, ObjectId storage.
|
|
548
|
+
* - `false` → single-tenant (no plugin, field still present, not required).
|
|
549
|
+
* - `{ fieldType: 'string' }` → string orgIds (UUID/slug hosts).
|
|
550
|
+
* - `{ strategy: 'custom', resolve: ... }` → composite / derived scope.
|
|
551
|
+
*
|
|
552
|
+
* See PACKAGE_RULES.md §9.
|
|
553
|
+
*/
|
|
554
|
+
scope?: TenantConfig | boolean | undefined;
|
|
555
|
+
commission?: CommissionConfig | undefined;
|
|
556
|
+
retry?: RetryConfig | undefined;
|
|
557
|
+
circuitBreaker?: boolean | undefined;
|
|
558
|
+
/**
|
|
559
|
+
* Set `false` to disable Mongoose auto-index on boot. Indexes are then
|
|
560
|
+
* managed explicitly via `engine.syncIndexes()` or a deploy-time script.
|
|
561
|
+
*/
|
|
562
|
+
autoIndex?: boolean | Partial<Record<'Transaction' | 'Subscription' | 'Settlement', boolean>> | undefined;
|
|
563
|
+
/**
|
|
564
|
+
* Optional prefix prepended to every physical collection this package
|
|
565
|
+
* creates (see PACKAGE_RULES.md §20.1). Unset → default names
|
|
566
|
+
* (`revenue_transactions`, `revenue_subscriptions`, `revenue_settlements`).
|
|
567
|
+
* Model names and `ref:` populate are unaffected.
|
|
568
|
+
*/
|
|
569
|
+
collectionPrefix?: string | undefined;
|
|
570
|
+
/**
|
|
571
|
+
* When true, existing Mongoose models with revenue's names are deleted
|
|
572
|
+
* from the connection before re-registering. Hot-reload / test fixtures
|
|
573
|
+
* only. Default `false` — collision throws `RevenueModelCollisionError`.
|
|
574
|
+
* Hosts that need two revenue engines should use two Mongoose connections
|
|
575
|
+
* (`mongoose.createConnection(...)`). See PACKAGE_RULES.md §21.
|
|
576
|
+
*/
|
|
577
|
+
forceRecreate?: boolean | undefined;
|
|
578
|
+
logger?: {
|
|
579
|
+
info: (...args: unknown[]) => void;
|
|
580
|
+
error: (...args: unknown[]) => void;
|
|
581
|
+
warn: (...args: unknown[]) => void;
|
|
582
|
+
debug: (...args: unknown[]) => void;
|
|
583
|
+
} | undefined;
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* RevenueEngine — no service facade.
|
|
587
|
+
*
|
|
588
|
+
* Repositories ARE the domain layer. CRUD is inherited from mongokit.
|
|
589
|
+
* Domain verbs (verify, refund, hold, activate, etc.) live on repositories.
|
|
590
|
+
* Arc's BaseController/adapter plugs into repositories directly.
|
|
591
|
+
*
|
|
592
|
+
* `events` is structurally compatible with `@classytic/arc`'s
|
|
593
|
+
* `EventTransport`. Hosts subscribe glob-style:
|
|
594
|
+
*
|
|
595
|
+
* await revenue.events.subscribe('revenue:payment.*', handler);
|
|
596
|
+
*
|
|
597
|
+
* and the same transport can be wired into the outbox relay for durable
|
|
598
|
+
* delivery (see mongokit's `outbox-recipe.ts`). See PACKAGE_RULES §13.
|
|
599
|
+
*/
|
|
600
|
+
interface RevenueEngine {
|
|
601
|
+
config: Readonly<RevenueConfig>;
|
|
602
|
+
models: RevenueModels;
|
|
603
|
+
repositories: RevenueRepositories;
|
|
604
|
+
providers: ProviderRegistry;
|
|
605
|
+
events: EventTransport;
|
|
606
|
+
/** Explicitly build all schema-declared indexes. Non-destructive. */
|
|
607
|
+
syncIndexes(): Promise<void>;
|
|
608
|
+
destroy(): Promise<void>;
|
|
609
|
+
}
|
|
610
|
+
//#endregion
|
|
611
|
+
export { SettlementRepository as a, RevenueModels as c, SubscriptionDocument as d, TransactionDocument as f, RevenueEngine as i, RevenueSchemaOptions as l, RetryConfig as n, SubscriptionRepository as o, RevenueConfig as r, TransactionRepository as s, CommissionConfig as t, SettlementDocument as u };
|