Conversation
WalkthroughThe changes introduce a billing persistence layer and wire it into team and organization flows. New enums and interfaces (Plan, SubscriptionStatus, IBillingRepository) are added, with Prisma-backed repositories for team and organization billing and a factory to select between them. InternalTeamBilling is updated to use this repository and exposes saveTeamBilling. Team creation routes now conditionally persist billing details when a checkout session/subscription exists. The invoice.paid webhook logs invoices and persists organization billing. A Prisma migration and schema add TeamBilling and OrganizationBilling tables and relations. Tests are added/updated for the repository factory, InternalTeamBilling, and billing-related mocks. Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Disabled knowledge base sources:
📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ 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). (5)
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 |
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
This reverts commit bbb2e5d.
| include: { members: true }, | ||
| }); | ||
|
|
||
| if (checkoutSessionSubscription) { |
There was a problem hiding this comment.
If billing information is present then write to the billing table for new teams
| subscriptionItemId: checkoutSessionSubscription.items.data[0].id, | ||
| customerId: checkoutSessionSubscription.customer as string, | ||
| // TODO: Implement true subscription status when webhook events are implemented | ||
| status: SubscriptionStatus.ACTIVE, |
There was a problem hiding this comment.
Until we implement handling webhook events from Stripe, assume all new subscriptions coming from the checkout session are active
| }, | ||
| }); | ||
|
|
||
| if (checkoutSession && subscription) { |
There was a problem hiding this comment.
Same here. If creating a new team then write to the TeamBilling table and set the status to ACTIVE
| paymentSubscriptionItemId, | ||
| }); | ||
|
|
||
| const internalTeamBillingService = new InternalTeamBilling(organization); |
There was a problem hiding this comment.
This is the webhook event when a new organization is created and the payment succeeds. Write to the OrganizationBilling table
| private billingRepository: IBillingRepository; | ||
| constructor(team: TeamBillingInput) { | ||
| this.team = team; | ||
| this.billingRepository = BillingRepositoryFactory.getRepository(team.isOrganization); |
There was a problem hiding this comment.
When initializing the InternalTeamBilling we also initialize the right billing repository
|
|
||
| model TeamBilling { | ||
| id String @id @default(uuid()) | ||
| teamId Int @unique |
There was a problem hiding this comment.
There should be a 1:1 relationship between teams and subscriptions so we teamId and subscriptionId should be unique. Also adds an index to these fields.
| teamId Int @unique | ||
| team Team @relation("OrganizationBilling", fields: [teamId], references: [id], onDelete: Cascade) |
There was a problem hiding this comment.
I thought about naming these to be organization specific but this would cause some confusion and added boiler plate around if (team) or if (org). Ideally the service should handle all of this logic and the caller of the service only cares about the subscription output.
- Fix credit-service.test.ts Prisma mock to export prisma object - Replace any types with proper TypeScript types in credit-service.test.ts - Add unit tests for PrismaTeamBillingRepository covering record creation, enum casting, and error handling - Add unit tests for PrismaOrganizationBillingRepository with same coverage - Add unit tests for BillingRepositoryFactory to verify correct repository selection - Add unit tests for InternalTeamBilling.saveTeamBilling() method testing delegation to correct repositories - All 53 tests pass with TZ=UTC yarn test - Type checking passes with yarn type-check:ci --force Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (7)
packages/prisma/schema.prisma (1)
2603-2631: TeamBilling and OrganizationBilling models look solid.The 1:1 relationships (via unique constraints on
teamIdandsubscriptionId) and CASCADE deletes are correctly configured.Minor suggestions:
- Consider adding indexes on
customerIdif you'll frequently query billing records by customer- Consider adding an index on
statusif you'll filter by subscription status- Consider defining
statusandplanNameas Prisma enums (matching the TypeScript enums in IBillingRepository) for additional type safety at the schema levelpackages/prisma/migrations/20250929190134_init_team_and_org_billing_tables/migration.sql (2)
2-29: Consider consolidating identical table structures.Both
TeamBillingandOrganizationBillinghave identical schemas. Since organizations are teams (distinguished by theisOrganizationflag), a singleBillingtable withteamIdcould reduce duplication and simplify schema maintenance. The current separate-table approach may be intentional for data isolation or query optimization, but the maintenance cost of keeping two identical structures in sync should be weighed against those benefits.
31-47: Consider additional indexes for common query patterns.While the unique indexes on
teamIdandsubscriptionIdare appropriate, consider whether queries bycustomerId(e.g., listing all billing records for a Stripe customer) or composite queries are common enough to warrant additional indexes. This is a performance optimization that can be deferred if these query patterns don't exist yet.packages/features/ee/billing/teams/internal-team-billing.ts (1)
208-210: Consider returning the created record and adding error handling.While the delegation is clean, consider these improvements:
- Return value: Other persistence operations might need the created record's ID or timestamp. Consider returning the
BillingRecord.- Error handling: Unlike other methods in this class (e.g.,
cancel,updateQuantity),saveTeamBillingdoesn't catch errors. Should it callthis.logErrorFromUnknown(error)for consistency?Example refactor:
- async saveTeamBilling(args: IBillingRepositoryCreateArgs) { - await this.billingRepository.create(args); + async saveTeamBilling(args: IBillingRepositoryCreateArgs): Promise<BillingRecord> { + try { + const billingRecord = await this.billingRepository.create(args); + log.info(`Saved billing record for team ${args.teamId}`); + return billingRecord; + } catch (error) { + this.logErrorFromUnknown(error); + throw error; + } }packages/features/ee/billing/repository/IBillingRepository.ts (3)
14-22: Consider including timestamps in BillingRecord interface.The
BillingRecordinterface doesn't includecreatedAtandupdatedAtfields, but the test files show these are returned by the repository implementations. Consider whether these timestamps should be part of the public interface for audit trail purposes.If timestamps should be public, apply this diff:
export interface BillingRecord { id: string; teamId: number; subscriptionId: string; subscriptionItemId: string; customerId: string; planName: Plan; status: SubscriptionStatus; + createdAt: Date; + updatedAt: Date; }
24-26: Consider extending IBillingRepository with additional methods.The interface currently only includes
create, but billing repositories typically need additional operations. Consider whether methods likeupdate,findByTeamId, orfindBySubscriptionIdshould be added to the interface now or documented as future enhancements.
28-31: Remove unused interface IBillingRepositoryConstructorArgs.This interface is not used anywhere in the codebase. Both
PrismaOrganizationBillingRepositoryandPrismaTeamBillingRepositoryconstructors takePrismaClientdirectly instead:constructor(private readonly prismaClient: PrismaClient) {}Consider removing the unused interface to keep the code clean.
📜 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 (15)
apps/web/app/api/teams/api/create/route.ts(2 hunks)apps/web/app/api/teams/create/route.ts(2 hunks)packages/features/ee/billing/api/webhook/_invoice.paid.org.ts(3 hunks)packages/features/ee/billing/credit-service.test.ts(7 hunks)packages/features/ee/billing/repository/IBillingRepository.ts(1 hunks)packages/features/ee/billing/repository/PrismaOrganizationBillingRepository.test.ts(1 hunks)packages/features/ee/billing/repository/PrismaOrganizationBillingRepository.ts(1 hunks)packages/features/ee/billing/repository/PrismaTeamBillingRepository.test.ts(1 hunks)packages/features/ee/billing/repository/PrismaTeamBillingRepository.ts(1 hunks)packages/features/ee/billing/repository/billingRepositoryFactory.test.ts(1 hunks)packages/features/ee/billing/repository/billingRepositoryFactory.ts(1 hunks)packages/features/ee/billing/teams/internal-team-billing.test.ts(1 hunks)packages/features/ee/billing/teams/internal-team-billing.ts(3 hunks)packages/prisma/migrations/20250929190134_init_team_and_org_billing_tables/migration.sql(1 hunks)packages/prisma/schema.prisma(2 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/repository/billingRepositoryFactory.test.tspackages/features/ee/billing/repository/PrismaOrganizationBillingRepository.test.tsapps/web/app/api/teams/create/route.tspackages/features/ee/billing/repository/billingRepositoryFactory.tspackages/features/ee/billing/teams/internal-team-billing.tsapps/web/app/api/teams/api/create/route.tspackages/features/ee/billing/repository/PrismaOrganizationBillingRepository.tspackages/features/ee/billing/repository/IBillingRepository.tspackages/features/ee/billing/repository/PrismaTeamBillingRepository.tspackages/features/ee/billing/repository/PrismaTeamBillingRepository.test.tspackages/features/ee/billing/teams/internal-team-billing.test.tspackages/features/ee/billing/api/webhook/_invoice.paid.org.tspackages/features/ee/billing/credit-service.test.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/repository/billingRepositoryFactory.test.tspackages/features/ee/billing/repository/PrismaOrganizationBillingRepository.test.tsapps/web/app/api/teams/create/route.tspackages/features/ee/billing/repository/billingRepositoryFactory.tspackages/features/ee/billing/teams/internal-team-billing.tsapps/web/app/api/teams/api/create/route.tspackages/features/ee/billing/repository/PrismaOrganizationBillingRepository.tspackages/features/ee/billing/repository/IBillingRepository.tspackages/features/ee/billing/repository/PrismaTeamBillingRepository.tspackages/features/ee/billing/repository/PrismaTeamBillingRepository.test.tspackages/features/ee/billing/teams/internal-team-billing.test.tspackages/features/ee/billing/api/webhook/_invoice.paid.org.tspackages/features/ee/billing/credit-service.test.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/repository/billingRepositoryFactory.test.tspackages/features/ee/billing/repository/PrismaOrganizationBillingRepository.test.tsapps/web/app/api/teams/create/route.tspackages/features/ee/billing/repository/billingRepositoryFactory.tspackages/features/ee/billing/teams/internal-team-billing.tsapps/web/app/api/teams/api/create/route.tspackages/features/ee/billing/repository/PrismaOrganizationBillingRepository.tspackages/features/ee/billing/repository/IBillingRepository.tspackages/features/ee/billing/repository/PrismaTeamBillingRepository.tspackages/features/ee/billing/repository/PrismaTeamBillingRepository.test.tspackages/features/ee/billing/teams/internal-team-billing.test.tspackages/features/ee/billing/api/webhook/_invoice.paid.org.tspackages/features/ee/billing/credit-service.test.ts
**/*Repository.ts
📄 CodeRabbit inference engine (.cursor/rules/review.mdc)
Repository files must include
Repositorysuffix, prefix with technology if applicable (e.g.,PrismaAppRepository.ts), and use PascalCase matching the exported class
Files:
packages/features/ee/billing/repository/PrismaOrganizationBillingRepository.tspackages/features/ee/billing/repository/IBillingRepository.tspackages/features/ee/billing/repository/PrismaTeamBillingRepository.ts
🧬 Code graph analysis (12)
packages/features/ee/billing/repository/billingRepositoryFactory.test.ts (3)
packages/features/ee/billing/repository/billingRepositoryFactory.ts (1)
BillingRepositoryFactory(6-13)packages/features/ee/billing/repository/PrismaOrganizationBillingRepository.ts (1)
PrismaOrganizationBillingRepository(11-26)packages/features/ee/billing/repository/PrismaTeamBillingRepository.ts (1)
PrismaTeamBillingRepository(11-26)
packages/features/ee/billing/repository/PrismaOrganizationBillingRepository.test.ts (1)
packages/features/ee/billing/repository/PrismaOrganizationBillingRepository.ts (1)
PrismaOrganizationBillingRepository(11-26)
apps/web/app/api/teams/create/route.ts (1)
packages/features/ee/billing/teams/internal-team-billing.ts (1)
InternalTeamBilling(23-211)
packages/features/ee/billing/repository/billingRepositoryFactory.ts (2)
packages/features/ee/billing/repository/PrismaOrganizationBillingRepository.ts (1)
PrismaOrganizationBillingRepository(11-26)packages/features/ee/billing/repository/PrismaTeamBillingRepository.ts (1)
PrismaTeamBillingRepository(11-26)
packages/features/ee/billing/teams/internal-team-billing.ts (2)
packages/features/ee/billing/repository/IBillingRepository.ts (2)
IBillingRepository(24-26)IBillingRepositoryCreateArgs(33-40)packages/features/ee/billing/repository/billingRepositoryFactory.ts (1)
BillingRepositoryFactory(6-13)
apps/web/app/api/teams/api/create/route.ts (1)
packages/features/ee/billing/teams/internal-team-billing.ts (1)
InternalTeamBilling(23-211)
packages/features/ee/billing/repository/PrismaOrganizationBillingRepository.ts (1)
packages/features/ee/billing/repository/IBillingRepository.ts (3)
IBillingRepository(24-26)IBillingRepositoryCreateArgs(33-40)BillingRecord(14-22)
packages/features/ee/billing/repository/PrismaTeamBillingRepository.ts (1)
packages/features/ee/billing/repository/IBillingRepository.ts (3)
IBillingRepository(24-26)IBillingRepositoryCreateArgs(33-40)BillingRecord(14-22)
packages/features/ee/billing/repository/PrismaTeamBillingRepository.test.ts (1)
packages/features/ee/billing/repository/PrismaTeamBillingRepository.ts (1)
PrismaTeamBillingRepository(11-26)
packages/features/ee/billing/teams/internal-team-billing.test.ts (1)
packages/features/ee/billing/teams/internal-team-billing.ts (1)
InternalTeamBilling(23-211)
packages/features/ee/billing/api/webhook/_invoice.paid.org.ts (1)
packages/features/ee/billing/teams/internal-team-billing.ts (1)
InternalTeamBilling(23-211)
packages/features/ee/billing/credit-service.test.ts (1)
packages/lib/server/repository/team.ts (1)
TeamRepository(170-414)
⏰ 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). (10)
- GitHub Check: Install dependencies / Yarn install & cache
- GitHub Check: Install dependencies / Yarn install & cache
- GitHub Check: Install dependencies / Yarn install & cache
- GitHub Check: Install dependencies / Yarn install & cache
- GitHub Check: Install dependencies / Yarn install & cache
- GitHub Check: Install dependencies / Yarn install & cache
- GitHub Check: Install dependencies / Yarn install & cache
- GitHub Check: Install dependencies / Yarn install & cache
- GitHub Check: Install dependencies / Yarn install & cache
- GitHub Check: Install dependencies / Yarn install & cache
🔇 Additional comments (32)
packages/features/ee/billing/credit-service.test.ts (3)
17-28: LGTM: Prisma mock setup is thorough.The async
importOriginal()and spread of the actual module ensure full coverage of both default and named exports. Addingprisma.$transactionalongsidedefault.$transactionhandles multiple import patterns consistently.
93-104: LGTM: Improved Stripe mock typing.Typing
stripeMockasPartial<Stripe>and usingvi.mocked(Stripe).mockImplementation()improves type safety while maintaining test flexibility.
408-408: LGTM: Stricter typing for TeamRepository mocks.Replacing
as anywithas unknown as TeamRepositoryimproves type safety by requiring an explicit double cast, without changing runtime behavior. All four instances are updated consistently.Also applies to: 429-429, 458-458, 481-481
apps/web/app/api/teams/api/create/route.ts (2)
7-8: LGTM: Billing imports are correct.The imports for
Plan,SubscriptionStatus, andInternalTeamBillingare appropriate for the new billing persistence logic.
67-67: TODO acknowledged: Subscription status hardcoded.The TODO correctly identifies that
SubscriptionStatus.ACTIVEis assumed for all new subscriptions. True status tracking via webhooks should be implemented in a follow-up.packages/features/ee/billing/repository/billingRepositoryFactory.test.ts (1)
1-40: LGTM: Comprehensive test coverage for the factory.The test suite thoroughly validates
BillingRepositoryFactory.getRepository:
- Correct repository selection based on
isOrganizationflag- Type consistency across multiple calls
- Type exclusivity with negative assertions
The coverage is complete for the factory's selection logic.
apps/web/app/api/teams/create/route.ts (2)
7-8: LGTM: Billing imports are correct.The imports for
Plan,SubscriptionStatus, andInternalTeamBillingare appropriate for the billing persistence logic.
97-97: TODO acknowledged: Subscription status hardcoded.The TODO correctly identifies that
SubscriptionStatus.ACTIVEis assumed for all new subscriptions. Webhook-based status tracking should be implemented in a follow-up.packages/prisma/schema.prisma (2)
593-594: LGTM: Team billing relations are correctly defined.The optional relations
teamBillingandorganizationBillingare appropriately added to the Team model with explicit relation names.
2636-2636: LGTM: CalendarCacheEventStatus enum extended.Adding
cancelledto theCalendarCacheEventStatusenum is appropriate. Note: this change is unrelated to the billing tables added in this PR.packages/features/ee/billing/repository/billingRepositoryFactory.ts (1)
1-13: LGTM! Factory pattern correctly implemented.The factory correctly selects between organization and team repositories based on the
isOrganizationflag. The use of a static method is appropriate for this stateless factory, and the shared Prisma client follows the standard pattern in this codebase.packages/features/ee/billing/api/webhook/_invoice.paid.org.ts (2)
3-4: LGTM! Imports are appropriate.The new imports for
Plan,SubscriptionStatus, andInternalTeamBillingsupport the billing persistence functionality added below.
100-109: Missing webhook handlers for subscription lifecycle - ACTIVE status is correct but incomplete.The hardcoded
ACTIVEstatus is appropriate forinvoice.paidevents. However, verification reveals significant gaps in subscription status management:Missing handlers for organization billing:
- No
customer.subscription.updatedhandler exists for team/org billing (only for CalAI phone numbers). This meansPAST_DUE,TRIALING, and other status transitions won't be captured.customer.subscription.deletedcallsdowngrade()which clears metadata but does NOT update thestatusfield toCANCELLEDin the new billing tables.Impact on OrganizationBilling table:
The newOrganizationBillingtable introduced in this PR will have stale status data after initial activation, as onlyinvoice.paidsets the status field.Recommendations:
- Implement
customer.subscription.updatedhandler for organizations to map Stripe subscription statuses (active, past_due, trialing, etc.) toSubscriptionStatusenum values- Update the
downgrade()method or subscription.deleted handler to set status toCANCELLEDin billing tables- Address the TODO comment as a high priority
The current implementation is functionally correct for the happy path (successful payments) but lacks status synchronization for the full subscription lifecycle.
packages/prisma/migrations/20250929190134_init_team_and_org_billing_tables/migration.sql (1)
10-11: Prisma manages updatedAt — verify only raw‑SQL/edge casespackages/prisma/schema.prisma defines TeamBilling and OrganizationBilling with
updatedAt DateTime @updatedAt, so Prisma Client will set/update that column automatically for normal Prisma writes. (prismagraphql.com)Notes / caveats:
- Migration created
updatedAtas NOT NULL without a DB default (packages/prisma/migrations/20250929190134_init_team_and_org_billing_tables/migration.sql lines 10–11) — fine for Prisma-managed writes but raw DB writes must supply a value.- Raw SQL /
prisma.$executeRawor external DB scripts do not trigger Prisma’s@updatedAtbehavior; those paths must setupdatedAtexplicitly or you must add a DB trigger/DEFAULT. (prismagraphql.com)- If an update call provides an empty
data: {}the@updatedAtvalue will remain unchanged. (prismagraphql.com)- Certain nested
updateMany/ nested-update patterns have known cases where related records’@updatedAtisn’t updated — check for nestedupdateManyagainst billing records. (github.com)Action: confirm there are no raw SQL/direct DB updates to TeamBilling or OrganizationBilling (or update those paths to set
updatedAt/ add a DB trigger or DEFAULT).packages/features/ee/billing/repository/PrismaOrganizationBillingRepository.ts (1)
11-26: LGTM! Repository implementation follows established patterns.The implementation correctly mirrors
PrismaTeamBillingRepositorywith proper dependency injection and delegation to Prisma. The type casts on lines 22-23 align with the contract defined inIBillingRepository.packages/features/ee/billing/teams/internal-team-billing.ts (2)
15-16: LGTM! Clean repository integration.The new imports properly introduce the billing repository abstraction layer.
27-27: LGTM! Proper dependency injection.The private billing repository member follows the existing class structure and enables testability.
packages/features/ee/billing/repository/PrismaTeamBillingRepository.test.ts (1)
1-154: LGTM! Comprehensive test coverage.The test suite thoroughly validates the repository implementation:
- ✅ Basic create operation with complete data structure verification
- ✅ Enum casting for both
planNameandstatus- ✅ Error propagation from Prisma
- ✅ Proper args spreading with
objectContaining- ✅ Multiple enum variations (TEAM, ENTERPRISE, ORGANIZATION plans; ACTIVE, TRIALING, PAST_DUE statuses)
- ✅ Proper test isolation with
beforeEachandvi.clearAllMocksThe tests provide good coverage for the critical repository behaviors and align well with the implementation in
PrismaTeamBillingRepository.ts.packages/features/ee/billing/repository/PrismaOrganizationBillingRepository.test.ts (6)
1-14: LGTM! Clean test setup.The imports and test setup follow best practices with proper mock clearing in
beforeEach.
17-53: LGTM! Comprehensive data structure validation.This test thoroughly validates the create operation, checking both the arguments passed to Prisma and the returned BillingRecord structure.
55-78: LGTM! Important table routing validation.This test correctly verifies that the organization repository writes to the
organizationBillingtable and not toteamBilling, which is critical for data integrity.
80-128: LGTM! Enum casting validation is thorough.Both tests correctly verify that the repository casts Prisma's string values to the appropriate enum types while maintaining runtime string type, which aligns with TypeScript enum behavior.
130-146: LGTM! Error propagation verified.This test ensures that Prisma errors are properly propagated to callers, which is essential for proper error handling upstream.
148-177: LGTM! Interface contract validation.This test validates that the repository correctly implements the
IBillingRepositoryinterface by checking all required properties are present in the returned object.packages/features/ee/billing/repository/PrismaTeamBillingRepository.ts (2)
1-9: LGTM! Clean imports following guidelines.All imports use named exports as per coding guidelines, and only necessary types are imported.
11-26: LGTM! Solid repository implementation.The implementation correctly:
- Follows the
Repositorysuffix naming convention as per coding guidelines- Delegates to the appropriate Prisma table (
teamBilling)- Casts string values to enum types for type safety
- Uses
readonlyfor the PrismaClient dependencypackages/features/ee/billing/teams/internal-team-billing.test.ts (4)
177-209: LGTM! Correct repository delegation for organizations.This test validates that organization teams correctly route to the
organizationBillingrepository and don't accidentally write to theteamBillingtable.
211-242: LGTM! Correct repository delegation for regular teams.This test validates that non-organization teams correctly route to the
teamBillingrepository, providing complete coverage of the routing logic.
244-281: LGTM! Comprehensive argument validation.This test ensures all billing fields (teamId, subscriptionId, subscriptionItemId, customerId, planName, status) are correctly passed through to the repository, including different enum values (ENTERPRISE, TRIALING).
283-307: LGTM! Error propagation validated.This test ensures that repository errors (like database constraint violations) are properly propagated to callers rather than being swallowed, which is critical for proper error handling.
packages/features/ee/billing/repository/IBillingRepository.ts (2)
1-5: LGTM! Well-defined Plan enum.The
Planenum clearly defines the three billing tiers with string values matching their keys, which aids debugging and logging.
7-12: Consider additional subscription statuses for future webhook implementation.The current enum defines 4 statuses (ACTIVE, CANCELLED, PAST_DUE, TRIALING), which match the most commonly checked states in the codebase. However, note that:
- Runtime status checks currently use raw Stripe status strings (e.g.,
subscriptionStatus === "active"), not this enum- This enum is primarily used for database storage in billing records
- No webhook handlers exist yet for team/organization subscription updates (unlike phone number subscriptions which have comprehensive status mapping)
- Multiple TODOs indicate webhook implementation is pending
When implementing webhook handlers for team/organization subscriptions, consider adding:
INCOMPLETE- subscription requiring payment actionINCOMPLETE_EXPIRED- expired incomplete subscriptionUNPAID- subscription with unpaid invoicePAUSED- paused subscription (if using Stripe's pause feature)Reference the existing phone number subscription webhook handler at
packages/features/ee/billing/api/webhook/_customer.subscription.updated.tswhich demonstrates comprehensive Stripe status mapping.
- Replace prismaMock usage with BillingRepositoryFactory mock - Mock IBillingRepository interface instead of Prisma directly - Follow repository mocking pattern from handleResponse.test.ts - All tests passing (53 total) Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
E2E results are ready! |
volnei
left a comment
There was a problem hiding this comment.
We need to get rid of prismaMock.
| @@ -0,0 +1,178 @@ | |||
| import prismaMock from "../../../../../tests/libs/__mocks__/prismaMock"; | |||
There was a problem hiding this comment.
anyway to avoid using prismaMock here?
| @@ -0,0 +1,154 @@ | |||
| import prismaMock from "../../../../../tests/libs/__mocks__/prismaMock"; | |||
|
@volnei removed the repository tests as these don't have any logic in them. |
| import { InternalTeamBilling } from "@calcom/features/ee/billing/teams/internal-team-billing"; | ||
| import stripe from "@calcom/features/ee/payments/server/stripe"; | ||
| import { HttpError } from "@calcom/lib/http-error"; | ||
| import prisma from "@calcom/prisma"; |
There was a problem hiding this comment.
Since we are here can you replace this by { prisma } instead of the default exports?
What does this PR do?
TeamBillingandOrganizationBillingtablesMandatory Tasks (DO NOT REMOVE)
How should this be tested?
TeamBillingtable should have the corresponding billing infoOrganizationBillingtable should have the corresponding billing info