1
1
import Api from "api/api" ;
2
2
import axios , { AxiosInstance , AxiosRequestConfig } from "axios" ;
3
+ import { useSelector } from "react-redux" ;
4
+ import { getUser , getCurrentUser } from "redux/selectors/usersSelectors" ;
5
+ import { useEffect , useState } from "react" ;
6
+ import { calculateFlowCode } from "./apiUtils" ;
3
7
8
+ // Interfaces
4
9
export interface CustomerAddress {
5
10
line1 : string ;
6
11
line2 : string ;
@@ -52,13 +57,45 @@ export interface StripeCustomer {
52
57
test_clock : string | null ;
53
58
}
54
59
60
+ export interface Pricing {
61
+ type : string ;
62
+ amount : string ;
63
+ }
64
+
65
+ export interface Product {
66
+ title : string ;
67
+ description : string ;
68
+ image : string ;
69
+ pricingType : string ;
70
+ pricing : Pricing [ ] ;
71
+ activeSubscription : boolean ;
72
+ accessLink : string ;
73
+ subscriptionId : string ;
74
+ checkoutLink : string ;
75
+ checkoutLinkDataLoaded ?: boolean ;
76
+ type ?: string ;
77
+ quantity_entity ?: string ;
78
+ }
79
+
80
+ export interface SubscriptionItem {
81
+ id : string ;
82
+ object : string ;
83
+ plan : {
84
+ id : string ;
85
+ product : string ;
86
+ } ;
87
+ quantity : number ;
88
+ }
89
+
55
90
export type ResponseType = {
56
91
response : any ;
57
92
} ;
58
93
59
- const currentPage = 1 ;
60
- const currentQuery = '' ;
61
- const currentData = [ ] ;
94
+ // Axios Configuration
95
+ const lcHeaders = {
96
+ "Lowcoder-Token" : calculateFlowCode ( ) ,
97
+ "Content-Type" : "application/json"
98
+ } ;
62
99
63
100
let axiosIns : AxiosInstance | null = null ;
64
101
@@ -69,7 +106,7 @@ const getAxiosInstance = (clientSecret?: string) => {
69
106
70
107
const headers : Record < string , string > = {
71
108
"Content-Type" : "application/json" ,
72
- }
109
+ } ;
73
110
74
111
const apiRequestConfig : AxiosRequestConfig = {
75
112
baseURL : "http://localhost:8080/api/flow" ,
@@ -78,10 +115,9 @@ const getAxiosInstance = (clientSecret?: string) => {
78
115
79
116
axiosIns = axios . create ( apiRequestConfig ) ;
80
117
return axiosIns ;
81
- }
118
+ } ;
82
119
83
120
class SubscriptionApi extends Api {
84
-
85
121
static async secureRequest ( body : any ) : Promise < any > {
86
122
let response ;
87
123
try {
@@ -92,11 +128,296 @@ class SubscriptionApi extends Api {
92
128
} ) ;
93
129
} catch ( error ) {
94
130
console . error ( "Error at Secure Flow Request:" , error ) ;
95
- // throw error;
96
131
}
97
132
return response ;
98
133
}
99
-
100
134
}
101
135
136
+ // API Functions
137
+
138
+ export const searchCustomer = async ( subscriptionCustomer : LowcoderCustomer ) => {
139
+ const apiBody = {
140
+ path : "webhook/secure/search-customer" ,
141
+ data : subscriptionCustomer ,
142
+ method : "post" ,
143
+ headers : lcHeaders
144
+ } ;
145
+ try {
146
+ const result = await SubscriptionApi . secureRequest ( apiBody ) ;
147
+ return result ?. data ?. data ?. length === 1 ? result . data . data [ 0 ] as StripeCustomer : null ;
148
+ } catch ( error ) {
149
+ console . error ( "Error searching customer:" , error ) ;
150
+ throw error ;
151
+ }
152
+ } ;
153
+
154
+ export const searchSubscriptions = async ( customerId : string ) => {
155
+ const apiBody = {
156
+ path : "webhook/secure/search-subscriptions" ,
157
+ data : { customerId } ,
158
+ method : "post" ,
159
+ headers : lcHeaders
160
+ } ;
161
+ try {
162
+ const result = await SubscriptionApi . secureRequest ( apiBody ) ;
163
+ return result ?. data ?. data ?? [ ] ;
164
+ } catch ( error ) {
165
+ console . error ( "Error searching subscriptions:" , error ) ;
166
+ throw error ;
167
+ }
168
+ } ;
169
+
170
+ export const createCustomer = async ( subscriptionCustomer : LowcoderCustomer ) => {
171
+ const apiBody = {
172
+ path : "webhook/secure/create-customer" ,
173
+ data : subscriptionCustomer ,
174
+ method : "post" ,
175
+ headers : lcHeaders
176
+ } ;
177
+ try {
178
+ const result = await SubscriptionApi . secureRequest ( apiBody ) ;
179
+ return result ?. data as StripeCustomer ;
180
+ } catch ( error ) {
181
+ console . error ( "Error creating customer:" , error ) ;
182
+ throw error ;
183
+ }
184
+ } ;
185
+
186
+ export const createCheckoutLink = async ( customer : StripeCustomer , priceId : string , quantity : number , discount ?: number ) => {
187
+ const domain = window . location . protocol + "//" + window . location . hostname + ( window . location . port ? ':' + window . location . port : '' ) ;
188
+
189
+ const apiBody = {
190
+ path : "webhook/secure/create-checkout-link" ,
191
+ data : {
192
+ "customerId" : customer . id ,
193
+ "priceId" : priceId ,
194
+ "quantity" : quantity ,
195
+ "discount" : discount ,
196
+ baseUrl : domain
197
+ } ,
198
+ method : "post" ,
199
+ headers : lcHeaders
200
+ } ;
201
+ try {
202
+ const result = await SubscriptionApi . secureRequest ( apiBody ) ;
203
+ return result ?. data ? { id : result . data . id , url : result . data . url } : null ;
204
+ } catch ( error ) {
205
+ console . error ( "Error creating checkout link:" , error ) ;
206
+ throw error ;
207
+ }
208
+ } ;
209
+
210
+ // Hooks
211
+
212
+ export const InitializeSubscription = ( ) => {
213
+ const [ customer , setCustomer ] = useState < StripeCustomer | null > ( null ) ;
214
+ const [ isCreatingCustomer , setIsCreatingCustomer ] = useState < boolean > ( false ) ; // Track customer creation
215
+ const [ customerDataError , setCustomerDataError ] = useState < boolean > ( false ) ;
216
+ const [ subscriptions , setSubscriptions ] = useState < SubscriptionItem [ ] > ( [ ] ) ;
217
+ const [ subscriptionDataLoaded , setSubscriptionDataLoaded ] = useState < boolean > ( false ) ;
218
+ const [ subscriptionDataError , setSubscriptionDataError ] = useState < boolean > ( false ) ;
219
+ const [ checkoutLinkDataLoaded , setCheckoutLinkDataLoaded ] = useState < boolean > ( false ) ;
220
+ const [ checkoutLinkDataError , setCheckoutLinkDataError ] = useState < boolean > ( false ) ;
221
+ const [ products , setProducts ] = useState < Product [ ] > ( [
222
+ {
223
+ title : "Support Subscription" ,
224
+ description : "Support Ticket System and SLAs to guarantee response time and your project success." ,
225
+ image : "https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png" ,
226
+ pricingType : "Monthly, per User" ,
227
+ pricing : [
228
+ { type : "User" , amount : "$3.49 (user, month)" } ,
229
+ { type : "> 10 Users" , amount : "$2.49 (user, month)" } ,
230
+ { type : "> 100 Users" , amount : "$1.49 (user, month)" }
231
+ ] ,
232
+ activeSubscription : false ,
233
+ accessLink : "1PhH38DDlQgecLSfSukEgIeV" ,
234
+ subscriptionId : "" ,
235
+ checkoutLink : "" ,
236
+ checkoutLinkDataLoaded : false ,
237
+ type : "org" ,
238
+ quantity_entity : "orgUser" ,
239
+ } ,
240
+ {
241
+ title : "Premium Media Subscription" ,
242
+ description : "Access to all features." ,
243
+ image : "https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png" ,
244
+ pricingType : "Monthly, per User" ,
245
+ pricing : [
246
+ { type : "Volume Price" , amount : "$20/month" } ,
247
+ { type : "Single Price" , amount : "$25/month" }
248
+ ] ,
249
+ activeSubscription : false ,
250
+ accessLink : "1Pf65wDDlQgecLSf6OFlbsD5" ,
251
+ checkoutLink : "" ,
252
+ checkoutLinkDataLoaded : false ,
253
+ subscriptionId : "" ,
254
+ type : "user" ,
255
+ quantity_entity : "singleItem" ,
256
+ }
257
+ ] ) ;
258
+
259
+ const user = useSelector ( getUser ) ;
260
+ const currentUser = useSelector ( getCurrentUser ) ;
261
+ const currentOrg = user . orgs . find ( org => org . id === user . currentOrgId ) ;
262
+ const orgID = user . currentOrgId ;
263
+ const domain = window . location . protocol + "//" + window . location . hostname + ( window . location . port ? ':' + window . location . port : '' ) ;
264
+ const admin = user . orgRoleMap . get ( orgID ) === "admin" ? "admin" : "member" ;
265
+
266
+ const subscriptionCustomer : LowcoderCustomer = {
267
+ hostname : domain ,
268
+ email : currentUser . email ,
269
+ orgId : orgID ,
270
+ userId : user . id ,
271
+ userName : user . username ,
272
+ type : admin ? "admin" : "user" ,
273
+ companyName : currentOrg ?. name || "Unknown" ,
274
+ } ;
275
+
276
+ useEffect ( ( ) => {
277
+ const initializeCustomer = async ( ) => {
278
+ try {
279
+ setIsCreatingCustomer ( true ) ;
280
+ const existingCustomer = await searchCustomer ( subscriptionCustomer ) ;
281
+ if ( existingCustomer ) {
282
+ setCustomer ( existingCustomer ) ;
283
+ } else {
284
+ const newCustomer = await createCustomer ( subscriptionCustomer ) ;
285
+ setCustomer ( newCustomer ) ;
286
+ }
287
+ } catch ( error ) {
288
+ setCustomerDataError ( true ) ;
289
+ } finally {
290
+ setIsCreatingCustomer ( false ) ;
291
+ }
292
+ } ;
293
+
294
+ initializeCustomer ( ) ;
295
+ } , [ ] ) ;
296
+
297
+ useEffect ( ( ) => {
298
+ const fetchSubscriptions = async ( ) => {
299
+ if ( customer ) {
300
+ try {
301
+ const subs = await searchSubscriptions ( customer . id ) ;
302
+ setSubscriptions ( subs ) ;
303
+ setSubscriptionDataLoaded ( true ) ;
304
+ } catch ( error ) {
305
+ setSubscriptionDataError ( true ) ;
306
+ }
307
+ }
308
+ } ;
309
+
310
+ fetchSubscriptions ( ) ;
311
+ } , [ customer ] ) ;
312
+
313
+ useEffect ( ( ) => {
314
+ const prepareCheckout = async ( ) => {
315
+ if ( subscriptionDataLoaded ) {
316
+ try {
317
+ const updatedProducts = await Promise . all (
318
+ products . map ( async ( product ) => {
319
+ const matchingSubscription = subscriptions . find (
320
+ ( sub ) => sub . plan . id === "price_" + product . accessLink
321
+ ) ;
322
+
323
+ if ( matchingSubscription ) {
324
+ return {
325
+ ...product ,
326
+ activeSubscription : true ,
327
+ checkoutLinkDataLoaded : true ,
328
+ subscriptionId : matchingSubscription . id . substring ( 4 ) ,
329
+ } ;
330
+ } else {
331
+ const checkoutLink = await createCheckoutLink ( customer ! , product . accessLink , 1 ) ;
332
+ return {
333
+ ...product ,
334
+ activeSubscription : false ,
335
+ checkoutLink : checkoutLink ? checkoutLink . url : "" ,
336
+ checkoutLinkDataLoaded : true ,
337
+ } ;
338
+ }
339
+ } )
340
+ ) ;
341
+
342
+ setProducts ( updatedProducts ) ;
343
+ } catch ( error ) {
344
+ setCheckoutLinkDataError ( true ) ;
345
+ }
346
+ }
347
+ } ;
348
+
349
+ prepareCheckout ( ) ;
350
+ } , [ subscriptionDataLoaded ] ) ;
351
+
352
+ return {
353
+ customer,
354
+ isCreatingCustomer,
355
+ customerDataError,
356
+ subscriptions,
357
+ subscriptionDataLoaded,
358
+ subscriptionDataError,
359
+ checkoutLinkDataLoaded,
360
+ checkoutLinkDataError,
361
+ products,
362
+ } ;
363
+ } ;
364
+
365
+
366
+
367
+ export const CheckSubscriptions = ( ) => {
368
+ const [ customer , setCustomer ] = useState < StripeCustomer | null > ( null ) ;
369
+ const [ customerDataError , setCustomerDataError ] = useState < boolean > ( false ) ;
370
+ const [ subscriptions , setSubscriptions ] = useState < SubscriptionItem [ ] > ( [ ] ) ;
371
+ const [ subscriptionDataLoaded , setSubscriptionDataLoaded ] = useState < boolean > ( false ) ;
372
+ const [ subscriptionDataError , setSubscriptionDataError ] = useState < boolean > ( false ) ;
373
+ const [ loading , setLoading ] = useState < boolean > ( true ) ;
374
+
375
+ const user = useSelector ( getUser ) ;
376
+ const currentUser = useSelector ( getCurrentUser ) ;
377
+ const orgID = user . currentOrgId ;
378
+ const domain = window . location . protocol + "//" + window . location . hostname + ( window . location . port ? ':' + window . location . port : '' ) ;
379
+
380
+ const subscriptionCustomer : LowcoderCustomer = {
381
+ hostname : domain ,
382
+ email : currentUser . email ,
383
+ orgId : orgID ,
384
+ userId : user . id ,
385
+ userName : user . username ,
386
+ type : user . orgRoleMap . get ( orgID ) === "admin" ? "admin" : "user" ,
387
+ companyName : user . currentOrgId ,
388
+ } ;
389
+
390
+ useEffect ( ( ) => {
391
+ const fetchCustomerAndSubscriptions = async ( ) => {
392
+ try {
393
+ const existingCustomer = await searchCustomer ( subscriptionCustomer ) ;
394
+ if ( existingCustomer ) {
395
+ setCustomer ( existingCustomer ) ;
396
+ const subs = await searchSubscriptions ( existingCustomer . id ) ;
397
+ setSubscriptions ( subs ) ;
398
+ setSubscriptionDataLoaded ( true ) ;
399
+ } else {
400
+ setCustomer ( null ) ;
401
+ }
402
+ } catch ( error ) {
403
+ setCustomerDataError ( true ) ;
404
+ setSubscriptionDataError ( true ) ;
405
+ } finally {
406
+ setLoading ( false ) ;
407
+ }
408
+ } ;
409
+
410
+ fetchCustomerAndSubscriptions ( ) ;
411
+ } , [ ] ) ;
412
+
413
+ return {
414
+ customer,
415
+ customerDataError,
416
+ subscriptions,
417
+ subscriptionDataLoaded,
418
+ subscriptionDataError,
419
+ loading,
420
+ } ;
421
+ } ;
422
+
102
423
export default SubscriptionApi ;
0 commit comments