@classytic/revenue 1.0.6 → 1.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/README.md +582 -633
- 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-CorrWz7A.d.mts +787 -0
- package/dist/escrow.enums-CZGrrdg7.mjs +101 -0
- package/dist/escrow.enums-DwdLuuve.d.mts +78 -0
- 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.mts +239 -0
- 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-HAuAS9Pp.d.mts +70 -0
- 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.mts +193 -0
- package/dist/reconciliation/index.mjs +192 -0
- package/dist/retry-HHCOXYdn.d.mts +186 -0
- package/dist/revenue-9scqKSef.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-DFhkqZEY.d.mts +132 -0
- package/dist/settlement.schema-D5uWB5tP.d.mts +344 -0
- package/dist/settlement.service-BxuiHpNC.d.mts +594 -0
- package/dist/settlement.service-CUxbUTzT.mjs +2510 -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.mts +428 -0
- package/dist/utils/index.mjs +346 -0
- package/package.json +48 -33
- package/dist/actions-Ctf2XUL-.d.ts +0 -519
- package/dist/core/index.d.ts +0 -890
- package/dist/core/index.js +0 -3005
- package/dist/core/index.js.map +0 -1
- package/dist/enums/index.d.ts +0 -138
- package/dist/enums/index.js +0 -263
- package/dist/enums/index.js.map +0 -1
- package/dist/index-BnEXsnLJ.d.ts +0 -378
- package/dist/index-C5SsOrV0.d.ts +0 -534
- package/dist/index.d.ts +0 -43
- package/dist/index.js +0 -4498
- package/dist/index.js.map +0 -1
- package/dist/payment.enums-B_RwB8iR.d.ts +0 -184
- package/dist/providers/index.d.ts +0 -132
- package/dist/providers/index.js +0 -122
- package/dist/providers/index.js.map +0 -1
- package/dist/retry-80lBCmSe.d.ts +0 -234
- package/dist/schemas/index.d.ts +0 -1051
- package/dist/schemas/index.js +0 -627
- package/dist/schemas/index.js.map +0 -1
- package/dist/schemas/validation.d.ts +0 -384
- package/dist/schemas/validation.js +0 -302
- package/dist/schemas/validation.js.map +0 -1
- package/dist/services/index.d.ts +0 -3
- package/dist/services/index.js +0 -1702
- package/dist/services/index.js.map +0 -1
- package/dist/split.schema-DLVF3XBI.d.ts +0 -1122
- package/dist/transaction.enums-7uBnuswI.d.ts +0 -87
- package/dist/utils/index.d.ts +0 -24
- package/dist/utils/index.js +0 -1140
- package/dist/utils/index.js.map +0 -1
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import "../../index-Dsp7H5Wb.mjs";
|
|
2
|
+
import "../../settlement.schema-D5uWB5tP.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";
|
|
6
|
+
|
|
7
|
+
//#region src/infrastructure/plugins/business/logging.plugin.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Logging plugin options
|
|
10
|
+
*/
|
|
11
|
+
interface LoggingPluginOptions {
|
|
12
|
+
/** Log level: 'debug' or 'info' */
|
|
13
|
+
level?: 'debug' | 'info';
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Logging plugin - logs all operations
|
|
17
|
+
*
|
|
18
|
+
* Logs payment creation, verification, and refund operations
|
|
19
|
+
*
|
|
20
|
+
* @param options - Plugin options
|
|
21
|
+
* @returns Logging plugin
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import { Revenue } from '@classytic/revenue';
|
|
26
|
+
* import { loggingPlugin } from '@classytic/revenue/plugins';
|
|
27
|
+
*
|
|
28
|
+
* const revenue = Revenue
|
|
29
|
+
* .create()
|
|
30
|
+
* .withPlugin(loggingPlugin({ level: 'debug' }))
|
|
31
|
+
* .build();
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
declare function loggingPlugin(options?: LoggingPluginOptions): RevenuePlugin;
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/infrastructure/plugins/business/audit.plugin.d.ts
|
|
37
|
+
/**
|
|
38
|
+
* Audit entry record
|
|
39
|
+
*/
|
|
40
|
+
interface AuditEntry {
|
|
41
|
+
action: string;
|
|
42
|
+
requestId: string;
|
|
43
|
+
timestamp: Date;
|
|
44
|
+
input: Record<string, unknown>;
|
|
45
|
+
output: Record<string, unknown>;
|
|
46
|
+
idempotencyKey?: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Audit plugin options
|
|
50
|
+
*/
|
|
51
|
+
interface AuditPluginOptions {
|
|
52
|
+
/** Custom storage function for audit entries */
|
|
53
|
+
store?: (entry: AuditEntry) => Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Audit plugin - records all operations for compliance
|
|
57
|
+
*
|
|
58
|
+
* Records payment creation, refunds, and other operations with sanitized data
|
|
59
|
+
*
|
|
60
|
+
* @param options - Plugin options
|
|
61
|
+
* @returns Audit plugin
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* import { Revenue } from '@classytic/revenue';
|
|
66
|
+
* import { auditPlugin } from '@classytic/revenue/plugins';
|
|
67
|
+
*
|
|
68
|
+
* const revenue = Revenue
|
|
69
|
+
* .create()
|
|
70
|
+
* .withPlugin(auditPlugin({
|
|
71
|
+
* store: async (entry) => {
|
|
72
|
+
* await AuditLog.create(entry);
|
|
73
|
+
* }
|
|
74
|
+
* }))
|
|
75
|
+
* .build();
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
declare function auditPlugin(options?: AuditPluginOptions): RevenuePlugin;
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/infrastructure/plugins/business/metrics.plugin.d.ts
|
|
81
|
+
/**
|
|
82
|
+
* Metric record
|
|
83
|
+
*/
|
|
84
|
+
interface Metric {
|
|
85
|
+
name: string;
|
|
86
|
+
duration: number;
|
|
87
|
+
success: boolean;
|
|
88
|
+
error?: string;
|
|
89
|
+
[key: string]: unknown;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Metrics plugin options
|
|
93
|
+
*/
|
|
94
|
+
interface MetricsPluginOptions {
|
|
95
|
+
/** Callback for each metric */
|
|
96
|
+
onMetric?: (metric: Metric) => void;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Metrics plugin - collects operation metrics
|
|
100
|
+
*
|
|
101
|
+
* Tracks duration and success/failure of operations
|
|
102
|
+
*
|
|
103
|
+
* @param options - Plugin options
|
|
104
|
+
* @returns Metrics plugin
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* import { Revenue } from '@classytic/revenue';
|
|
109
|
+
* import { metricsPlugin } from '@classytic/revenue/plugins';
|
|
110
|
+
*
|
|
111
|
+
* const revenue = Revenue
|
|
112
|
+
* .create()
|
|
113
|
+
* .withPlugin(metricsPlugin({
|
|
114
|
+
* onMetric: (metric) => {
|
|
115
|
+
* // Send to Datadog, Prometheus, etc.
|
|
116
|
+
* statsd.timing(metric.name, metric.duration);
|
|
117
|
+
* if (!metric.success) {
|
|
118
|
+
* statsd.increment(`${metric.name}.error`);
|
|
119
|
+
* }
|
|
120
|
+
* }
|
|
121
|
+
* }))
|
|
122
|
+
* .build();
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
declare function metricsPlugin(options?: MetricsPluginOptions): RevenuePlugin;
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/infrastructure/plugins/business/tax.plugin.d.ts
|
|
128
|
+
/**
|
|
129
|
+
* Tax Plugin Options
|
|
130
|
+
*/
|
|
131
|
+
interface TaxPluginOptions {
|
|
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[];
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Create Tax Plugin
|
|
179
|
+
*
|
|
180
|
+
* Automatically calculates and applies tax to transactions during monetization.create()
|
|
181
|
+
*
|
|
182
|
+
* @param options - Plugin options
|
|
183
|
+
* @returns Tax plugin
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```typescript
|
|
187
|
+
* import { Revenue } from '@classytic/revenue';
|
|
188
|
+
* import { createTaxPlugin } from '@classytic/revenue/plugins';
|
|
189
|
+
*
|
|
190
|
+
* const revenue = Revenue
|
|
191
|
+
* .create({ defaultCurrency: 'USD' })
|
|
192
|
+
* .withModels({ Transaction, Subscription })
|
|
193
|
+
* .withProvider('stripe', stripeProvider)
|
|
194
|
+
* .withPlugin(createTaxPlugin({
|
|
195
|
+
* getTaxConfig: async (orgId) => {
|
|
196
|
+
* const org = await Organization.findById(orgId);
|
|
197
|
+
* return {
|
|
198
|
+
* isRegistered: true,
|
|
199
|
+
* defaultRate: 0.15, // 15% tax
|
|
200
|
+
* pricesIncludeTax: false,
|
|
201
|
+
* exemptCategories: ['education'],
|
|
202
|
+
* };
|
|
203
|
+
* },
|
|
204
|
+
* categoryMappings: {
|
|
205
|
+
* Order: 'order_subscription',
|
|
206
|
+
* Membership: 'gym_membership',
|
|
207
|
+
* },
|
|
208
|
+
* }))
|
|
209
|
+
* .build();
|
|
210
|
+
*
|
|
211
|
+
* // Tax is now automatically calculated
|
|
212
|
+
* await revenue.monetization.create({
|
|
213
|
+
* data: { organizationId: 'org_123', customerId: 'cust_456' },
|
|
214
|
+
* planKey: 'monthly',
|
|
215
|
+
* amount: 10000, // $100
|
|
216
|
+
* entity: 'Order',
|
|
217
|
+
* monetizationType: 'subscription',
|
|
218
|
+
* });
|
|
219
|
+
* // → Creates transaction with tax: {
|
|
220
|
+
* // isApplicable: true,
|
|
221
|
+
* // rate: 0.15,
|
|
222
|
+
* // baseAmount: 10000,
|
|
223
|
+
* // taxAmount: 1500,
|
|
224
|
+
* // totalAmount: 11500,
|
|
225
|
+
* // type: 'collected'
|
|
226
|
+
* // }
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
declare function createTaxPlugin(options: TaxPluginOptions): RevenuePlugin;
|
|
230
|
+
//#endregion
|
|
231
|
+
//#region src/infrastructure/plugins/index.d.ts
|
|
232
|
+
declare const _default: {
|
|
233
|
+
loggingPlugin: typeof loggingPlugin;
|
|
234
|
+
auditPlugin: typeof auditPlugin;
|
|
235
|
+
metricsPlugin: typeof metricsPlugin;
|
|
236
|
+
createTaxPlugin: typeof createTaxPlugin;
|
|
237
|
+
};
|
|
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 };
|