@windrun-huaiin/backend-core 10.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.
Files changed (198) hide show
  1. package/LICENSE +21 -0
  2. package/dist/app/api/stripe/checkout/route.d.ts +19 -0
  3. package/dist/app/api/stripe/checkout/route.d.ts.map +1 -0
  4. package/dist/app/api/stripe/checkout/route.js +120 -0
  5. package/dist/app/api/stripe/checkout/route.mjs +118 -0
  6. package/dist/app/api/stripe/customer-portal/route.d.ts +11 -0
  7. package/dist/app/api/stripe/customer-portal/route.d.ts.map +1 -0
  8. package/dist/app/api/stripe/customer-portal/route.js +73 -0
  9. package/dist/app/api/stripe/customer-portal/route.mjs +71 -0
  10. package/dist/app/api/user/anonymous/init/route.d.ts +7 -0
  11. package/dist/app/api/user/anonymous/init/route.d.ts.map +1 -0
  12. package/dist/app/api/user/anonymous/init/route.js +210 -0
  13. package/dist/app/api/user/anonymous/init/route.mjs +208 -0
  14. package/dist/app/api/webhook/clerk/user/route.d.ts +7 -0
  15. package/dist/app/api/webhook/clerk/user/route.d.ts.map +1 -0
  16. package/dist/app/api/webhook/clerk/user/route.js +202 -0
  17. package/dist/app/api/webhook/clerk/user/route.mjs +200 -0
  18. package/dist/app/api/webhook/stripe/route.d.ts +8 -0
  19. package/dist/app/api/webhook/stripe/route.d.ts.map +1 -0
  20. package/dist/app/api/webhook/stripe/route.js +70 -0
  21. package/dist/app/api/webhook/stripe/route.mjs +67 -0
  22. package/dist/index.d.ts +7 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +83 -0
  25. package/dist/index.mjs +18 -0
  26. package/dist/lib/auth-utils.d.ts +46 -0
  27. package/dist/lib/auth-utils.d.ts.map +1 -0
  28. package/dist/lib/auth-utils.js +107 -0
  29. package/dist/lib/auth-utils.mjs +102 -0
  30. package/dist/lib/credit-init.d.ts +8 -0
  31. package/dist/lib/credit-init.d.ts.map +1 -0
  32. package/dist/lib/credit-init.js +16 -0
  33. package/dist/lib/credit-init.mjs +10 -0
  34. package/dist/lib/index.d.ts +5 -0
  35. package/dist/lib/index.d.ts.map +1 -0
  36. package/dist/lib/index.js +31 -0
  37. package/dist/lib/index.mjs +4 -0
  38. package/dist/lib/money-price-config.d.ts +51 -0
  39. package/dist/lib/money-price-config.d.ts.map +1 -0
  40. package/dist/lib/money-price-config.js +156 -0
  41. package/dist/lib/money-price-config.mjs +151 -0
  42. package/dist/lib/stripe-config.d.ts +31 -0
  43. package/dist/lib/stripe-config.d.ts.map +1 -0
  44. package/dist/lib/stripe-config.js +278 -0
  45. package/dist/lib/stripe-config.mjs +268 -0
  46. package/dist/node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.js +48 -0
  47. package/dist/node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.mjs +45 -0
  48. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/errors.js +54 -0
  49. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/errors.mjs +51 -0
  50. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/iso.js +44 -0
  51. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/iso.mjs +35 -0
  52. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/parse.js +31 -0
  53. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/parse.mjs +18 -0
  54. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/schemas.js +587 -0
  55. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/schemas.mjs +527 -0
  56. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/api.js +447 -0
  57. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/api.mjs +399 -0
  58. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/checks.js +245 -0
  59. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/checks.mjs +232 -0
  60. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/core.js +68 -0
  61. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/core.mjs +62 -0
  62. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/doc.js +39 -0
  63. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/doc.mjs +37 -0
  64. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/errors.js +80 -0
  65. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/errors.mjs +75 -0
  66. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/parse.js +101 -0
  67. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/parse.mjs +86 -0
  68. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/regexes.js +102 -0
  69. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/regexes.mjs +76 -0
  70. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/registries.js +56 -0
  71. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/registries.mjs +52 -0
  72. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/schemas.js +1205 -0
  73. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/schemas.mjs +1157 -0
  74. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/util.js +407 -0
  75. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/util.mjs +374 -0
  76. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/versions.js +9 -0
  77. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/versions.mjs +7 -0
  78. package/dist/prisma/client.d.ts +2 -0
  79. package/dist/prisma/client.d.ts.map +1 -0
  80. package/dist/prisma/client.js +12 -0
  81. package/dist/prisma/client.mjs +1 -0
  82. package/dist/prisma/index.d.ts +4 -0
  83. package/dist/prisma/index.d.ts.map +1 -0
  84. package/dist/prisma/index.js +10 -0
  85. package/dist/prisma/index.mjs +2 -0
  86. package/dist/prisma/prisma-transaction-util.d.ts +3 -0
  87. package/dist/prisma/prisma-transaction-util.d.ts.map +1 -0
  88. package/dist/prisma/prisma-transaction-util.js +29 -0
  89. package/dist/prisma/prisma-transaction-util.mjs +27 -0
  90. package/dist/prisma/prisma.d.ts +4 -0
  91. package/dist/prisma/prisma.d.ts.map +1 -0
  92. package/dist/prisma/prisma.js +109 -0
  93. package/dist/prisma/prisma.mjs +106 -0
  94. package/dist/services/aggregate/billing.aggregate.service.d.ts +83 -0
  95. package/dist/services/aggregate/billing.aggregate.service.d.ts.map +1 -0
  96. package/dist/services/aggregate/billing.aggregate.service.js +308 -0
  97. package/dist/services/aggregate/billing.aggregate.service.mjs +306 -0
  98. package/dist/services/aggregate/index.d.ts +3 -0
  99. package/dist/services/aggregate/index.d.ts.map +1 -0
  100. package/dist/services/aggregate/index.js +9 -0
  101. package/dist/services/aggregate/index.mjs +2 -0
  102. package/dist/services/aggregate/user.aggregate.service.d.ts +34 -0
  103. package/dist/services/aggregate/user.aggregate.service.d.ts.map +1 -0
  104. package/dist/services/aggregate/user.aggregate.service.js +136 -0
  105. package/dist/services/aggregate/user.aggregate.service.mjs +133 -0
  106. package/dist/services/context/index.d.ts +2 -0
  107. package/dist/services/context/index.d.ts.map +1 -0
  108. package/dist/services/context/index.js +13 -0
  109. package/dist/services/context/index.mjs +1 -0
  110. package/dist/services/context/user-context-service.d.ts +30 -0
  111. package/dist/services/context/user-context-service.d.ts.map +1 -0
  112. package/dist/services/context/user-context-service.js +170 -0
  113. package/dist/services/context/user-context-service.mjs +162 -0
  114. package/dist/services/database/apilog.service.d.ts +39 -0
  115. package/dist/services/database/apilog.service.d.ts.map +1 -0
  116. package/dist/services/database/apilog.service.js +174 -0
  117. package/dist/services/database/apilog.service.mjs +170 -0
  118. package/dist/services/database/constants.d.ts +73 -0
  119. package/dist/services/database/constants.d.ts.map +1 -0
  120. package/dist/services/database/constants.js +135 -0
  121. package/dist/services/database/constants.mjs +117 -0
  122. package/dist/services/database/credit.service.d.ts +107 -0
  123. package/dist/services/database/credit.service.d.ts.map +1 -0
  124. package/dist/services/database/credit.service.js +515 -0
  125. package/dist/services/database/credit.service.mjs +512 -0
  126. package/dist/services/database/creditAuditLog.service.d.ts +73 -0
  127. package/dist/services/database/creditAuditLog.service.d.ts.map +1 -0
  128. package/dist/services/database/creditAuditLog.service.js +305 -0
  129. package/dist/services/database/creditAuditLog.service.mjs +302 -0
  130. package/dist/services/database/index.d.ts +10 -0
  131. package/dist/services/database/index.d.ts.map +1 -0
  132. package/dist/services/database/index.js +38 -0
  133. package/dist/services/database/index.mjs +8 -0
  134. package/dist/services/database/prisma-model-type.d.ts +3 -0
  135. package/dist/services/database/prisma-model-type.d.ts.map +1 -0
  136. package/dist/services/database/subscription.service.d.ts +48 -0
  137. package/dist/services/database/subscription.service.d.ts.map +1 -0
  138. package/dist/services/database/subscription.service.js +267 -0
  139. package/dist/services/database/subscription.service.mjs +264 -0
  140. package/dist/services/database/transaction.service.d.ts +92 -0
  141. package/dist/services/database/transaction.service.d.ts.map +1 -0
  142. package/dist/services/database/transaction.service.js +326 -0
  143. package/dist/services/database/transaction.service.mjs +323 -0
  144. package/dist/services/database/user.service.d.ts +45 -0
  145. package/dist/services/database/user.service.d.ts.map +1 -0
  146. package/dist/services/database/user.service.js +180 -0
  147. package/dist/services/database/user.service.mjs +177 -0
  148. package/dist/services/database/userBackup.service.d.ts +45 -0
  149. package/dist/services/database/userBackup.service.d.ts.map +1 -0
  150. package/dist/services/database/userBackup.service.js +249 -0
  151. package/dist/services/database/userBackup.service.mjs +246 -0
  152. package/dist/services/stripe/index.d.ts +2 -0
  153. package/dist/services/stripe/index.d.ts.map +1 -0
  154. package/dist/services/stripe/index.js +7 -0
  155. package/dist/services/stripe/index.mjs +1 -0
  156. package/dist/services/stripe/webhook-handler.d.ts +6 -0
  157. package/dist/services/stripe/webhook-handler.d.ts.map +1 -0
  158. package/dist/services/stripe/webhook-handler.js +537 -0
  159. package/dist/services/stripe/webhook-handler.mjs +535 -0
  160. package/migrations/create.sql +176 -0
  161. package/migrations/db.init.sql +13 -0
  162. package/migrations/init-schema.sql +19 -0
  163. package/migrations/purge.sql +27 -0
  164. package/migrations/test-check.sql +167 -0
  165. package/package.json +123 -0
  166. package/prisma/schema.prisma +191 -0
  167. package/src/app/api/stripe/checkout/route.ts +145 -0
  168. package/src/app/api/stripe/customer-portal/route.ts +83 -0
  169. package/src/app/api/user/anonymous/init/route.ts +284 -0
  170. package/src/app/api/webhook/clerk/user/route.ts +249 -0
  171. package/src/app/api/webhook/stripe/route.ts +93 -0
  172. package/src/index.ts +6 -0
  173. package/src/lib/auth-utils.ts +101 -0
  174. package/src/lib/credit-init.ts +9 -0
  175. package/src/lib/index.ts +4 -0
  176. package/src/lib/money-price-config.ts +168 -0
  177. package/src/lib/stripe-config.ts +333 -0
  178. package/src/prisma/client.ts +2 -0
  179. package/src/prisma/index.ts +3 -0
  180. package/src/prisma/prisma-transaction-util.ts +24 -0
  181. package/src/prisma/prisma.ts +122 -0
  182. package/src/services/aggregate/billing.aggregate.service.ts +498 -0
  183. package/src/services/aggregate/index.ts +2 -0
  184. package/src/services/aggregate/user.aggregate.service.ts +168 -0
  185. package/src/services/context/index.ts +1 -0
  186. package/src/services/context/user-context-service.ts +200 -0
  187. package/src/services/database/apilog.service.ts +185 -0
  188. package/src/services/database/constants.ts +148 -0
  189. package/src/services/database/credit.service.ts +747 -0
  190. package/src/services/database/creditAuditLog.service.ts +402 -0
  191. package/src/services/database/index.ts +41 -0
  192. package/src/services/database/prisma-model-type.ts +13 -0
  193. package/src/services/database/subscription.service.ts +319 -0
  194. package/src/services/database/transaction.service.ts +447 -0
  195. package/src/services/database/user.service.ts +218 -0
  196. package/src/services/database/userBackup.service.ts +290 -0
  197. package/src/services/stripe/index.ts +1 -0
  198. package/src/services/stripe/webhook-handler.ts +648 -0
@@ -0,0 +1,319 @@
1
+ import { SubscriptionStatus } from './constants';
2
+ import { checkAndFallbackWithNonTCClient } from '../../prisma/index';
3
+ import type { Prisma, Subscription } from './prisma-model-type';
4
+
5
+ export class SubscriptionService {
6
+
7
+ /**
8
+ * Initialize a placeholder subscription record for new users
9
+ * This allows Stripe webhook handlers to UPDATE instead of CREATE,
10
+ * ensuring consistent logic across all subscription scenarios.
11
+ *
12
+ * The record will be updated once the user subscribes via Stripe.
13
+ *
14
+ * @param userId - The user ID to initialize subscription for
15
+ * @returns The created placeholder subscription record
16
+ */
17
+ async initializeSubscription(userId: string, tx?: Prisma.TransactionClient): Promise<Subscription> {
18
+ const client = checkAndFallbackWithNonTCClient(tx);
19
+
20
+ return await client.subscription.create({
21
+ data: {
22
+ userId,
23
+ status: SubscriptionStatus.INCOMPLETE,
24
+ creditsAllocated: 0,
25
+ },
26
+ });
27
+ }
28
+
29
+ // Create a new subscription
30
+ async createSubscription(data: {
31
+ userId: string;
32
+ orderId: string;
33
+ paySubscriptionId?: string;
34
+ priceId?: string;
35
+ priceName?: string;
36
+ status?: string;
37
+ creditsAllocated: number;
38
+ subPeriodStart?: Date;
39
+ subPeriodEnd?: Date;
40
+ }, tx?: Prisma.TransactionClient): Promise<Subscription> {
41
+ const client = checkAndFallbackWithNonTCClient(tx);
42
+
43
+ return await client.subscription.create({
44
+ data: {
45
+ userId: data.userId,
46
+ orderId: data.orderId,
47
+ paySubscriptionId: data.paySubscriptionId,
48
+ priceId: data.priceId,
49
+ priceName: data.priceName,
50
+ status: data.status || SubscriptionStatus.INCOMPLETE,
51
+ creditsAllocated: data.creditsAllocated,
52
+ subPeriodStart: data.subPeriodStart,
53
+ subPeriodEnd: data.subPeriodEnd,
54
+ }
55
+ });
56
+ }
57
+
58
+ // Find subscription by pay subscription ID
59
+ async findByPaySubscriptionId(
60
+ paySubscriptionId: string,
61
+ tx?: Prisma.TransactionClient
62
+ ): Promise<Subscription | null> {
63
+ const client = checkAndFallbackWithNonTCClient(tx);
64
+
65
+ return await client.subscription.findFirst({
66
+ where: { paySubscriptionId, deleted: 0 }
67
+ });
68
+ }
69
+
70
+ // Get user's active subscription
71
+ async getActiveSubscription(userId: string, tx?: Prisma.TransactionClient): Promise<Subscription | null> {
72
+ const client = checkAndFallbackWithNonTCClient(tx);
73
+
74
+ return await client.subscription.findUnique({
75
+ where: {
76
+ userId,
77
+ status: SubscriptionStatus.ACTIVE,
78
+ deleted: 0
79
+ }
80
+ });
81
+ }
82
+
83
+ async getNonActiveSubscription(userId: string, tx?: Prisma.TransactionClient): Promise<Subscription | null> {
84
+ const client = checkAndFallbackWithNonTCClient(tx);
85
+
86
+ return await client.subscription.findUnique({
87
+ where: {
88
+ userId,
89
+ status: { not: SubscriptionStatus.ACTIVE },
90
+ deleted: 0
91
+ }
92
+ });
93
+ }
94
+
95
+
96
+ // Update subscription
97
+ async updateSubscription(
98
+ id: bigint,
99
+ data: Prisma.SubscriptionUpdateInput,
100
+ tx?: Prisma.TransactionClient
101
+ ): Promise<Subscription> {
102
+ const client = checkAndFallbackWithNonTCClient(tx);
103
+
104
+ return await client.subscription.update({
105
+ where: { id },
106
+ data,
107
+ });
108
+ }
109
+
110
+ // Update subscription status
111
+ async updateStatus(
112
+ id: bigint,
113
+ status: string,
114
+ tx?: Prisma.TransactionClient
115
+ ): Promise<Subscription> {
116
+ const client = checkAndFallbackWithNonTCClient(tx);
117
+
118
+ return await client.subscription.update({
119
+ where: { id },
120
+ data: { status },
121
+ });
122
+ }
123
+
124
+ // Update subscription period
125
+ async updatePeriod(
126
+ id: bigint,
127
+ subPeriodStart: Date,
128
+ subPeriodEnd: Date,
129
+ tx?: Prisma.TransactionClient
130
+ ): Promise<Subscription> {
131
+ const client = checkAndFallbackWithNonTCClient(tx);
132
+
133
+ return await client.subscription.update({
134
+ where: { id },
135
+ data: {
136
+ subPeriodStart,
137
+ subPeriodEnd,
138
+ },
139
+ });
140
+ }
141
+
142
+ // Cancel subscription
143
+ async cancelSubscription(
144
+ id: bigint,
145
+ cancelAtPeriodEnd: boolean = true,
146
+ tx?: Prisma.TransactionClient
147
+ ): Promise<Subscription> {
148
+ const updateData: Prisma.SubscriptionUpdateInput = {
149
+ status: SubscriptionStatus.CANCELED,
150
+ };
151
+
152
+ if (!cancelAtPeriodEnd) {
153
+ updateData.subPeriodEnd = new Date();
154
+ }
155
+
156
+ const client = checkAndFallbackWithNonTCClient(tx);
157
+
158
+ return await client.subscription.update({
159
+ where: { id },
160
+ data: updateData,
161
+ });
162
+ }
163
+
164
+ // Renew subscription
165
+ async renewSubscription(
166
+ id: bigint,
167
+ newPeriodEnd: Date,
168
+ creditsToAdd?: number,
169
+ tx?: Prisma.TransactionClient
170
+ ): Promise<Subscription> {
171
+ const client = checkAndFallbackWithNonTCClient(tx);
172
+
173
+ const subscription = await client.subscription.findFirst({
174
+ where: { id },
175
+ });
176
+
177
+ if (!subscription) {
178
+ throw new Error('Subscription not found');
179
+ }
180
+
181
+ return await client.subscription.update({
182
+ where: { id },
183
+ data: {
184
+ status: SubscriptionStatus.ACTIVE,
185
+ subPeriodStart: subscription.subPeriodEnd || new Date(),
186
+ subPeriodEnd: newPeriodEnd,
187
+ creditsAllocated: creditsToAdd
188
+ ? subscription.creditsAllocated + creditsToAdd
189
+ : subscription.creditsAllocated,
190
+ },
191
+ });
192
+ }
193
+
194
+ // Soft Delete subscription
195
+ async deleteSubscription(id: bigint, tx?: Prisma.TransactionClient): Promise<void> {
196
+ const client = checkAndFallbackWithNonTCClient(tx);
197
+
198
+ await client.subscription.update({
199
+ where: { id },
200
+ data: { deleted: 1, status: SubscriptionStatus.INCOMPLETE },
201
+ });
202
+ }
203
+
204
+ // Get expiring subscriptions (within 7 days)
205
+ async getExpiringSubscriptions(days: number = 7, tx?: Prisma.TransactionClient): Promise<Subscription[]> {
206
+ const now = new Date();
207
+ const expiryDate = new Date();
208
+ expiryDate.setDate(expiryDate.getDate() + days);
209
+
210
+ const client = checkAndFallbackWithNonTCClient(tx);
211
+
212
+ return await client.subscription.findMany({
213
+ where: {
214
+ status: SubscriptionStatus.ACTIVE,
215
+ deleted: 0,
216
+ subPeriodEnd: {
217
+ gte: now,
218
+ lte: expiryDate,
219
+ },
220
+ }
221
+ });
222
+ }
223
+
224
+ // Get expired subscriptions
225
+ async getExpiredSubscriptions(tx?: Prisma.TransactionClient): Promise<Subscription[]> {
226
+ const client = checkAndFallbackWithNonTCClient(tx);
227
+
228
+ return await client.subscription.findMany({
229
+ where: {
230
+ status: SubscriptionStatus.ACTIVE,
231
+ deleted: 0,
232
+ subPeriodEnd: {
233
+ lt: new Date(),
234
+ },
235
+ }
236
+ });
237
+ }
238
+
239
+ // Update expired subscriptions status
240
+ async updateExpiredSubscriptions(tx?: Prisma.TransactionClient): Promise<number> {
241
+ const client = checkAndFallbackWithNonTCClient(tx);
242
+
243
+ const result = await client.subscription.updateMany({
244
+ where: {
245
+ status: SubscriptionStatus.ACTIVE,
246
+ deleted: 0,
247
+ subPeriodEnd: {
248
+ lt: new Date(),
249
+ },
250
+ },
251
+ data: {
252
+ status: SubscriptionStatus.PAST_DUE,
253
+ },
254
+ });
255
+
256
+ return result.count;
257
+ }
258
+
259
+ // Get subscription statistics
260
+ async getSubscriptionStats(tx?: Prisma.TransactionClient): Promise<{
261
+ total: number;
262
+ active: number;
263
+ canceled: number;
264
+ pastDue: number;
265
+ incomplete: number;
266
+ trialing: number;
267
+ revenue: number;
268
+ }> {
269
+ const client = checkAndFallbackWithNonTCClient(tx);
270
+ const [total, active, canceled, pastDue, incomplete, trialing] =
271
+ await Promise.all([
272
+ client.subscription.count({ where: { deleted: 0 } }),
273
+ client.subscription.count({
274
+ where: { status: SubscriptionStatus.ACTIVE, deleted: 0 }
275
+ }),
276
+ client.subscription.count({
277
+ where: { status: SubscriptionStatus.CANCELED, deleted: 0 }
278
+ }),
279
+ client.subscription.count({
280
+ where: { status: SubscriptionStatus.PAST_DUE, deleted: 0 }
281
+ }),
282
+ client.subscription.count({
283
+ where: { status: SubscriptionStatus.INCOMPLETE, deleted: 0 }
284
+ }),
285
+ client.subscription.count({
286
+ where: { status: SubscriptionStatus.TRIALING, deleted: 0 }
287
+ }),
288
+ ]);
289
+
290
+ // Calculate active subscription revenue (need to combine with transaction table)
291
+ const activeSubscriptions = await client.subscription.findMany({
292
+ where: { status: SubscriptionStatus.ACTIVE, deleted: 0 },
293
+ select: { paySubscriptionId: true },
294
+ });
295
+
296
+ let revenue = 0;
297
+ if (activeSubscriptions.length > 0) {
298
+ const transactions = await client.transaction.findMany({
299
+ where: {
300
+ paySubscriptionId: {
301
+ in: activeSubscriptions
302
+ .map(s => s.paySubscriptionId)
303
+ .filter(Boolean) as string[],
304
+ },
305
+ orderStatus: 'success',
306
+ },
307
+ select: { amount: true },
308
+ });
309
+
310
+ revenue = transactions.reduce((sum, t) =>
311
+ sum + (t.amount ? parseFloat(t.amount.toString()) : 0), 0
312
+ );
313
+ }
314
+
315
+ return { total, active, canceled, pastDue, incomplete, trialing, revenue };
316
+ }
317
+ }
318
+
319
+ export const subscriptionService = new SubscriptionService();