1+ import React , { useState , useEffect } from "react" ;
2+ import { useParams , useNavigate } from 'react-router-dom' ;
3+ import "./style/billing.css" ;
4+ import axios from "axios" ;
5+ import urlJoin from "url-join" ;
6+
7+ const EXAMPLE_MAIN_URL = window . location . origin ;
8+
9+ export const Billing = ( ) => {
10+ const [ plans , setPlans ] = useState ( [ ] ) ;
11+ const [ currentSubscription , setCurrentSubscription ] = useState ( null ) ;
12+ const [ loading , setLoading ] = useState ( true ) ;
13+ const [ error , setError ] = useState ( null ) ;
14+ const { company_id, application_id } = useParams ( ) ;
15+ const navigate = useNavigate ( ) ;
16+
17+ useEffect ( ( ) => {
18+ fetchPlans ( ) ;
19+ fetchCurrentSubscription ( ) ;
20+ } , [ company_id ] ) ;
21+
22+ const fetchPlans = async ( ) => {
23+ try {
24+ const { data } = await axios . get ( urlJoin ( EXAMPLE_MAIN_URL , '/api/billing/plans' ) , {
25+ headers : {
26+ "x-company-id" : company_id ,
27+ } ,
28+ params : { company_id }
29+ } ) ;
30+
31+ // Handle the response structure: {"plans": [...]}
32+ const plansArray = data . plans || data ;
33+
34+ // Ensure data is an array
35+ if ( Array . isArray ( plansArray ) ) {
36+ setPlans ( plansArray ) ;
37+ } else {
38+ console . error ( "Plans data is not an array:" , plansArray ) ;
39+ setPlans ( [ ] ) ;
40+ }
41+ } catch ( error ) {
42+ console . error ( "Error fetching plans:" , error ) ;
43+ setError ( "Failed to load subscription plans" ) ;
44+ setPlans ( [ ] ) ;
45+ }
46+ } ;
47+
48+ const fetchCurrentSubscription = async ( ) => {
49+ try {
50+ const { data } = await axios . get ( urlJoin ( EXAMPLE_MAIN_URL , '/api/billing/subscription' ) , {
51+ headers : {
52+ "x-company-id" : company_id ,
53+ } ,
54+ params : { company_id }
55+ } ) ;
56+ console . log ( 'Current subscription data:' , data ) ;
57+
58+ // If we have a subscription with plan_id, fetch the plan details
59+ if ( data && data . plan_id ) {
60+ try {
61+ const planResponse = await axios . get ( urlJoin ( EXAMPLE_MAIN_URL , '/api/billing/plans' ) , {
62+ headers : {
63+ "x-company-id" : company_id ,
64+ } ,
65+ params : { company_id }
66+ } ) ;
67+
68+ const plans = planResponse . data . plans || planResponse . data ;
69+ const currentPlan = plans . find ( plan => plan . id === data . plan_id ) ;
70+
71+ if ( currentPlan ) {
72+ // Merge plan details with subscription data
73+ setCurrentSubscription ( {
74+ ...data ,
75+ plan_name : currentPlan . name ,
76+ plan_price : currentPlan . price ,
77+ plan_interval : currentPlan . interval
78+ } ) ;
79+ } else {
80+ setCurrentSubscription ( data ) ;
81+ }
82+ } catch ( planError ) {
83+ console . error ( "Error fetching plan details:" , planError ) ;
84+ setCurrentSubscription ( data ) ;
85+ }
86+ } else {
87+ setCurrentSubscription ( data ) ;
88+ }
89+ } catch ( error ) {
90+ console . error ( "Error fetching subscription:" , error ) ;
91+ setCurrentSubscription ( null ) ;
92+ } finally {
93+ setLoading ( false ) ;
94+ }
95+ } ;
96+
97+ const handleSubscribe = async ( planId ) => {
98+ try {
99+ console . log ( 'Subscribing to plan:' , planId ) ;
100+ // Use the same callback URL pattern as the working extension
101+ const callbackUrl = `${ EXAMPLE_MAIN_URL } /company/${ company_id } /subscription-status` ;
102+ const { data } = await axios . post ( urlJoin ( EXAMPLE_MAIN_URL , '/api/billing/subscribe' ) , {
103+ company_id,
104+ plan_id : planId ,
105+ callback_url : callbackUrl
106+ } , {
107+ headers : {
108+ "x-company-id" : company_id ,
109+ }
110+ } ) ;
111+
112+ console . log ( 'Subscription response:' , data ) ;
113+
114+ // Redirect to Fynd's billing page
115+ if ( data . redirect_url ) {
116+ window . location . href = data . redirect_url ;
117+ }
118+ } catch ( error ) {
119+ console . error ( "Error subscribing to plan:" , error ) ;
120+ setError ( "Failed to subscribe to plan" ) ;
121+ }
122+ } ;
123+
124+ const handleContinueToExtension = ( ) => {
125+ // Navigate to the main extension page
126+ if ( application_id ) {
127+ navigate ( `/company/${ company_id } /application/${ application_id } ` ) ;
128+ } else {
129+ navigate ( `/company/${ company_id } /` ) ;
130+ }
131+ } ;
132+
133+ if ( loading ) {
134+ return < div className = "loader" > Loading billing information...</ div > ;
135+ }
136+
137+ if ( error ) {
138+ return (
139+ < div className = "billing-container" >
140+ < div className = "error-message" >
141+ < h3 > Error</ h3 >
142+ < p > { error } </ p >
143+ < button onClick = { ( ) => window . location . reload ( ) } > Retry</ button >
144+ </ div >
145+ </ div >
146+ ) ;
147+ }
148+
149+ return (
150+ < div className = "billing-container" >
151+ < div className = "title" >
152+ Subscription Plans
153+ </ div >
154+
155+ { currentSubscription && currentSubscription . status === 'active' && (
156+ < div className = "current-subscription" >
157+ < h3 > Current Subscription</ h3 >
158+ < div className = "subscription-details" >
159+ < p > < strong > Plan:</ strong > { currentSubscription . plan_name } </ p >
160+ < p > < strong > Status:</ strong > { currentSubscription . status } </ p >
161+ < p > < strong > Activated:</ strong > { new Date ( currentSubscription . activated_on ) . toLocaleDateString ( ) } </ p >
162+ < p > < strong > Price:</ strong > ₹{ currentSubscription . plan_price ?. amount } /{ currentSubscription . plan_interval } </ p >
163+ </ div >
164+ < button
165+ className = "continue-button"
166+ onClick = { handleContinueToExtension }
167+ >
168+ Continue to Extension
169+ </ button >
170+ </ div >
171+ ) }
172+
173+ { ( ! currentSubscription || currentSubscription . status !== 'active' ) && (
174+ < div className = "plans-grid" >
175+ { Array . isArray ( plans ) && plans . length > 0 ? (
176+ plans . map ( ( plan ) => (
177+ < div key = { plan . id } className = "plan-card" >
178+ < div className = "plan-header" >
179+ < h3 > { plan . name } </ h3 >
180+ < div className = "plan-price" >
181+ ₹{ plan . price . amount } /{ plan . interval }
182+ </ div >
183+ </ div >
184+ < div className = "plan-tagline" > { plan . tagline } </ div >
185+ < ul className = "plan-features" >
186+ { plan . features . map ( ( feature , index ) => (
187+ < li key = { index } > { feature } </ li >
188+ ) ) }
189+ </ ul >
190+ < button
191+ className = "subscribe-button"
192+ onClick = { ( ) => handleSubscribe ( plan . id ) }
193+ disabled = { currentSubscription ?. plan_id === plan . id && currentSubscription ?. status === 'active' }
194+ >
195+ { currentSubscription ?. plan_id === plan . id && currentSubscription ?. status === 'active' ? 'Current Plan' : 'Subscribe' }
196+ </ button >
197+ </ div >
198+ ) )
199+ ) : (
200+ < div className = "no-plans" >
201+ < p > No subscription plans available at the moment.</ p >
202+ < button
203+ className = "continue-button"
204+ onClick = { handleContinueToExtension }
205+ >
206+ Continue to Extension
207+ </ button >
208+ </ div >
209+ ) }
210+ </ div >
211+ ) }
212+ </ div >
213+ ) ;
214+ } ;
0 commit comments