@classytic/revenue 0.0.1

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.
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Dependency Injection Container
3
+ * @classytic/revenue
4
+ *
5
+ * Lightweight DI container for managing dependencies
6
+ * Inspired by: Awilix, InversifyJS but much simpler
7
+ */
8
+
9
+ export class Container {
10
+ constructor() {
11
+ this._services = new Map();
12
+ this._singletons = new Map();
13
+ }
14
+
15
+ /**
16
+ * Register a service
17
+ * @param {string} name - Service name
18
+ * @param {any} implementation - Service implementation or factory
19
+ * @param {Object} options - Registration options
20
+ */
21
+ register(name, implementation, options = {}) {
22
+ this._services.set(name, {
23
+ implementation,
24
+ singleton: options.singleton !== false, // Default to singleton
25
+ factory: options.factory || false,
26
+ });
27
+ return this;
28
+ }
29
+
30
+ /**
31
+ * Register a singleton service
32
+ * @param {string} name - Service name
33
+ * @param {any} implementation - Service implementation
34
+ */
35
+ singleton(name, implementation) {
36
+ return this.register(name, implementation, { singleton: true });
37
+ }
38
+
39
+ /**
40
+ * Register a transient service (new instance each time)
41
+ * @param {string} name - Service name
42
+ * @param {Function} factory - Factory function
43
+ */
44
+ transient(name, factory) {
45
+ return this.register(name, factory, { singleton: false, factory: true });
46
+ }
47
+
48
+ /**
49
+ * Get a service from the container
50
+ * @param {string} name - Service name
51
+ * @returns {any} Service instance
52
+ */
53
+ get(name) {
54
+ // Check if already instantiated as singleton
55
+ if (this._singletons.has(name)) {
56
+ return this._singletons.get(name);
57
+ }
58
+
59
+ const service = this._services.get(name);
60
+ if (!service) {
61
+ throw new Error(`Service "${name}" not registered in container`);
62
+ }
63
+
64
+ // Handle factory functions
65
+ if (service.factory) {
66
+ const instance = service.implementation(this);
67
+ if (service.singleton) {
68
+ this._singletons.set(name, instance);
69
+ }
70
+ return instance;
71
+ }
72
+
73
+ // Handle direct values
74
+ if (service.singleton) {
75
+ this._singletons.set(name, service.implementation);
76
+ }
77
+ return service.implementation;
78
+ }
79
+
80
+ /**
81
+ * Check if service is registered
82
+ * @param {string} name - Service name
83
+ * @returns {boolean}
84
+ */
85
+ has(name) {
86
+ return this._services.has(name);
87
+ }
88
+
89
+ /**
90
+ * Get all registered service names
91
+ * @returns {string[]}
92
+ */
93
+ keys() {
94
+ return Array.from(this._services.keys());
95
+ }
96
+
97
+ /**
98
+ * Clear all services (useful for testing)
99
+ */
100
+ clear() {
101
+ this._services.clear();
102
+ this._singletons.clear();
103
+ }
104
+
105
+ /**
106
+ * Create a child container (for scoped dependencies)
107
+ * @returns {Container}
108
+ */
109
+ createScope() {
110
+ const scope = new Container();
111
+ // Copy parent services
112
+ this._services.forEach((value, key) => {
113
+ scope._services.set(key, value);
114
+ });
115
+ return scope;
116
+ }
117
+ }
118
+
119
+ export default Container;
package/core/errors.js ADDED
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Revenue Error Classes
3
+ * @classytic/revenue
4
+ *
5
+ * Typed errors with codes for better error handling
6
+ */
7
+
8
+ /**
9
+ * Base Revenue Error
10
+ */
11
+ export class RevenueError extends Error {
12
+ constructor(message, code, options = {}) {
13
+ super(message);
14
+ this.name = this.constructor.name;
15
+ this.code = code;
16
+ this.retryable = options.retryable || false;
17
+ this.metadata = options.metadata || {};
18
+ Error.captureStackTrace(this, this.constructor);
19
+ }
20
+
21
+ toJSON() {
22
+ return {
23
+ name: this.name,
24
+ message: this.message,
25
+ code: this.code,
26
+ retryable: this.retryable,
27
+ metadata: this.metadata,
28
+ };
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Configuration Errors
34
+ */
35
+ export class ConfigurationError extends RevenueError {
36
+ constructor(message, metadata = {}) {
37
+ super(message, 'CONFIGURATION_ERROR', { retryable: false, metadata });
38
+ }
39
+ }
40
+
41
+ export class ModelNotRegisteredError extends ConfigurationError {
42
+ constructor(modelName) {
43
+ super(
44
+ `Model "${modelName}" is not registered. Register it via createRevenue({ models: { ${modelName}: ... } })`,
45
+ { modelName }
46
+ );
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Provider Errors
52
+ */
53
+ export class ProviderError extends RevenueError {
54
+ constructor(message, code, options = {}) {
55
+ super(message, code, options);
56
+ }
57
+ }
58
+
59
+ export class ProviderNotFoundError extends ProviderError {
60
+ constructor(providerName, availableProviders = []) {
61
+ super(
62
+ `Payment provider "${providerName}" not found. Available: ${availableProviders.join(', ')}`,
63
+ 'PROVIDER_NOT_FOUND',
64
+ { retryable: false, metadata: { providerName, availableProviders } }
65
+ );
66
+ }
67
+ }
68
+
69
+ export class ProviderCapabilityError extends ProviderError {
70
+ constructor(providerName, capability) {
71
+ super(
72
+ `Provider "${providerName}" does not support ${capability}`,
73
+ 'PROVIDER_CAPABILITY_NOT_SUPPORTED',
74
+ { retryable: false, metadata: { providerName, capability } }
75
+ );
76
+ }
77
+ }
78
+
79
+ export class PaymentIntentCreationError extends ProviderError {
80
+ constructor(providerName, originalError) {
81
+ super(
82
+ `Failed to create payment intent with provider "${providerName}": ${originalError.message}`,
83
+ 'PAYMENT_INTENT_CREATION_FAILED',
84
+ { retryable: true, metadata: { providerName, originalError: originalError.message } }
85
+ );
86
+ }
87
+ }
88
+
89
+ export class PaymentVerificationError extends ProviderError {
90
+ constructor(paymentIntentId, reason) {
91
+ super(
92
+ `Payment verification failed for intent "${paymentIntentId}": ${reason}`,
93
+ 'PAYMENT_VERIFICATION_FAILED',
94
+ { retryable: true, metadata: { paymentIntentId, reason } }
95
+ );
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Resource Not Found Errors
101
+ */
102
+ export class NotFoundError extends RevenueError {
103
+ constructor(message, code, metadata = {}) {
104
+ super(message, code, { retryable: false, metadata });
105
+ }
106
+ }
107
+
108
+ export class SubscriptionNotFoundError extends NotFoundError {
109
+ constructor(subscriptionId) {
110
+ super(
111
+ `Subscription not found: ${subscriptionId}`,
112
+ 'SUBSCRIPTION_NOT_FOUND',
113
+ { subscriptionId }
114
+ );
115
+ }
116
+ }
117
+
118
+ export class TransactionNotFoundError extends NotFoundError {
119
+ constructor(transactionId) {
120
+ super(
121
+ `Transaction not found: ${transactionId}`,
122
+ 'TRANSACTION_NOT_FOUND',
123
+ { transactionId }
124
+ );
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Validation Errors
130
+ */
131
+ export class ValidationError extends RevenueError {
132
+ constructor(message, metadata = {}) {
133
+ super(message, 'VALIDATION_ERROR', { retryable: false, metadata });
134
+ }
135
+ }
136
+
137
+ export class InvalidAmountError extends ValidationError {
138
+ constructor(amount) {
139
+ super(`Invalid amount: ${amount}. Amount must be non-negative`, { amount });
140
+ }
141
+ }
142
+
143
+ export class MissingRequiredFieldError extends ValidationError {
144
+ constructor(fieldName) {
145
+ super(`Missing required field: ${fieldName}`, { fieldName });
146
+ }
147
+ }
148
+
149
+ /**
150
+ * State Errors
151
+ */
152
+ export class StateError extends RevenueError {
153
+ constructor(message, code, metadata = {}) {
154
+ super(message, code, { retryable: false, metadata });
155
+ }
156
+ }
157
+
158
+ export class AlreadyVerifiedError extends StateError {
159
+ constructor(transactionId) {
160
+ super(
161
+ `Transaction ${transactionId} is already verified`,
162
+ 'ALREADY_VERIFIED',
163
+ { transactionId }
164
+ );
165
+ }
166
+ }
167
+
168
+ export class InvalidStateTransitionError extends StateError {
169
+ constructor(resourceType, resourceId, fromState, toState) {
170
+ super(
171
+ `Invalid state transition for ${resourceType} ${resourceId}: ${fromState} → ${toState}`,
172
+ 'INVALID_STATE_TRANSITION',
173
+ { resourceType, resourceId, fromState, toState }
174
+ );
175
+ }
176
+ }
177
+
178
+ export class SubscriptionNotActiveError extends StateError {
179
+ constructor(subscriptionId) {
180
+ super(
181
+ `Subscription ${subscriptionId} is not active`,
182
+ 'SUBSCRIPTION_NOT_ACTIVE',
183
+ { subscriptionId }
184
+ );
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Operation Errors
190
+ */
191
+ export class OperationError extends RevenueError {
192
+ constructor(message, code, options = {}) {
193
+ super(message, code, options);
194
+ }
195
+ }
196
+
197
+ export class RefundNotSupportedError extends OperationError {
198
+ constructor(providerName) {
199
+ super(
200
+ `Refunds are not supported by provider "${providerName}"`,
201
+ 'REFUND_NOT_SUPPORTED',
202
+ { retryable: false, metadata: { providerName } }
203
+ );
204
+ }
205
+ }
206
+
207
+ export class RefundError extends OperationError {
208
+ constructor(transactionId, reason) {
209
+ super(
210
+ `Refund failed for transaction ${transactionId}: ${reason}`,
211
+ 'REFUND_FAILED',
212
+ { retryable: true, metadata: { transactionId, reason } }
213
+ );
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Error Code Constants
219
+ */
220
+ export const ERROR_CODES = {
221
+ // Configuration
222
+ CONFIGURATION_ERROR: 'CONFIGURATION_ERROR',
223
+ MODEL_NOT_REGISTERED: 'MODEL_NOT_REGISTERED',
224
+
225
+ // Provider
226
+ PROVIDER_NOT_FOUND: 'PROVIDER_NOT_FOUND',
227
+ PROVIDER_CAPABILITY_NOT_SUPPORTED: 'PROVIDER_CAPABILITY_NOT_SUPPORTED',
228
+ PAYMENT_INTENT_CREATION_FAILED: 'PAYMENT_INTENT_CREATION_FAILED',
229
+ PAYMENT_VERIFICATION_FAILED: 'PAYMENT_VERIFICATION_FAILED',
230
+
231
+ // Not Found
232
+ SUBSCRIPTION_NOT_FOUND: 'SUBSCRIPTION_NOT_FOUND',
233
+ TRANSACTION_NOT_FOUND: 'TRANSACTION_NOT_FOUND',
234
+
235
+ // Validation
236
+ VALIDATION_ERROR: 'VALIDATION_ERROR',
237
+ INVALID_AMOUNT: 'INVALID_AMOUNT',
238
+ MISSING_REQUIRED_FIELD: 'MISSING_REQUIRED_FIELD',
239
+
240
+ // State
241
+ ALREADY_VERIFIED: 'ALREADY_VERIFIED',
242
+ INVALID_STATE_TRANSITION: 'INVALID_STATE_TRANSITION',
243
+ SUBSCRIPTION_NOT_ACTIVE: 'SUBSCRIPTION_NOT_ACTIVE',
244
+
245
+ // Operations
246
+ REFUND_NOT_SUPPORTED: 'REFUND_NOT_SUPPORTED',
247
+ REFUND_FAILED: 'REFUND_FAILED',
248
+ };
249
+
250
+ /**
251
+ * Check if error is retryable
252
+ */
253
+ export function isRetryable(error) {
254
+ return error instanceof RevenueError && error.retryable;
255
+ }
256
+
257
+ /**
258
+ * Check if error is from revenue package
259
+ */
260
+ export function isRevenueError(error) {
261
+ return error instanceof RevenueError;
262
+ }
package/enums/index.js ADDED
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @classytic/revenue - Centralized Enums
3
+ * All enums for the revenue management system
4
+ *
5
+ * This file serves as the single source of truth for all enum values
6
+ * used across monetization and payment subsystems.
7
+ *
8
+ * @module @classytic/revenue/enums
9
+ */
10
+
11
+ // Re-export all enums
12
+ export * from './transaction.enums.js';
13
+ export * from './payment.enums.js';
14
+ export * from './subscription.enums.js';
15
+ export * from './monetization.enums.js';
16
+
17
+ // Default export for convenience
18
+ import {
19
+ TRANSACTION_STATUS,
20
+ TRANSACTION_STATUS_VALUES,
21
+ LIBRARY_CATEGORIES,
22
+ LIBRARY_CATEGORY_VALUES,
23
+ } from './transaction.enums.js';
24
+
25
+ import {
26
+ PAYMENT_STATUS,
27
+ PAYMENT_STATUS_VALUES,
28
+ PAYMENT_GATEWAY_TYPE,
29
+ PAYMENT_GATEWAY_TYPE_VALUES,
30
+ GATEWAY_TYPES,
31
+ GATEWAY_TYPE_VALUES,
32
+ } from './payment.enums.js';
33
+
34
+ import {
35
+ SUBSCRIPTION_STATUS,
36
+ SUBSCRIPTION_STATUS_VALUES,
37
+ PLAN_KEYS,
38
+ PLAN_KEY_VALUES,
39
+ } from './subscription.enums.js';
40
+
41
+ import {
42
+ MONETIZATION_TYPES,
43
+ MONETIZATION_TYPE_VALUES,
44
+ } from './monetization.enums.js';
45
+
46
+ export default {
47
+ // Transaction enums
48
+ TRANSACTION_STATUS,
49
+ TRANSACTION_STATUS_VALUES,
50
+ LIBRARY_CATEGORIES,
51
+ LIBRARY_CATEGORY_VALUES,
52
+
53
+ // Payment enums
54
+ PAYMENT_STATUS,
55
+ PAYMENT_STATUS_VALUES,
56
+ PAYMENT_GATEWAY_TYPE,
57
+ PAYMENT_GATEWAY_TYPE_VALUES,
58
+ GATEWAY_TYPES,
59
+ GATEWAY_TYPE_VALUES,
60
+
61
+ // Subscription enums
62
+ SUBSCRIPTION_STATUS,
63
+ SUBSCRIPTION_STATUS_VALUES,
64
+ PLAN_KEYS,
65
+ PLAN_KEY_VALUES,
66
+
67
+ // Monetization enums
68
+ MONETIZATION_TYPES,
69
+ MONETIZATION_TYPE_VALUES,
70
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Monetization Enums
3
+ * @classytic/revenue
4
+ *
5
+ * General monetization enums and constants
6
+ */
7
+
8
+ // ============ MONETIZATION TYPES ============
9
+ export const MONETIZATION_TYPES = {
10
+ FREE: 'free',
11
+ PURCHASE: 'purchase',
12
+ SUBSCRIPTION: 'subscription',
13
+ };
14
+
15
+ export const MONETIZATION_TYPE_VALUES = Object.values(MONETIZATION_TYPES);
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Payment Enums
3
+ * @classytic/revenue
4
+ *
5
+ * Library-managed payment enums only.
6
+ * Users define their own payment methods in their schema.
7
+ */
8
+
9
+ // ============ PAYMENT STATUS ============
10
+ /**
11
+ * Payment Status - Library-managed states
12
+ */
13
+ export const PAYMENT_STATUS = {
14
+ PENDING: 'pending',
15
+ VERIFIED: 'verified',
16
+ FAILED: 'failed',
17
+ REFUNDED: 'refunded',
18
+ CANCELLED: 'cancelled',
19
+ };
20
+
21
+ export const PAYMENT_STATUS_VALUES = Object.values(PAYMENT_STATUS);
22
+
23
+ // ============ PAYMENT GATEWAY TYPES ============
24
+ /**
25
+ * Gateway types that providers can be built for
26
+ *
27
+ * MANUAL: Built-in manual provider
28
+ * STRIPE: Stripe provider (build with @classytic/revenue-stripe)
29
+ * SSLCOMMERZ: SSLCommerz provider (build with @classytic/revenue-sslcommerz)
30
+ *
31
+ * Users can register custom providers for any gateway type
32
+ */
33
+ export const PAYMENT_GATEWAY_TYPE = {
34
+ MANUAL: 'manual',
35
+ STRIPE: 'stripe',
36
+ SSLCOMMERZ: 'sslcommerz',
37
+ };
38
+
39
+ export const PAYMENT_GATEWAY_TYPE_VALUES = Object.values(PAYMENT_GATEWAY_TYPE);
40
+
41
+ // Backward compatibility alias
42
+ export const GATEWAY_TYPES = PAYMENT_GATEWAY_TYPE;
43
+ export const GATEWAY_TYPE_VALUES = PAYMENT_GATEWAY_TYPE_VALUES;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Subscription Enums
3
+ * @classytic/revenue
4
+ *
5
+ * All subscription-related enums and constants
6
+ */
7
+
8
+ // ============ SUBSCRIPTION STATUS ============
9
+ /**
10
+ * Subscription Status
11
+ */
12
+ export const SUBSCRIPTION_STATUS = {
13
+ ACTIVE: 'active',
14
+ PAUSED: 'paused',
15
+ CANCELLED: 'cancelled',
16
+ EXPIRED: 'expired',
17
+ PENDING: 'pending',
18
+ INACTIVE: 'inactive',
19
+ };
20
+
21
+ export const SUBSCRIPTION_STATUS_VALUES = Object.values(SUBSCRIPTION_STATUS);
22
+
23
+ // ============ PLAN KEYS ============
24
+ /**
25
+ * Supported plan intervals
26
+ */
27
+ export const PLAN_KEYS = {
28
+ MONTHLY: 'monthly',
29
+ QUARTERLY: 'quarterly',
30
+ YEARLY: 'yearly',
31
+ };
32
+
33
+ export const PLAN_KEY_VALUES = Object.values(PLAN_KEYS);
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Transaction Enums
3
+ * @classytic/revenue
4
+ *
5
+ * Library-managed transaction enums only.
6
+ * Users should define their own categories and merge with these.
7
+ */
8
+
9
+ // ============ TRANSACTION STATUS ============
10
+ /**
11
+ * Transaction Status - Library-managed states
12
+ */
13
+ export const TRANSACTION_STATUS = {
14
+ PENDING: 'pending',
15
+ PAYMENT_INITIATED: 'payment_initiated',
16
+ PROCESSING: 'processing',
17
+ REQUIRES_ACTION: 'requires_action',
18
+ VERIFIED: 'verified',
19
+ COMPLETED: 'completed',
20
+ FAILED: 'failed',
21
+ CANCELLED: 'cancelled',
22
+ EXPIRED: 'expired',
23
+ REFUNDED: 'refunded',
24
+ PARTIALLY_REFUNDED: 'partially_refunded',
25
+ };
26
+
27
+ export const TRANSACTION_STATUS_VALUES = Object.values(TRANSACTION_STATUS);
28
+
29
+ // ============ LIBRARY CATEGORIES ============
30
+ /**
31
+ * Categories managed by this library
32
+ *
33
+ * SUBSCRIPTION: Recurring subscription payments
34
+ * PURCHASE: One-time purchases
35
+ *
36
+ * Users should spread these into their own category enums:
37
+ *
38
+ * @example
39
+ * import { LIBRARY_CATEGORIES } from '@classytic/revenue';
40
+ *
41
+ * export const MY_CATEGORIES = {
42
+ * ...LIBRARY_CATEGORIES,
43
+ * SALARY: 'salary',
44
+ * RENT: 'rent',
45
+ * EQUIPMENT: 'equipment',
46
+ * };
47
+ */
48
+ export const LIBRARY_CATEGORIES = {
49
+ SUBSCRIPTION: 'subscription',
50
+ PURCHASE: 'purchase',
51
+ };
52
+
53
+ export const LIBRARY_CATEGORY_VALUES = Object.values(LIBRARY_CATEGORIES);
package/index.js ADDED
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @classytic/revenue
3
+ * Enterprise Revenue Management System
4
+ *
5
+ * A unified, enterprise-grade revenue management system combining
6
+ * monetization (subscriptions, purchases, proration) and payment processing
7
+ * (verification, refunds, webhooks) into a single, cohesive package.
8
+ *
9
+ * Thin, focused, production-ready library with smart defaults.
10
+ *
11
+ * @version 1.0.0
12
+ * @author Classytic (Classytic)
13
+ * @license MIT
14
+ */
15
+
16
+ // ============ CORE API ============
17
+ export { createRevenue } from './core/builder.js';
18
+ export { Container } from './core/container.js';
19
+ import { createRevenue as _createRevenue } from './core/builder.js';
20
+ import { Container as _Container } from './core/container.js';
21
+
22
+ // ============ ERROR CLASSES ============
23
+ export * from './core/errors.js';
24
+ import { RevenueError } from './core/errors.js';
25
+
26
+ // ============ PROVIDER SYSTEM ============
27
+ export {
28
+ PaymentProvider,
29
+ PaymentIntent,
30
+ PaymentResult,
31
+ RefundResult,
32
+ WebhookEvent,
33
+ } from './providers/base.js';
34
+ import { PaymentProvider as _PaymentProvider } from './providers/base.js';
35
+ // Note: ManualProvider moved to @classytic/revenue-manual (separate package)
36
+
37
+ // ============ SERVICES (ADVANCED USAGE) ============
38
+ export { SubscriptionService } from './services/subscription.service.js';
39
+ export { PaymentService } from './services/payment.service.js';
40
+ export { TransactionService } from './services/transaction.service.js';
41
+
42
+ // ============ ENUMS & SCHEMAS (FOR INJECTION) ============
43
+ export * from './enums/index.js';
44
+ export * from './schemas/index.js';
45
+
46
+ // ============ UTILITIES ============
47
+ export {
48
+ logger,
49
+ setLogger,
50
+ } from './utils/index.js';
51
+
52
+ // ============ DEFAULT EXPORT ============
53
+ export default {
54
+ createRevenue: _createRevenue,
55
+ PaymentProvider: _PaymentProvider,
56
+ RevenueError,
57
+ Container: _Container,
58
+ };