-
Notifications
You must be signed in to change notification settings - Fork 350
Subscription #423
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Subscription #423
Conversation
brandonkachen
left a comment
There was a problem hiding this 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)
|
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 |
brandonkachen
left a comment
There was a problem hiding this 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) { |
There was a problem hiding this comment.
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', |
There was a problem hiding this comment.
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”.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| }, | ||
| ) | ||
|
|
||
| try { |
There was a problem hiding this comment.
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
jahooma
left a comment
There was a problem hiding this 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
No description provided.