Files
calendar/apps/api/v2/test/fixtures/repository/billing.repository.fixture.ts
devin-ai-integration[bot]GitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>[email protected] <[email protected]>Benny JooMorgan
8b75bf347f feat: add BillingCacheService with 1-hour TTL for team subscription data (#23934)
* feat: add BillingCacheService with 1-hour TTL for team subscription data

- Create BillingCacheService following CalendarsCacheService pattern
- Use teamId-based cache keys with 1-hour TTL (3,600,000 ms)
- Integrate caching into getBillingData method in BillingService
- Add cache invalidation to all webhook handlers:
  - handleStripeSubscriptionDeleted
  - handleStripePaymentSuccess
  - handleStripePaymentFailed
  - handleStripePaymentPastDue
  - handleStripeCheckoutEvents
- Add cache invalidation to cancelTeamSubscription method
- Add RedisModule import to billing module
- Add BillingCacheService to billing module providers
- Add findTeamByPlatformBillingId method to OrganizationsRepository for cache invalidation

Co-Authored-By: [email protected] <[email protected]>

* refactor: implement BillingServiceCachingProxy pattern

- Extract IBillingService interface with all public methods
- Create BillingServiceCachingProxy that implements caching logic
- Remove all caching logic from original BillingService
- Simplify cache invalidation using billing.id = team.id
- Update module to use proxy with proper dependency injection
- Update controller to inject proxy interface
- Remove unused BillingService import from controller

This follows the proxy pattern requested in PR feedback, separating
caching concerns from core billing logic for better maintainability.

Co-Authored-By: [email protected] <[email protected]>

* chore: add e2e for billing check

* chore: eslint rule for blocking importing features from appstore, lib, prisma (#23832)

* eslint rule

* improve

* fix

* improve msg

* chore: fix any types set by devin

* fix: add mising expect in test

* refactor: move cache methods into BillingServiceCachingProxy

- Remove BillingCacheService abstraction as suggested by @keithwillcode
- Move cache methods directly into proxy as private methods
- Update proxy to inject RedisService directly
- Move BillingData type to interface for better type safety
- Remove BillingCacheService from module providers
- Delete unused billing-cache.service.ts file

This simplifies the architecture by removing unnecessary abstraction
and follows standard caching proxy patterns.

Co-Authored-By: [email protected] <[email protected]>

* fix: test and legacy starter

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: [email protected] <[email protected]>
Co-authored-by: Benny Joo <[email protected]>
Co-authored-by: Morgan <[email protected]>
2025-09-24 07:36:02 -03:00

60 lines
1.6 KiB
TypeScript

import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { TestingModule } from "@nestjs/testing";
export class PlatformBillingRepositoryFixture {
private prismaReadClient: PrismaReadService["prisma"];
private prismaWriteClient: PrismaWriteService["prisma"];
constructor(private readonly module: TestingModule) {
this.prismaReadClient = module.get(PrismaReadService).prisma;
this.prismaWriteClient = module.get(PrismaWriteService).prisma;
}
async create(orgId: number, plan?: string) {
const randomString = Date.now().toString(36);
return this.prismaWriteClient.platformBilling.create({
data: {
id: orgId,
customerId: `cus_123_${randomString}`,
subscriptionId: `sub_123_${randomString}`,
plan: plan || "FREE",
},
});
}
async get(orgId: number) {
return this.prismaWriteClient.platformBilling.findFirst({
where: {
id: orgId,
},
include: {
managerBilling: true,
managedBillings: true,
},
});
}
async getByCustomerSubscriptionIds(customerId: string, subscriptionId: string) {
return this.prismaWriteClient.platformBilling.findMany({
where: {
customerId,
subscriptionId,
},
});
}
async deleteSubscriptionForTeam(teamId: number) {
// silently try to delete the subscription
try {
await this.prismaWriteClient.platformBilling.delete({
where: {
id: teamId,
},
});
} catch (err) {
console.error(err);
}
}
}