@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,27 +1,16 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import 'mongoose';
|
|
7
|
-
import '@classytic/shared-types';
|
|
8
|
-
import '../../providers/index.js';
|
|
9
|
-
import '../../settlement.schema-CpamV7ZY.js';
|
|
10
|
-
import '../../split.enums-DG3TxQf9.js';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Logging Plugin
|
|
14
|
-
* @classytic/revenue
|
|
15
|
-
*
|
|
16
|
-
* Logs all revenue operations at specified log level
|
|
17
|
-
*/
|
|
1
|
+
import "../../index-Dsp7H5Wb.mjs";
|
|
2
|
+
import "../../settlement.schema-DnNSFpGd.mjs";
|
|
3
|
+
import "../../base-CsTlVQJe.mjs";
|
|
4
|
+
import { c as definePlugin, o as RevenuePlugin } from "../../plugin-BbK0OVHy.mjs";
|
|
5
|
+
import { n as TaxConfig } from "../../tax-BoCt5cEd.mjs";
|
|
18
6
|
|
|
7
|
+
//#region src/infrastructure/plugins/business/logging.plugin.d.ts
|
|
19
8
|
/**
|
|
20
9
|
* Logging plugin options
|
|
21
10
|
*/
|
|
22
11
|
interface LoggingPluginOptions {
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
/** Log level: 'debug' or 'info' */
|
|
13
|
+
level?: 'debug' | 'info';
|
|
25
14
|
}
|
|
26
15
|
/**
|
|
27
16
|
* Logging plugin - logs all operations
|
|
@@ -43,31 +32,25 @@ interface LoggingPluginOptions {
|
|
|
43
32
|
* ```
|
|
44
33
|
*/
|
|
45
34
|
declare function loggingPlugin(options?: LoggingPluginOptions): RevenuePlugin;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
* Audit Plugin
|
|
49
|
-
* @classytic/revenue
|
|
50
|
-
*
|
|
51
|
-
* Records all operations for compliance and audit trails
|
|
52
|
-
*/
|
|
53
|
-
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/infrastructure/plugins/business/audit.plugin.d.ts
|
|
54
37
|
/**
|
|
55
38
|
* Audit entry record
|
|
56
39
|
*/
|
|
57
40
|
interface AuditEntry {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
41
|
+
action: string;
|
|
42
|
+
requestId: string;
|
|
43
|
+
timestamp: Date;
|
|
44
|
+
input: Record<string, unknown>;
|
|
45
|
+
output: Record<string, unknown>;
|
|
46
|
+
idempotencyKey?: string;
|
|
64
47
|
}
|
|
65
48
|
/**
|
|
66
49
|
* Audit plugin options
|
|
67
50
|
*/
|
|
68
51
|
interface AuditPluginOptions {
|
|
69
|
-
|
|
70
|
-
|
|
52
|
+
/** Custom storage function for audit entries */
|
|
53
|
+
store?: (entry: AuditEntry) => Promise<void>;
|
|
71
54
|
}
|
|
72
55
|
/**
|
|
73
56
|
* Audit plugin - records all operations for compliance
|
|
@@ -93,30 +76,24 @@ interface AuditPluginOptions {
|
|
|
93
76
|
* ```
|
|
94
77
|
*/
|
|
95
78
|
declare function auditPlugin(options?: AuditPluginOptions): RevenuePlugin;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
* Metrics Plugin
|
|
99
|
-
* @classytic/revenue
|
|
100
|
-
*
|
|
101
|
-
* Collects operation metrics (duration, success/failure)
|
|
102
|
-
*/
|
|
103
|
-
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/infrastructure/plugins/business/metrics.plugin.d.ts
|
|
104
81
|
/**
|
|
105
82
|
* Metric record
|
|
106
83
|
*/
|
|
107
84
|
interface Metric {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
85
|
+
name: string;
|
|
86
|
+
duration: number;
|
|
87
|
+
success: boolean;
|
|
88
|
+
error?: string;
|
|
89
|
+
[key: string]: unknown;
|
|
113
90
|
}
|
|
114
91
|
/**
|
|
115
92
|
* Metrics plugin options
|
|
116
93
|
*/
|
|
117
94
|
interface MetricsPluginOptions {
|
|
118
|
-
|
|
119
|
-
|
|
95
|
+
/** Callback for each metric */
|
|
96
|
+
onMetric?: (metric: Metric) => void;
|
|
120
97
|
}
|
|
121
98
|
/**
|
|
122
99
|
* Metrics plugin - collects operation metrics
|
|
@@ -146,55 +123,56 @@ interface MetricsPluginOptions {
|
|
|
146
123
|
* ```
|
|
147
124
|
*/
|
|
148
125
|
declare function metricsPlugin(options?: MetricsPluginOptions): RevenuePlugin;
|
|
149
|
-
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/infrastructure/plugins/business/tax.plugin.d.ts
|
|
150
128
|
/**
|
|
151
129
|
* Tax Plugin Options
|
|
152
130
|
*/
|
|
153
131
|
interface TaxPluginOptions {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
132
|
+
/**
|
|
133
|
+
* Function to get tax configuration for an organization
|
|
134
|
+
* Apps implement this to return jurisdiction-specific config
|
|
135
|
+
*
|
|
136
|
+
* @param orgId - Organization ID
|
|
137
|
+
* @returns Tax configuration or null if not registered
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```typescript
|
|
141
|
+
* getTaxConfig: async (orgId) => {
|
|
142
|
+
* const org = await Organization.findById(orgId);
|
|
143
|
+
* if (!org) return null;
|
|
144
|
+
*
|
|
145
|
+
* return {
|
|
146
|
+
* isRegistered: org.country === 'AU',
|
|
147
|
+
* defaultRate: org.country === 'AU' ? 0.10 : 0, // 10% GST in Australia
|
|
148
|
+
* pricesIncludeTax: org.pricesIncludeTax || false,
|
|
149
|
+
* exemptCategories: ['education', 'medical'],
|
|
150
|
+
* };
|
|
151
|
+
* }
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
getTaxConfig: (orgId: string) => Promise<TaxConfig | null>;
|
|
155
|
+
/**
|
|
156
|
+
* Category mappings for resolving transaction categories
|
|
157
|
+
* Maps entity names to category strings
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```typescript
|
|
161
|
+
* {
|
|
162
|
+
* Order: 'order_subscription',
|
|
163
|
+
* PlatformSubscription: 'platform_subscription',
|
|
164
|
+
* Membership: 'gym_membership',
|
|
165
|
+
* }
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
categoryMappings?: Record<string, string>;
|
|
169
|
+
/**
|
|
170
|
+
* Categories that represent income (vs expense)
|
|
171
|
+
* Used to determine tax type: 'collected' vs 'paid'
|
|
172
|
+
*
|
|
173
|
+
* Default: ['subscription', 'purchase', 'course_enrollment']
|
|
174
|
+
*/
|
|
175
|
+
incomeCategories?: string[];
|
|
198
176
|
}
|
|
199
177
|
/**
|
|
200
178
|
* Create Tax Plugin
|
|
@@ -249,19 +227,13 @@ interface TaxPluginOptions {
|
|
|
249
227
|
* ```
|
|
250
228
|
*/
|
|
251
229
|
declare function createTaxPlugin(options: TaxPluginOptions): RevenuePlugin;
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
* Built-in Plugins
|
|
255
|
-
* @classytic/revenue/plugins
|
|
256
|
-
*
|
|
257
|
-
* Collection of built-in plugins for common use cases
|
|
258
|
-
*/
|
|
259
|
-
|
|
230
|
+
//#endregion
|
|
231
|
+
//#region src/infrastructure/plugins/index.d.ts
|
|
260
232
|
declare const _default: {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
233
|
+
loggingPlugin: typeof loggingPlugin;
|
|
234
|
+
auditPlugin: typeof auditPlugin;
|
|
235
|
+
metricsPlugin: typeof metricsPlugin;
|
|
236
|
+
createTaxPlugin: typeof createTaxPlugin;
|
|
265
237
|
};
|
|
266
|
-
|
|
267
|
-
export { type AuditEntry, type AuditPluginOptions, type LoggingPluginOptions, type Metric, type MetricsPluginOptions, type TaxPluginOptions, auditPlugin, createTaxPlugin, _default as default, loggingPlugin, metricsPlugin };
|
|
238
|
+
//#endregion
|
|
239
|
+
export { type AuditEntry, type AuditPluginOptions, type LoggingPluginOptions, type Metric, type MetricsPluginOptions, type TaxPluginOptions, auditPlugin, createTaxPlugin, _default as default, definePlugin, loggingPlugin, metricsPlugin };
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { r as definePlugin } from "../../plugin-Cd_V04Em.mjs";
|
|
2
|
+
import { t as resolveCategory } from "../../category-resolver-DV83N8ok.mjs";
|
|
3
|
+
import { n as getTaxType, t as calculateTax } from "../../tax-EQ15DO81.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/infrastructure/plugins/business/logging.plugin.ts
|
|
6
|
+
/**
|
|
7
|
+
* Logging Plugin
|
|
8
|
+
* @classytic/revenue
|
|
9
|
+
*
|
|
10
|
+
* Logs all revenue operations at specified log level
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Logging plugin - logs all operations
|
|
14
|
+
*
|
|
15
|
+
* Logs payment creation, verification, and refund operations
|
|
16
|
+
*
|
|
17
|
+
* @param options - Plugin options
|
|
18
|
+
* @returns Logging plugin
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* import { Revenue } from '@classytic/revenue';
|
|
23
|
+
* import { loggingPlugin } from '@classytic/revenue/plugins';
|
|
24
|
+
*
|
|
25
|
+
* const revenue = Revenue
|
|
26
|
+
* .create()
|
|
27
|
+
* .withPlugin(loggingPlugin({ level: 'debug' }))
|
|
28
|
+
* .build();
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
function loggingPlugin(options = {}) {
|
|
32
|
+
const level = options.level ?? "info";
|
|
33
|
+
return definePlugin({
|
|
34
|
+
name: "logging",
|
|
35
|
+
version: "1.0.0",
|
|
36
|
+
description: "Logs all revenue operations",
|
|
37
|
+
hooks: {
|
|
38
|
+
"payment.create.after": async (ctx, input, next) => {
|
|
39
|
+
ctx.logger[level]("Creating payment", {
|
|
40
|
+
amount: input.amount,
|
|
41
|
+
currency: input.currency
|
|
42
|
+
});
|
|
43
|
+
const result = await next();
|
|
44
|
+
ctx.logger[level]("Payment created", { paymentIntentId: result?.paymentIntentId });
|
|
45
|
+
return result;
|
|
46
|
+
},
|
|
47
|
+
"payment.verify.after": async (ctx, input, next) => {
|
|
48
|
+
ctx.logger[level]("Verifying payment", { id: input.id });
|
|
49
|
+
const result = await next();
|
|
50
|
+
ctx.logger[level]("Payment verified", { verified: result?.verified });
|
|
51
|
+
return result;
|
|
52
|
+
},
|
|
53
|
+
"payment.refund.after": async (ctx, input, next) => {
|
|
54
|
+
ctx.logger[level]("Processing refund", {
|
|
55
|
+
transactionId: input.transactionId,
|
|
56
|
+
amount: input.amount
|
|
57
|
+
});
|
|
58
|
+
const result = await next();
|
|
59
|
+
ctx.logger[level]("Refund processed", { refundId: result?.refundId });
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/infrastructure/plugins/business/audit.plugin.ts
|
|
68
|
+
/**
|
|
69
|
+
* Audit Plugin
|
|
70
|
+
* @classytic/revenue
|
|
71
|
+
*
|
|
72
|
+
* Records all operations for compliance and audit trails
|
|
73
|
+
*/
|
|
74
|
+
/**
|
|
75
|
+
* Sanitize input by removing sensitive fields
|
|
76
|
+
* @private
|
|
77
|
+
*/
|
|
78
|
+
function sanitizeInput(input) {
|
|
79
|
+
if (typeof input !== "object" || !input) return {};
|
|
80
|
+
const sanitized = { ...input };
|
|
81
|
+
delete sanitized.apiKey;
|
|
82
|
+
delete sanitized.secretKey;
|
|
83
|
+
delete sanitized.password;
|
|
84
|
+
return sanitized;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Sanitize output
|
|
88
|
+
* @private
|
|
89
|
+
*/
|
|
90
|
+
function sanitizeOutput(output) {
|
|
91
|
+
if (typeof output !== "object" || !output) return {};
|
|
92
|
+
return { ...output };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Audit plugin - records all operations for compliance
|
|
96
|
+
*
|
|
97
|
+
* Records payment creation, refunds, and other operations with sanitized data
|
|
98
|
+
*
|
|
99
|
+
* @param options - Plugin options
|
|
100
|
+
* @returns Audit plugin
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```typescript
|
|
104
|
+
* import { Revenue } from '@classytic/revenue';
|
|
105
|
+
* import { auditPlugin } from '@classytic/revenue/plugins';
|
|
106
|
+
*
|
|
107
|
+
* const revenue = Revenue
|
|
108
|
+
* .create()
|
|
109
|
+
* .withPlugin(auditPlugin({
|
|
110
|
+
* store: async (entry) => {
|
|
111
|
+
* await AuditLog.create(entry);
|
|
112
|
+
* }
|
|
113
|
+
* }))
|
|
114
|
+
* .build();
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
function auditPlugin(options = {}) {
|
|
118
|
+
const entries = [];
|
|
119
|
+
const store = options.store ?? (async (entry) => {
|
|
120
|
+
entries.push(entry);
|
|
121
|
+
});
|
|
122
|
+
return definePlugin({
|
|
123
|
+
name: "audit",
|
|
124
|
+
version: "1.0.0",
|
|
125
|
+
description: "Audit trail for all operations",
|
|
126
|
+
hooks: {
|
|
127
|
+
"payment.create.after": async (ctx, input, next) => {
|
|
128
|
+
const result = await next();
|
|
129
|
+
await store({
|
|
130
|
+
action: "payment.create",
|
|
131
|
+
requestId: ctx.meta.requestId,
|
|
132
|
+
timestamp: ctx.meta.timestamp,
|
|
133
|
+
input: sanitizeInput(input),
|
|
134
|
+
output: sanitizeOutput(result),
|
|
135
|
+
idempotencyKey: ctx.meta.idempotencyKey
|
|
136
|
+
});
|
|
137
|
+
return result;
|
|
138
|
+
},
|
|
139
|
+
"payment.refund.after": async (ctx, input, next) => {
|
|
140
|
+
const result = await next();
|
|
141
|
+
await store({
|
|
142
|
+
action: "payment.refund",
|
|
143
|
+
requestId: ctx.meta.requestId,
|
|
144
|
+
timestamp: ctx.meta.timestamp,
|
|
145
|
+
input: sanitizeInput(input),
|
|
146
|
+
output: sanitizeOutput(result),
|
|
147
|
+
idempotencyKey: ctx.meta.idempotencyKey
|
|
148
|
+
});
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
//#endregion
|
|
156
|
+
//#region src/infrastructure/plugins/business/metrics.plugin.ts
|
|
157
|
+
/**
|
|
158
|
+
* Metrics Plugin
|
|
159
|
+
* @classytic/revenue
|
|
160
|
+
*
|
|
161
|
+
* Collects operation metrics (duration, success/failure)
|
|
162
|
+
*/
|
|
163
|
+
/**
|
|
164
|
+
* Metrics plugin - collects operation metrics
|
|
165
|
+
*
|
|
166
|
+
* Tracks duration and success/failure of operations
|
|
167
|
+
*
|
|
168
|
+
* @param options - Plugin options
|
|
169
|
+
* @returns Metrics plugin
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```typescript
|
|
173
|
+
* import { Revenue } from '@classytic/revenue';
|
|
174
|
+
* import { metricsPlugin } from '@classytic/revenue/plugins';
|
|
175
|
+
*
|
|
176
|
+
* const revenue = Revenue
|
|
177
|
+
* .create()
|
|
178
|
+
* .withPlugin(metricsPlugin({
|
|
179
|
+
* onMetric: (metric) => {
|
|
180
|
+
* // Send to Datadog, Prometheus, etc.
|
|
181
|
+
* statsd.timing(metric.name, metric.duration);
|
|
182
|
+
* if (!metric.success) {
|
|
183
|
+
* statsd.increment(`${metric.name}.error`);
|
|
184
|
+
* }
|
|
185
|
+
* }
|
|
186
|
+
* }))
|
|
187
|
+
* .build();
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
function metricsPlugin(options = {}) {
|
|
191
|
+
const metrics = [];
|
|
192
|
+
const record = options.onMetric ?? ((metric) => {
|
|
193
|
+
metrics.push(metric);
|
|
194
|
+
});
|
|
195
|
+
return definePlugin({
|
|
196
|
+
name: "metrics",
|
|
197
|
+
version: "1.0.0",
|
|
198
|
+
description: "Collects operation metrics",
|
|
199
|
+
hooks: { "payment.create.before": async (_ctx, input, next) => {
|
|
200
|
+
const start = Date.now();
|
|
201
|
+
try {
|
|
202
|
+
const result = await next();
|
|
203
|
+
record({
|
|
204
|
+
name: "payment.create",
|
|
205
|
+
duration: Date.now() - start,
|
|
206
|
+
success: true,
|
|
207
|
+
amount: input.amount,
|
|
208
|
+
currency: input.currency
|
|
209
|
+
});
|
|
210
|
+
return result;
|
|
211
|
+
} catch (error) {
|
|
212
|
+
record({
|
|
213
|
+
name: "payment.create",
|
|
214
|
+
duration: Date.now() - start,
|
|
215
|
+
success: false,
|
|
216
|
+
error: error.message
|
|
217
|
+
});
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
} }
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
//#endregion
|
|
225
|
+
//#region src/infrastructure/plugins/business/tax.plugin.ts
|
|
226
|
+
/**
|
|
227
|
+
* Tax Plugin
|
|
228
|
+
* @classytic/revenue
|
|
229
|
+
*
|
|
230
|
+
* Automatic tax calculation for transactions
|
|
231
|
+
* Integrates with monetization.create.before hook
|
|
232
|
+
*/
|
|
233
|
+
/**
|
|
234
|
+
* Create Tax Plugin
|
|
235
|
+
*
|
|
236
|
+
* Automatically calculates and applies tax to transactions during monetization.create()
|
|
237
|
+
*
|
|
238
|
+
* @param options - Plugin options
|
|
239
|
+
* @returns Tax plugin
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```typescript
|
|
243
|
+
* import { Revenue } from '@classytic/revenue';
|
|
244
|
+
* import { createTaxPlugin } from '@classytic/revenue/plugins';
|
|
245
|
+
*
|
|
246
|
+
* const revenue = Revenue
|
|
247
|
+
* .create({ defaultCurrency: 'USD' })
|
|
248
|
+
* .withModels({ Transaction, Subscription })
|
|
249
|
+
* .withProvider('stripe', stripeProvider)
|
|
250
|
+
* .withPlugin(createTaxPlugin({
|
|
251
|
+
* getTaxConfig: async (orgId) => {
|
|
252
|
+
* const org = await Organization.findById(orgId);
|
|
253
|
+
* return {
|
|
254
|
+
* isRegistered: true,
|
|
255
|
+
* defaultRate: 0.15, // 15% tax
|
|
256
|
+
* pricesIncludeTax: false,
|
|
257
|
+
* exemptCategories: ['education'],
|
|
258
|
+
* };
|
|
259
|
+
* },
|
|
260
|
+
* categoryMappings: {
|
|
261
|
+
* Order: 'order_subscription',
|
|
262
|
+
* Membership: 'gym_membership',
|
|
263
|
+
* },
|
|
264
|
+
* }))
|
|
265
|
+
* .build();
|
|
266
|
+
*
|
|
267
|
+
* // Tax is now automatically calculated
|
|
268
|
+
* await revenue.monetization.create({
|
|
269
|
+
* data: { organizationId: 'org_123', customerId: 'cust_456' },
|
|
270
|
+
* planKey: 'monthly',
|
|
271
|
+
* amount: 10000, // $100
|
|
272
|
+
* entity: 'Order',
|
|
273
|
+
* monetizationType: 'subscription',
|
|
274
|
+
* });
|
|
275
|
+
* // → Creates transaction with tax: {
|
|
276
|
+
* // isApplicable: true,
|
|
277
|
+
* // rate: 0.15,
|
|
278
|
+
* // baseAmount: 10000,
|
|
279
|
+
* // taxAmount: 1500,
|
|
280
|
+
* // totalAmount: 11500,
|
|
281
|
+
* // type: 'collected'
|
|
282
|
+
* // }
|
|
283
|
+
* ```
|
|
284
|
+
*/
|
|
285
|
+
function createTaxPlugin(options) {
|
|
286
|
+
const { getTaxConfig, categoryMappings = {}, incomeCategories = [
|
|
287
|
+
"subscription",
|
|
288
|
+
"purchase",
|
|
289
|
+
"course_enrollment",
|
|
290
|
+
"product_order"
|
|
291
|
+
] } = options;
|
|
292
|
+
return definePlugin({
|
|
293
|
+
name: "tax",
|
|
294
|
+
version: "1.0.0",
|
|
295
|
+
description: "Automatic tax calculation for transactions",
|
|
296
|
+
hooks: { "monetization.create.before": async (ctx, input, next) => {
|
|
297
|
+
const orgId = input.data.organizationId;
|
|
298
|
+
if (!orgId) {
|
|
299
|
+
ctx.logger.debug("Tax plugin: No organizationId in input.data, skipping tax calculation");
|
|
300
|
+
return next();
|
|
301
|
+
}
|
|
302
|
+
try {
|
|
303
|
+
const config = await getTaxConfig(orgId);
|
|
304
|
+
if (!config) {
|
|
305
|
+
ctx.logger.debug("Tax plugin: No tax config for org", { orgId });
|
|
306
|
+
return next();
|
|
307
|
+
}
|
|
308
|
+
const category = resolveCategory(input.entity, input.monetizationType || "subscription", categoryMappings);
|
|
309
|
+
const transactionFlow = incomeCategories.includes(category) ? "inflow" : "outflow";
|
|
310
|
+
const taxCalc = calculateTax(input.amount, category, config);
|
|
311
|
+
const taxType = getTaxType(transactionFlow, category, config.exemptCategories);
|
|
312
|
+
input.tax = {
|
|
313
|
+
...taxCalc,
|
|
314
|
+
type: taxType
|
|
315
|
+
};
|
|
316
|
+
ctx.logger.debug("Tax plugin: Tax calculated", {
|
|
317
|
+
orgId,
|
|
318
|
+
category,
|
|
319
|
+
entity: input.entity,
|
|
320
|
+
monetizationType: input.monetizationType,
|
|
321
|
+
taxAmount: taxCalc.taxAmount,
|
|
322
|
+
type: taxType
|
|
323
|
+
});
|
|
324
|
+
} catch (error) {
|
|
325
|
+
ctx.logger.error("Tax plugin: Failed to calculate tax", {
|
|
326
|
+
orgId,
|
|
327
|
+
error: error.message
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
return next();
|
|
331
|
+
} }
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
//#endregion
|
|
336
|
+
//#region src/infrastructure/plugins/index.ts
|
|
337
|
+
var plugins_default = {
|
|
338
|
+
loggingPlugin,
|
|
339
|
+
auditPlugin,
|
|
340
|
+
metricsPlugin,
|
|
341
|
+
createTaxPlugin
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
//#endregion
|
|
345
|
+
export { auditPlugin, createTaxPlugin, plugins_default as default, definePlugin, loggingPlugin, metricsPlugin };
|