@misterhomer1992/miit-bot-payment 1.1.6 → 2.0.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/dist/config/ConfigurationManager.d.ts +64 -0
- package/dist/config/ConfigurationManager.d.ts.map +1 -0
- package/dist/config/ConfigurationManager.js +144 -0
- package/dist/config/ConfigurationManager.js.map +1 -0
- package/dist/config/defaults.d.ts +18 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +26 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/environment.d.ts +38 -0
- package/dist/config/environment.d.ts.map +1 -0
- package/dist/config/environment.js +91 -0
- package/dist/config/environment.js.map +1 -0
- package/dist/config/index.d.ts +5 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +18 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/types.d.ts +53 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +3 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/cache/InMemoryCache.d.ts +17 -0
- package/dist/modules/cache/InMemoryCache.d.ts.map +1 -0
- package/dist/modules/cache/InMemoryCache.js +77 -0
- package/dist/modules/cache/InMemoryCache.js.map +1 -0
- package/dist/modules/cache/index.d.ts +3 -0
- package/dist/modules/cache/index.d.ts.map +1 -0
- package/dist/modules/cache/index.js +19 -0
- package/dist/modules/cache/index.js.map +1 -0
- package/dist/modules/cache/types.d.ts +52 -0
- package/dist/modules/cache/types.d.ts.map +1 -0
- package/dist/modules/cache/types.js +3 -0
- package/dist/modules/cache/types.js.map +1 -0
- package/dist/modules/errors/index.d.ts +2 -0
- package/dist/modules/errors/index.d.ts.map +1 -0
- package/dist/modules/errors/index.js +19 -0
- package/dist/modules/errors/index.js.map +1 -0
- package/dist/modules/errors/types.d.ts +112 -0
- package/dist/modules/errors/types.d.ts.map +1 -0
- package/dist/modules/errors/types.js +174 -0
- package/dist/modules/errors/types.js.map +1 -0
- package/dist/modules/payments/api.d.ts +63 -1
- package/dist/modules/payments/api.d.ts.map +1 -1
- package/dist/modules/payments/api.js +103 -1
- package/dist/modules/payments/api.js.map +1 -1
- package/dist/modules/payments/const.d.ts.map +1 -1
- package/dist/modules/payments/const.js +1 -0
- package/dist/modules/payments/const.js.map +1 -1
- package/dist/modules/payments/index.d.ts +8 -0
- package/dist/modules/payments/index.d.ts.map +1 -1
- package/dist/modules/payments/index.js +8 -0
- package/dist/modules/payments/index.js.map +1 -1
- package/dist/modules/payments/service.d.ts +42 -2
- package/dist/modules/payments/service.d.ts.map +1 -1
- package/dist/modules/payments/service.js +132 -3
- package/dist/modules/payments/service.js.map +1 -1
- package/dist/modules/payments/subscription-check-webhook.handler.d.ts +85 -0
- package/dist/modules/payments/subscription-check-webhook.handler.d.ts.map +1 -0
- package/dist/modules/payments/subscription-check-webhook.handler.js +155 -0
- package/dist/modules/payments/subscription-check-webhook.handler.js.map +1 -0
- package/dist/modules/payments/subscription-check-webhook.service.d.ts +59 -0
- package/dist/modules/payments/subscription-check-webhook.service.d.ts.map +1 -0
- package/dist/modules/payments/subscription-check-webhook.service.js +330 -0
- package/dist/modules/payments/subscription-check-webhook.service.js.map +1 -0
- package/dist/modules/payments/subscription-check-webhook.types.d.ts +25 -0
- package/dist/modules/payments/subscription-check-webhook.types.d.ts.map +1 -0
- package/dist/modules/payments/subscription-check-webhook.types.js +3 -0
- package/dist/modules/payments/subscription-check-webhook.types.js.map +1 -0
- package/dist/modules/payments/types.d.ts +69 -2
- package/dist/modules/payments/types.d.ts.map +1 -1
- package/dist/modules/payments/utils.d.ts +151 -5
- package/dist/modules/payments/utils.d.ts.map +1 -1
- package/dist/modules/payments/utils.js +253 -9
- package/dist/modules/payments/utils.js.map +1 -1
- package/dist/modules/payments/wayforpay.service.d.ts +39 -0
- package/dist/modules/payments/wayforpay.service.d.ts.map +1 -0
- package/dist/modules/payments/wayforpay.service.js +217 -0
- package/dist/modules/payments/wayforpay.service.js.map +1 -0
- package/dist/modules/payments/wayforpay.types.d.ts +115 -0
- package/dist/modules/payments/wayforpay.types.d.ts.map +1 -0
- package/dist/modules/payments/wayforpay.types.js +3 -0
- package/dist/modules/payments/wayforpay.types.js.map +1 -0
- package/dist/modules/payments/webhook.handler.d.ts +98 -0
- package/dist/modules/payments/webhook.handler.d.ts.map +1 -0
- package/dist/modules/payments/webhook.handler.js +153 -0
- package/dist/modules/payments/webhook.handler.js.map +1 -0
- package/dist/modules/payments/webhook.service.d.ts +99 -0
- package/dist/modules/payments/webhook.service.d.ts.map +1 -0
- package/dist/modules/payments/webhook.service.js +672 -0
- package/dist/modules/payments/webhook.service.js.map +1 -0
- package/dist/modules/payments/webhook.types.d.ts +35 -0
- package/dist/modules/payments/webhook.types.d.ts.map +1 -0
- package/dist/modules/payments/webhook.types.js +3 -0
- package/dist/modules/payments/webhook.types.js.map +1 -0
- package/dist/modules/subscription/change.service.d.ts +80 -0
- package/dist/modules/subscription/change.service.d.ts.map +1 -0
- package/dist/modules/subscription/change.service.js +226 -0
- package/dist/modules/subscription/change.service.js.map +1 -0
- package/dist/modules/subscription/index.d.ts +2 -0
- package/dist/modules/subscription/index.d.ts.map +1 -1
- package/dist/modules/subscription/index.js +2 -0
- package/dist/modules/subscription/index.js.map +1 -1
- package/dist/modules/subscription/service.d.ts +8 -1
- package/dist/modules/subscription/service.d.ts.map +1 -1
- package/dist/modules/subscription/service.js +59 -2
- package/dist/modules/subscription/service.js.map +1 -1
- package/dist/modules/subscription/status-check.handler.d.ts +117 -0
- package/dist/modules/subscription/status-check.handler.d.ts.map +1 -0
- package/dist/modules/subscription/status-check.handler.js +164 -0
- package/dist/modules/subscription/status-check.handler.js.map +1 -0
- package/dist/modules/subscription/types.d.ts +37 -1
- package/dist/modules/subscription/types.d.ts.map +1 -1
- package/dist/modules/subscriptionPlan/const.d.ts +5 -0
- package/dist/modules/subscriptionPlan/const.d.ts.map +1 -0
- package/dist/modules/subscriptionPlan/const.js +106 -0
- package/dist/modules/subscriptionPlan/const.js.map +1 -0
- package/dist/modules/subscriptionPlan/index.d.ts +5 -0
- package/dist/modules/subscriptionPlan/index.d.ts.map +1 -0
- package/dist/modules/subscriptionPlan/index.js +21 -0
- package/dist/modules/subscriptionPlan/index.js.map +1 -0
- package/dist/modules/subscriptionPlan/repository.d.ts +22 -0
- package/dist/modules/subscriptionPlan/repository.d.ts.map +1 -0
- package/dist/modules/subscriptionPlan/repository.js +95 -0
- package/dist/modules/subscriptionPlan/repository.js.map +1 -0
- package/dist/modules/subscriptionPlan/service.d.ts +21 -0
- package/dist/modules/subscriptionPlan/service.d.ts.map +1 -0
- package/dist/modules/subscriptionPlan/service.js +128 -0
- package/dist/modules/subscriptionPlan/service.js.map +1 -0
- package/dist/modules/subscriptionPlan/types.d.ts +40 -0
- package/dist/modules/subscriptionPlan/types.d.ts.map +1 -0
- package/dist/modules/subscriptionPlan/types.js +3 -0
- package/dist/modules/subscriptionPlan/types.js.map +1 -0
- package/dist/modules/token/const.d.ts +7 -0
- package/dist/modules/token/const.d.ts.map +1 -0
- package/dist/modules/token/const.js +66 -0
- package/dist/modules/token/const.js.map +1 -0
- package/dist/modules/token/index.d.ts +4 -0
- package/dist/modules/token/index.d.ts.map +1 -0
- package/dist/modules/token/index.js +20 -0
- package/dist/modules/token/index.js.map +1 -0
- package/dist/modules/token/service.d.ts +46 -0
- package/dist/modules/token/service.d.ts.map +1 -0
- package/dist/modules/token/service.js +249 -0
- package/dist/modules/token/service.js.map +1 -0
- package/dist/modules/token/types.d.ts +109 -0
- package/dist/modules/token/types.d.ts.map +1 -0
- package/dist/modules/token/types.js +3 -0
- package/dist/modules/token/types.js.map +1 -0
- package/dist/modules/tokenPack/const.d.ts +4 -0
- package/dist/modules/tokenPack/const.d.ts.map +1 -0
- package/dist/modules/tokenPack/const.js +10 -0
- package/dist/modules/tokenPack/const.js.map +1 -0
- package/dist/modules/tokenPack/index.d.ts +5 -0
- package/dist/modules/tokenPack/index.d.ts.map +1 -0
- package/dist/modules/tokenPack/index.js +21 -0
- package/dist/modules/tokenPack/index.js.map +1 -0
- package/dist/modules/tokenPack/repository.d.ts +32 -0
- package/dist/modules/tokenPack/repository.d.ts.map +1 -0
- package/dist/modules/tokenPack/repository.js +103 -0
- package/dist/modules/tokenPack/repository.js.map +1 -0
- package/dist/modules/tokenPack/service.d.ts +28 -0
- package/dist/modules/tokenPack/service.d.ts.map +1 -0
- package/dist/modules/tokenPack/service.js +106 -0
- package/dist/modules/tokenPack/service.js.map +1 -0
- package/dist/modules/tokenPack/types.d.ts +124 -0
- package/dist/modules/tokenPack/types.d.ts.map +1 -0
- package/dist/modules/tokenPack/types.js +3 -0
- package/dist/modules/tokenPack/types.js.map +1 -0
- package/package.json +9 -5
- package/src/config/ConfigurationManager.ts +159 -0
- package/src/config/defaults.ts +27 -0
- package/src/config/environment.ts +94 -0
- package/src/config/index.ts +22 -0
- package/src/config/types.ts +56 -0
- package/src/index.ts +29 -0
- package/src/modules/cache/InMemoryCache.ts +98 -0
- package/src/modules/cache/index.ts +2 -0
- package/src/modules/cache/types.ts +60 -0
- package/src/modules/cancellableAPI/utils.ts +60 -0
- package/src/modules/errors/index.ts +16 -0
- package/src/modules/errors/types.ts +201 -0
- package/src/modules/invoice/const.ts +7 -0
- package/src/modules/invoice/index.ts +4 -0
- package/src/modules/invoice/repository.ts +52 -0
- package/src/modules/invoice/service.ts +44 -0
- package/src/modules/invoice/types.ts +47 -0
- package/src/modules/logger/types.ts +8 -0
- package/src/modules/network/utils.ts +24 -0
- package/src/modules/payments/api.ts +289 -0
- package/src/modules/payments/const.ts +11 -0
- package/src/modules/payments/index.ts +14 -0
- package/src/modules/payments/repository.ts +125 -0
- package/src/modules/payments/service.test.ts +400 -0
- package/src/modules/payments/service.ts +365 -0
- package/src/modules/payments/subscription-check-webhook.handler.integration.test.ts +935 -0
- package/src/modules/payments/subscription-check-webhook.handler.ts +211 -0
- package/src/modules/payments/subscription-check-webhook.service.ts +398 -0
- package/src/modules/payments/subscription-check-webhook.types.ts +29 -0
- package/src/modules/payments/types.ts +193 -0
- package/src/modules/payments/utils.ts +428 -0
- package/src/modules/payments/wayforpay.service.test.ts +375 -0
- package/src/modules/payments/wayforpay.service.ts +284 -0
- package/src/modules/payments/wayforpay.types.ts +138 -0
- package/src/modules/payments/webhook.handler.integration.test.ts +975 -0
- package/src/modules/payments/webhook.handler.ts +219 -0
- package/src/modules/payments/webhook.service.ts +812 -0
- package/src/modules/payments/webhook.types.ts +38 -0
- package/src/modules/subscription/change.service.ts +317 -0
- package/src/modules/subscription/const.ts +9 -0
- package/src/modules/subscription/index.ts +5 -0
- package/src/modules/subscription/repository.ts +277 -0
- package/src/modules/subscription/service.test.ts +665 -0
- package/src/modules/subscription/service.ts +328 -0
- package/src/modules/subscription/status-check.handler.ts +254 -0
- package/src/modules/subscription/types.ts +267 -0
- package/src/modules/subscription/utils.ts +5 -0
- package/src/modules/subscriptionPlan/const.ts +106 -0
- package/src/modules/subscriptionPlan/index.ts +4 -0
- package/src/modules/subscriptionPlan/repository.ts +129 -0
- package/src/modules/subscriptionPlan/service.test.ts +401 -0
- package/src/modules/subscriptionPlan/service.ts +148 -0
- package/src/modules/subscriptionPlan/types.ts +67 -0
- package/src/modules/token/const.ts +64 -0
- package/src/modules/token/index.ts +3 -0
- package/src/modules/token/service.test.ts +499 -0
- package/src/modules/token/service.ts +297 -0
- package/src/modules/token/types.ts +124 -0
- package/src/modules/tokenPack/const.ts +9 -0
- package/src/modules/tokenPack/index.ts +4 -0
- package/src/modules/tokenPack/repository.ts +144 -0
- package/src/modules/tokenPack/service.ts +119 -0
- package/src/modules/tokenPack/types.ts +131 -0
- package/src/modules/user/index.ts +3 -0
- package/src/modules/user/types.ts +143 -0
- package/src/modules/user/userRepository.ts +64 -0
- package/src/modules/user/userService.ts +68 -0
- package/src/types/extend-express.d.ts +16 -0
- package/src/types/function.ts +5 -0
- package/src/types/utilities.ts +22 -0
- package/src/utils.ts +53 -0
- package/tsconfig.json +29 -0
- package/dist/modules/subscription/subscriptionPlan.d.ts +0 -4
- package/dist/modules/subscription/subscriptionPlan.d.ts.map +0 -1
- package/dist/modules/subscription/subscriptionPlan.js +0 -67
- package/dist/modules/subscription/subscriptionPlan.js.map +0 -1
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { NestedPathsAccess } from '../../types/utilities';
|
|
2
|
+
import { SubscriptionPlanEntity } from '../subscriptionPlan/types';
|
|
3
|
+
import { PaymentEntity } from '../payments/types';
|
|
4
|
+
|
|
5
|
+
type UpgradePreview = {
|
|
6
|
+
currentPlan: SubscriptionPlanEntity;
|
|
7
|
+
newPlan: SubscriptionPlanEntity;
|
|
8
|
+
remainingDays: number;
|
|
9
|
+
remainingValue: number;
|
|
10
|
+
upgradeCost: number;
|
|
11
|
+
currency: 'UAH' | 'USD';
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type UpgradeResult = {
|
|
15
|
+
payment: PaymentEntity;
|
|
16
|
+
paymentUrl: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type SubscriptionEntity = {
|
|
20
|
+
/** Auto-generated unique ID (document ID) */
|
|
21
|
+
id: string;
|
|
22
|
+
/** The Telegram User ID this subscription belongs to. Links back to users. */
|
|
23
|
+
userId: string;
|
|
24
|
+
/** An identifier for the subscription plan (e.g., "basic-daily", "pro-monthly") */
|
|
25
|
+
planId: string;
|
|
26
|
+
/** The lifecycle state of this service period */
|
|
27
|
+
status: 'active' | 'expired' | 'cancelled';
|
|
28
|
+
/** The key field for the cron job. Timestamp when this paid-for period ends. */
|
|
29
|
+
expiresAt: string;
|
|
30
|
+
/** Timestamp when this period began */
|
|
31
|
+
startedAt: string;
|
|
32
|
+
/** Platform/app namespace */
|
|
33
|
+
platform: string;
|
|
34
|
+
/** Payment provider */
|
|
35
|
+
provider: 'wayforpay' | 'platform';
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
type SubscriptionPlan = {
|
|
39
|
+
id: string;
|
|
40
|
+
titleCode: string;
|
|
41
|
+
descriptionCode: string;
|
|
42
|
+
amount: number;
|
|
43
|
+
currency: 'UAH' | 'USD';
|
|
44
|
+
features: {
|
|
45
|
+
messages: number;
|
|
46
|
+
images: number;
|
|
47
|
+
voice: number;
|
|
48
|
+
};
|
|
49
|
+
regularMode: 'daily' | 'monthly' | 'yearly';
|
|
50
|
+
count: number;
|
|
51
|
+
credits: number;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
type SubscriptionFieldPath = NestedPathsAccess<SubscriptionEntity>;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Interface for subscription repository operations.
|
|
58
|
+
* Defines the contract for data access layer.
|
|
59
|
+
*/
|
|
60
|
+
interface ISubscriptionRepository {
|
|
61
|
+
/**
|
|
62
|
+
* Retrieves a subscription for a specific user.
|
|
63
|
+
* @param params - Query parameters
|
|
64
|
+
* @returns Promise resolving to SubscriptionEntity or null if not found
|
|
65
|
+
*/
|
|
66
|
+
getByUser(params: {
|
|
67
|
+
userId: string;
|
|
68
|
+
platform: string;
|
|
69
|
+
status?: SubscriptionEntity['status'] | SubscriptionEntity['status'][];
|
|
70
|
+
}): Promise<SubscriptionEntity | null>;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Creates a new subscription record in the database.
|
|
74
|
+
* @param params - Subscription creation parameters
|
|
75
|
+
* @returns Promise resolving to created SubscriptionEntity with ID
|
|
76
|
+
*/
|
|
77
|
+
create(params: {
|
|
78
|
+
userId: string;
|
|
79
|
+
platform: string;
|
|
80
|
+
planId: string;
|
|
81
|
+
expiresAt: string;
|
|
82
|
+
startedAt: string;
|
|
83
|
+
}): Promise<SubscriptionEntity>;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Updates specific fields of a subscription identified by userId and platform.
|
|
87
|
+
* @param params - Update parameters
|
|
88
|
+
*/
|
|
89
|
+
updateFieldsByUserId(params: { userId: string; platform: string; fields: any }): Promise<void>;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Retrieves all active subscriptions that have expired.
|
|
93
|
+
* @returns Promise resolving to array of expired SubscriptionEntity
|
|
94
|
+
*/
|
|
95
|
+
getExpiredActiveSubscriptions(): Promise<SubscriptionEntity[]>;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Updates specific fields of a subscription identified by subscription ID.
|
|
99
|
+
* @param params - Update parameters
|
|
100
|
+
*/
|
|
101
|
+
updateFieldsById(params: { subscriptionId: string; fields: any }): Promise<void>;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Activates a subscription for a user using a transaction.
|
|
105
|
+
* @param params - Activation parameters
|
|
106
|
+
* @returns Promise resolving to void
|
|
107
|
+
*/
|
|
108
|
+
activateSubscription(params: Omit<SubscriptionEntity, 'id' | 'status'>): Promise<void>;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Renews a subscription for a user using a transaction.
|
|
112
|
+
* @param params - Renewal parameters
|
|
113
|
+
*/
|
|
114
|
+
renewSubscription(params: Pick<SubscriptionEntity, 'userId' | 'platform' | 'expiresAt'>): Promise<void>;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Retrieves subscriptions by status.
|
|
118
|
+
* @param params - Status parameters
|
|
119
|
+
* @returns Promise resolving to array of SubscriptionEntity
|
|
120
|
+
*/
|
|
121
|
+
getByStatus(params: {
|
|
122
|
+
status: SubscriptionEntity['status'] | SubscriptionEntity['status'][];
|
|
123
|
+
}): Promise<SubscriptionEntity[]>;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Deactivates a subscription for a user using a transaction.
|
|
127
|
+
* @param params - Deactivation parameters
|
|
128
|
+
*/
|
|
129
|
+
deactivateSubscription({ id }: Pick<SubscriptionEntity, 'id'>): Promise<void>;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Cancels a subscription for a user using a transaction.
|
|
133
|
+
* @param params - Cancellation parameters
|
|
134
|
+
*/
|
|
135
|
+
cancelSubscription(params: Pick<SubscriptionEntity, 'id'>): Promise<void>;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Interface for subscription service operations.
|
|
140
|
+
* Defines the contract for business logic layer.
|
|
141
|
+
*/
|
|
142
|
+
interface ISubscriptionService {
|
|
143
|
+
/**
|
|
144
|
+
* Retrieves a subscription for a specific user.
|
|
145
|
+
* @param params - Query parameters
|
|
146
|
+
* @returns Promise resolving to SubscriptionEntity or null if not found
|
|
147
|
+
*/
|
|
148
|
+
getByUser(params: {
|
|
149
|
+
userId: string;
|
|
150
|
+
platform: string;
|
|
151
|
+
status?: SubscriptionEntity['status'] | SubscriptionEntity['status'][];
|
|
152
|
+
}): Promise<SubscriptionEntity | null>;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Creates a new subscription record in the database.
|
|
156
|
+
* @param params - Subscription creation parameters
|
|
157
|
+
* @returns Promise resolving to created SubscriptionEntity with ID
|
|
158
|
+
*/
|
|
159
|
+
create(params: {
|
|
160
|
+
userId: string;
|
|
161
|
+
platform: string;
|
|
162
|
+
planId: string;
|
|
163
|
+
expiresAt: string;
|
|
164
|
+
startedAt: string;
|
|
165
|
+
}): Promise<SubscriptionEntity>;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Updates specific fields of a subscription identified by userId and platform.
|
|
169
|
+
* @param params - Update parameters
|
|
170
|
+
*/
|
|
171
|
+
updateFieldsByUserId(params: { userId: string; platform: string; fields: any }): Promise<void>;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Retrieves all active subscriptions that have expired.
|
|
175
|
+
* @returns Promise resolving to array of expired SubscriptionEntity
|
|
176
|
+
*/
|
|
177
|
+
getExpiredActiveSubscriptions(): Promise<SubscriptionEntity[]>;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Updates specific fields of a subscription identified by subscription ID.
|
|
181
|
+
* @param params - Update parameters
|
|
182
|
+
*/
|
|
183
|
+
updateFieldsById(params: { subscriptionId: string; fields: any }): Promise<void>;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Gets or creates a subscription payment URL.
|
|
187
|
+
* @param params - Payment URL creation parameters
|
|
188
|
+
* @returns Promise resolving to the payment URL string
|
|
189
|
+
*/
|
|
190
|
+
getOrCreateSubscriptionPaymentUrl(params: {
|
|
191
|
+
userId: string;
|
|
192
|
+
platform: string;
|
|
193
|
+
languageCode: string;
|
|
194
|
+
translate: (params: { code: string; path: string }) => string;
|
|
195
|
+
subscriptionPlan: SubscriptionPlan;
|
|
196
|
+
}): Promise<string>;
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Activates a subscription for a user.
|
|
200
|
+
* @param params - Activation parameters
|
|
201
|
+
*/
|
|
202
|
+
activateSubscription(params: Omit<SubscriptionEntity, 'id' | 'status'>): Promise<void>;
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Retrieves all subscriptions(active and cancelled) that have an expiration date in the past.
|
|
206
|
+
*/
|
|
207
|
+
getSubscriptionsForDeactivationCheck(): Promise<SubscriptionEntity[]>;
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Deactivates a subscription for a user.
|
|
211
|
+
* @param params - Deactivation parameters
|
|
212
|
+
*/
|
|
213
|
+
deactivateSubscription({ id }: Pick<SubscriptionEntity, 'id'>): Promise<void>;
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Cancels a subscription for a user.
|
|
217
|
+
* @param params - Cancellation parameters
|
|
218
|
+
*/
|
|
219
|
+
cancelSubscription(params: Pick<SubscriptionEntity, 'id'>): Promise<void>;
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Renews a subscription for a user by updating the expiration date.
|
|
223
|
+
* @param params - Renewal parameters
|
|
224
|
+
*/
|
|
225
|
+
renewSubscription(params: Pick<SubscriptionEntity, 'userId' | 'platform' | 'expiresAt'>): Promise<void>;
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Retrieves all subscriptions that have expired past the grace period.
|
|
229
|
+
* Only returns subscriptions that should be deactivated.
|
|
230
|
+
* @returns Promise resolving to array of expired SubscriptionEntity
|
|
231
|
+
*/
|
|
232
|
+
getExpiredSubscriptions(): Promise<SubscriptionEntity[]>;
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Retrieves all subscriptions that have had failed renewal attempts.
|
|
236
|
+
* These are cancelled subscriptions that expired and need deactivation.
|
|
237
|
+
* @returns Promise resolving to array of SubscriptionEntity with failed renewals
|
|
238
|
+
*/
|
|
239
|
+
getFailedRenewals(): Promise<SubscriptionEntity[]>;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Deactivates a subscription by ID (alias for deactivateSubscription).
|
|
243
|
+
* @param params - Deactivation parameters
|
|
244
|
+
*/
|
|
245
|
+
deactivate({ id }: Pick<SubscriptionEntity, 'id'>): Promise<void>;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
interface ISubscriptionCapacity {
|
|
249
|
+
geminiTextCompletionTokens: number;
|
|
250
|
+
geminiImageGenerationCount: number;
|
|
251
|
+
|
|
252
|
+
openaiTextCompletionTokens: number;
|
|
253
|
+
openaiTextToSpeechChars: number;
|
|
254
|
+
openaiImageVisionTokens: number;
|
|
255
|
+
openaiTextTranscriptionMinutes: number;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export type {
|
|
259
|
+
SubscriptionEntity,
|
|
260
|
+
SubscriptionPlan,
|
|
261
|
+
SubscriptionFieldPath,
|
|
262
|
+
ISubscriptionRepository,
|
|
263
|
+
ISubscriptionService,
|
|
264
|
+
ISubscriptionCapacity,
|
|
265
|
+
UpgradePreview,
|
|
266
|
+
UpgradeResult,
|
|
267
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { SubscriptionPlanEntity } from './types';
|
|
2
|
+
|
|
3
|
+
export const SUBSCRIPTION_PLAN_COLLECTION = 'subscription_plans';
|
|
4
|
+
|
|
5
|
+
export const CACHE_TTL_MS = 20 * 60 * 60 * 1000; // 20 hours in milliseconds
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_SUBSCRIPTION_PLANS: Omit<SubscriptionPlanEntity, 'createdAt' | 'updatedAt'>[] = [
|
|
8
|
+
// UAH Plans
|
|
9
|
+
{
|
|
10
|
+
id: 'basic-monthly',
|
|
11
|
+
titleCode: 'subscription.plan.basic.title',
|
|
12
|
+
descriptionCode: 'subscription.plan.basic.description',
|
|
13
|
+
amount: 99,
|
|
14
|
+
currency: 'UAH',
|
|
15
|
+
features: {
|
|
16
|
+
messages: 1000,
|
|
17
|
+
images: 50,
|
|
18
|
+
voice: 100,
|
|
19
|
+
},
|
|
20
|
+
regularMode: 'monthly',
|
|
21
|
+
count: 1,
|
|
22
|
+
credits: 1000,
|
|
23
|
+
isActive: true,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'pro-monthly',
|
|
27
|
+
titleCode: 'subscription.plan.pro.title',
|
|
28
|
+
descriptionCode: 'subscription.plan.pro.description',
|
|
29
|
+
amount: 199,
|
|
30
|
+
currency: 'UAH',
|
|
31
|
+
features: {
|
|
32
|
+
messages: 5000,
|
|
33
|
+
images: 200,
|
|
34
|
+
voice: 500,
|
|
35
|
+
},
|
|
36
|
+
regularMode: 'monthly',
|
|
37
|
+
count: 1,
|
|
38
|
+
credits: 5000,
|
|
39
|
+
isActive: true,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: 'premium-monthly',
|
|
43
|
+
titleCode: 'subscription.plan.premium.title',
|
|
44
|
+
descriptionCode: 'subscription.plan.premium.description',
|
|
45
|
+
amount: 399,
|
|
46
|
+
currency: 'UAH',
|
|
47
|
+
features: {
|
|
48
|
+
messages: 15000,
|
|
49
|
+
images: 500,
|
|
50
|
+
voice: 1000,
|
|
51
|
+
},
|
|
52
|
+
regularMode: 'monthly',
|
|
53
|
+
count: 1,
|
|
54
|
+
credits: 15000,
|
|
55
|
+
isActive: true,
|
|
56
|
+
},
|
|
57
|
+
// USD Plans - $7, $10, $14 tiers
|
|
58
|
+
{
|
|
59
|
+
id: 'starter-monthly-usd',
|
|
60
|
+
titleCode: 'subscription.plan.starter.title',
|
|
61
|
+
descriptionCode: 'subscription.plan.starter.description',
|
|
62
|
+
amount: 7,
|
|
63
|
+
currency: 'USD',
|
|
64
|
+
features: {
|
|
65
|
+
messages: 2000,
|
|
66
|
+
images: 100,
|
|
67
|
+
voice: 200,
|
|
68
|
+
},
|
|
69
|
+
regularMode: 'monthly',
|
|
70
|
+
count: 1,
|
|
71
|
+
credits: 2000,
|
|
72
|
+
isActive: true,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: 'standard-monthly-usd',
|
|
76
|
+
titleCode: 'subscription.plan.standard.title',
|
|
77
|
+
descriptionCode: 'subscription.plan.standard.description',
|
|
78
|
+
amount: 10,
|
|
79
|
+
currency: 'USD',
|
|
80
|
+
features: {
|
|
81
|
+
messages: 5000,
|
|
82
|
+
images: 250,
|
|
83
|
+
voice: 500,
|
|
84
|
+
},
|
|
85
|
+
regularMode: 'monthly',
|
|
86
|
+
count: 1,
|
|
87
|
+
credits: 5000,
|
|
88
|
+
isActive: true,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
id: 'professional-monthly-usd',
|
|
92
|
+
titleCode: 'subscription.plan.professional.title',
|
|
93
|
+
descriptionCode: 'subscription.plan.professional.description',
|
|
94
|
+
amount: 14,
|
|
95
|
+
currency: 'USD',
|
|
96
|
+
features: {
|
|
97
|
+
messages: 10000,
|
|
98
|
+
images: 500,
|
|
99
|
+
voice: 1000,
|
|
100
|
+
},
|
|
101
|
+
regularMode: 'monthly',
|
|
102
|
+
count: 1,
|
|
103
|
+
credits: 10000,
|
|
104
|
+
isActive: true,
|
|
105
|
+
},
|
|
106
|
+
];
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { FieldValue, Firestore, getFirestore, QueryDocumentSnapshot } from 'firebase-admin/firestore';
|
|
2
|
+
import { SubscriptionPlanEntity, SubscriptionPlanFieldPath, ISubscriptionPlanRepository } from './types';
|
|
3
|
+
import { SUBSCRIPTION_PLAN_COLLECTION } from './const';
|
|
4
|
+
|
|
5
|
+
export type UpdateDBSubscriptionPlanFields = [
|
|
6
|
+
SubscriptionPlanFieldPath,
|
|
7
|
+
FieldValue | string | number | boolean | Date | [] | {},
|
|
8
|
+
][];
|
|
9
|
+
|
|
10
|
+
export class SubscriptionPlanRepository implements ISubscriptionPlanRepository {
|
|
11
|
+
private readonly db: Firestore;
|
|
12
|
+
private readonly collectionName = SUBSCRIPTION_PLAN_COLLECTION;
|
|
13
|
+
|
|
14
|
+
constructor({ db }: { db?: Firestore } = {}) {
|
|
15
|
+
this.db = db || getFirestore();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public async getAll(): Promise<SubscriptionPlanEntity[]> {
|
|
19
|
+
const querySnapshot = await this.db.collection(this.collectionName).get();
|
|
20
|
+
|
|
21
|
+
if (querySnapshot.empty) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return querySnapshot.docs.map((doc) => this.mapDocumentToEntity(doc));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public async getById(id: string): Promise<SubscriptionPlanEntity | null> {
|
|
29
|
+
const docSnapshot = await this.db.collection(this.collectionName).doc(id).get();
|
|
30
|
+
|
|
31
|
+
if (!docSnapshot.exists) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
id: docSnapshot.id,
|
|
37
|
+
...docSnapshot.data(),
|
|
38
|
+
} as SubscriptionPlanEntity;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public async create(
|
|
42
|
+
params: Omit<SubscriptionPlanEntity, 'id' | 'createdAt' | 'updatedAt'>,
|
|
43
|
+
): Promise<SubscriptionPlanEntity> {
|
|
44
|
+
const docRef = this.db.collection(this.collectionName).doc();
|
|
45
|
+
const now = new Date().toISOString();
|
|
46
|
+
|
|
47
|
+
const planEntity: SubscriptionPlanEntity = {
|
|
48
|
+
...params,
|
|
49
|
+
id: docRef.id,
|
|
50
|
+
createdAt: now,
|
|
51
|
+
updatedAt: now,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
await docRef.set(planEntity);
|
|
55
|
+
|
|
56
|
+
return planEntity;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public async update(
|
|
60
|
+
id: string,
|
|
61
|
+
params: Partial<Omit<SubscriptionPlanEntity, 'id' | 'createdAt' | 'updatedAt'>>,
|
|
62
|
+
): Promise<SubscriptionPlanEntity> {
|
|
63
|
+
const docRef = this.db.collection(this.collectionName).doc(id);
|
|
64
|
+
const docSnapshot = await docRef.get();
|
|
65
|
+
|
|
66
|
+
if (!docSnapshot.exists) {
|
|
67
|
+
throw new Error(`Subscription plan not found: ${id}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const now = new Date().toISOString();
|
|
71
|
+
const updateData = {
|
|
72
|
+
...params,
|
|
73
|
+
updatedAt: now,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
await docRef.update(updateData);
|
|
77
|
+
|
|
78
|
+
const updatedDoc = await docRef.get();
|
|
79
|
+
return {
|
|
80
|
+
id: updatedDoc.id,
|
|
81
|
+
...updatedDoc.data(),
|
|
82
|
+
} as SubscriptionPlanEntity;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public async deactivate(id: string): Promise<void> {
|
|
86
|
+
const docRef = this.db.collection(this.collectionName).doc(id);
|
|
87
|
+
const docSnapshot = await docRef.get();
|
|
88
|
+
|
|
89
|
+
if (!docSnapshot.exists) {
|
|
90
|
+
throw new Error(`Subscription plan not found: ${id}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
await docRef.update({
|
|
94
|
+
isActive: false,
|
|
95
|
+
updatedAt: new Date().toISOString(),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public async seedDefaults(
|
|
100
|
+
defaultPlans: Omit<SubscriptionPlanEntity, 'createdAt' | 'updatedAt'>[],
|
|
101
|
+
): Promise<void> {
|
|
102
|
+
const batch = this.db.batch();
|
|
103
|
+
const now = new Date().toISOString();
|
|
104
|
+
|
|
105
|
+
for (const plan of defaultPlans) {
|
|
106
|
+
const docRef = this.db.collection(this.collectionName).doc(plan.id);
|
|
107
|
+
const planEntity: SubscriptionPlanEntity = {
|
|
108
|
+
...plan,
|
|
109
|
+
createdAt: now,
|
|
110
|
+
updatedAt: now,
|
|
111
|
+
};
|
|
112
|
+
batch.set(docRef, planEntity);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
await batch.commit();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
public async isEmpty(): Promise<boolean> {
|
|
119
|
+
const querySnapshot = await this.db.collection(this.collectionName).limit(1).get();
|
|
120
|
+
return querySnapshot.empty;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private mapDocumentToEntity(doc: QueryDocumentSnapshot): SubscriptionPlanEntity {
|
|
124
|
+
return {
|
|
125
|
+
id: doc.id,
|
|
126
|
+
...doc.data(),
|
|
127
|
+
} as SubscriptionPlanEntity;
|
|
128
|
+
}
|
|
129
|
+
}
|