1+
2+ import { loadStripe } from '@stripe/stripe-js' ;
3+ const STRIPE_KEY = process . env . NEXT_PUBLIC_APP_STRIPE_KEY ;
4+
5+ export default function Billing ( ) {
6+
7+
8+
9+
10+
11+ const redirectToCheckout = async ( event ) => {
12+ try {
13+ const stripe = await loadStripe ( STRIPE_KEY ) ;
14+ const subscriptionType = document . getElementById ( 'paymentType' ) . value ;
15+ console . log ( subscriptionType ) ;
16+
17+ const response = await fetch (
18+ `${ process . env . NEXT_PUBLIC_API_URL } /payments/stripe/create-checkout-session` ,
19+ {
20+ method : 'POST' ,
21+ body : JSON . stringify ( {
22+ subType : subscriptionType ,
23+ quantity : 1 ,
24+ operation : 'subscription' ,
25+ data : { } ,
26+ } ) ,
27+ headers : {
28+ 'Content-Type' : 'application/json' ,
29+ } ,
30+ credentials : 'include'
31+ }
32+ ) ;
33+
34+ const session = await response . json ( ) ;
35+ if ( session . error ) {
36+ console . log ( 'Creating the stripe session failed' ) ;
37+ return ;
38+ }
39+
40+ const result = await stripe . redirectToCheckout ( {
41+ sessionId : session . sessionId ,
42+ } ) ;
43+
44+ if ( result . error ) {
45+ console . log ( result . error . message ) ;
46+ }
47+ } catch ( error ) {
48+ console . log ( error ) ;
49+ } g
50+ } ;
51+
52+ const updateCardInfo = async ( ) => {
53+ try {
54+ const stripe = await loadStripe ( STRIPE_KEY ) ;
55+ const subscriptionType = document . getElementById ( 'paymentType' ) . value ;
56+
57+ const response = await fetch (
58+ `${ process . env . NEXT_PUBLIC_API_URL } /payments/stripe/update-card` ,
59+ {
60+ method : 'POST' ,
61+ credentials : 'include' ,
62+ body : JSON . stringify ( {
63+ subType : subscriptionType ,
64+ } ) ,
65+ headers : {
66+ 'Content-Type' : 'application/json' ,
67+ } ,
68+ }
69+ ) ;
70+
71+ const session = await response . json ( ) ;
72+
73+ await stripe . redirectToCheckout ( {
74+ sessionId : session . sessionId ,
75+ } ) ;
76+ } catch ( error ) {
77+ console . log ( error ) ;
78+ }
79+ } ;
80+
81+ const cancelSubscription = async ( ) => {
82+ try {
83+ const subscriptionType = document . getElementById ( 'paymentType' ) . value ;
84+ const url = `${ process . env . NEXT_PUBLIC_API_URL } /payments/stripe/cancel` ;
85+ const response = await fetch ( url , {
86+ method : 'PUT' ,
87+ credentials : 'include' ,
88+ body : JSON . stringify ( {
89+ subType : subscriptionType ,
90+ } ) ,
91+ headers : {
92+ 'Content-Type' : 'application/json' ,
93+ } ,
94+ } ) ;
95+ const data = await response . json ( ) ;
96+ console . log ( data . message ) ;
97+ } catch ( err ) {
98+ console . log ( err ) ;
99+ }
100+ } ;
101+
102+ return (
103+ < div className = "flex-1 xl:overflow-y-auto" >
104+ < div className = "mx-auto max-w-3xl px-4 py-10 sm:px-6 lg:px-8 lg:py-12" >
105+ < h1 className = "mb-3 text-3xl font-bold tracking-tight text-white" >
106+ Billing
107+ </ h1 >
108+
109+ < div className = "w-2/3 rounded-sm bg-neutral-800 px-4 py-4 pb-12" >
110+ < div className = "flex" >
111+ < div >
112+ < h1 className = "text-white" > FREE</ h1 >
113+ < h1 className = "text-xl text-white" > Standard Account</ h1 >
114+ </ div >
115+ < div className = "ml-auto" >
116+ < button className = "mt-2 hidden border border-blue-600 px-2 py-1 text-blue-600 hover:bg-neutral-700" >
117+ CHANGE PLAN
118+ </ button >
119+ </ div >
120+ </ div >
121+ < h1 className = "mt-4 font-semibold text-white" >
122+ Usage Limits
123+ </ h1 >
124+
125+ < div className = "mb-1 flex justify-between" >
126+ < span className = "text-base font-medium text-white" >
127+ Terminal Usage
128+ </ span >
129+ < span className = "text-sm font-medium text-white" >
130+ 0%{ ' ' }
131+ </ span >
132+ </ div >
133+ < div className = "h-2.5 w-full rounded-full bg-neutral-700" >
134+ < div
135+ className = "h-2.5 rounded-full bg-blue-600"
136+ style = { { width : '0%' } }
137+ > </ div >
138+ </ div >
139+
140+ < div className = "mb-1 mt-4 flex justify-between" >
141+ < span className = "text-base font-medium text-white" >
142+ CPU Burst Usage
143+ </ span >
144+ < span className = "text-sm font-medium text-white" > 0%</ span >
145+ </ div >
146+ < div className = "h-2.5 w-full rounded-full bg-neutral-700" >
147+ < div
148+ className = "h-2.5 rounded-full bg-blue-600"
149+ style = { { width : '0%' } }
150+ > </ div >
151+ </ div >
152+
153+ < h1 className = "mt-4 text-white " >
154+ Container Hardware: Standard Compute
155+ </ h1 >
156+ </ div >
157+
158+ < p className = "mt-5 text-white" >
159+ CTFGuide currently has a very generous grant from Google Cloud
160+ Platform, which allows us to provide free compute to our
161+ users. However, this grant is limited, and we may have to
162+ start charging for compute in the future. If we do, we will
163+ give you a 30 day notice before we start charging for compute.
164+ </ p >
165+
166+ < div className = "hidden items-center justify-between text-white" >
167+ < hr className = "mb-2 mt-2 border-neutral-600 text-white" />
168+ < h1 className = "mt-4 text-center text-4xl" >
169+ Upgrade to{ ' ' }
170+ < span className = "font-bold text-blue-500" >
171+ CTFGuide Pro
172+ </ span >
173+ </ h1 >
174+ < div className = "grid grid-cols-2 gap-4" >
175+ < div
176+ className = "hover mt-4 rounded border border-neutral-700 px-4 py-4 text-white"
177+ style = { { cursor : 'pointer' } }
178+ >
179+ < h1 className = "text-center text-3xl" > Monthly</ h1 >
180+ < h1 className = "text-center text-xl" > $4.99/month</ h1 >
181+ </ div >
182+ < div
183+ className = "hover mt-4 rounded border border-neutral-700 px-4 py-4 text-white"
184+ style = { { cursor : 'pointer' } }
185+ >
186+ < h1 className = "text-center text-3xl" > Annually</ h1 >
187+ < h1 className = "text-center text-xl" > $35.88/year</ h1 >
188+ </ div >
189+ </ div >
190+ < h1 className = "mb-1 mt-4 text-center text-xl" >
191+ What do you get?
192+ </ h1 >
193+ < div className = "px-2 py-1 text-center" >
194+ < p > Access to exclusive learning content.</ p >
195+ </ div >
196+ < div className = "mt-1 px-2 py-1 text-center" >
197+ < p > Show of an exclusive CTFGuide Pro badge</ p >
198+ </ div >
199+ < div className = "mt-1 px-2 py-1 text-center" >
200+ < p > Animated profile pictures**</ p >
201+ </ div >
202+ < div className = "mt-1 px-2 py-1 text-center" >
203+ < p > Increased container time</ p >
204+ </ div >
205+ < div className = "mt-1 px-2 py-1 text-center" >
206+ < p > AI Tutor**</ p >
207+ </ div >
208+ < p className = "mt-4 text-sm text-gray-500" >
209+ * For the features marked with a star, it means it has not
210+ been released yet. For every month you have CTFGuide Pro, if
211+ the feature has not been implemented yet, you'll be given an
212+ additional free month of Pro.
213+ </ p >
214+ </ div >
215+
216+ < div className = "hidden" >
217+ < hr className = "mt-4 border-neutral-500" > </ hr >
218+ < h1 className = "mt-4 text-white" > Dev Testing</ h1 >
219+
220+ < select
221+ id = "paymentType"
222+ className = "mt-4 border-none bg-neutral-800 py-1 text-white"
223+ >
224+ < option value = "CTFGuidePro" > CTFGuidePro</ option >
225+ < option value = "CTFGuideStudentEDU" >
226+ CTFGuideStudentsEDU
227+ </ option >
228+ < option value = "CTFGuideInstitutionEDU" >
229+ CTFGuideInstitutionEDU
230+ </ option >
231+ </ select >
232+
233+ < br > </ br >
234+ < button
235+ onClick = { redirectToCheckout }
236+ className = "text-md mt-4 rounded-lg bg-blue-600 px-2 py-1 text-white"
237+ >
238+ Stripe Checkout Demo
239+ </ button >
240+ < br > </ br >
241+
242+ < button
243+ onClick = { updateCardInfo }
244+ className = "text-md mt-4 rounded-lg bg-blue-600 px-2 py-1 text-white"
245+ >
246+ Update card infomation
247+ </ button >
248+ < button
249+ onClick = { cancelSubscription }
250+ className = "text-md mt-4 rounded-lg bg-blue-600 px-2 py-1 text-white"
251+ >
252+ cancel subscription
253+ </ button >
254+ </ div >
255+ </ div >
256+ </ div >
257+ ) ;
258+ }
0 commit comments