@classytic/revenue 1.1.2 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -7
- package/dist/application/services/index.d.mts +4 -0
- package/dist/application/services/index.mjs +3 -0
- package/dist/base-CsTlVQJe.d.mts +136 -0
- package/dist/base-DCoyIUj6.mjs +152 -0
- package/dist/category-resolver-DV83N8ok.mjs +284 -0
- package/dist/commission-split-BzB8cd39.mjs +485 -0
- package/dist/core/events.d.mts +294 -0
- package/dist/core/events.mjs +100 -0
- package/dist/core/index.d.mts +9 -0
- package/dist/core/index.mjs +8 -0
- package/dist/enums/index.d.mts +157 -0
- package/dist/enums/index.mjs +56 -0
- package/dist/errors-rRdOqnWx.d.mts +787 -0
- package/dist/escrow.enums-CZGrrdg7.mjs +101 -0
- package/dist/{escrow.enums-CE0VQsfe.d.ts → escrow.enums-DwdLuuve.d.mts} +30 -28
- package/dist/idempotency-DaYcUGY1.mjs +172 -0
- package/dist/index-Dsp7H5Wb.d.mts +471 -0
- package/dist/index.d.mts +9 -0
- package/dist/index.mjs +38 -0
- package/dist/infrastructure/plugins/{index.d.ts → index.d.mts} +81 -109
- package/dist/infrastructure/plugins/index.mjs +345 -0
- package/dist/money-CvrDOijQ.mjs +271 -0
- package/dist/money-DPG8AtJ8.d.mts +112 -0
- package/dist/{payment.enums-C1BiGlRa.d.ts → payment.enums-HAuAS9Pp.d.mts} +14 -13
- package/dist/payment.enums-tEFVa-Xp.mjs +69 -0
- package/dist/plugin-BbK0OVHy.d.mts +327 -0
- package/dist/plugin-Cd_V04Em.mjs +210 -0
- package/dist/providers/index.d.mts +3 -0
- package/dist/providers/index.mjs +3 -0
- package/dist/reconciliation/{index.d.ts → index.d.mts} +90 -112
- package/dist/reconciliation/index.mjs +192 -0
- package/dist/retry-HHCOXYdn.d.mts +186 -0
- package/dist/revenue-BhdS7nXh.mjs +553 -0
- package/dist/schemas/index.d.mts +2665 -0
- package/dist/schemas/index.mjs +717 -0
- package/dist/schemas/validation.d.mts +375 -0
- package/dist/schemas/validation.mjs +325 -0
- package/dist/{settlement.enums-ByC1x0ye.d.ts → settlement.enums-DFhkqZEY.d.mts} +31 -29
- package/dist/settlement.schema-DnNSFpGd.d.mts +344 -0
- package/dist/settlement.service-DjzAjezU.d.mts +594 -0
- package/dist/settlement.service-DmdKv0Zu.mjs +2511 -0
- package/dist/split.enums-BrjabxIX.mjs +86 -0
- package/dist/split.enums-DmskfLOM.d.mts +43 -0
- package/dist/tax-BoCt5cEd.d.mts +61 -0
- package/dist/tax-EQ15DO81.mjs +162 -0
- package/dist/transaction.enums-pCyMFT4Z.mjs +96 -0
- package/dist/utils/{index.d.ts → index.d.mts} +91 -161
- package/dist/utils/index.mjs +346 -0
- package/package.json +39 -37
- package/dist/application/services/index.d.ts +0 -6
- package/dist/application/services/index.js +0 -3288
- package/dist/application/services/index.js.map +0 -1
- package/dist/core/events.d.ts +0 -455
- package/dist/core/events.js +0 -122
- package/dist/core/events.js.map +0 -1
- package/dist/core/index.d.ts +0 -13
- package/dist/core/index.js +0 -4591
- package/dist/core/index.js.map +0 -1
- package/dist/enums/index.d.ts +0 -159
- package/dist/enums/index.js +0 -296
- package/dist/enums/index.js.map +0 -1
- package/dist/index-DxIK0UmZ.d.ts +0 -633
- package/dist/index-EnfKzDbs.d.ts +0 -806
- package/dist/index-cLJBLUvx.d.ts +0 -478
- package/dist/index.d.ts +0 -43
- package/dist/index.js +0 -4864
- package/dist/index.js.map +0 -1
- package/dist/infrastructure/plugins/index.js +0 -292
- package/dist/infrastructure/plugins/index.js.map +0 -1
- package/dist/money-widWVD7r.d.ts +0 -111
- package/dist/plugin-Bb9HOE10.d.ts +0 -336
- package/dist/providers/index.d.ts +0 -145
- package/dist/providers/index.js +0 -141
- package/dist/providers/index.js.map +0 -1
- package/dist/reconciliation/index.js +0 -140
- package/dist/reconciliation/index.js.map +0 -1
- package/dist/retry-D4hFUwVk.d.ts +0 -194
- package/dist/schemas/index.d.ts +0 -2655
- package/dist/schemas/index.js +0 -841
- package/dist/schemas/index.js.map +0 -1
- package/dist/schemas/validation.d.ts +0 -384
- package/dist/schemas/validation.js +0 -303
- package/dist/schemas/validation.js.map +0 -1
- package/dist/settlement.schema-CpamV7ZY.d.ts +0 -343
- package/dist/split.enums-DG3TxQf9.d.ts +0 -42
- package/dist/tax-CV8A0sxl.d.ts +0 -60
- package/dist/utils/index.js +0 -1202
- package/dist/utils/index.js.map +0 -1
|
@@ -1,18 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
export { C as CircuitBreaker, l as CircuitBreakerConfig, n as CircuitState, k as RetryConfig, j as createCircuitBreaker, r as retry } from '../retry-D4hFUwVk.js';
|
|
6
|
-
import 'mongoose';
|
|
7
|
-
import '@classytic/shared-types';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Commission Calculation Utility
|
|
11
|
-
* @classytic/revenue
|
|
12
|
-
*
|
|
13
|
-
* Handles platform commission calculation with gateway fee deduction
|
|
14
|
-
*/
|
|
1
|
+
import { B as TransactionDocument, E as ProratedAmountParams, F as SplitInfo, L as SplitRule, R as SubscriptionDocument, T as PeriodRangeResult, U as TransactionTypeOptions, c as FieldUpdateValidationResult, d as Logger, i as CommissionInfo, m as MonetizationTypeValue, o as DurationResult, w as PeriodRangeParams, z as SubscriptionEntity } from "../index-Dsp7H5Wb.mjs";
|
|
2
|
+
import { a as createCircuitBreaker, i as RetryConfig, l as Result, n as CircuitBreakerConfig, o as retry, r as CircuitState, t as CircuitBreaker } from "../retry-HHCOXYdn.mjs";
|
|
3
|
+
import { i as toSmallestUnit, n as MoneyValue, r as fromSmallestUnit, t as Money } from "../money-DPG8AtJ8.mjs";
|
|
4
|
+
import { n as TaxConfig, r as TaxType, t as TaxCalculation } from "../tax-BoCt5cEd.mjs";
|
|
15
5
|
|
|
6
|
+
//#region src/shared/utils/calculators/commission.d.ts
|
|
16
7
|
/**
|
|
17
8
|
* Build commission object for transaction
|
|
18
9
|
*
|
|
@@ -31,15 +22,8 @@ declare function calculateCommission(amount: number, commissionRate: number, gat
|
|
|
31
22
|
* @returns Reversed commission or null
|
|
32
23
|
*/
|
|
33
24
|
declare function reverseCommission(originalCommission: CommissionInfo | null | undefined, originalAmount: number, refundAmount: number): CommissionInfo | null;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
* Tax Utilities
|
|
37
|
-
* @classytic/revenue
|
|
38
|
-
*
|
|
39
|
-
* Tax calculation utilities
|
|
40
|
-
* Philosophy: Apps provide rates, library does math (like Stripe)
|
|
41
|
-
*/
|
|
42
|
-
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/shared/utils/calculators/tax.d.ts
|
|
43
27
|
/**
|
|
44
28
|
* Calculate tax for a transaction
|
|
45
29
|
*
|
|
@@ -140,18 +124,12 @@ declare function getTaxType(transactionFlow: 'inflow' | 'outflow', category: str
|
|
|
140
124
|
* ```
|
|
141
125
|
*/
|
|
142
126
|
declare function reverseTax(originalTax: TaxCalculation & {
|
|
143
|
-
|
|
127
|
+
type?: TaxType;
|
|
144
128
|
}, originalAmount: number, refundAmount: number): TaxCalculation & {
|
|
145
|
-
|
|
129
|
+
type?: TaxType;
|
|
146
130
|
};
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
* Commission Split Utilities
|
|
150
|
-
* @classytic/revenue
|
|
151
|
-
*
|
|
152
|
-
* Multi-party commission split calculation for affiliate/referral systems
|
|
153
|
-
*/
|
|
154
|
-
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/shared/utils/calculators/commission-split.d.ts
|
|
155
133
|
/**
|
|
156
134
|
* Calculate multi-party commission splits
|
|
157
135
|
*
|
|
@@ -190,104 +168,91 @@ declare function calculateOrganizationPayout(amount: number, splits?: SplitInfo[
|
|
|
190
168
|
* @returns Reversed splits
|
|
191
169
|
*/
|
|
192
170
|
declare function reverseSplits(originalSplits: SplitInfo[] | undefined | null, originalAmount: number, refundAmount: number): SplitInfo[];
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
* Idempotency Utilities
|
|
196
|
-
* @classytic/revenue
|
|
197
|
-
*
|
|
198
|
-
* Prevent duplicate operations with idempotency keys
|
|
199
|
-
* Inspired by: Stripe, Amazon SQS deduplication
|
|
200
|
-
*/
|
|
201
|
-
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region src/shared/utils/resilience/idempotency.d.ts
|
|
202
173
|
interface IdempotencyRecord<T = unknown> {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
174
|
+
/** Idempotency key */
|
|
175
|
+
key: string;
|
|
176
|
+
/** Operation result (if completed) */
|
|
177
|
+
result?: T;
|
|
178
|
+
/** Operation status */
|
|
179
|
+
status: 'pending' | 'completed' | 'failed';
|
|
180
|
+
/** Creation timestamp */
|
|
181
|
+
createdAt: Date;
|
|
182
|
+
/** Completion timestamp */
|
|
183
|
+
completedAt?: Date;
|
|
184
|
+
/** Request hash for validation */
|
|
185
|
+
requestHash: string;
|
|
186
|
+
/** TTL - when record expires */
|
|
187
|
+
expiresAt: Date;
|
|
217
188
|
}
|
|
218
189
|
interface IdempotencyStore {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
190
|
+
/** Get record by key */
|
|
191
|
+
get<T>(key: string): Promise<IdempotencyRecord<T> | null>;
|
|
192
|
+
/** Set or update record */
|
|
193
|
+
set<T>(key: string, record: IdempotencyRecord<T>): Promise<void>;
|
|
194
|
+
/** Delete record */
|
|
195
|
+
delete(key: string): Promise<void>;
|
|
196
|
+
/** Check if key exists */
|
|
197
|
+
exists(key: string): Promise<boolean>;
|
|
227
198
|
}
|
|
228
199
|
interface IdempotencyConfig {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
200
|
+
/** TTL in milliseconds (default: 24 hours) */
|
|
201
|
+
ttl?: number;
|
|
202
|
+
/** Custom store implementation */
|
|
203
|
+
store?: IdempotencyStore;
|
|
204
|
+
/** Key prefix */
|
|
205
|
+
prefix?: string;
|
|
235
206
|
}
|
|
236
207
|
declare class IdempotencyError extends Error {
|
|
237
|
-
|
|
238
|
-
|
|
208
|
+
readonly code: 'DUPLICATE_REQUEST' | 'REQUEST_IN_PROGRESS' | 'REQUEST_MISMATCH';
|
|
209
|
+
constructor(message: string, code: 'DUPLICATE_REQUEST' | 'REQUEST_IN_PROGRESS' | 'REQUEST_MISMATCH');
|
|
239
210
|
}
|
|
240
211
|
/**
|
|
241
212
|
* Idempotency manager
|
|
242
213
|
*/
|
|
243
214
|
declare class IdempotencyManager {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
215
|
+
private store;
|
|
216
|
+
private ttl;
|
|
217
|
+
private prefix;
|
|
218
|
+
constructor(config?: IdempotencyConfig);
|
|
219
|
+
/**
|
|
220
|
+
* Generate a unique idempotency key
|
|
221
|
+
*/
|
|
222
|
+
generateKey(): string;
|
|
223
|
+
/**
|
|
224
|
+
* Hash request parameters for validation
|
|
225
|
+
* Uses deterministic JSON serialization and simple hash function
|
|
226
|
+
*/
|
|
227
|
+
private hashRequest;
|
|
228
|
+
/**
|
|
229
|
+
* Execute operation with idempotency protection
|
|
230
|
+
*/
|
|
231
|
+
execute<T>(key: string, params: unknown, operation: () => Promise<T>): Promise<Result<T, IdempotencyError>>;
|
|
232
|
+
/**
|
|
233
|
+
* Check if operation with key was already completed
|
|
234
|
+
*/
|
|
235
|
+
wasCompleted(key: string): Promise<boolean>;
|
|
236
|
+
/**
|
|
237
|
+
* Get cached result for key
|
|
238
|
+
*/
|
|
239
|
+
getCached<T>(key: string): Promise<T | null>;
|
|
240
|
+
/**
|
|
241
|
+
* Invalidate a key (force re-execution on next call)
|
|
242
|
+
*/
|
|
243
|
+
invalidate(key: string): Promise<void>;
|
|
244
|
+
/**
|
|
245
|
+
* Destroy the idempotency manager and cleanup resources
|
|
246
|
+
* Call this when shutting down to prevent memory leaks
|
|
247
|
+
*/
|
|
248
|
+
destroy(): void;
|
|
278
249
|
}
|
|
279
250
|
/**
|
|
280
251
|
* Create idempotency manager
|
|
281
252
|
*/
|
|
282
253
|
declare function createIdempotencyManager(config?: IdempotencyConfig): IdempotencyManager;
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
* Category Resolver Utility
|
|
286
|
-
* @classytic/revenue
|
|
287
|
-
*
|
|
288
|
-
* Resolves transaction category based on categoryMappings
|
|
289
|
-
*/
|
|
290
|
-
|
|
254
|
+
//#endregion
|
|
255
|
+
//#region src/shared/utils/validators/category-resolver.d.ts
|
|
291
256
|
/**
|
|
292
257
|
* Resolve category for a transaction based on entity and monetizationType
|
|
293
258
|
*
|
|
@@ -324,23 +289,14 @@ declare function createIdempotencyManager(config?: IdempotencyConfig): Idempoten
|
|
|
324
289
|
* // Returns: 'platform_subscription'
|
|
325
290
|
*/
|
|
326
291
|
declare function resolveCategory(entity: string | null | undefined, monetizationType: MonetizationTypeValue, categoryMappings?: Record<string, string>): string;
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
* Transaction Type Detection & Classification
|
|
330
|
-
*
|
|
331
|
-
* Distinguishes between:
|
|
332
|
-
* - Monetization-managed transactions (library-controlled, strict rules)
|
|
333
|
-
* - Manual admin transactions (flexible, admin-controlled)
|
|
334
|
-
*
|
|
335
|
-
* @module @classytic/revenue/utils/transaction-type
|
|
336
|
-
*/
|
|
337
|
-
|
|
292
|
+
//#endregion
|
|
293
|
+
//#region src/utils/transaction-type.d.ts
|
|
338
294
|
/**
|
|
339
295
|
* Transaction types with different protection rules
|
|
340
296
|
*/
|
|
341
297
|
declare const TRANSACTION_MANAGEMENT_TYPE: {
|
|
342
|
-
|
|
343
|
-
|
|
298
|
+
readonly MONETIZATION: "monetization";
|
|
299
|
+
readonly MANUAL: "manual";
|
|
344
300
|
};
|
|
345
301
|
type TransactionManagementType = typeof TRANSACTION_MANAGEMENT_TYPE[keyof typeof TRANSACTION_MANAGEMENT_TYPE];
|
|
346
302
|
/**
|
|
@@ -416,22 +372,8 @@ declare function validateFieldUpdate(transaction: Partial<TransactionDocument>,
|
|
|
416
372
|
* @param options - Options for transaction type detection
|
|
417
373
|
*/
|
|
418
374
|
declare function canSelfVerify(transaction: Partial<TransactionDocument>, options?: TransactionTypeOptions): boolean;
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
* Logger Abstraction for Monetization Library
|
|
422
|
-
*
|
|
423
|
-
* Defaults to console for standalone usage
|
|
424
|
-
* Can be overridden with custom logger (pino, winston, etc)
|
|
425
|
-
*
|
|
426
|
-
* Usage:
|
|
427
|
-
* ```typescript
|
|
428
|
-
* import { setLogger } from '@classytic/revenue';
|
|
429
|
-
*
|
|
430
|
-
* // Optional: Use your own logger
|
|
431
|
-
* setLogger(myPinoLogger);
|
|
432
|
-
* ```
|
|
433
|
-
*/
|
|
434
|
-
|
|
375
|
+
//#endregion
|
|
376
|
+
//#region src/utils/logger.d.ts
|
|
435
377
|
/**
|
|
436
378
|
* Set custom logger implementation
|
|
437
379
|
* @param customLogger - Logger instance with info, warn, error, debug methods
|
|
@@ -441,14 +383,8 @@ declare function setLogger(customLogger: Logger): void;
|
|
|
441
383
|
* Logger proxy - delegates to current logger implementation
|
|
442
384
|
*/
|
|
443
385
|
declare const logger: Logger;
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
* Subscription Period Utilities
|
|
447
|
-
* @classytic/revenue/utils/subscription
|
|
448
|
-
*
|
|
449
|
-
* Universal period calculation, proration, and date utilities
|
|
450
|
-
*/
|
|
451
|
-
|
|
386
|
+
//#endregion
|
|
387
|
+
//#region src/utils/subscription/period.d.ts
|
|
452
388
|
type DurationUnit = 'days' | 'day' | 'weeks' | 'week' | 'months' | 'month' | 'years' | 'year';
|
|
453
389
|
/**
|
|
454
390
|
* Add duration to date
|
|
@@ -466,14 +402,8 @@ declare function calculateProratedAmount(params: ProratedAmountParams): number;
|
|
|
466
402
|
* Convert interval + count to duration/unit
|
|
467
403
|
*/
|
|
468
404
|
declare function resolveIntervalToDuration(interval?: string, intervalCount?: number): DurationResult;
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
* Subscription Action Utilities
|
|
472
|
-
* @classytic/revenue/utils/subscription
|
|
473
|
-
*
|
|
474
|
-
* Eligibility checks for subscription actions
|
|
475
|
-
*/
|
|
476
|
-
|
|
405
|
+
//#endregion
|
|
406
|
+
//#region src/utils/subscription/actions.d.ts
|
|
477
407
|
/**
|
|
478
408
|
* Check if subscription is active
|
|
479
409
|
*/
|
|
@@ -494,5 +424,5 @@ declare function canPauseSubscription(entity: SubscriptionEntity | null | undefi
|
|
|
494
424
|
* Check if can resume
|
|
495
425
|
*/
|
|
496
426
|
declare function canResumeSubscription(entity: SubscriptionEntity | null | undefined): boolean;
|
|
497
|
-
|
|
498
|
-
export { type DurationUnit, EDITABLE_MONETIZATION_FIELDS_PRE_VERIFICATION, IdempotencyManager, MANUAL_TRANSACTION_CREATE_FIELDS, MANUAL_TRANSACTION_UPDATE_FIELDS, PROTECTED_MONETIZATION_FIELDS, TRANSACTION_MANAGEMENT_TYPE,
|
|
427
|
+
//#endregion
|
|
428
|
+
export { CircuitBreaker, CircuitBreakerConfig, CircuitState, type DurationUnit, EDITABLE_MONETIZATION_FIELDS_PRE_VERIFICATION, IdempotencyManager, MANUAL_TRANSACTION_CREATE_FIELDS, MANUAL_TRANSACTION_UPDATE_FIELDS, Money, MoneyValue, PROTECTED_MONETIZATION_FIELDS, RetryConfig, TRANSACTION_MANAGEMENT_TYPE, TransactionManagementType, addDuration, calculateCommission, calculateOrganizationPayout, calculatePeriodRange, calculateProratedAmount, calculateSplits, calculateTax, canCancelSubscription, canPauseSubscription, canRenewSubscription, canResumeSubscription, canSelfVerify, createCircuitBreaker, createIdempotencyManager, fromSmallestUnit, getAllowedUpdateFields, getTaxType, getTransactionType, isManualTransaction, isMonetizationTransaction, isSubscriptionActive, logger, logger as loggerDefault, resolveCategory, resolveIntervalToDuration, retry, reverseCommission, reverseSplits, reverseTax, setLogger, toSmallestUnit, validateFieldUpdate };
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { a as reverseCommission, c as retry, i as calculateCommission, n as calculateSplits, o as CircuitBreaker, r as reverseSplits, s as createCircuitBreaker, t as calculateOrganizationPayout } from "../commission-split-BzB8cd39.mjs";
|
|
2
|
+
import { n as createIdempotencyManager, t as IdempotencyManager } from "../idempotency-DaYcUGY1.mjs";
|
|
3
|
+
import { t as resolveCategory } from "../category-resolver-DV83N8ok.mjs";
|
|
4
|
+
import { f as SUBSCRIPTION_STATUS } from "../split.enums-BrjabxIX.mjs";
|
|
5
|
+
import { n as fromSmallestUnit, r as toSmallestUnit, t as Money } from "../money-CvrDOijQ.mjs";
|
|
6
|
+
import { n as getTaxType, r as reverseTax, t as calculateTax } from "../tax-EQ15DO81.mjs";
|
|
7
|
+
|
|
8
|
+
//#region src/utils/transaction-type.ts
|
|
9
|
+
/**
|
|
10
|
+
* Transaction types with different protection rules
|
|
11
|
+
*/
|
|
12
|
+
const TRANSACTION_MANAGEMENT_TYPE = {
|
|
13
|
+
MONETIZATION: "monetization",
|
|
14
|
+
MANUAL: "manual"
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Default monetization categories
|
|
18
|
+
* Users can extend this via config.categoryMappings
|
|
19
|
+
*/
|
|
20
|
+
const DEFAULT_MONETIZATION_CATEGORIES = ["subscription", "purchase"];
|
|
21
|
+
/**
|
|
22
|
+
* Check if category is monetization-related
|
|
23
|
+
* @param category - Transaction category
|
|
24
|
+
* @param additionalCategories - Additional categories from user config
|
|
25
|
+
*/
|
|
26
|
+
function isMonetizationCategory(category, additionalCategories = []) {
|
|
27
|
+
return [...DEFAULT_MONETIZATION_CATEGORIES, ...additionalCategories].includes(category);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Check if transaction is monetization-managed
|
|
31
|
+
*
|
|
32
|
+
* Monetization-managed means:
|
|
33
|
+
* - Created through subscription/purchase flows via the library
|
|
34
|
+
* - Status controlled by payment webhooks/verification
|
|
35
|
+
* - Amount/commission calculated by library
|
|
36
|
+
* - Protected fields: status, amount, commission, gateway, verifiedAt, verifiedBy
|
|
37
|
+
*
|
|
38
|
+
* @param transaction - Transaction document or data
|
|
39
|
+
* @param options - Options
|
|
40
|
+
*/
|
|
41
|
+
function isMonetizationTransaction(transaction, options = {}) {
|
|
42
|
+
const { targetModels = ["Subscription", "Membership"], additionalCategories = [] } = options;
|
|
43
|
+
if (transaction.sourceModel && targetModels.includes(transaction.sourceModel)) return true;
|
|
44
|
+
if (transaction.category) return isMonetizationCategory(transaction.category, additionalCategories);
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Check if transaction is manual admin transaction
|
|
49
|
+
*
|
|
50
|
+
* Manual transactions:
|
|
51
|
+
* - Created directly by admins for operational expenses/income
|
|
52
|
+
* - Can be self-verified by admins
|
|
53
|
+
* - More flexible updates allowed
|
|
54
|
+
* - No commission/gateway complexity
|
|
55
|
+
*
|
|
56
|
+
* @param transaction - Transaction document or data
|
|
57
|
+
* @param options - Options (same as isMonetizationTransaction)
|
|
58
|
+
*/
|
|
59
|
+
function isManualTransaction(transaction, options = {}) {
|
|
60
|
+
return !isMonetizationTransaction(transaction, options);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get transaction type
|
|
64
|
+
*
|
|
65
|
+
* @param transaction - Transaction document or data
|
|
66
|
+
* @param options - Options (same as isMonetizationTransaction)
|
|
67
|
+
*/
|
|
68
|
+
function getTransactionType(transaction, options = {}) {
|
|
69
|
+
return isMonetizationTransaction(transaction, options) ? TRANSACTION_MANAGEMENT_TYPE.MONETIZATION : TRANSACTION_MANAGEMENT_TYPE.MANUAL;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Protected fields for monetization transactions
|
|
73
|
+
* These fields cannot be updated directly by admins
|
|
74
|
+
*/
|
|
75
|
+
const PROTECTED_MONETIZATION_FIELDS = [
|
|
76
|
+
"status",
|
|
77
|
+
"amount",
|
|
78
|
+
"platformCommission",
|
|
79
|
+
"netAmount",
|
|
80
|
+
"verifiedAt",
|
|
81
|
+
"verifiedBy",
|
|
82
|
+
"gateway",
|
|
83
|
+
"webhook",
|
|
84
|
+
"metadata.commission",
|
|
85
|
+
"metadata.gateway",
|
|
86
|
+
"type",
|
|
87
|
+
"category",
|
|
88
|
+
"sourceModel",
|
|
89
|
+
"sourceId"
|
|
90
|
+
];
|
|
91
|
+
/**
|
|
92
|
+
* Editable fields for monetization transactions (before verification)
|
|
93
|
+
* These fields can be updated by frontend/customer before payment is verified
|
|
94
|
+
*/
|
|
95
|
+
const EDITABLE_MONETIZATION_FIELDS_PRE_VERIFICATION = [
|
|
96
|
+
"reference",
|
|
97
|
+
"paymentDetails",
|
|
98
|
+
"notes"
|
|
99
|
+
];
|
|
100
|
+
/**
|
|
101
|
+
* Allowed fields for manual transaction creation
|
|
102
|
+
*/
|
|
103
|
+
const MANUAL_TRANSACTION_CREATE_FIELDS = [
|
|
104
|
+
"organizationId",
|
|
105
|
+
"type",
|
|
106
|
+
"category",
|
|
107
|
+
"amount",
|
|
108
|
+
"method",
|
|
109
|
+
"reference",
|
|
110
|
+
"paymentDetails",
|
|
111
|
+
"notes",
|
|
112
|
+
"date",
|
|
113
|
+
"description"
|
|
114
|
+
];
|
|
115
|
+
/**
|
|
116
|
+
* Allowed fields for manual transaction updates
|
|
117
|
+
*/
|
|
118
|
+
const MANUAL_TRANSACTION_UPDATE_FIELDS = [
|
|
119
|
+
"amount",
|
|
120
|
+
"method",
|
|
121
|
+
"reference",
|
|
122
|
+
"paymentDetails",
|
|
123
|
+
"notes",
|
|
124
|
+
"date",
|
|
125
|
+
"description"
|
|
126
|
+
];
|
|
127
|
+
/**
|
|
128
|
+
* Get allowed update fields based on transaction type and status
|
|
129
|
+
*
|
|
130
|
+
* @param transaction - Transaction document
|
|
131
|
+
* @param options - Options for transaction type detection
|
|
132
|
+
*/
|
|
133
|
+
function getAllowedUpdateFields(transaction, options = {}) {
|
|
134
|
+
if (getTransactionType(transaction, options) === TRANSACTION_MANAGEMENT_TYPE.MONETIZATION) {
|
|
135
|
+
if (transaction.status === "pending") return EDITABLE_MONETIZATION_FIELDS_PRE_VERIFICATION;
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
if (transaction.status === "verified" || transaction.status === "completed") return ["notes"];
|
|
139
|
+
return MANUAL_TRANSACTION_UPDATE_FIELDS;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Validate if field update is allowed
|
|
143
|
+
*
|
|
144
|
+
* @param transaction - Transaction document
|
|
145
|
+
* @param fieldName - Field being updated
|
|
146
|
+
* @param options - Options for transaction type detection
|
|
147
|
+
*/
|
|
148
|
+
function validateFieldUpdate(transaction, fieldName, options = {}) {
|
|
149
|
+
if (getAllowedUpdateFields(transaction, options).includes(fieldName)) return { allowed: true };
|
|
150
|
+
if (getTransactionType(transaction, options) === TRANSACTION_MANAGEMENT_TYPE.MONETIZATION) {
|
|
151
|
+
if (PROTECTED_MONETIZATION_FIELDS.includes(fieldName)) return {
|
|
152
|
+
allowed: false,
|
|
153
|
+
reason: `Field "${fieldName}" is protected for monetization transactions. Updates must go through payment flow.`
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
allowed: false,
|
|
158
|
+
reason: `Field "${fieldName}" cannot be updated for ${transaction.status} transactions.`
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Check if transaction can be self-verified by admin
|
|
163
|
+
*
|
|
164
|
+
* @param transaction - Transaction document
|
|
165
|
+
* @param options - Options for transaction type detection
|
|
166
|
+
*/
|
|
167
|
+
function canSelfVerify(transaction, options = {}) {
|
|
168
|
+
if (getTransactionType(transaction, options) === TRANSACTION_MANAGEMENT_TYPE.MANUAL) return transaction.status === "pending";
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region src/utils/logger.ts
|
|
174
|
+
let _logger = console;
|
|
175
|
+
/**
|
|
176
|
+
* Set custom logger implementation
|
|
177
|
+
* @param customLogger - Logger instance with info, warn, error, debug methods
|
|
178
|
+
*/
|
|
179
|
+
function setLogger(customLogger) {
|
|
180
|
+
_logger = customLogger;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Logger proxy - delegates to current logger implementation
|
|
184
|
+
*/
|
|
185
|
+
const logger = {
|
|
186
|
+
info: (...args) => {
|
|
187
|
+
(_logger.info ?? _logger.log)?.call(_logger, ...args);
|
|
188
|
+
},
|
|
189
|
+
warn: (...args) => {
|
|
190
|
+
(_logger.warn ?? _logger.log)?.call(_logger, "WARN:", ...args);
|
|
191
|
+
},
|
|
192
|
+
error: (...args) => {
|
|
193
|
+
(_logger.error ?? _logger.log)?.call(_logger, "ERROR:", ...args);
|
|
194
|
+
},
|
|
195
|
+
debug: (...args) => {
|
|
196
|
+
(_logger.debug ?? _logger.log)?.call(_logger, "DEBUG:", ...args);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
//#endregion
|
|
201
|
+
//#region src/utils/subscription/period.ts
|
|
202
|
+
/**
|
|
203
|
+
* Add duration to date
|
|
204
|
+
*/
|
|
205
|
+
function addDuration(startDate, duration, unit = "days") {
|
|
206
|
+
const date = new Date(startDate);
|
|
207
|
+
switch (unit) {
|
|
208
|
+
case "months":
|
|
209
|
+
case "month":
|
|
210
|
+
date.setMonth(date.getMonth() + duration);
|
|
211
|
+
return date;
|
|
212
|
+
case "years":
|
|
213
|
+
case "year":
|
|
214
|
+
date.setFullYear(date.getFullYear() + duration);
|
|
215
|
+
return date;
|
|
216
|
+
case "weeks":
|
|
217
|
+
case "week":
|
|
218
|
+
date.setDate(date.getDate() + duration * 7);
|
|
219
|
+
return date;
|
|
220
|
+
default:
|
|
221
|
+
date.setDate(date.getDate() + duration);
|
|
222
|
+
return date;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Calculate subscription period start/end dates
|
|
227
|
+
*/
|
|
228
|
+
function calculatePeriodRange(params) {
|
|
229
|
+
const { currentEndDate = null, startDate = null, duration, unit = "days", now = /* @__PURE__ */ new Date() } = params;
|
|
230
|
+
let periodStart;
|
|
231
|
+
if (startDate) periodStart = new Date(startDate);
|
|
232
|
+
else if (currentEndDate) {
|
|
233
|
+
const end = new Date(currentEndDate);
|
|
234
|
+
periodStart = end > now ? end : now;
|
|
235
|
+
} else periodStart = now;
|
|
236
|
+
const periodEnd = addDuration(periodStart, duration, unit);
|
|
237
|
+
return {
|
|
238
|
+
startDate: periodStart,
|
|
239
|
+
endDate: periodEnd
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Calculate prorated refund amount for unused period
|
|
244
|
+
*/
|
|
245
|
+
function calculateProratedAmount(params) {
|
|
246
|
+
const { amountPaid, startDate, endDate, asOfDate = /* @__PURE__ */ new Date(), precision = 2 } = params;
|
|
247
|
+
if (!amountPaid || amountPaid <= 0) return 0;
|
|
248
|
+
const start = new Date(startDate);
|
|
249
|
+
const end = new Date(endDate);
|
|
250
|
+
const asOf = new Date(asOfDate);
|
|
251
|
+
const totalMs = end.getTime() - start.getTime();
|
|
252
|
+
if (totalMs <= 0) return 0;
|
|
253
|
+
const remainingMs = Math.max(0, end.getTime() - asOf.getTime());
|
|
254
|
+
if (remainingMs <= 0) return 0;
|
|
255
|
+
const amount = amountPaid * (remainingMs / totalMs);
|
|
256
|
+
const factor = 10 ** precision;
|
|
257
|
+
return Math.round(amount * factor) / factor;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Convert interval + count to duration/unit
|
|
261
|
+
*/
|
|
262
|
+
function resolveIntervalToDuration(interval = "month", intervalCount = 1) {
|
|
263
|
+
const normalized = (interval || "month").toLowerCase();
|
|
264
|
+
const count = Number(intervalCount) > 0 ? Number(intervalCount) : 1;
|
|
265
|
+
switch (normalized) {
|
|
266
|
+
case "year":
|
|
267
|
+
case "years": return {
|
|
268
|
+
duration: count,
|
|
269
|
+
unit: "years"
|
|
270
|
+
};
|
|
271
|
+
case "week":
|
|
272
|
+
case "weeks": return {
|
|
273
|
+
duration: count,
|
|
274
|
+
unit: "weeks"
|
|
275
|
+
};
|
|
276
|
+
case "quarter":
|
|
277
|
+
case "quarters": return {
|
|
278
|
+
duration: count * 3,
|
|
279
|
+
unit: "months"
|
|
280
|
+
};
|
|
281
|
+
case "day":
|
|
282
|
+
case "days": return {
|
|
283
|
+
duration: count,
|
|
284
|
+
unit: "days"
|
|
285
|
+
};
|
|
286
|
+
default: return {
|
|
287
|
+
duration: count,
|
|
288
|
+
unit: "months"
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
//#endregion
|
|
294
|
+
//#region src/utils/subscription/actions.ts
|
|
295
|
+
/**
|
|
296
|
+
* Subscription Action Utilities
|
|
297
|
+
* @classytic/revenue/utils/subscription
|
|
298
|
+
*
|
|
299
|
+
* Eligibility checks for subscription actions
|
|
300
|
+
*/
|
|
301
|
+
/**
|
|
302
|
+
* Check if subscription is active
|
|
303
|
+
*/
|
|
304
|
+
function isSubscriptionActive(subscription) {
|
|
305
|
+
if (!subscription) return false;
|
|
306
|
+
if (!subscription.isActive) return false;
|
|
307
|
+
if (subscription.endDate) {
|
|
308
|
+
const now = /* @__PURE__ */ new Date();
|
|
309
|
+
if (new Date(subscription.endDate) < now) return false;
|
|
310
|
+
}
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Check if can renew
|
|
315
|
+
*/
|
|
316
|
+
function canRenewSubscription(entity) {
|
|
317
|
+
if (!entity?.subscription) return false;
|
|
318
|
+
return isSubscriptionActive(entity.subscription);
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Check if can cancel
|
|
322
|
+
*/
|
|
323
|
+
function canCancelSubscription(entity) {
|
|
324
|
+
if (!entity?.subscription) return false;
|
|
325
|
+
if (!isSubscriptionActive(entity.subscription)) return false;
|
|
326
|
+
return !entity.subscription.canceledAt;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Check if can pause
|
|
330
|
+
*/
|
|
331
|
+
function canPauseSubscription(entity) {
|
|
332
|
+
if (!entity?.subscription) return false;
|
|
333
|
+
if (entity.status === SUBSCRIPTION_STATUS.PAUSED) return false;
|
|
334
|
+
if (entity.status === SUBSCRIPTION_STATUS.CANCELLED) return false;
|
|
335
|
+
return isSubscriptionActive(entity.subscription);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Check if can resume
|
|
339
|
+
*/
|
|
340
|
+
function canResumeSubscription(entity) {
|
|
341
|
+
if (!entity?.subscription) return false;
|
|
342
|
+
return entity.status === SUBSCRIPTION_STATUS.PAUSED;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
//#endregion
|
|
346
|
+
export { CircuitBreaker, EDITABLE_MONETIZATION_FIELDS_PRE_VERIFICATION, IdempotencyManager, MANUAL_TRANSACTION_CREATE_FIELDS, MANUAL_TRANSACTION_UPDATE_FIELDS, Money, PROTECTED_MONETIZATION_FIELDS, TRANSACTION_MANAGEMENT_TYPE, addDuration, calculateCommission, calculateOrganizationPayout, calculatePeriodRange, calculateProratedAmount, calculateSplits, calculateTax, canCancelSubscription, canPauseSubscription, canRenewSubscription, canResumeSubscription, canSelfVerify, createCircuitBreaker, createIdempotencyManager, fromSmallestUnit, getAllowedUpdateFields, getTaxType, getTransactionType, isManualTransaction, isMonetizationTransaction, isSubscriptionActive, logger, logger as loggerDefault, resolveCategory, resolveIntervalToDuration, retry, reverseCommission, reverseSplits, reverseTax, setLogger, toSmallestUnit, validateFieldUpdate };
|