@windrun-huaiin/backend-core 30.0.0 → 31.0.0

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 (51) hide show
  1. package/README.md +95 -0
  2. package/dist/app/api/user/anonymous/init/route.d.ts +1 -1
  3. package/dist/app/api/user/anonymous/init/route.d.ts.map +1 -1
  4. package/dist/app/api/user/anonymous/init/route.js +18 -19
  5. package/dist/app/api/user/anonymous/init/route.mjs +18 -19
  6. package/dist/app/api/webhook/clerk/user/route.js +16 -16
  7. package/dist/app/api/webhook/clerk/user/route.mjs +16 -16
  8. package/dist/auth/auth-utils.d.ts +8 -23
  9. package/dist/auth/auth-utils.d.ts.map +1 -1
  10. package/dist/auth/auth-utils.js +8 -20
  11. package/dist/auth/auth-utils.mjs +8 -20
  12. package/dist/lib/money-price-config.d.ts +28 -28
  13. package/dist/lib/money-price-config.js +31 -31
  14. package/dist/lib/money-price-config.mjs +31 -31
  15. package/dist/lib/stripe-config.js +3 -3
  16. package/dist/lib/stripe-config.mjs +3 -3
  17. package/dist/prisma/prisma-transaction-util.js +1 -1
  18. package/dist/prisma/prisma-transaction-util.mjs +1 -1
  19. package/dist/prisma/prisma.d.ts.map +1 -1
  20. package/dist/prisma/prisma.js +18 -19
  21. package/dist/prisma/prisma.mjs +18 -19
  22. package/dist/services/aggregate/billing.aggregate.service.js +6 -6
  23. package/dist/services/aggregate/billing.aggregate.service.mjs +6 -6
  24. package/dist/services/aggregate/user.aggregate.service.d.ts +9 -9
  25. package/dist/services/aggregate/user.aggregate.service.js +16 -16
  26. package/dist/services/aggregate/user.aggregate.service.mjs +16 -16
  27. package/dist/services/database/constants.js +34 -34
  28. package/dist/services/database/constants.mjs +34 -34
  29. package/dist/services/database/credit.service.js +2 -2
  30. package/dist/services/database/credit.service.mjs +2 -2
  31. package/dist/services/database/transaction.service.js +1 -1
  32. package/dist/services/database/transaction.service.mjs +1 -1
  33. package/dist/services/database/user.service.js +2 -2
  34. package/dist/services/database/user.service.mjs +2 -2
  35. package/dist/services/stripe/webhook-handler.js +5 -5
  36. package/dist/services/stripe/webhook-handler.mjs +5 -5
  37. package/package.json +13 -6
  38. package/src/app/api/user/anonymous/init/route.ts +21 -22
  39. package/src/app/api/webhook/clerk/user/route.ts +17 -17
  40. package/src/auth/auth-utils.ts +8 -23
  41. package/src/lib/money-price-config.ts +31 -32
  42. package/src/lib/stripe-config.ts +3 -3
  43. package/src/prisma/prisma-transaction-util.ts +1 -1
  44. package/src/prisma/prisma.ts +18 -19
  45. package/src/services/aggregate/billing.aggregate.service.ts +7 -7
  46. package/src/services/aggregate/user.aggregate.service.ts +16 -16
  47. package/src/services/database/constants.ts +34 -34
  48. package/src/services/database/credit.service.ts +2 -2
  49. package/src/services/database/transaction.service.ts +1 -1
  50. package/src/services/database/user.service.ts +2 -2
  51. package/src/services/stripe/webhook-handler.ts +5 -5
@@ -97,7 +97,7 @@ export const createCheckoutSession = async (
97
97
 
98
98
  // One-time payment specific configuration
99
99
  if (isSubscriptionMode) {
100
- // 在这里注入订单元数据,以保证后续事件处理能根据订单去匹配处理,只能在订阅模式里设置数据,否则Stripe报错
100
+ // Attach order metadata for later event matching. Stripe only allows this in subscription mode.
101
101
  sessionParams.subscription_data = subscriptionData;
102
102
  } else {
103
103
  // One-time payments don't create invoices
@@ -129,7 +129,7 @@ export const createCheckoutSession = async (
129
129
  }
130
130
  };
131
131
 
132
- // 根据发票ID去查支付ID
132
+ // Resolve the payment ID from an invoice ID.
133
133
  export const fetchPaymentId = async (invoiceId: string ): Promise<string> => {
134
134
  const fullInvoice = await getStripe().invoices.retrieve(invoiceId, {
135
135
  expand: ['payments']
@@ -193,7 +193,7 @@ export const createOrGetCustomer = async (params: {
193
193
  }
194
194
  }
195
195
 
196
- // 创建新客户
196
+ // Create a new customer.
197
197
  const customerParams: Stripe.CustomerCreateParams = {
198
198
  metadata: {
199
199
  user_id: userId,
@@ -1,7 +1,7 @@
1
1
  import { getBackendCorePrisma } from './prisma';
2
2
  import type { Prisma } from '@core/db/prisma-model-type';
3
3
 
4
- // 事务工具类,回滚时打印回滚日志
4
+ // Transaction helper that logs rollback details on failure.
5
5
  export async function runInTransaction<T>(
6
6
  fn: (tx: Prisma.TransactionClient) => Promise<T>,
7
7
  operationName?: string
@@ -41,7 +41,7 @@ const globalForPrisma = globalThis as unknown as {
41
41
  __prisma_ssl_warning_logged?: boolean;
42
42
  };
43
43
 
44
- // ==================== 日志配置 ====================
44
+ // ==================== Logging Configuration ====================
45
45
  const getLogConfig = () => {
46
46
  if (process.env.PRISMA_DEBUG === 'true') {
47
47
  return [
@@ -125,42 +125,41 @@ function registerDevelopmentQueryLogger(prismaClient: BackendCoreHostPrismaClien
125
125
  globalForPrisma[ID_KEY] = listenerId;
126
126
  console.log(`Prisma Query Logger Registered | Listener ID: ${listenerId} | Instance ID: ${instanceId}`);
127
127
 
128
- // --- 自定义SQL拼接 ---
128
+ // --- Custom SQL interpolation ---
129
129
  const interpolate = (query: string, params: string) => {
130
- // 1. 【核心修改】:安全检查和参数解析
130
+ // 1. Validate and parse parameters safely.
131
131
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
132
132
  let parameters: any[] = [];
133
133
  try {
134
- // 尝试解析 params 字符串
135
- // 如果 params 是空字符串 "",或者不是有效的 JSON,这里会捕获错误
134
+ // Parse the params string. Empty strings or invalid JSON are handled by the catch block.
136
135
  parameters = params && params.length > 0 ? JSON.parse(params) : [];
137
136
  // eslint-disable-next-line unused-imports/no-unused-vars
138
137
  } catch (e) {
139
- // 如果无法解析,则直接返回原始查询,跳过替换
138
+ // If parsing fails, return the original query without interpolation.
140
139
  return query;
141
140
  }
142
141
 
143
- // 确保 parameters 是一个数组
142
+ // Ensure parameters is an array.
144
143
  if (!Array.isArray(parameters)) {
145
- console.warn('Prisma params解析结果不是数组,跳过参数替换。Result:', parameters);
144
+ console.warn('Prisma params did not parse to an array; skipping parameter interpolation. Result:', parameters);
146
145
  return query;
147
146
  }
148
147
 
149
- // 如果没有参数,直接返回查询
148
+ // If there are no parameters, return the query as-is.
150
149
  if (parameters.length === 0) {
151
150
  return query;
152
151
  }
153
152
 
154
- // 2. 将参数列表的值进行安全的字符串化处理
153
+ // 2. Safely stringify parameter values.
155
154
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
156
155
  const safeValues = parameters.map((p: any) => {
157
156
  if (p === null) return 'NULL';
158
- // 对字符串类型的值加上单引号并转义(这是SQL安全的关键)
157
+ // Quote and escape string values for readable SQL logging.
159
158
  if (typeof p === 'string') return `'${p.replace(/'/g, "''")}'`;
160
- return p; // 数字、布尔值等直接返回
159
+ return p; // Numbers, booleans, and similar values can be returned directly.
161
160
  });
162
161
 
163
- // 3. 循环替换 $1, $2, ...
162
+ // 3. Replace $1, $2, ... placeholders.
164
163
  let sql = query;
165
164
  for (let i = 0; i < safeValues.length; i++) {
166
165
  const placeholder = new RegExp('\\$' + (i + 1) + '(?!\\d)', 'g');
@@ -176,23 +175,23 @@ function registerDevelopmentQueryLogger(prismaClient: BackendCoreHostPrismaClien
176
175
  const interpolatedSql = interpolate(event.query, event.params);
177
176
 
178
177
  const clean = interpolatedSql
179
- .replace(/"[^"]+"\./g, '') // "".
180
- .replace(/= '([^']+)'/g, `= '$1'`) // 已经替换成单引号,此处可以优化
181
- .replace(/"/g, ''); // 彻底灭双引号
178
+ .replace(/"[^"]+"\./g, '') // Remove "table". prefixes.
179
+ .replace(/= '([^']+)'/g, `= '$1'`) // Keep normalized quoted values.
180
+ .replace(/"/g, ''); // Remove remaining double quotes.
182
181
 
183
182
  console.log('─'.repeat(60));
184
183
  console.log(`Prisma Instance ID: ${instanceId} | Listener ID: ${listenerId}`);
185
184
  console.log(`${clean};`);
186
- console.log(`⏰ 耗时: ${ms}ms, ${slow}`);
185
+ console.log(`Duration: ${ms}ms, ${slow}`);
187
186
  };
188
- // 注册包装后的 handler
187
+ // Register the wrapped handler.
189
188
  prismaClient.$on?.('query' as never, wrappedHandler);
190
189
 
191
190
  globalForPrisma[REGISTERED_KEY] = true;
192
191
  }
193
192
  }
194
193
 
195
- // ==================== 便捷方法, 入参事务客户端不存在或者不传, 就返回全局非事务客户端 ====================
194
+ // ==================== Client Helper: fall back to the global non-transaction client when no transaction client is provided ====================
196
195
  export function checkAndFallbackWithNonTCClient(tx?: Prisma.TransactionClient): Prisma.TransactionClient | BackendCorePrismaClient {
197
196
  return tx ?? getBackendCorePrisma();
198
197
  }
@@ -35,7 +35,7 @@ type BasicOrderContext = {
35
35
  };
36
36
 
37
37
  type RenewalOrderContext = BasicOrderContext & {
38
- // 续订时以Stripe的发票价格为准
38
+ // Use Stripe invoice price as the source of truth for renewal
39
39
  amountPaidCents: number;
40
40
  currency: string;
41
41
  }
@@ -64,7 +64,7 @@ class BillingAggregateService {
64
64
  ): Promise<void> {
65
65
  await runInTransaction(async (tx) => {
66
66
  const now = new Date();
67
- // 订阅截止时间统一为到期日最后1s
67
+ // Set subscription expiry to the last second of the expiration date
68
68
  const originSubPeriodEnd = context.periodEnd;
69
69
  const specialEnd = originSubPeriodEnd ? new Date(originSubPeriodEnd.setHours(23, 59, 59, 999)) : originSubPeriodEnd;
70
70
  const updatedSubscription = await subscriptionService.updateSubscription(
@@ -237,7 +237,7 @@ class BillingAggregateService {
237
237
  },
238
238
  tx
239
239
  );
240
- // 订阅截止时间统一为到期日最后1s
240
+ // Set subscription expiry to the last second of the expiration date
241
241
  const originSubPeriodEnd = context.periodEnd;
242
242
  const specialEnd = originSubPeriodEnd ? new Date(originSubPeriodEnd.setHours(23, 59, 59, 999)) : originSubPeriodEnd;
243
243
 
@@ -365,7 +365,7 @@ class BillingAggregateService {
365
365
  ): Promise<void> {
366
366
  await runInTransaction(async (tx) => {
367
367
  if (params.isUserCancel) {
368
- // 记录用户取消订阅的时间信息
368
+ // Record the time when the user unsubscribes
369
369
  await transactionService.update(
370
370
  params.orderId,
371
371
  {
@@ -391,7 +391,7 @@ class BillingAggregateService {
391
391
  context: SubscriptionCancelContext
392
392
  ): Promise<void> {
393
393
  await runInTransaction(async (tx) => {
394
- // 更新订单, 记录取消信息
394
+ // Update the order and log cancellation details
395
395
  await transactionService.update(
396
396
  context.orderId,
397
397
  {
@@ -400,10 +400,10 @@ class BillingAggregateService {
400
400
  },
401
401
  tx
402
402
  )
403
- // 更新订阅信息
403
+ // Update subscription information
404
404
  await subscriptionService.updateStatus(context.subIdKey, SubscriptionStatus.CANCELED, tx);
405
405
 
406
- // 清理积分并留痕
406
+ // Clear credits and keep audit trail
407
407
  await creditService.purgePaidCredit(context.userId, 'cancel_subscription_purge', context.orderId, tx);
408
408
  })
409
409
  }
@@ -40,17 +40,17 @@ export class UserAggregateService {
40
40
  }
41
41
 
42
42
  /**
43
- * 创建新的注册用户
43
+ * Create a new registered user
44
44
  *
45
- * 初始化步骤(与 credit 平行):
46
- * 1. 创建 User 记录
47
- * 2. 初始化 Credit 记录(免费积分)
48
- * 3. 初始化 Subscription 记录(占位符,status=INCOMPLETE
49
- * 4. 记录 CreditUsage(审计)
45
+ * Initialization steps (parallel to credit):
46
+ * 1. Create User record
47
+ * 2. Initialize Credit record (free credits)
48
+ * 3. Initialize Subscription record (placeholder, status=INCOMPLETE)
49
+ * 4. Record CreditUsage (audit)
50
50
  *
51
- * 后续当用户通过 Stripe 订阅时:
52
- * - session.completed invoice.paid UPDATE subscription 记录
53
- * - 不需要 CREATE,只需 UPDATE 确保逻辑一致
51
+ * When the user subscribes via Stripe later:
52
+ * - session.completed or invoice.paid will UPDATE the subscription record
53
+ * - No CREATE needed, only UPDATE to ensure logical consistency
54
54
  */
55
55
  async createNewRegisteredUser(
56
56
  clerkUserId: string,
@@ -89,7 +89,7 @@ export class UserAggregateService {
89
89
  });
90
90
  }
91
91
 
92
- // 注意积分审查日志的处理
92
+ // Note: Handle credit review logs
93
93
  async upgradeToRegistered(
94
94
  userId: string,
95
95
  email: string,
@@ -107,9 +107,9 @@ export class UserAggregateService {
107
107
  tx
108
108
  );
109
109
 
110
- // 先清空匿名积分并审计日志留痕
110
+ // Clear anonymous credits first and audit for traceability
111
111
  await creditService.purgeFreeCredit(userId, 'user_registered_purge', userId, tx);
112
- // 再初始化完成注册获得免费积分
112
+ // Then initialize free credits upon successful registration
113
113
  const credit = await creditService.initializeCreditWithFree(
114
114
  {
115
115
  userId: updateUser.userId,
@@ -128,21 +128,21 @@ export class UserAggregateService {
128
128
 
129
129
  async handleUserUnregister(clerkUserId: string): Promise<string | null> {
130
130
  return runInTransaction(async (tx) => {
131
- // 根据clerkUserId查找用户
131
+ // query DB user
132
132
  const user = await userService.findByClerkUserId(clerkUserId, tx);
133
133
  if (!user) {
134
134
  console.log(`User with clerkUserId ${clerkUserId} not found`);
135
135
  return null;
136
136
  }
137
137
  const userId = user.userId;
138
- // 更改用户状态,保留user信息尤其是FingerprintId,防止反复注册薅羊毛
138
+ // Update user status and retain user info (especially FingerprintId) to prevent repeated registration abuse
139
139
  await userService.unregister(user.userId, tx);
140
- // 清空积分
140
+ // Clear credits
141
141
  await creditService.purgeCredit(userId, 'soft_delete_user', userId, tx);
142
142
 
143
143
  const subscription = await subscriptionService.getActiveSubscription(userId, tx);
144
144
  if (subscription) {
145
- // 如果有订阅信息,则要更新
145
+ // Update subscription info if it exists
146
146
  await subscriptionService.cancelSubscription(subscription.id, true, tx);
147
147
  }
148
148
 
@@ -2,80 +2,80 @@
2
2
  // Keep in sync with DB CHECK constraints
3
3
 
4
4
  export const UserStatus = {
5
- // 匿名用户
5
+ // Anonymous user
6
6
  ANONYMOUS: 'anonymous',
7
- // 注册用户
7
+ // Registered user
8
8
  REGISTERED: 'registered',
9
- // 管理员介入管控
9
+ // Frozen by admin intervention
10
10
  FROZEN: 'frozen',
11
- // 用户注销,数据软删除,用户数据将不可复用
11
+ // Soft-deleted user data that must not be reused
12
12
  DELETED: 'deleted',
13
13
  } as const;
14
14
 
15
15
  export const SubscriptionStatus = {
16
- // 初始状态或注销后状态
16
+ // Initial or post-cancellation state
17
17
  INCOMPLETE: 'incomplete',
18
- // 订阅试用期
18
+ // Trial subscription period
19
19
  TRIALING: 'trialing',
20
- // 有效订阅激活
20
+ // Active subscription
21
21
  ACTIVE: 'active',
22
- // 订阅过期
22
+ // Past-due subscription
23
23
  PAST_DUE: 'past_due',
24
- // 取消订阅
24
+ // Canceled subscription
25
25
  CANCELED: 'canceled',
26
26
  } as const;
27
27
 
28
28
  export const OrderStatus = {
29
- // 初始状态
29
+ // Initial state
30
30
  CREATED: 'created',
31
- // 中间状态,待支付,可能是支付失败事件触发
31
+ // Intermediate state, awaiting payment; may be triggered by a payment failure event
32
32
  PENDING_UNPAID: 'pending_unpaid',
33
- // 中间状态(也可是最终状态),支付成功,后续可变为「退款或取消」
33
+ // Intermediate or final state; payment succeeded and may later become refunded or canceled
34
34
  SUCCESS: 'success',
35
- // 中间状态(也可是最终状态),CheckOut失败或者是支付失败,后续可变为「退款或取消」
35
+ // Intermediate or final state; checkout or payment failed and may later become refunded or canceled
36
36
  FAILED: 'failed',
37
- // 最终状态,已退款
37
+ // Final state, refunded
38
38
  REFUNDED: 'refunded',
39
- // 最终状态,已取消
39
+ // Final state, canceled
40
40
  CANCELED: 'canceled',
41
41
  } as const;
42
42
 
43
43
  export const TransactionType = {
44
- // 订阅订单
44
+ // Subscription order
45
45
  SUBSCRIPTION: 'subscription',
46
- // 即付订单
46
+ // One-time payment order
47
47
  ONE_TIME: 'one_time',
48
48
  } as const;
49
49
 
50
50
  export const CreditType = {
51
- // 订阅积分
51
+ // Subscription credits
52
52
  PAID: 'paid',
53
- // 即付积分
53
+ // One-time paid credits
54
54
  ONE_TIME_PAID: 'one_time_paid',
55
- // 免费积分
55
+ // Free credits
56
56
  FREE: 'free',
57
57
  } as const;
58
58
 
59
59
  export const OperationType = {
60
- // 系统奖励积分
60
+ // System-granted credits
61
61
  SYS_GIFT: 'system_gift',
62
- // 用户消费积分
62
+ // User credit consumption
63
63
  CONSUME: 'consume',
64
- // 用户充值积分
64
+ // User credit recharge
65
65
  RECHARGE: 'recharge',
66
- // 管理员介入冻结积分
66
+ // Admin credit freeze
67
67
  FREEZE: 'freeze',
68
- // 管理员介入解冻积分
68
+ // Admin credit unfreeze
69
69
  UNFREEZE: 'unfreeze',
70
- // 管理员介入赠送积分
70
+ // Admin credit increase
71
71
  ADJUST_INCREASE: 'adjust_increase',
72
- // 管理员介入抹去积分
72
+ // Admin credit decrease
73
73
  ADJUST_DECREASE: 'adjust_decrease',
74
- // 清理积分,事件触发或者是积分过期触发
74
+ // Credit purge triggered by an event or expiration
75
75
  PURGE: 'purge',
76
76
  } as const;
77
77
 
78
- // 支付厂商类型
78
+ // Payment provider types
79
79
  export const PaySupplier = {
80
80
  STRIPE: 'Stripe',
81
81
  APPLE: 'Apple',
@@ -83,18 +83,18 @@ export const PaySupplier = {
83
83
  } as const;
84
84
 
85
85
  export const BillingReason = {
86
- // 首次订阅
86
+ // Initial subscription
87
87
  SUBSCRIPTION_CREATE: 'subscription_create',
88
- // 续订
88
+ // Subscription renewal
89
89
  SUBSCRIPTION_CYCLE: 'subscription_cycle',
90
90
  } as const;
91
91
 
92
92
  export const PaymentStatus = {
93
- // 已支付
93
+ // Paid
94
94
  PAID: 'paid',
95
- // 待支付
95
+ // Pending payment
96
96
  UN_PAID: 'un_paid',
97
- // 无需支付
97
+ // No payment required
98
98
  NO_PAYMENT_REQUIRED: 'no_payment_required',
99
99
  } as const;
100
100
 
@@ -300,7 +300,7 @@ export class CreditService {
300
300
  this.ensureNonNegative(normalized, 'initializeCredit');
301
301
  const client = checkAndFallbackWithNonTCClient(tx);
302
302
 
303
- // 这里使用upsert语义是为了代码复用,处理匿名初始化和匿名->注册的初始化
303
+ // Use upsert semantics to share initialization logic for anonymous users and anonymous-to-registered upgrades.
304
304
  const credit = await client.credit.upsert({
305
305
  where: {
306
306
  userId: init.userId
@@ -636,7 +636,7 @@ export class CreditService {
636
636
  data: updateData as Prisma.CreditUpdateInput,
637
637
  });
638
638
 
639
- // 强制留痕,即使是积分变化为0也记录,操作留痕
639
+ // Always write an audit entry, even when the credit change is zero.
640
640
  const usage = await this.recordCreditAuditLog(client, userId, OperationType.PURGE, normalizedDeduction, { feature: reason, operationReferId })
641
641
 
642
642
  return { credit, usage };
@@ -43,7 +43,7 @@ export class TransactionService {
43
43
  orderId: data.orderId,
44
44
  orderStatus: data.orderStatus || OrderStatus.CREATED,
45
45
  paymentStatus: data.paymentStatus || PaymentStatus.UN_PAID,
46
- orderExpiredAt: data.orderExpiredAt || new Date(Date.now() + 30 * 60 * 1000), // 默认30分钟过期
46
+ orderExpiredAt: data.orderExpiredAt || new Date(Date.now() + 30 * 60 * 1000), // Default expiration: 30 minutes
47
47
  paySupplier: data.paySupplier,
48
48
  payTransactionId: data.payTransactionId,
49
49
  paySubscriptionId: data.paySubscriptionId,
@@ -83,7 +83,7 @@ export class UserService {
83
83
  async findByClerkUserId(clerkUserId: string, tx?: Prisma.TransactionClient): Promise<User | null> {
84
84
  const client = checkAndFallbackWithNonTCClient(tx);
85
85
 
86
- // DB的部分索引与这里的状态查询相对应,因而可以使用findUnique
86
+ // Partial DB indexes match this status filter, so findUnique is valid here.
87
87
  return await client.user.findUnique({
88
88
  where: {
89
89
  clerkUserId,
@@ -183,7 +183,7 @@ export class UserService {
183
183
  return { users, total };
184
184
  }
185
185
 
186
- // 批量创建匿名用户
186
+ // Create anonymous users in bulk
187
187
  async createBatchAnonymousUsers(
188
188
  fingerprintIds: string[],
189
189
  tx?: Prisma.TransactionClient
@@ -292,7 +292,7 @@ async function handleInvoicePaid(invoice: Stripe.Invoice) {
292
292
  });
293
293
 
294
294
  if (isInitialPayment) {
295
- // 首次订阅校验
295
+ // check
296
296
  const nonActiveSubscription = await subscriptionService.getNonActiveSubscription(userId);
297
297
  if (!nonActiveSubscription) {
298
298
  throw new Error(`Subscription status is ACTIVE for user ${userId}, forbidden to re-active!`);
@@ -324,7 +324,7 @@ async function handleInvoicePaid(invoice: Stripe.Invoice) {
324
324
  }
325
325
 
326
326
  if (isRenewal) {
327
- // 续订时,一定要查到订阅记录
327
+ // must query it's subscription db record
328
328
  const subscription = await subscriptionService.findByPaySubscriptionId(subscriptionId);
329
329
  if (!subscription) {
330
330
  throw new Error(`Subscription not found for renewal: ${subscriptionId}`);
@@ -337,8 +337,8 @@ async function handleInvoicePaid(invoice: Stripe.Invoice) {
337
337
  }
338
338
 
339
339
  // Get credits from current price configuration (handles plan upgrades/downgrades)
340
- // 优先从配置中取,取不到就以上个周期的为准作为Fallback,后续有问题再人工补偿,优先保证能用
341
- // 只要配置正确,这里就不会出错!
340
+ // Prefer values from configuration; fall back to the previous cycle value if unavailable. Manual remediation will be applied later if issues occur, prioritizing functional availability
341
+ // No error occurs here as long as the configuration is correct!
342
342
  const creditsForRenewal = subscription.priceId
343
343
  ? getCreditsFromPriceId(subscription.priceId)
344
344
  : subscription.creditsAllocated;
@@ -467,7 +467,7 @@ async function handleInvoicePaymentFailed(invoice: Stripe.Invoice) {
467
467
  const subscriptionId = parentDetails.subscription;
468
468
  const subscriptionMetadata = parentDetails.metadata || {};
469
469
 
470
- // 支付ID
470
+ // PaymentIntentId
471
471
  const paymentIntentId = await fetchPaymentId(invoice.id)
472
472
 
473
473
  console.log('Invoice payment failed event key-info:', {