Skip to content

Comments

feat: Handle team subscription update events#24390

Draft
joeauyeung wants to merge 1204 commits intodevin/team-subscription-payment-failed-email-1760634153from
handle-stripe-webhook-events
Draft

feat: Handle team subscription update events#24390
joeauyeung wants to merge 1204 commits intodevin/team-subscription-payment-failed-email-1760634153from
handle-stripe-webhook-events

Conversation

@joeauyeung
Copy link
Contributor

@joeauyeung joeauyeung commented Oct 9, 2025

Stacked on #24518

What does this PR do?

Adds comprehensive Stripe webhook handling for team and organization subscription events to Cal.com. This extends the existing customer.subscription.updated webhook handler to process subscription updates for teams and organizations, while maintaining backwards compatibility with existing phone number subscription handling.

Key additions:

  • Modular webhook handler architecture with separate handlers for different product types
  • Team/organization subscription event processing via _teamAndOrgUpdateHandler.ts
  • Cal AI phone number subscription handling via _calAIPhoneNumberUpdateHandler.ts
  • New TeamSubscriptionEventHandler service class for subscription management
  • Billing repository methods for subscription lookup and updates
  • Status mapping utility between Stripe and Cal.com subscription statuses
  • Migration logic for existing subscriptions not yet in billing database
  • Comprehensive test coverage for all new components

Updates since last revision

Merged main into this branch and resolved 12 merge conflicts:

  • Updated webhook handlers to use DI pattern (getBillingProviderService()) instead of static methods from the deleted stripe-billing-service.ts
  • Preserved this branch's modular webhook handler architecture while adopting main's interface changes
  • Adapted to main's repository refactoring (method name changes: getBySubscriptionIdfindBySubscriptionId, updateupdateById)
  • Updated IBillingRepository interface with new fields from main (billingPeriod, pricePerSeat, paidSeats)

Visual Demo (For contributors especially)

https://www.loom.com/share/31035b700fe74f2397cdb0c96b99d425?sid=ca463f26-32ae-4d83-952c-a2506614d9e8

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

Environment variables required:

  • STRIPE_TEAM_PRODUCT_ID - Stripe product ID for team subscriptions
  • STRIPE_ORG_PRODUCT_ID - Stripe product ID for organization subscriptions
  • STRIPE_CAL_AI_PHONE_NUMBER_PRODUCT_ID - Stripe product ID for Cal AI phone numbers

Database requirements:

  • Ensure TeamBilling and OrganizationBilling models exist in Prisma schema
  • Run yarn prisma generate to update TypeScript types

Testing scenarios:

  1. New team subscription webhook: Send webhook with team product ID, verify billing record is created
  2. Existing subscription status update: Send webhook for subscription already in database, verify status is updated
  3. Migration scenario: Send webhook for subscription not in billing table but team exists with subscriptionId in metadata
  4. Phone number subscription: Send webhook with phone number product ID, verify it's handled by _calAIPhoneNumberUpdateHandler
  5. Fallback behavior: Send webhook without recognized product ID, verify appropriate handling

Checklist

  • My code follows the style guidelines of this project
  • I have commented my code, particularly in hard-to-understand areas
  • I have checked if my changes generate no new warnings

Human Review Checklist

Critical items to verify:

  • Import paths in tests: Verify TeamSubscriptionEventHandler.test.ts imports from correct path (../repository/billing/IBillingRepository vs ../repository/IBillingRepository)
  • DI pattern migration: Confirm all handlers use getBillingProviderService() instead of static StripeBillingService methods
  • Handler routing: Verify _handler.ts correctly routes to appropriate handler based on product ID
  • Database schema: Confirm TeamBilling and OrganizationBilling models exist in packages/prisma/schema.prisma
  • Environment variables: Verify all three Stripe product ID env vars are properly configured in deployment
  • Backwards compatibility: Verify phone number subscription handling still works when no team/org product ID is present

Link to Devin run: https://app.devin.ai/sessions/2e1197c2397b44e2b1308638447969b5
Requested by: @joeauyeung

This PR resolves the issue where Stripe webhook events for team and organization subscriptions were not being properly handled, ensuring subscription status synchronization between Stripe and Cal.com's billing system.

@github-actions
Copy link
Contributor

github-actions bot commented Oct 9, 2025

Hey there and thank you for opening this pull request! 👋🏼

We require pull request titles to follow the Conventional Commits specification and it looks like your proposed title needs to be adjusted.

Details:

No release type found in pull request title "Handle stripe webhook events". Add a prefix to indicate what kind of release this pull request corresponds to. For reference, see https://www.conventionalcommits.org/

Available types:
 - feat: A new feature
 - fix: A bug fix
 - docs: Documentation only changes
 - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
 - refactor: A code change that neither fixes a bug nor adds a feature
 - perf: A code change that improves performance
 - test: Adding missing tests or correcting existing tests
 - build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
 - ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
 - chore: Other changes that don't modify src or test files
 - revert: Reverts a previous commit

@keithwillcode keithwillcode added core area: core, team members only enterprise area: enterprise, audit log, organisation, SAML, SSO labels Oct 9, 2025
@joeauyeung joeauyeung changed the title Handle stripe webhook events feat: Handle team subscription update events Oct 9, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 9, 2025

Walkthrough

Adds typed, lazy-loaded Stripe webhook handling and a single runtime-facing stripeWebhookHandler; introduces a customer.subscription.updated handler that dispatches per-product sub-handlers (team/org and Cal AI phone-number), plus a new TeamSubscriptionEventHandler to manage subscription migrations and updates. Billing model and repositories gain subscriptionStart, subscriptionTrialEnd, subscriptionEnd fields and new getBySubscriptionId/update methods. TeamRepository adds findBySubscriptionId. API routes and invoice/checkout handlers propagate the extra subscription date fields. New environment constants and tests for webhook flows and subscription handling are included. A Stripe status-mapping method was renamed.

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed Title communicates the primary feature addition of handling team subscription update events, aligning with most of the webhook enhancements in this PR. It correctly identifies a central change area—subscription update handling via Stripe webhooks—and is concise and clear. Although it doesn’t mention organization subscription support, it still accurately reflects a significant part of the changeset without using vague terms.
Description Check ✅ Passed The description provides detailed information on the scope and purpose of the changes, including environment variables, database requirements, and testing scenarios, and it directly relates to the enhancements made to Stripe webhook handling. It clearly outlines new features such as team and organization subscription processing, migration logic, and comprehensive test coverage. Therefore, it is neither off-topic nor too generic.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch handle-stripe-webhook-events

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@vercel
Copy link

vercel bot commented Oct 9, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
cal Ignored Ignored Oct 15, 2025 7:48pm
cal-eu Ignored Ignored Oct 15, 2025 7:48pm

const teamOrOrgSubscriptionItem = orgSubscriptionItem || teamSubscriptionItem;

// A subscription will either have a team/org item or a cal.ai phone number item
const calAiPhoneNumberItem = subscription.items.data.find(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Subscriptions for cal.ai phone numbers are handled in the same webhook endpoint. So we only run cal.ai logic if the phone number product id is a part of the subscription. CC @Udit-takkar

Comment on lines 105 to 107
const billingRepository = BillingRepositoryFactory.getRepository(isOrganization);
const teamRepository = new TeamRepository(prisma);
const teamSubscriptionEventHandler = new TeamSubscriptionEventHandler(billingRepository, teamRepository);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initialize dependencies for TeamSubscriptionEventHandler

Comment on lines 109 to 115
const status = StripeBillingService.mapSubscriptionStatusToCalStatus({
stripeStatus: subscription.status,
subscriptionId: subscription.id,
});

const { subscriptionStart, subscriptionTrialEnd, subscriptionEnd } =
StripeBillingService.extractSubscriptionDates(subscription);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle stripe specific data before passing to the service

Comment on lines 42 to 44
export type IBillingRepositoryCreateArgs = Omit<BillingRecord, "id">;

export type IBillingRepositoryUpdateArgs = Omit<
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have the args extend from the BillingRecord type so it acts as the source of truth.

StripeBillingService.extractSubscriptionDates(subscription);

try {
await teamSubscriptionEventHandler.handleUpdate({
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the preprocessing in the Stripe webhook handler, the teamSubscriptionEventHandler doesn't depend on any specific data from Stripe.

}

// if (teamSubscriptionInDb && teamSubscriptionInDb.status !== subscriptionStatus) {
if (this.hasSubscriptionChanged({ subscription, dbSubscription: teamSubscriptionInDb })) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only update the subscription in the DB if any of the fields have changed from Stripe vs in the DB

const teamSubscriptionInDb = await this.billingRepository.getBySubscriptionId(subscriptionId);

// If the subscription doesn't exist in the DB, migrate it
if (!teamSubscriptionInDb) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the subscription is not in the DB then take this opportunity to migrate the subscription to the billing tables.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be a good idea to mark it in metadata that this has been migrated. This makes it convenient to check whether a particular metadata is migrated or not.

Also, It allows us to identify when the migration is fully complete and thus we can do cleanup.

subscription: TSubscriptionUpdate;
dbSubscription: BillingRecord;
}) {
const fieldsToCompare = ["status", "subscriptionTrialEnd", "subscriptionEnd"];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are the only fields that should be changed for a subscription

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder what's the harm in checking all the fields or just updating the DB without checking if Stripe is sending updated event. This kind of whitelisting could make us out of sync with Stripe data for certain fields.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor recommends allowing update for atleast subscriptionItemId also as that could change due to upgrade/downgrade.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unable to authenticate your request. Please make sure to connect your GitHub account to Cursor. Go to Cursor

return !conflictingTeam;
}

async findBySubscriptionId(subscriptionId: string) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Used to find teams where the subscriptionId is still in the team metadata

STRIPE_PRIVATE_KEY=
STRIPE_CLIENT_ID=

STRIPE_CAL_AI_PHONE_NUMBER_PRODUCT_ID=
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need this to determine if the subscription is for cal.ai phone number

@github-actions
Copy link
Contributor

github-actions bot commented Oct 15, 2025

E2E results are ready!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create custom product handlers for the _customer.subscription.updated event so we can lazy load the handlers similar to what we're doing in _customer.subscription.deleted

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All this logic is moved out of _customer.subscription.updated handler.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Udit-takkar moved cal.ai subscription update logic here.

Comment on lines 5 to 6
import { HttpCode } from "../../../lib/httpCode";
import type { SWHMap } from "../../../lib/types";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the types and HttpCode class out of the __handler into their own files.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Main handler for the _custpmer.subscription.updated event. Moved this and all product update handlers into a separate folder.

Keeping this layer thin by moving product handlers into their own files.


const results = [];

for (const item of subscription.items.data) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There could be the possibility of a subscription containing multiple tracked products (ex. org sub item and cal ai sub item). If that happens we should handle all possibilities since they will share the same subscription status.

}

if (results.length > 0) {
log.warn(`Subscription ${subscription.id} contains multiple tracked products`);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will need to add this log statement as an alert

Comment on lines 9 to 29
/** Stripe Webhook Handler Mappings */
export type SWHMap = {
[T in Stripe.DiscriminatedEvent as T["type"]]: {
[K in keyof T as Exclude<K, "type">]: T[K];
};
};

export type LazyModule<D> = Promise<{
default: (data: D) => unknown | Promise<unknown>;
}>;

type SWHandlers = {
[K in keyof SWHMap]?: () => LazyModule<SWHMap[K]["data"]>;
};

/** Just a shorthand for HttpError */
export class HttpCode extends HttpError {
constructor(statusCode: number, message: string) {
super({ statusCode, message });
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Abstracted these to their own files so we're not importing from the main _handler file

Comment on lines 103 to 104
const { subscriptionStart, subscriptionTrialEnd, subscriptionEnd } =
StripeBillingService.extractSubscriptionDates(stripeSubscription);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are now always passing these dates since the type is now Date | null.

"payment_intent.succeeded": () => import("./_payment_intent.succeeded"),
"customer.subscription.deleted": () => import("./_customer.subscription.deleted"),
"customer.subscription.updated": () => import("./_customer.subscription.updated"),
"customer.subscription.updated": () => import("./_customer.subscription.updated/_handler"),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We moved the main handler into it's own folder to organizer the handler for the update event and product specific handlers.

subscriptionTrialEnd?: Date;
subscriptionEnd?: Date;
}
export type IBillingRepositoryCreateArgs = Omit<BillingRecord, "id">;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Args are now based on the main BillingRecord type as the source of truth.

this.teamRepository = teamRepository;
}

async handleUpdate(subscription: Omit<IBillingRepositoryCreateArgs, "teamId" | "planName">) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The args in this method aren't dependent on the raw data from Stripe.

// This is a placeholder to showcase adding new event handlers
const handler = async (data: SWHMap["payment_intent.succeeded"]["data"]) => {
const paymentIntent = data.object;
console.log(paymentIntent);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need this or else eslint throws an error that paymentIntent is an unused var

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this is an unused var right? Can we just comment this line instead?

@joeauyeung joeauyeung marked this pull request as ready for review October 15, 2025 19:29
@graphite-app graphite-app bot requested review from a team October 15, 2025 19:29
@dosubot dosubot bot added billing area: billing, stripe, payments, paypal, get paid teams area: teams, round robin, collective, managed event-types labels Oct 15, 2025
hbjORbj and others added 30 commits January 26, 2026 13:11
…dules (#27221)

* refactor: move booking-audit client components from packages/features to apps/web/modules

This is part of the larger effort to move tRPC-dependent UI components from packages/features to apps/web/modules to eliminate circular dependencies.

Changes:
- Move BookingHistory.tsx and BookingHistoryPage.tsx to apps/web/modules/booking-audit/components/
- Update imports in apps/web to use the new location

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* refactor: move formbricks client from packages/features to apps/web/modules

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* refactor: move hooks and stores from packages/features to apps/web/modules

- Move useAppsData hook from packages/features/apps/hooks to apps/web/modules/apps/hooks
- Move onboardingStore from packages/features/ee/organizations/lib to apps/web/modules/ee/organizations/lib
- Move useWelcomeModal hook from packages/features/ee/organizations/hooks to apps/web/modules/ee/organizations/hooks
- Move useAgentsData hook from packages/features/ee/workflows/hooks to apps/web/modules/ee/workflows/hooks
- Update all import paths in consuming files

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* refactor: move onboardingStore test file to apps/web/modules

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor(companion): split calcom.ts into modular service files

Split the monolithic 1692-line calcom.ts file into focused modules:
- auth.ts: Authentication configuration and token management
- bookings.ts: Booking CRUD operations and actions
- conferencing.ts: Conferencing options
- event-types.ts: Event type CRUD operations
- private-links.ts: Private link management for event types
- request.ts: Core HTTP request functionality
- schedules.ts: Schedule CRUD operations
- user.ts: User profile management
- utils.ts: JSON parsing and payload sanitization utilities
- webhooks.ts: Webhook management (global and event type specific)
- index.ts: Re-exports all functions and maintains backward compatibility

This improves code organization, maintainability, and makes it easier
to understand and modify specific functionality.

Co-Authored-By: peer@cal.com <peer@cal.com>

* fix(companion): avoid logging sensitive booking response data

Replace logging of full responseText (which may contain PII like attendee
emails and names) with responseLength for safer debugging.

Addresses Cubic AI review feedback (confidence 9/10).

Co-Authored-By: unknown <>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
… conflict (#27121)

* fix: use createPortal for FeatureOptInBanner to avoid Intercom widget conflict

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix: guard portal rendering against non-browser environments

Add typeof document check before accessing document.body in createPortal
to prevent SSR/test crashes when document is undefined.

Co-Authored-By: unknown <>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* wip flow

* add tests

* WIP migrateing view

* push back step

* fix tests and logic for adding new members to existing teams

* few UI fixes

* type fixes

* fix nits

* few UI + re-route fixes

* fix teamId when migrating
…ions (#27211)

Co-authored-by: peer@cal.com <peer@cal.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: pasqualevitiello <pasqualevitiello@gmail.com>
…27242)

* fix: remove sticky positioning from page headers in pages using ShellMain

* correct fix (mostly)

* fix: disable sticky headers on routing forms and members pages

Applied disableSticky={true} to remaining affected pages:
- Routing Forms page
- Organization Members view
- Platform Members view

---------

Co-authored-by: Dhairyashil <dhairyashil10101010@gmail.com>
* style(ui): improve padding for VerticalTabItem

* correct fix

---------

Co-authored-by: Dhairyashil <dhairyashil10101010@gmail.com>
… rate limits (#27260)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…27270)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: ali@cal.com <ali@cal.com>
* fix: improve error handling in Stripe collectCard method

- Add CollectCardFailure error code for better error categorization
- Replace generic error with ErrorWithCode for consistent error handling
- Add error mappings for common Stripe errors (customer not found, invalid API key, rate limit, etc.)
- Include Stripe error message when available for better debugging
- Follow the same pattern used in chargeCard method

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* fix: add CollectCardFailure to getHttpStatusCode mapping

This ensures the error returns 400 instead of 500 when collectCard fails.

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* docs: add comments explaining ChargeCardFailure vs CollectCardFailure

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* refactor: simplify collectCard error handling

Remove complex error mapping logic per code review feedback.

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* test: add CollectCardFailure to HTTP status code tests

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Add three new optional callbacks to CalProvider that enable users to react during the access token refresh lifecycle:
- onTokenRefreshStart: Called when token refresh begins
- onTokenRefreshSuccess: Called when token refresh succeeds
- onTokenRefreshError: Called when token refresh fails with error message

These callbacks are invoked in both token refresh locations:
1. Response interceptor (when API returns 498 status)
2. Initial access token validation

The callbacks are properly passed through the component hierarchy:
CalProvider -> BaseCalProvider -> useOAuthFlow hook

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Rajiv Sahal <sahalrajiv-extc@atharvacoe.ac.in>
* feat: add proration invoice and reminder email templates

Add email templates for monthly proration billing notifications:

- ProrationInvoiceEmail: Sent when invoice is created for additional seats
- ProrationReminderEmail: Sent 7 days later if invoice remains unpaid

Includes:
- React email templates using V2BaseEmailHtml
- BaseEmail classes for rendering
- Billing email service functions
- Translation keys for email content

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: use allSettled

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…#27289)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: lowercase identifier values in routing form editor

Co-Authored-By: peer@cal.com <peer@cal.com>

* fix: remove unused @ts-expect-error directives

Co-Authored-By: peer@cal.com <peer@cal.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Create `getSalesforceTokenLifetime`

* When connecting salesforce, add token_lifetime

* Migrate token_lifetime and refetch if token expiry doesn't match

* Add tests for Salesforce token lifetime feature

- Add unit tests for getSalesforceTokenLifetime function
- Add integration tests for token lifecycle management in CrmService
- Fix type error in CrmService.ts by extracting refreshToken variable

Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>

* chore: re-trigger CI

Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: apply Frame component to webhooks settings page

Co-Authored-By: peer@cal.com <peer@cal.com>

* refactor: include header and description in Frame component

Co-Authored-By: peer@cal.com <peer@cal.com>

* refactor: wrap each webhook in individual FramePanel

Co-Authored-By: peer@cal.com <peer@cal.com>

* refactor: implement two-row badge limit with overflow tooltip for webhooks

Co-Authored-By: peer@cal.com <peer@cal.com>

* style: improve spacing between webhook items and header

Co-Authored-By: peer@cal.com <peer@cal.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Resolved repository/interface refactoring conflicts (IBillingRepository, PrismaTeamBillingRepository, PrismaOrganizationBillingRepository, TeamRepository)
- Updated webhook handlers to use DI pattern instead of static methods from deleted stripe-billing-service.ts
- Resolved team creation route conflicts
- Preserved branch's modular webhook handler architecture while adopting main's DI patterns

Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

billing area: billing, stripe, payments, paypal, get paid core area: core, team members only devin-conflict-resolution enterprise area: enterprise, audit log, organisation, SAML, SSO ❗️ .env changes contains changes to env variables ✨ feature New feature or request ❗️ migrations contains migration files ready-for-e2e size/XL Stale teams area: teams, round robin, collective, managed event-types

Projects

None yet

Development

Successfully merging this pull request may close these issues.