File tree Expand file tree Collapse file tree 5 files changed +112
-0
lines changed
app/team/[team_slug]/(team)/subscribe Expand file tree Collapse file tree 5 files changed +112
-0
lines changed Original file line number Diff line number Diff line change @@ -115,6 +115,7 @@ const SENTRY_OPTIONS: SentryBuildOptions = {
115115} ;
116116
117117const baseNextConfig : NextConfig = {
118+ serverExternalPackages : [ "pino-pretty" ] ,
118119 async headers ( ) {
119120 return [
120121 {
Original file line number Diff line number Diff line change 1+ import "server-only" ;
2+ import { API_SERVER_URL , getAbsoluteUrlFromPath } from "@/constants/env" ;
3+ import { getAuthToken } from "../../app/api/lib/getAuthToken" ;
4+
5+ export async function getStripeCheckoutLink ( slug : string , sku : string ) {
6+ const token = await getAuthToken ( ) ;
7+
8+ if ( ! token ) {
9+ return {
10+ status : 401 ,
11+ link : null ,
12+ } ;
13+ }
14+
15+ const res = await fetch (
16+ `${ API_SERVER_URL } /v1/teams/${ slug } /checkout/create-link` ,
17+ {
18+ method : "POST" ,
19+ body : JSON . stringify ( {
20+ sku : decodeURIComponent ( sku ) ,
21+ redirectTo : getAbsoluteUrlFromPath (
22+ `/team/${ slug } /~/settings/billing` ,
23+ ) . toString ( ) ,
24+ } ) ,
25+ headers : {
26+ "Content-Type" : "application/json" ,
27+ Authorization : `Bearer ${ token } ` ,
28+ } ,
29+ } ,
30+ ) ;
31+ if ( res . ok ) {
32+ return {
33+ status : 200 ,
34+ link : ( await res . json ( ) ) ?. result as string ,
35+ } as const ;
36+ }
37+ console . log ( await res . json ( ) ) ;
38+ return {
39+ status : res . status ,
40+ link : null ,
41+ } as const ;
42+ }
Original file line number Diff line number Diff line change @@ -33,3 +33,14 @@ export const THIRDWEB_ENGINE_URL = process.env.THIRDWEB_ENGINE_URL;
3333export const THIRDWEB_ACCESS_TOKEN = process . env . THIRDWEB_ACCESS_TOKEN ;
3434// Comma-separated list of chain IDs to disable faucet for.
3535export const DISABLE_FAUCET_CHAIN_IDS = process . env . DISABLE_FAUCET_CHAIN_IDS ;
36+
37+ export function getAbsoluteUrlFromPath ( path : string ) {
38+ const url = new URL (
39+ isProd
40+ ? "https://thirdweb.com"
41+ : process . env . NEXT_PUBLIC_VERCEL_BRANCH_URL || "https://thirdweb-dev.com" ,
42+ ) ;
43+
44+ url . pathname = path ;
45+ return url ;
46+ }
Original file line number Diff line number Diff line change 1+ Note: this path is meant specifically for subscribing to a plan or product, it renders no UI and instead will redirect the user to stripe UI to complete the subscription process.
2+
3+ Why have this at all? Because this way we can have a public link that will (after login complete) redirect the user to the correct subscrtiption page.
Original file line number Diff line number Diff line change 1+ import { getStripeCheckoutLink } from "@/api/team-billing" ;
2+ import { RedirectType , notFound , redirect } from "next/navigation" ;
3+
4+ interface PageParams {
5+ team_slug : string ;
6+ sku : string ;
7+ }
8+
9+ interface PageProps {
10+ params : Promise < PageParams > ;
11+ }
12+
13+ export default async function TeamProductSkuSubscriptionRedirect (
14+ props : PageProps ,
15+ ) {
16+ const params = await props . params ;
17+ // get the stripe checkout link for the team + sku from the API
18+ // this returns a status code and a link (if success)
19+ // 200: success
20+ // 400: invalid SKU
21+ // 401: user not authenticated
22+ // 403: user not allowed to subscribe (not admin)
23+ // 500: something random else went wrong
24+ const { link, status } = await getStripeCheckoutLink (
25+ params . team_slug ,
26+ params . sku ,
27+ ) ;
28+
29+ console . log ( "sku" , params . sku ) ;
30+
31+ console . log ( "status" , status ) ;
32+
33+ if ( link ) {
34+ // we want to REPLACE so when the user navigates BACK the do not end up back here but on the previous page
35+ redirect ( link , RedirectType . replace ) ;
36+ }
37+
38+ switch ( status ) {
39+ case 400 : {
40+ return < div > Invalid SKU: { params . sku } </ div > ;
41+ }
42+ case 401 : {
43+ return < div > User not authenticated</ div > ;
44+ }
45+ case 403 : {
46+ return < div > User not allowed to subscribe</ div > ;
47+ }
48+
49+ // default case
50+ default : {
51+ // todo handle this better
52+ notFound ( ) ;
53+ }
54+ }
55+ }
You can’t perform that action at this time.
0 commit comments