Skip to content

Commit fe5324a

Browse files
committed
Let users upgrade/downgrade from pricing page (linked from hitting limit in cli)
1 parent 4461825 commit fe5324a

File tree

5 files changed

+216
-66
lines changed

5 files changed

+216
-66
lines changed

common/src/types/subscription.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* Core subscription information for an active subscription.
33
*/
44
export interface SubscriptionInfo {
5+
id: string
56
status: string
67
billingPeriodEnd: string
78
cancelAtPeriodEnd: boolean
@@ -64,4 +65,3 @@ export interface ActiveSubscriptionResponse {
6465
* Use `hasSubscription` to narrow the type.
6566
*/
6667
export type SubscriptionResponse = NoSubscriptionResponse | ActiveSubscriptionResponse
67-

web/src/app/api/user/billing-portal/_post.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,33 @@ export type Session = {
1313

1414
export type GetSessionFn = () => Promise<Session>
1515

16-
export type CreateBillingPortalSessionFn = (params: {
16+
export type BillingPortalFlowData = {
17+
type: 'subscription_update'
18+
subscription_update: {
19+
subscription: string
20+
}
21+
}
22+
23+
export type CreateBillingPortalSessionParams = {
1724
customer: string
1825
return_url: string
19-
}) => Promise<{ url: string }>
26+
flow_data?: BillingPortalFlowData
27+
}
28+
29+
export type CreateBillingPortalSessionFn = (
30+
params: CreateBillingPortalSessionParams
31+
) => Promise<{ url: string }>
2032

2133
export type PostBillingPortalParams = {
2234
getSession: GetSessionFn
2335
createBillingPortalSession: CreateBillingPortalSessionFn
2436
logger: Logger
2537
returnUrl: string
38+
flowData?: BillingPortalFlowData
2639
}
2740

2841
export async function postBillingPortal(params: PostBillingPortalParams) {
29-
const { getSession, createBillingPortalSession, logger, returnUrl } = params
42+
const { getSession, createBillingPortalSession, logger, returnUrl, flowData } = params
3043

3144
const session = await getSession()
3245
if (!session?.user?.id) {
@@ -42,10 +55,16 @@ export async function postBillingPortal(params: PostBillingPortalParams) {
4255
}
4356

4457
try {
45-
const portalSession = await createBillingPortalSession({
58+
const portalParams: CreateBillingPortalSessionParams = {
4659
customer: stripeCustomerId,
4760
return_url: returnUrl,
48-
})
61+
}
62+
63+
if (flowData) {
64+
portalParams.flow_data = flowData
65+
}
66+
67+
const portalSession = await createBillingPortalSession(portalParams)
4968

5069
return NextResponse.json({ url: portalSession.url })
5170
} catch (error) {

web/src/app/api/user/billing-portal/route.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,37 @@ import { env } from '@codebuff/internal/env'
22
import { stripeServer } from '@codebuff/internal/util/stripe'
33
import { getServerSession } from 'next-auth'
44

5+
import type { NextRequest } from 'next/server'
6+
57
import { authOptions } from '@/app/api/auth/[...nextauth]/auth-options'
68
import { logger } from '@/util/logger'
79

810
import { postBillingPortal } from './_post'
911

10-
export async function POST() {
12+
import type { BillingPortalFlowData } from './_post'
13+
14+
export async function POST(req: NextRequest) {
15+
// Parse optional subscriptionId from request body for deep-linking to subscription update
16+
let flowData: BillingPortalFlowData | undefined
17+
const body = await req.json().catch(() => null)
18+
if (body?.subscriptionId) {
19+
flowData = {
20+
type: 'subscription_update',
21+
subscription_update: {
22+
subscription: body.subscriptionId,
23+
},
24+
}
25+
}
26+
27+
// Determine return URL - use provided returnUrl or default to /pricing
28+
const returnUrl = body?.returnUrl || `${env.NEXT_PUBLIC_CODEBUFF_APP_URL}/pricing`
29+
1130
return postBillingPortal({
1231
getSession: () => getServerSession(authOptions),
1332
createBillingPortalSession: (params) =>
1433
stripeServer.billingPortal.sessions.create(params),
1534
logger,
16-
returnUrl: `${env.NEXT_PUBLIC_CODEBUFF_APP_URL}/profile`,
35+
returnUrl,
36+
flowData,
1737
})
1838
}

web/src/app/api/user/subscription/route.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export async function GET() {
5151
hasSubscription: true,
5252
displayName: SUBSCRIPTION_DISPLAY_NAME,
5353
subscription: {
54+
id: subscription.stripe_subscription_id,
5455
status: subscription.status,
5556
billingPeriodEnd: subscription.billing_period_end.toISOString(),
5657
cancelAtPeriodEnd: subscription.cancel_at_period_end,

0 commit comments

Comments
 (0)