Skip to content

Implement proration#1

Open
charleslpan wants to merge 30 commits into
mainfrom
feat/proration
Open

Implement proration#1
charleslpan wants to merge 30 commits into
mainfrom
feat/proration

Conversation

@charleslpan
Copy link
Copy Markdown
Owner

What does this PR do?

  • Fixes #XXXX (GitHub issue number)
  • Fixes CAL-XXXX (Linear issue number - should be visible at the bottom of the GitHub issue description)

Visual Demo (For contributors especially)

A visual demonstration is strongly recommended, for both the original and new change (video / image - any one).

Video Demo (if applicable):

  • Show screen recordings of the issue or feature.
  • Demonstrate how to reproduce the issue, the behavior before and after the change.

Image Demo (if applicable):

  • Add side-by-side screenshots of the original and updated change.
  • Highlight any significant change(s).

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?

  • Are there environment variables that should be set?
  • What are the minimal test data to have?
  • What is expected (happy path) to have (input and output)?
  • Any other important info that could help to test that PR

Checklist

  • I haven't read the contributing guide
  • My code doesn't follow the style guidelines of this project
  • I haven't commented my code, particularly in hard-to-understand areas
  • I haven't checked if my changes generate no new warnings

sean-brydon and others added 30 commits January 12, 2026 13:45
- skip sub updates monthy proration
- seat change logs for new user invites
- cron month key params
- Add operationId field to SeatChangeLog for idempotency (prevents duplicate seat change logs from race conditions)
- Add voidInvoice method to billing service (prevents double charging on retry)
- Add days_until_due for send_invoice collection method (ensures invoices have proper due dates)
- Fix type error in stripe-subscription-utils (null to undefined conversion)

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

Addresses Cubic AI feedback: silent error suppression could lead to
double charging if the Stripe API call fails. Now the error is logged
and re-thrown so callers can handle failures appropriately.

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: sean@cal.com <Sean@brydon.io>
…sitory

Co-Authored-By: sean@cal.com <Sean@brydon.io>
Copy link
Copy Markdown
Collaborator

@dastratakos dastratakos left a comment

Choose a reason for hiding this comment

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

comment review

Comment thread apps/web/app/api/cron/monthly-proration/route.ts
Comment on lines 7 to 10
import { getBillingProviderService } from "@calcom/ee/billing/di/containers/Billing";
import { getTeamBillingServiceFactory } from "@calcom/ee/billing/di/containers/Billing";
import { extractBillingDataFromStripeSubscription } from "@calcom/features/ee/billing/lib/stripe-subscription-utils";
import { Plan, SubscriptionStatus } from "@calcom/features/ee/billing/repository/billing/IBillingRepository";
Copy link
Copy Markdown
Collaborator

@dastratakos dastratakos Feb 20, 2026

Choose a reason for hiding this comment

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

inline comment with markdown

Markdown comment

header 2

bold and italics

code

quote

link

  1. numbered
  2. list
  • []

  • []

  • []

  • []

  • []

  • []

  • bullet

  • list

  • task

  • list


Note

This is a note. It highlights information that users should take into account, even when skimming.

Copy link
Copy Markdown
Collaborator

@dastratakos dastratakos left a comment

Choose a reason for hiding this comment

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

asdf

@@ -0,0 +1,131 @@
import process from "node:process";
import { getMonthlyProrationTasker } from "@calcom/features/ee/billing/di/tasker/MonthlyProrationTasker.container";
import { formatMonthKey } from "@calcom/features/ee/billing/lib/month-key";
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

test comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pending

Comment thread apps/web/app/api/cron/monthly-proration/route.ts
const monthKey = requestedMonthKey || defaultMonthKey;

// Validate monthKey is not in the future and is within a reasonable range (12 months)
if (requestedMonthKey) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

what's going on here?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

test reply


log.info(`Scheduling monthly proration tasks for ${monthKey}`);

const teamRepository = new MonthlyProrationTeamRepository(prisma);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pending comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pending reply

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pending reply 2

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

a

Comment thread apps/web/app/api/cron/monthly-proration/route.ts
@@ -124,7 +125,11 @@ const handler = async (data: SWHMap["invoice.paid"]["data"]) => {
// Get the Stripe subscription object
const stripeSubscription = await stripe.subscriptions.retrieve(paymentSubscriptionId);
const billingService = getBillingProviderService();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pending on left

quantity: number;
prorationBehavior?: ProrationBehavior;
logger?: ISimpleLogger;
}): Promise<void> {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

vvvv

Comment thread apps/web/app/api/cron/monthly-proration/route.ts
Copy link
Copy Markdown
Collaborator

@dastratakos dastratakos left a comment

Choose a reason for hiding this comment

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

test comment

Comment thread apps/web/app/api/cron/monthly-proration/route.ts
Copy link
Copy Markdown
Collaborator

@dastratakos dastratakos left a comment

Choose a reason for hiding this comment

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

test comment

Comment thread apps/web/app/api/teams/create/route.ts
Comment on lines +114 to +118
subscriptionEnd,
subscriptionTrialEnd,
billingPeriod,
pricePerSeat,
paidSeats,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pending reply

Copy link
Copy Markdown
Collaborator

@dastratakos dastratakos left a comment

Choose a reason for hiding this comment

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

comment

Comment thread yarn.lock
linkType: hard

"@opentelemetry/api-logs@npm:0.209.0":
version: 0.209.0
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pending comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pending reply

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pending

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

direct reply

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

test

Comment thread yarn.lock
dependencies:
"@opentelemetry/api": "npm:^1.3.0"
checksum: 10/bd270d8b60b2fb8124174f0ec0babe7b57eca69f58feb6f4e1394714591324939dd4049a29499f435e948011cc8e5bdc9d0da2a2a96bd8be8ee9e5a3d297dfa0
checksum: 10/fc9bf8693105f73162d8e6999443d5f15b2b3226f36a88815d5c5cc5a9771ecec92a808356087da062c24a4ad467891b33d8ae884fbe515cb1711bb7b192e371
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pending comment to existing

Comment thread yarn.lock
"@opentelemetry/api-logs@npm:0.209.0":
version: 0.209.0
resolution: "@opentelemetry/api-logs@npm:0.209.0"
"@opentelemetry/api-logs@npm:0.210.0":
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pending

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

test pending reply

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

direct

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

start review

Comment thread apps/web/app/api/cron/monthly-proration/route.ts
Copy link
Copy Markdown
Collaborator

@dastratakos dastratakos left a comment

Choose a reason for hiding this comment

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

review comment no inlines

@@ -0,0 +1,131 @@
import process from "node:process";
import { getMonthlyProrationTasker } from "@calcom/features/ee/billing/di/tasker/MonthlyProrationTasker.container";
import { formatMonthKey } from "@calcom/features/ee/billing/lib/month-key";
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pending

Comment thread apps/web/app/api/cron/monthly-proration/route.ts
Comment thread apps/web/app/api/cron/monthly-proration/route.ts
const isEnabled = await featuresRepository.checkIfFeatureIsEnabledGlobally(
"monthly-proration"
);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pending

Comment thread apps/web/app/api/cron/monthly-proration/route.ts
Comment thread apps/web/app/api/cron/monthly-proration/route.ts
Comment thread apps/web/app/api/cron/monthly-proration/route.ts
Copy link
Copy Markdown
Collaborator

@dastratakos dastratakos left a comment

Choose a reason for hiding this comment

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

pending

Comment thread packages/prisma/schema.prisma
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pending

@@ -3000,6 +3003,7 @@ model SeatChangeLog {
organizationBillingId String?
organizationBilling OrganizationBilling? @relation(fields: [organizationBillingId], references: [id])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pending

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pending

organizationBilling: {
findUnique: vi.fn(),
update: vi.fn(),
},
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pending

Copy link
Copy Markdown
Collaborator

@dastratakos dastratakos left a comment

Choose a reason for hiding this comment

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

approve

Copy link
Copy Markdown
Collaborator

@dastratakos dastratakos left a comment

Choose a reason for hiding this comment

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

cmon

Copy link
Copy Markdown
Collaborator

@dastratakos dastratakos left a comment

Choose a reason for hiding this comment

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

cmon

Copy link
Copy Markdown
Collaborator

@dastratakos dastratakos left a comment

Choose a reason for hiding this comment

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

dog

Copy link
Copy Markdown
Collaborator

@dastratakos dastratakos left a comment

Choose a reason for hiding this comment

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

x

} = params;
const monthKey = providedMonthKey || formatMonthKey(new Date());

const { teamBillingId, organizationBillingId } = await this.repository.getTeamBillingIds(teamId);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

buss

Copy link
Copy Markdown
Collaborator

@dastratakos dastratakos left a comment

Choose a reason for hiding this comment

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

a

@@ -3000,6 +3003,7 @@ model SeatChangeLog {
organizationBillingId String?
organizationBilling OrganizationBilling? @relation(fields: [organizationBillingId], references: [id])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

pending

Copy link
Copy Markdown
Collaborator

@dastratakos dastratakos left a comment

Choose a reason for hiding this comment

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

a

Copy link
Copy Markdown
Collaborator

@dastratakos dastratakos left a comment

Choose a reason for hiding this comment

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

x

Comment thread apps/web/app/api/cron/monthly-proration/route.ts
@dastratakos
Copy link
Copy Markdown
Collaborator

LaTeX
This sentence uses $ delimiters to show math inline: $\sqrt{3x-1}+(1+x)^2$

This sentence uses $` and `$ delimiters to show math inline: $\sqrt{3x-1}+(1+x)^2$

The Cauchy-Schwarz Inequality
$$\left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right)$$

The Cauchy-Schwarz Inequality

$$ \left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right) $$

The Cauchy-Schwarz Inequality

$$\left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right)$$

This expression uses \$ to display a dollar sign: $\sqrt{\$4}$

To split $100 in half, we calculate $100/2$

Copy link
Copy Markdown
Collaborator

test

@dastratakos dastratakos marked this pull request as ready for review April 24, 2026 02:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants