@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.
Files changed (248) hide show
  1. package/dist/config/ConfigurationManager.d.ts +64 -0
  2. package/dist/config/ConfigurationManager.d.ts.map +1 -0
  3. package/dist/config/ConfigurationManager.js +144 -0
  4. package/dist/config/ConfigurationManager.js.map +1 -0
  5. package/dist/config/defaults.d.ts +18 -0
  6. package/dist/config/defaults.d.ts.map +1 -0
  7. package/dist/config/defaults.js +26 -0
  8. package/dist/config/defaults.js.map +1 -0
  9. package/dist/config/environment.d.ts +38 -0
  10. package/dist/config/environment.d.ts.map +1 -0
  11. package/dist/config/environment.js +91 -0
  12. package/dist/config/environment.js.map +1 -0
  13. package/dist/config/index.d.ts +5 -0
  14. package/dist/config/index.d.ts.map +1 -0
  15. package/dist/config/index.js +18 -0
  16. package/dist/config/index.js.map +1 -0
  17. package/dist/config/types.d.ts +53 -0
  18. package/dist/config/types.d.ts.map +1 -0
  19. package/dist/config/types.js +3 -0
  20. package/dist/config/types.js.map +1 -0
  21. package/dist/index.d.ts +21 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +23 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/modules/cache/InMemoryCache.d.ts +17 -0
  26. package/dist/modules/cache/InMemoryCache.d.ts.map +1 -0
  27. package/dist/modules/cache/InMemoryCache.js +77 -0
  28. package/dist/modules/cache/InMemoryCache.js.map +1 -0
  29. package/dist/modules/cache/index.d.ts +3 -0
  30. package/dist/modules/cache/index.d.ts.map +1 -0
  31. package/dist/modules/cache/index.js +19 -0
  32. package/dist/modules/cache/index.js.map +1 -0
  33. package/dist/modules/cache/types.d.ts +52 -0
  34. package/dist/modules/cache/types.d.ts.map +1 -0
  35. package/dist/modules/cache/types.js +3 -0
  36. package/dist/modules/cache/types.js.map +1 -0
  37. package/dist/modules/errors/index.d.ts +2 -0
  38. package/dist/modules/errors/index.d.ts.map +1 -0
  39. package/dist/modules/errors/index.js +19 -0
  40. package/dist/modules/errors/index.js.map +1 -0
  41. package/dist/modules/errors/types.d.ts +112 -0
  42. package/dist/modules/errors/types.d.ts.map +1 -0
  43. package/dist/modules/errors/types.js +174 -0
  44. package/dist/modules/errors/types.js.map +1 -0
  45. package/dist/modules/payments/api.d.ts +63 -1
  46. package/dist/modules/payments/api.d.ts.map +1 -1
  47. package/dist/modules/payments/api.js +103 -1
  48. package/dist/modules/payments/api.js.map +1 -1
  49. package/dist/modules/payments/const.d.ts.map +1 -1
  50. package/dist/modules/payments/const.js +1 -0
  51. package/dist/modules/payments/const.js.map +1 -1
  52. package/dist/modules/payments/index.d.ts +8 -0
  53. package/dist/modules/payments/index.d.ts.map +1 -1
  54. package/dist/modules/payments/index.js +8 -0
  55. package/dist/modules/payments/index.js.map +1 -1
  56. package/dist/modules/payments/service.d.ts +42 -2
  57. package/dist/modules/payments/service.d.ts.map +1 -1
  58. package/dist/modules/payments/service.js +132 -3
  59. package/dist/modules/payments/service.js.map +1 -1
  60. package/dist/modules/payments/subscription-check-webhook.handler.d.ts +85 -0
  61. package/dist/modules/payments/subscription-check-webhook.handler.d.ts.map +1 -0
  62. package/dist/modules/payments/subscription-check-webhook.handler.js +155 -0
  63. package/dist/modules/payments/subscription-check-webhook.handler.js.map +1 -0
  64. package/dist/modules/payments/subscription-check-webhook.service.d.ts +59 -0
  65. package/dist/modules/payments/subscription-check-webhook.service.d.ts.map +1 -0
  66. package/dist/modules/payments/subscription-check-webhook.service.js +330 -0
  67. package/dist/modules/payments/subscription-check-webhook.service.js.map +1 -0
  68. package/dist/modules/payments/subscription-check-webhook.types.d.ts +25 -0
  69. package/dist/modules/payments/subscription-check-webhook.types.d.ts.map +1 -0
  70. package/dist/modules/payments/subscription-check-webhook.types.js +3 -0
  71. package/dist/modules/payments/subscription-check-webhook.types.js.map +1 -0
  72. package/dist/modules/payments/types.d.ts +69 -2
  73. package/dist/modules/payments/types.d.ts.map +1 -1
  74. package/dist/modules/payments/utils.d.ts +151 -5
  75. package/dist/modules/payments/utils.d.ts.map +1 -1
  76. package/dist/modules/payments/utils.js +253 -9
  77. package/dist/modules/payments/utils.js.map +1 -1
  78. package/dist/modules/payments/wayforpay.service.d.ts +39 -0
  79. package/dist/modules/payments/wayforpay.service.d.ts.map +1 -0
  80. package/dist/modules/payments/wayforpay.service.js +217 -0
  81. package/dist/modules/payments/wayforpay.service.js.map +1 -0
  82. package/dist/modules/payments/wayforpay.types.d.ts +115 -0
  83. package/dist/modules/payments/wayforpay.types.d.ts.map +1 -0
  84. package/dist/modules/payments/wayforpay.types.js +3 -0
  85. package/dist/modules/payments/wayforpay.types.js.map +1 -0
  86. package/dist/modules/payments/webhook.handler.d.ts +98 -0
  87. package/dist/modules/payments/webhook.handler.d.ts.map +1 -0
  88. package/dist/modules/payments/webhook.handler.js +153 -0
  89. package/dist/modules/payments/webhook.handler.js.map +1 -0
  90. package/dist/modules/payments/webhook.service.d.ts +99 -0
  91. package/dist/modules/payments/webhook.service.d.ts.map +1 -0
  92. package/dist/modules/payments/webhook.service.js +672 -0
  93. package/dist/modules/payments/webhook.service.js.map +1 -0
  94. package/dist/modules/payments/webhook.types.d.ts +35 -0
  95. package/dist/modules/payments/webhook.types.d.ts.map +1 -0
  96. package/dist/modules/payments/webhook.types.js +3 -0
  97. package/dist/modules/payments/webhook.types.js.map +1 -0
  98. package/dist/modules/subscription/change.service.d.ts +80 -0
  99. package/dist/modules/subscription/change.service.d.ts.map +1 -0
  100. package/dist/modules/subscription/change.service.js +226 -0
  101. package/dist/modules/subscription/change.service.js.map +1 -0
  102. package/dist/modules/subscription/index.d.ts +2 -0
  103. package/dist/modules/subscription/index.d.ts.map +1 -1
  104. package/dist/modules/subscription/index.js +2 -0
  105. package/dist/modules/subscription/index.js.map +1 -1
  106. package/dist/modules/subscription/service.d.ts +8 -1
  107. package/dist/modules/subscription/service.d.ts.map +1 -1
  108. package/dist/modules/subscription/service.js +59 -2
  109. package/dist/modules/subscription/service.js.map +1 -1
  110. package/dist/modules/subscription/status-check.handler.d.ts +117 -0
  111. package/dist/modules/subscription/status-check.handler.d.ts.map +1 -0
  112. package/dist/modules/subscription/status-check.handler.js +164 -0
  113. package/dist/modules/subscription/status-check.handler.js.map +1 -0
  114. package/dist/modules/subscription/types.d.ts +37 -1
  115. package/dist/modules/subscription/types.d.ts.map +1 -1
  116. package/dist/modules/subscriptionPlan/const.d.ts +5 -0
  117. package/dist/modules/subscriptionPlan/const.d.ts.map +1 -0
  118. package/dist/modules/subscriptionPlan/const.js +106 -0
  119. package/dist/modules/subscriptionPlan/const.js.map +1 -0
  120. package/dist/modules/subscriptionPlan/index.d.ts +5 -0
  121. package/dist/modules/subscriptionPlan/index.d.ts.map +1 -0
  122. package/dist/modules/subscriptionPlan/index.js +21 -0
  123. package/dist/modules/subscriptionPlan/index.js.map +1 -0
  124. package/dist/modules/subscriptionPlan/repository.d.ts +22 -0
  125. package/dist/modules/subscriptionPlan/repository.d.ts.map +1 -0
  126. package/dist/modules/subscriptionPlan/repository.js +95 -0
  127. package/dist/modules/subscriptionPlan/repository.js.map +1 -0
  128. package/dist/modules/subscriptionPlan/service.d.ts +21 -0
  129. package/dist/modules/subscriptionPlan/service.d.ts.map +1 -0
  130. package/dist/modules/subscriptionPlan/service.js +128 -0
  131. package/dist/modules/subscriptionPlan/service.js.map +1 -0
  132. package/dist/modules/subscriptionPlan/types.d.ts +40 -0
  133. package/dist/modules/subscriptionPlan/types.d.ts.map +1 -0
  134. package/dist/modules/subscriptionPlan/types.js +3 -0
  135. package/dist/modules/subscriptionPlan/types.js.map +1 -0
  136. package/dist/modules/token/const.d.ts +7 -0
  137. package/dist/modules/token/const.d.ts.map +1 -0
  138. package/dist/modules/token/const.js +66 -0
  139. package/dist/modules/token/const.js.map +1 -0
  140. package/dist/modules/token/index.d.ts +4 -0
  141. package/dist/modules/token/index.d.ts.map +1 -0
  142. package/dist/modules/token/index.js +20 -0
  143. package/dist/modules/token/index.js.map +1 -0
  144. package/dist/modules/token/service.d.ts +46 -0
  145. package/dist/modules/token/service.d.ts.map +1 -0
  146. package/dist/modules/token/service.js +249 -0
  147. package/dist/modules/token/service.js.map +1 -0
  148. package/dist/modules/token/types.d.ts +109 -0
  149. package/dist/modules/token/types.d.ts.map +1 -0
  150. package/dist/modules/token/types.js +3 -0
  151. package/dist/modules/token/types.js.map +1 -0
  152. package/dist/modules/tokenPack/const.d.ts +4 -0
  153. package/dist/modules/tokenPack/const.d.ts.map +1 -0
  154. package/dist/modules/tokenPack/const.js +10 -0
  155. package/dist/modules/tokenPack/const.js.map +1 -0
  156. package/dist/modules/tokenPack/index.d.ts +5 -0
  157. package/dist/modules/tokenPack/index.d.ts.map +1 -0
  158. package/dist/modules/tokenPack/index.js +21 -0
  159. package/dist/modules/tokenPack/index.js.map +1 -0
  160. package/dist/modules/tokenPack/repository.d.ts +32 -0
  161. package/dist/modules/tokenPack/repository.d.ts.map +1 -0
  162. package/dist/modules/tokenPack/repository.js +103 -0
  163. package/dist/modules/tokenPack/repository.js.map +1 -0
  164. package/dist/modules/tokenPack/service.d.ts +28 -0
  165. package/dist/modules/tokenPack/service.d.ts.map +1 -0
  166. package/dist/modules/tokenPack/service.js +106 -0
  167. package/dist/modules/tokenPack/service.js.map +1 -0
  168. package/dist/modules/tokenPack/types.d.ts +124 -0
  169. package/dist/modules/tokenPack/types.d.ts.map +1 -0
  170. package/dist/modules/tokenPack/types.js +3 -0
  171. package/dist/modules/tokenPack/types.js.map +1 -0
  172. package/package.json +9 -5
  173. package/src/config/ConfigurationManager.ts +159 -0
  174. package/src/config/defaults.ts +27 -0
  175. package/src/config/environment.ts +94 -0
  176. package/src/config/index.ts +22 -0
  177. package/src/config/types.ts +56 -0
  178. package/src/index.ts +29 -0
  179. package/src/modules/cache/InMemoryCache.ts +98 -0
  180. package/src/modules/cache/index.ts +2 -0
  181. package/src/modules/cache/types.ts +60 -0
  182. package/src/modules/cancellableAPI/utils.ts +60 -0
  183. package/src/modules/errors/index.ts +16 -0
  184. package/src/modules/errors/types.ts +201 -0
  185. package/src/modules/invoice/const.ts +7 -0
  186. package/src/modules/invoice/index.ts +4 -0
  187. package/src/modules/invoice/repository.ts +52 -0
  188. package/src/modules/invoice/service.ts +44 -0
  189. package/src/modules/invoice/types.ts +47 -0
  190. package/src/modules/logger/types.ts +8 -0
  191. package/src/modules/network/utils.ts +24 -0
  192. package/src/modules/payments/api.ts +289 -0
  193. package/src/modules/payments/const.ts +11 -0
  194. package/src/modules/payments/index.ts +14 -0
  195. package/src/modules/payments/repository.ts +125 -0
  196. package/src/modules/payments/service.test.ts +400 -0
  197. package/src/modules/payments/service.ts +365 -0
  198. package/src/modules/payments/subscription-check-webhook.handler.integration.test.ts +935 -0
  199. package/src/modules/payments/subscription-check-webhook.handler.ts +211 -0
  200. package/src/modules/payments/subscription-check-webhook.service.ts +398 -0
  201. package/src/modules/payments/subscription-check-webhook.types.ts +29 -0
  202. package/src/modules/payments/types.ts +193 -0
  203. package/src/modules/payments/utils.ts +428 -0
  204. package/src/modules/payments/wayforpay.service.test.ts +375 -0
  205. package/src/modules/payments/wayforpay.service.ts +284 -0
  206. package/src/modules/payments/wayforpay.types.ts +138 -0
  207. package/src/modules/payments/webhook.handler.integration.test.ts +975 -0
  208. package/src/modules/payments/webhook.handler.ts +219 -0
  209. package/src/modules/payments/webhook.service.ts +812 -0
  210. package/src/modules/payments/webhook.types.ts +38 -0
  211. package/src/modules/subscription/change.service.ts +317 -0
  212. package/src/modules/subscription/const.ts +9 -0
  213. package/src/modules/subscription/index.ts +5 -0
  214. package/src/modules/subscription/repository.ts +277 -0
  215. package/src/modules/subscription/service.test.ts +665 -0
  216. package/src/modules/subscription/service.ts +328 -0
  217. package/src/modules/subscription/status-check.handler.ts +254 -0
  218. package/src/modules/subscription/types.ts +267 -0
  219. package/src/modules/subscription/utils.ts +5 -0
  220. package/src/modules/subscriptionPlan/const.ts +106 -0
  221. package/src/modules/subscriptionPlan/index.ts +4 -0
  222. package/src/modules/subscriptionPlan/repository.ts +129 -0
  223. package/src/modules/subscriptionPlan/service.test.ts +401 -0
  224. package/src/modules/subscriptionPlan/service.ts +148 -0
  225. package/src/modules/subscriptionPlan/types.ts +67 -0
  226. package/src/modules/token/const.ts +64 -0
  227. package/src/modules/token/index.ts +3 -0
  228. package/src/modules/token/service.test.ts +499 -0
  229. package/src/modules/token/service.ts +297 -0
  230. package/src/modules/token/types.ts +124 -0
  231. package/src/modules/tokenPack/const.ts +9 -0
  232. package/src/modules/tokenPack/index.ts +4 -0
  233. package/src/modules/tokenPack/repository.ts +144 -0
  234. package/src/modules/tokenPack/service.ts +119 -0
  235. package/src/modules/tokenPack/types.ts +131 -0
  236. package/src/modules/user/index.ts +3 -0
  237. package/src/modules/user/types.ts +143 -0
  238. package/src/modules/user/userRepository.ts +64 -0
  239. package/src/modules/user/userService.ts +68 -0
  240. package/src/types/extend-express.d.ts +16 -0
  241. package/src/types/function.ts +5 -0
  242. package/src/types/utilities.ts +22 -0
  243. package/src/utils.ts +53 -0
  244. package/tsconfig.json +29 -0
  245. package/dist/modules/subscription/subscriptionPlan.d.ts +0 -4
  246. package/dist/modules/subscription/subscriptionPlan.d.ts.map +0 -1
  247. package/dist/modules/subscription/subscriptionPlan.js +0 -67
  248. 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,5 @@
1
+ function getSubscriptionTitle(planTitle: string) {
2
+ return `Miia telegram bot: ${planTitle}`;
3
+ }
4
+
5
+ export { getSubscriptionTitle };
@@ -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,4 @@
1
+ export * from './types';
2
+ export * from './const';
3
+ export * from './repository';
4
+ export * from './service';
@@ -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
+ }