-
Notifications
You must be signed in to change notification settings - Fork 11.6k
feat: auto-recharge-credit-balance #23826
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
@hemantmm is attempting to deploy a commit to the cal Team on Vercel. A member of the Team first needs to authorize it. |
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughAdds end-to-end auto-recharge for credit balances: database schema fields and purchase log changes; stores Stripe customer IDs on checkout completion; repository API refactors and new purchase/expense log methods; CreditService probes and processes auto-recharge, exposes get/update auto-recharge settings; StripeBillingService gains createAutoRechargePaymentIntent; TRPC adds getAutoRechargeSettings and updateAutoRechargeSettings guarded by IS_SMS_CREDITS_ENABLED; web BillingCredits UI shows status and a modal to configure settings; email templates include auto-recharge flags and adjusted subjects/text. Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
packages/features/ee/billing/api/webhook/_checkout.session.completed.ts (1)
40-41: Fix:sessionis out of scope in saveToCreditBalance (breaks on runtime).
saveToCreditBalancereferencessession.customer, butsessiononly exists inhandler. PassstripeCustomerIdfrom the caller and use it inside the function.Apply this diff:
- await saveToCreditBalance({ userId, teamId, nrOfCredits }); + await saveToCreditBalance({ + userId, + teamId, + nrOfCredits, + stripeCustomerId: + typeof session.customer === "string" ? session.customer : session.customer?.id, + });-async function saveToCreditBalance({ - userId, - teamId, - nrOfCredits, -}: { - userId?: number; - teamId?: number; - nrOfCredits: number; -}) { +async function saveToCreditBalance({ + userId, + teamId, + nrOfCredits, + stripeCustomerId, +}: { + userId?: number; + teamId?: number; + nrOfCredits: number; + stripeCustomerId?: string | null; +}) { const creditBalance = await CreditsRepository.findCreditBalance({ teamId, userId }); - - const stripeCustomerId = session.customer as string; + const customerId = stripeCustomerId ?? null;- await CreditsRepository.updateCreditBalance({ + await CreditsRepository.updateCreditBalance({ id: creditBalance.id, - data: { + data: { additionalCredits: { increment: nrOfCredits }, limitReachedAt: null, warningSentAt: null, - stripeCustomerId: stripeCustomerId, // Store customer ID for future auto-recharge + stripeCustomerId: customerId, // Store customer ID for future auto-recharge }, });- const newCreditBalance = await CreditsRepository.createCreditBalance({ + const newCreditBalance = await CreditsRepository.createCreditBalance({ teamId: teamId, userId: !teamId ? userId : undefined, additionalCredits: nrOfCredits, - stripeCustomerId: stripeCustomerId, // Store customer ID for future auto-recharge + stripeCustomerId: customerId, // Store customer ID for future auto-recharge });Also applies to: 56-57, 63-69, 75-76
packages/features/ee/billing/credit-service.ts (1)
404-423: Missing repository method: createCreditExpenseLog — update call or restore methodCreditsRepository no longer exposes createCreditExpenseLog; createCreditPurchaseLog exists in packages/lib/server/repository/credits.ts (≈line 235). Update the call in packages/features/ee/billing/credit-service.ts (lines 404–423) to use the maintained API (or reintroduce createCreditExpenseLog with equivalent behavior/signature).
packages/lib/server/repository/credits.ts (3)
20-35: Use composite unique key (teamId_userId) and explicit null checks.Schema has a composite unique on
(teamId, userId).findUnique({ where: { teamId } })/{ userId }won’t use that index and may fail after the schema change. Also avoid truthy checks for numeric IDs.- if (teamId) { - return await prismaClient.creditBalance.findUnique({ - where: { - teamId, - }, - select, - }); - } - - if (userId) { - return await prismaClient.creditBalance.findUnique({ - where: { userId }, - select, - }); - } + if (teamId != null && userId != null) { + throw new Error("Provide either teamId or userId, not both."); + } + const where = + teamId != null + ? ({ teamId_userId: { teamId, userId: null } } as Prisma.CreditBalanceWhereUniqueInput) + : userId != null + ? ({ teamId_userId: { teamId: null, userId } } as Prisma.CreditBalanceWhereUniqueInput) + : null; + if (!where) return null; + return await prismaClient.creditBalance.findUnique({ where, select });
95-109: Unify to composite unique lookup; remove separateifbranches.Use
(teamId_userId)with explicit nulls. This also avoids truthy checks on IDs.- if (teamId) { - return await prismaClient.creditBalance.findUnique({ - where: { - teamId, - }, - select, - }); - } - - if (userId) { - return await prismaClient.creditBalance.findUnique({ - where: { userId }, - select, - }); - } + if (teamId == null && userId == null) return null; + const where: Prisma.CreditBalanceWhereUniqueInput = { + teamId_userId: { teamId: teamId ?? null, userId: userId ?? null }, + }; + return await prismaClient.creditBalance.findUnique({ where, select });
126-159:findUniqueshould use the composite uniqueteamId_userId.The current
wheremixes single fields. Align with the composite key.- return await prismaClient.creditBalance.findUnique({ - where: { - teamId, - ...(!teamId ? { userId } : {}), - }, + return await prismaClient.creditBalance.findUnique({ + where: { + teamId_userId: { teamId: teamId ?? null, userId: userId ?? null }, + }, select: { additionalCredits: true, expenseLogs: { where: { date: { gte: startDate, lte: endDate, }, ...(creditType ? { creditType } : {}), }, orderBy: { date: "desc", }, select: { date: true, credits: true, creditType: true, bookingUid: true, smsSid: true, smsSegments: true, phoneNumber: true, email: true, callDuration: true, externalRef: true, }, }, }, });
🧹 Nitpick comments (9)
packages/features/ee/billing/api/webhook/_checkout.session.completed.ts (1)
60-85: Optional: wrap balance update + purchase log in a DB transaction.Prevents partial state if logging fails after increment.
packages/prisma/schema.prisma (1)
606-614: Check uniqueness strategy; add helpful indexes.
- You have unique on
userId, unique onteamId, and composite@@unique([teamId, userId]). The composite looks redundant; keep either the two single-column uniques or only the composite, not both, to avoid unnecessary indexes.Apply (if you prefer composite only):
model CreditBalance { id String @id @default(uuid()) - userId Int? @unique - teamId Int? @unique + userId Int? + teamId Int? additionalCredits Int @default(0) limitReachedAt DateTime? warningSentAt DateTime? team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade) user User? @relation(fields: [userId], references: [id], onDelete: Cascade) expenseLogs CreditExpenseLog[] purchaseLogs CreditPurchaseLog[] // Add auto-recharge fields autoRechargeEnabled Boolean @default(false) autoRechargeThreshold Int? autoRechargeAmount Int? stripeCustomerId String? lastAutoRechargeAt DateTime? @@unique([teamId, userId]) + @@index([stripeCustomerId]) + @@index([lastAutoRechargeAt]) }packages/emails/templates/credit-balance-limit-reached-email.ts (1)
62-69: Consider localizing plain-text fallback.
getTextBody()returns hard-coded English. If these are user-facing, considerthis.user.t(...)variants.apps/web/modules/settings/billing/components/BillingCredits.tsx (2)
183-190: Localize link text.Replace hard-coded “Learn more” with
t("learn_more")(or existing key).- Learn more + {t("learn_more")}
140-141: Nit: replace all spaces when generating filename.Current
.replace(" ", "-")changes only the first space.- const filename = `credit-expense-log-${selectedMonth.value.toLowerCase().replace(" ", "-")}.csv`; + const filename = `credit-expense-log-${selectedMonth.value.toLowerCase().replace(/\s+/g, "-")}.csv`;packages/features/ee/billing/credit-service.ts (1)
755-816: Consider basic throttling for auto‑recharge.To avoid multiple quick consecutive charges, gate by
lastAutoRechargeAt(e.g., min interval).- if (remainingCredits < creditBalance.autoRechargeThreshold) { + const minIntervalMs = 10 * 60 * 1000; // 10 minutes + const recent = creditBalance.lastAutoRechargeAt + ? Date.now() - new Date(creditBalance.lastAutoRechargeAt).getTime() < minIntervalMs + : false; + if (!recent && remainingCredits < creditBalance.autoRechargeThreshold) {packages/lib/server/repository/credits.ts (3)
63-94: Potential overfetch/PII fromteam.members.userselection.You’re selecting
localefor all team members. If only a subset is needed for notifications, consider trimming to the minimal fields to reduce payload and PII exposure.
116-118: Timezone semantics for defaultstartDate.
dayjs().startOf("month").toDate()is local-timezone based. Confirm this matches reporting expectations (vs. UTC boundaries).
122-122: Avoid falsy checks for IDs.
if (!teamId && !userId)treats0as absent. Use explicit null checks.- if (!teamId && !userId) return null; + if (teamId == null && userId == null) return null;
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (9)
apps/web/modules/settings/billing/components/BillingCredits.tsx(8 hunks)packages/emails/templates/credit-balance-limit-reached-email.ts(2 hunks)packages/emails/templates/credit-balance-low-warning-email.ts(3 hunks)packages/features/ee/billing/api/webhook/_checkout.session.completed.ts(1 hunks)packages/features/ee/billing/credit-service.ts(2 hunks)packages/features/ee/billing/stripe-billling-service.ts(4 hunks)packages/lib/server/repository/credits.ts(2 hunks)packages/prisma/schema.prisma(1 hunks)packages/trpc/server/routers/viewer/credits.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
**/*.ts: For Prisma queries, only select data you need; never useinclude, always useselect
Ensure thecredential.keyfield is never returned from tRPC endpoints or APIs
Files:
packages/features/ee/billing/api/webhook/_checkout.session.completed.tspackages/emails/templates/credit-balance-limit-reached-email.tspackages/emails/templates/credit-balance-low-warning-email.tspackages/lib/server/repository/credits.tspackages/features/ee/billing/credit-service.tspackages/features/ee/billing/stripe-billling-service.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js
.utc()in hot paths like loops
Files:
packages/features/ee/billing/api/webhook/_checkout.session.completed.tspackages/emails/templates/credit-balance-limit-reached-email.tspackages/trpc/server/routers/viewer/credits.tsxpackages/emails/templates/credit-balance-low-warning-email.tsapps/web/modules/settings/billing/components/BillingCredits.tsxpackages/lib/server/repository/credits.tspackages/features/ee/billing/credit-service.tspackages/features/ee/billing/stripe-billling-service.ts
**/*.{ts,tsx,js,jsx}
⚙️ CodeRabbit configuration file
Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.
Files:
packages/features/ee/billing/api/webhook/_checkout.session.completed.tspackages/emails/templates/credit-balance-limit-reached-email.tspackages/trpc/server/routers/viewer/credits.tsxpackages/emails/templates/credit-balance-low-warning-email.tsapps/web/modules/settings/billing/components/BillingCredits.tsxpackages/lib/server/repository/credits.tspackages/features/ee/billing/credit-service.tspackages/features/ee/billing/stripe-billling-service.ts
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
Always use
t()for text localization in frontend code; direct text embedding should trigger a warning
Files:
packages/trpc/server/routers/viewer/credits.tsxapps/web/modules/settings/billing/components/BillingCredits.tsx
🧠 Learnings (2)
📚 Learning: 2025-08-17T22:00:16.329Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts:117-126
Timestamp: 2025-08-17T22:00:16.329Z
Learning: In calcom/cal.com PR #22995, packages/trpc/server/routers/viewer/aiVoiceAgent/_router.ts, the enabled input parameter in the update endpoint is intentionally not forwarded to aiService.updateAgentConfiguration() as the enabled/disabled agent functionality is not required at the moment (per maintainer Udit-takkar). Future reviews should not flag this as missing functionality unless requirements change.
Applied to files:
packages/trpc/server/routers/viewer/credits.tsx
📚 Learning: 2025-08-14T10:48:52.586Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/trpc/server/routers/viewer/ai/_router.ts:46-84
Timestamp: 2025-08-14T10:48:52.586Z
Learning: In calcom/cal.com PR #22995, packages/trpc/server/routers/viewer/ai/_router.ts, the voiceId input parameter in the create endpoint is intentionally not forwarded to aiService.createAgent() as voice customization is not required at the moment (per maintainer Udit-takkar). Future reviews should not flag this as missing functionality unless requirements change.
Applied to files:
packages/trpc/server/routers/viewer/credits.tsx
🧬 Code graph analysis (5)
packages/features/ee/billing/api/webhook/_checkout.session.completed.ts (1)
packages/lib/server/repository/credits.ts (1)
CreditsRepository(6-256)
packages/emails/templates/credit-balance-limit-reached-email.ts (1)
packages/lib/constants.ts (1)
EMAIL_FROM_NAME(32-32)
packages/trpc/server/routers/viewer/credits.tsx (2)
packages/trpc/server/trpc.ts (1)
router(13-13)packages/lib/constants.ts (1)
IS_SMS_CREDITS_ENABLED(227-228)
apps/web/modules/settings/billing/components/BillingCredits.tsx (4)
packages/trpc/react/trpc.ts (1)
trpc(54-138)packages/ui/components/button/Button.tsx (1)
Button(221-349)packages/platform/atoms/src/components/ui/dialog.tsx (2)
DialogTitle(125-125)DialogDescription(126-126)packages/ui/components/form/index.ts (1)
InputError(22-22)
packages/features/ee/billing/credit-service.ts (2)
packages/lib/server/repository/credits.ts (1)
CreditsRepository(6-256)packages/features/ee/billing/stripe-billling-service.ts (1)
StripeBillingService(6-247)
🪛 Biome (2.1.2)
apps/web/modules/settings/billing/components/BillingCredits.tsx
[error] 120-120: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Install dependencies / Yarn install & cache
- GitHub Check: Detect changes
🔇 Additional comments (7)
packages/emails/templates/credit-balance-limit-reached-email.ts (2)
41-47: Subject logic LGTM.Conditional subjects read well and use i18n.
49-57: Payload wiring LGTM.Props include the new flags; matches template usage.
packages/emails/templates/credit-balance-low-warning-email.ts (1)
58-63: Looks good; matches settings flag.Plain-text branch varies on
autoRechargeEnabledappropriately.packages/lib/server/repository/credits.ts (4)
37-49: LGTM on targeted select.Narrow
selectand unique lookup byexternalReflook good.
145-155: PII in expense logs — ensure not exposed beyond server boundary.Returning
smsSid,phoneNumber, and
235-255: LGTM on purchase log creation.Minimal fields, explicit
date, andautoRechargedflag look good.
13-18: Auto‑recharge settings not selected by finder methods.If any caller expects
autoRechargeEnabled/Threshold/Amount/stripeCustomerIdfrom these finders (for threshold checks or UI), add them to theselect; otherwise confirm they’re fetched elsewhere.Also applies to: 64-68
Signed-off-by: Hemant M Mehta <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/lib/server/repository/credits.ts (1)
179-184: Bug:gte: startDate/lte: endDatepassundefinedto Prisma when omitted.Build the date filter conditionally; otherwise Prisma may reject the query.
- date: { - gte: startDate, - lte: endDate, - }, + ...(startDate || endDate + ? { + date: { + ...(startDate ? { gte: startDate } : {}), + ...(endDate ? { lte: endDate } : {}), + }, + } + : {}),If you prefer clarity, compute a local const
dateWherebefore the query and spread it.
♻️ Duplicate comments (3)
packages/features/ee/billing/stripe-billling-service.ts (1)
201-251: Charge amount calculation is incorrect; use configured Stripe Price and default PM.Hardcoding
1cent per credit will misbill; also select customer’s default payment method and fail fast on SCA.async createAutoRechargePaymentIntent({ customerId, - amount, + amount, // quantity of credits metadata, }: { customerId: string; amount: number; metadata: { creditBalanceId: string; teamId: string; userId: string; autoRecharge: string; }; }): Promise<{ success: boolean; error?: string }> { try { - const priceInCents = 1; // Price per credit in cents - const totalAmount = amount * priceInCents; + if (!Number.isInteger(amount) || amount <= 0) { + return { success: false, error: "Invalid recharge quantity" }; + } + const creditsPriceId = process.env.NEXT_PUBLIC_STRIPE_CREDITS_PRICE_ID; + if (!creditsPriceId) { + return { success: false, error: "Credits price not configured" }; + } + const price = await this.stripe.prices.retrieve(creditsPriceId); + const unitAmount = (price as Stripe.Price).unit_amount; + if (!unitAmount || unitAmount <= 0) { + return { success: false, error: "Invalid unit amount on credits price" }; + } + const totalAmount = unitAmount * amount; // Get customer's payment methods const paymentMethods = await this.stripe.paymentMethods.list({ customer: customerId, type: "card", }); if (!paymentMethods.data.length) { return { success: false, error: "No payment methods found for customer" }; } - // Use the default payment method (most recently added) - const defaultPaymentMethod = paymentMethods.data[0].id; + // Use customer's default PM if set; else first available + const customer = (await this.stripe.customers.retrieve(customerId)) as Stripe.Customer; + const defaultPm = + (customer.invoice_settings?.default_payment_method as string | null) ?? + paymentMethods.data[0].id; // Create and confirm a payment intent - await this.stripe.paymentIntents.create({ + await this.stripe.paymentIntents.create( + { amount: totalAmount, currency: "usd", customer: customerId, - payment_method: defaultPaymentMethod, + payment_method: defaultPm, off_session: true, - confirm: true, + confirm: true, + error_on_requires_action: true, metadata, - }); + }, + { + idempotencyKey: `auto_recharge:${metadata.creditBalanceId}:${metadata.userId}:${Date.now()}`, + } + ); return { success: true }; } catch (error) { console.error("Error processing auto-recharge payment:", error); return { success: false, error: error instanceof Error ? error.message : "Unknown error", }; } }packages/lib/server/repository/credits.ts (2)
237-256: Enforce exactly one of teamId or userId on create; avoid truthy checks.This was previously flagged; still missing the guard and uses truthy checks for IDs.
static async createCreditBalance( @@ ) { + if ((teamId == null) === (userId == null)) { + throw new Error("Exactly one of teamId or userId must be provided."); + } return tx.creditBalance.create({ data: { @@ - team: teamId + team: teamId != null ? { connect: { id: teamId, }, } : undefined, - user: userId + user: userId != null ? { connect: { id: userId, }, } : undefined,Also applies to: 264-277
221-229: Avoid truthy checks and clarify error in updateCreditBalance.Use explicit null checks; keep the guard error message consistent.
- if (id) { + if (id != null) { where = { id }; - } else if (teamId) { + } else if (teamId != null) { where = { teamId }; - } else if (userId) { + } else if (userId != null) { where = { userId }; } else { - throw new Error("Either id, teamId or userId must be provided"); + throw new Error("updateCreditBalance requires id or one of teamId/userId"); }
🧹 Nitpick comments (8)
packages/emails/src/templates/CreditBalanceLowWarningEmail.tsx (1)
27-39: Consider adding a “manage auto‑recharge” CTA when enabled.Helps recipients discover/adjust settings directly.
if (autoRechargeEnabled) { return ( <> <p style={{ fontWeight: 400, lineHeight: "24px" }}> {user.t("hi_user_name", { name: user.name })}, </p> <p style={{ fontWeight: 400, lineHeight: "24px", marginBottom: "20px" }}> {team ? user.t("team_credits_low_auto_recharge", { teamName: team.name, balance }) : user.t("user_credits_low_auto_recharge", { balance })} </p> + <div style={{ textAlign: "center", marginTop: "24px" }}> + <CallToAction + label={user.t("manage_auto_recharge")} + href={team ? `${WEBAPP_URL}/settings/teams/${team.id}/billing` : `${WEBAPP_URL}/settings/billing`} + endIconName="linkIcon" + /> + </div> </> ); }packages/emails/src/templates/CreditBalanceLimitReachedEmail.tsx (1)
106-116: Subject could reflect auto‑recharge enabled state.Optional: when
autoRechargeEnabledis true, subject can reduce urgency.const getSubject = () => { if (autoRechargeFailed) { return user.t("auto_recharge_failed"); } - - if (team) { + if (autoRechargeEnabled) { + return team + ? user.t("credit_limit_reached_auto_recharge", { teamName: team.name }) + : user.t("credit_limit_reached_auto_recharge_user"); + } + if (team) { return user.t("action_required_out_of_credits", { teamName: team.name }); } return user.t("action_required_user_out_of_credits"); };packages/emails/templates/credit-balance-limit-reached-email.ts (1)
9-9: Default export is acceptable here; consider named export later.Template classes are primary module exports; keep as is unless you plan a broader refactor.
packages/features/ee/billing/stripe-billling-service.ts (3)
8-12: Optional: set retry/timeout for Stripe client.Improve resilience on transient failures.
- this.stripe = new Stripe(key, { - apiVersion: "2023-10-16", - }); + this.stripe = new Stripe(key, { + apiVersion: "2023-10-16", + maxNetworkRetries: 2, + // timeout: 20000, // enable if supported in your Stripe SDK version + });
53-63: Avoidas never; use proper params type for Checkout session.Explicit types prevent masking API drift.
// import type { Stripe } from "stripe"; const params: Stripe.Checkout.SessionCreateParams = { line_items: [{ price: priceId, quantity }], mode: "payment", success_url: successUrl, cancel_url: cancelUrl, metadata, invoice_creation: { enabled: true }, }; const session = await this.stripe.checkout.sessions.create(params);
1-1: Nit: filename has triple “l” (billling).Consider renaming to
stripe-billing-service.tsfor consistency.packages/lib/server/repository/credits.ts (2)
164-169: Redundant prismaClient shadowing.You already default
txto prisma; drop the extra indirection.- const prismaClient = tx ?? prisma; - - return await prismaClient.creditBalance.findUnique({ + return await tx.creditBalance.findUnique({
282-301: Optional: allow date injection for backfills/replays.Consider accepting an optional date param here (defaulting to now) to support deterministic log backfills.
- autoRecharged = false, + autoRecharged = false, + date = new Date(), @@ - date: new Date(), + date,
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (8)
packages/emails/src/templates/CreditBalanceLimitReachedEmail.tsx(1 hunks)packages/emails/src/templates/CreditBalanceLowWarningEmail.tsx(1 hunks)packages/emails/templates/credit-balance-limit-reached-email.ts(2 hunks)packages/emails/templates/credit-balance-low-warning-email.ts(3 hunks)packages/features/ee/billing/credit-service.ts(2 hunks)packages/features/ee/billing/stripe-billling-service.ts(4 hunks)packages/lib/server/repository/credits.ts(3 hunks)packages/platform/libraries/index.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/emails/templates/credit-balance-low-warning-email.ts
- packages/features/ee/billing/credit-service.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
**/*.ts: For Prisma queries, only select data you need; never useinclude, always useselect
Ensure thecredential.keyfield is never returned from tRPC endpoints or APIs
Files:
packages/platform/libraries/index.tspackages/features/ee/billing/stripe-billling-service.tspackages/emails/templates/credit-balance-limit-reached-email.tspackages/lib/server/repository/credits.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js
.utc()in hot paths like loops
Files:
packages/platform/libraries/index.tspackages/emails/src/templates/CreditBalanceLimitReachedEmail.tsxpackages/features/ee/billing/stripe-billling-service.tspackages/emails/src/templates/CreditBalanceLowWarningEmail.tsxpackages/emails/templates/credit-balance-limit-reached-email.tspackages/lib/server/repository/credits.ts
**/*.{ts,tsx,js,jsx}
⚙️ CodeRabbit configuration file
Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.
Files:
packages/platform/libraries/index.tspackages/emails/src/templates/CreditBalanceLimitReachedEmail.tsxpackages/features/ee/billing/stripe-billling-service.tspackages/emails/src/templates/CreditBalanceLowWarningEmail.tsxpackages/emails/templates/credit-balance-limit-reached-email.tspackages/lib/server/repository/credits.ts
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
Always use
t()for text localization in frontend code; direct text embedding should trigger a warning
Files:
packages/emails/src/templates/CreditBalanceLimitReachedEmail.tsxpackages/emails/src/templates/CreditBalanceLowWarningEmail.tsx
🧬 Code graph analysis (3)
packages/emails/src/templates/CreditBalanceLimitReachedEmail.tsx (1)
packages/lib/constants.ts (1)
WEBAPP_URL(12-18)
packages/emails/src/templates/CreditBalanceLowWarningEmail.tsx (1)
packages/lib/constants.ts (1)
WEBAPP_URL(12-18)
packages/emails/templates/credit-balance-limit-reached-email.ts (1)
packages/lib/constants.ts (1)
EMAIL_FROM_NAME(32-32)
🔇 Additional comments (8)
packages/platform/libraries/index.ts (1)
141-141: Re-export looks fine; confirm server-only usage.Ensure
CreditServiceisn’t imported by client bundles (web/SSR boundary). If needed, gate via server-only entrypoint.packages/emails/src/templates/CreditBalanceLowWarningEmail.tsx (2)
24-41: Solid i18n usage and clear branching.Props and content paths look correct; CTA paths are appropriate for team vs user.
35-37: Verify new translation keys exist.Ensure these keys are added to locales:
team_credits_low_auto_recharge,user_credits_low_auto_recharge,team_credits_low_warning_message,user_credits_low_warning_message,buy_credits,manage_auto_recharge(if added).Also applies to: 49-50, 66-66
packages/emails/src/templates/CreditBalanceLimitReachedEmail.tsx (2)
26-65: Good consolidation via helpers; branches read well.
79-83: CTA destinations are correct (team vs user).No changes needed.
Also applies to: 96-100
packages/emails/templates/credit-balance-limit-reached-email.ts (2)
41-47: Subject logic matches TSX template.Looks consistent with UI template.
63-69: ```shell
#!/bin/bashsafer verification script — don't exit on first error
file="packages/emails/templates/credit-balance-limit-reached-email.ts"
echo "PWD: $(pwd)"
echoif [ -f "$file" ]; then
echo "=== SHOWING $file (with line numbers) ==="
awk '{printf "%4d: %s\n", NR, $0}' "$file" | sed -n '1,240p' || cat "$file"
else
echo "FILE_NOT_FOUND: $file"
echo "Searching for similar filenames..."
rg -n --hidden "credit-balance-limit-reached-email|credit.*balance.*limit" -S || true
fiecho
echo "=== Search for translation usage (.t, translate, i18n) ==="
rg -n --hidden "\.t\(|translate\(|\bi18n\b" -S || trueecho
echo "=== Search for 'this.user' / 'this.team' / 'user.t' occurrences ==="
rg -n --hidden "this\.user|this\.team|user\.t\(|this\.user\.t\(" -S || trueecho
echo "=== Search for autoRecharge flags and getTextBody definitions ==="
rg -n --hidden "autoRechargeFailed|autoRechargeEnabled|getTextBody\s*\(" -S || trueecho
echo "=== List files under packages/emails for context ==="
ls -la packages/emails || trueecho
echo "=== Done ==="</blockquote></details> <details> <summary>packages/lib/server/repository/credits.ts (1)</summary><blockquote> `11-26`: **Confirmed: CreditBalance.teamId and CreditBalance.userId are individually @unique (packages/prisma/schema.prisma).** Both fields have @unique and there is also @@unique([teamId, userId]); using findUnique({ where: { teamId } }) / findUnique({ where: { userId } }) is valid. </blockquote></details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
Devanshusharma2005
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lets Address the coderabbit suggestions.
Typechecks are failing also and please add unit tests also.
And before and after change in loom would be better.
Signed-off-by: Hemant M Mehta <[email protected]>
Signed-off-by: Hemant M Mehta <[email protected]>
…antmm/cal.com into auto-recharge-credit-balance
Signed-off-by: Hemant M Mehta <[email protected]>
Signed-off-by: Hemant M Mehta <[email protected]>
Signed-off-by: Hemant M Mehta <[email protected]>
|
This PR is being marked as stale due to inactivity. |
closes: #21976
What does this PR do?
✅ Added comprehensive auto-recharge functionality to CreditService
✅ Created dedicated methods for threshold monitoring and payment processing
✅ Added user-configurable threshold and recharge amount settings
✅ Enhanced email notifications for different recharge scenarios
✅ Integrated seamlessly with existing Stripe billing service
Mandatory Tasks (DO NOT REMOVE)
How should this be tested?
Checklist