Skip to content

Conversation

@jahooma
Copy link
Contributor

@jahooma jahooma commented Jan 28, 2026

No description provided.

Copy link
Collaborator

@brandonkachen brandonkachen left a comment

Choose a reason for hiding this comment

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

looks good for a first pass. but lots of LLM-slop here, particularly code patterns like this:

const customerId =
    typeof stripeSubscription.customer === 'string'
      ? stripeSubscription.customer
      : stripeSubscription.customer.id

we can instead add a generic function to do this:

export function resolveExpandableId<T extends { id: string }>(
  value: string | T,
): string {
  return typeof value === 'string' ? value : value.id
}

const customerId = resolveExpandableId(stripeSubscription.customer)

@brandonkachen
Copy link
Collaborator

oh and please make sure to run the db migration locally once you're happy with the db schema, so we have all the artifact files that drizzle will apply on prod when you merge

Copy link
Collaborator

@brandonkachen brandonkachen left a comment

Choose a reason for hiding this comment

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

looks good! most of my comments are code readability nits/suggestions, so that means we're on the right track!

case 'customer.subscription.updated':
case 'customer.subscription.updated': {
const sub = event.data.object as Stripe.Subscription
if (sub.metadata?.organization_id) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

aren't we deprecating the orgs stuff? if so, we can just error out earlier on in the webhook, instead of running code we don't want to maintain and cluttering up each of these cases

subscription.stripe_subscription_id,
{
items: [{ id: itemId, price: newPriceId }],
proration_behavior: 'create_prorations',
Copy link
Collaborator

Choose a reason for hiding this comment

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

IMPORTANT: this is incorrect. We want upgrades prorate now, downgrades scheduled at period end. ChatGPT says:

Upgrades
Use:

  • proration_behavior: "always_invoice"
  • (recommended) payment_behavior: "pending_if_incomplete" to avoid granting the upgrade if payment fails

Downgrades
Don’t “change price now”. Instead, schedule the downgrade for current_period_end using a Subscription Schedule. Stripe explicitly suggests subscription schedules if you’re changing a subscription at the end of the billing period.

Conceptually:

  • Phase 1: current price until current_period_end
  • Phase 2: cheaper price starting at current_period_end
    Set the phase transition proration_behavior: "none" so there’s no proration credit at the transition

This gives you exactly: “stay on current tier until renewal, then downgrade, no credit”.

Copy link
Collaborator

Choose a reason for hiding this comment

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

},
)

try {
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe this try/catch block can go into a webhook; this function/route can just focus on being a wrapper around stripe, and then the webhook can deal with the consequences.

this would let us organize our logic using:

  • routes as proxies for stripe actions
  • webhooks to propagate changes from stripe to our system

Copy link
Contributor Author

@jahooma jahooma left a comment

Choose a reason for hiding this comment

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

first pass, will address remaining

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