Skip to content

Commit cde7340

Browse files
authored
Merge pull request #1275 from lowcoder-org/updates-subscription-handling
Refactoring - subscription handling
2 parents bfbcd9e + 5af6c32 commit cde7340

21 files changed

+551
-476
lines changed
Lines changed: 6 additions & 358 deletions
Original file line numberDiff line numberDiff line change
@@ -1,128 +1,16 @@
11
import Api from "api/api";
22
import axios, { AxiosInstance, AxiosRequestConfig } from "axios";
33
import { useDispatch, useSelector } from "react-redux";
4-
import { getUser, getCurrentUser } from "redux/selectors/usersSelectors";
54
import { useEffect, useState} from "react";
65
import { calculateFlowCode } from "./apiUtils";
7-
import { getDeploymentId } from "@lowcoder-ee/redux/selectors/configSelectors";
86
import { fetchOrgUsersAction } from "redux/reduxActions/orgActions";
97
import { getOrgUsers } from "redux/selectors/orgSelectors";
108
import { AppState } from "@lowcoder-ee/redux/reducers";
11-
12-
// Interfaces
13-
export interface CustomerAddress {
14-
line1: string;
15-
line2: string;
16-
city: string;
17-
state: string;
18-
country: string;
19-
postalCode: string;
20-
}
21-
22-
export interface LowcoderNewCustomer {
23-
hostname: string;
24-
hostId: string;
25-
email: string;
26-
orgId: string;
27-
userId: string;
28-
userName: string;
29-
type: string;
30-
companyName: string;
31-
address?: CustomerAddress;
32-
}
33-
34-
export interface LowcoderSearchCustomer {
35-
hostname: string;
36-
hostId: string;
37-
email: string;
38-
orgId: string;
39-
userId: string;
40-
}
41-
42-
interface LowcoderMetadata {
43-
lowcoder_host: string;
44-
lowcoder_hostId: string;
45-
lowcoder_orgId: string;
46-
lowcoder_type: string;
47-
lowcoder_userId: string;
48-
}
49-
50-
export interface StripeCustomer {
51-
id: string;
52-
object: string;
53-
address?: object | null;
54-
balance: number;
55-
created: number;
56-
currency: string | null;
57-
default_source: string | null;
58-
delinquent: boolean;
59-
description: string | null;
60-
discount: string | null;
61-
email: string;
62-
invoice_prefix: string;
63-
invoice_settings: object | null;
64-
livemode: boolean;
65-
metadata: LowcoderMetadata;
66-
name: string;
67-
phone: string | null;
68-
preferred_locales: string[];
69-
shipping: string | null;
70-
tax_exempt: string;
71-
test_clock: string | null;
72-
}
73-
74-
export interface Pricing {
75-
type: string;
76-
amount: string;
77-
}
78-
79-
export interface Product {
80-
title?: string;
81-
description?: string;
82-
image?: string;
83-
pricingType: string;
84-
product: string;
85-
activeSubscription: boolean;
86-
accessLink: string;
87-
subscriptionId: string;
88-
checkoutLink: string;
89-
checkoutLinkDataLoaded?: boolean;
90-
type?: string;
91-
quantity_entity?: string;
92-
}
93-
94-
export interface SubscriptionItem {
95-
id: string;
96-
object: string;
97-
plan: {
98-
id: string;
99-
product: string;
100-
};
101-
quantity: number;
102-
}
103-
104-
export interface Subscription {
105-
id: string;
106-
collection_method: string;
107-
current_period_end: number;
108-
current_period_start: number;
109-
product: string;
110-
currency: string;
111-
interval: string;
112-
tiers_mode: string;
113-
status: string;
114-
start_date: number;
115-
quantity: number;
116-
billing_scheme: string;
117-
price: string;
118-
}
119-
120-
export interface SubscriptionsData {
121-
subscriptions: Subscription[];
122-
subscriptionDataLoaded: boolean;
123-
subscriptionDataError: boolean;
124-
loading: boolean;
125-
}
9+
import type {
10+
LowcoderNewCustomer,
11+
LowcoderSearchCustomer,
12+
StripeCustomer,
13+
} from "@lowcoder-ee/constants/subscriptionConstants";
12614

12715
export type ResponseType = {
12816
response: any;
@@ -274,7 +162,7 @@ export const getProducts = async () => {
274162
};
275163
try {
276164
const result = await SubscriptionApi.secureRequest(apiBody);
277-
return result?.data as any;
165+
return result?.data?.data as any[];
278166
} catch (error) {
279167
console.error("Error fetching product:", error);
280168
throw error;
@@ -380,244 +268,4 @@ export const useOrgUserCount = (orgId: string) => {
380268
return userCount;
381269
};
382270

383-
export const InitializeSubscription = () => {
384-
const [customer, setCustomer] = useState<StripeCustomer | null>(null);
385-
const [isCreatingCustomer, setIsCreatingCustomer] = useState<boolean>(false); // Track customer creation
386-
const [customerDataError, setCustomerDataError] = useState<boolean>(false);
387-
const [subscriptions, setSubscriptions] = useState<SubscriptionItem[]>([]);
388-
const [subscriptionDataLoaded, setSubscriptionDataLoaded] = useState<boolean>(false);
389-
const [subscriptionDataError, setSubscriptionDataError] = useState<boolean>(false);
390-
const [checkoutLinkDataLoaded, setCheckoutLinkDataLoaded] = useState<boolean>(false);
391-
const [checkoutLinkDataError, setCheckoutLinkDataError] = useState<boolean>(false);
392-
const [products, setProducts] = useState<Product[]>([
393-
{
394-
pricingType: "Monthly, per User",
395-
activeSubscription: false,
396-
accessLink: "1PhH38DDlQgecLSfSukEgIeV",
397-
product: "QW8L3WPMiNjQjI",
398-
subscriptionId: "",
399-
checkoutLink: "",
400-
checkoutLinkDataLoaded: false,
401-
type: "org",
402-
quantity_entity: "orgUser",
403-
},
404-
{
405-
pricingType: "Monthly, per User",
406-
activeSubscription: false,
407-
accessLink: "1Pf65wDDlQgecLSf6OFlbsD5",
408-
product: "QW8MpIBHxieKXd",
409-
checkoutLink: "",
410-
checkoutLinkDataLoaded: false,
411-
subscriptionId: "",
412-
type: "user",
413-
quantity_entity: "singleItem",
414-
},
415-
{
416-
pricingType: "Monthly, per User",
417-
activeSubscription: false,
418-
accessLink: "1PttHIDDlQgecLSf0XP27tXt",
419-
product: "QlQ7cdOh8Lv4dy",
420-
subscriptionId: "",
421-
checkoutLink: "",
422-
checkoutLinkDataLoaded: false,
423-
type: "org",
424-
quantity_entity: "singleItem",
425-
},
426-
]);
427-
428-
429-
const user = useSelector(getUser);
430-
const currentUser = useSelector(getCurrentUser);
431-
const deploymentId = useSelector(getDeploymentId);
432-
const currentOrg = user.orgs.find(org => org.id === user.currentOrgId);
433-
const orgID = user.currentOrgId;
434-
const domain = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port : '');
435-
const admin = user.orgRoleMap.get(orgID) === "admin" ? "admin" : "member";
436-
const dispatch = useDispatch();
437-
438-
const userCount = useOrgUserCount(orgID);
439-
440-
const subscriptionSearchCustomer: LowcoderSearchCustomer = {
441-
hostname: domain,
442-
hostId: deploymentId,
443-
email: currentUser.email,
444-
orgId: orgID,
445-
userId: user.id,
446-
};
447-
448-
const subscriptionNewCustomer: LowcoderNewCustomer = {
449-
hostname: domain,
450-
hostId: deploymentId,
451-
email: currentUser.email,
452-
orgId: orgID,
453-
userId: user.id,
454-
userName: user.username,
455-
type: admin,
456-
companyName: currentOrg?.name || "Unknown",
457-
};
458-
459-
useEffect(() => {
460-
const initializeCustomer = async () => {
461-
try {
462-
setIsCreatingCustomer(true);
463-
const existingCustomer = await searchCustomer(subscriptionSearchCustomer);
464-
if (existingCustomer != null) {
465-
setCustomer(existingCustomer);
466-
} else {
467-
const newCustomer = await createCustomer(subscriptionNewCustomer);
468-
setCustomer(newCustomer);
469-
}
470-
} catch (error) {
471-
setCustomerDataError(true);
472-
} finally {
473-
setIsCreatingCustomer(false);
474-
}
475-
};
476-
477-
if (Boolean(deploymentId)) {
478-
initializeCustomer();
479-
}
480-
}, [deploymentId]);
481-
482-
useEffect(() => {
483-
const fetchSubscriptions = async () => {
484-
if (customer) {
485-
try {
486-
const subs = await searchSubscriptions(customer.id);
487-
setSubscriptions(subs);
488-
setSubscriptionDataLoaded(true);
489-
} catch (error) {
490-
setSubscriptionDataError(true);
491-
}
492-
}
493-
};
494-
495-
fetchSubscriptions();
496-
}, [customer]);
497-
498-
useEffect(() => {
499-
const prepareCheckout = async () => {
500-
if (subscriptionDataLoaded && userCount > 0) { // Ensure user count is available
501-
try {
502-
console.log("Total Users in Organization:", userCount);
503-
504-
const updatedProducts = await Promise.all(
505-
products.map(async (product) => {
506-
const matchingSubscription = subscriptions.find(
507-
(sub) => sub.plan.id === "price_" + product.accessLink
508-
);
509-
510-
if (matchingSubscription) {
511-
return {
512-
...product,
513-
activeSubscription: true,
514-
checkoutLinkDataLoaded: true,
515-
subscriptionId: matchingSubscription.id.substring(4),
516-
};
517-
} else {
518-
// Use the user count to set the quantity for checkout link
519-
const checkoutLink = await createCheckoutLink(customer!, product.accessLink, userCount);
520-
return {
521-
...product,
522-
activeSubscription: false,
523-
checkoutLink: checkoutLink ? checkoutLink.url : "",
524-
checkoutLinkDataLoaded: true,
525-
};
526-
}
527-
})
528-
);
529-
530-
setProducts(updatedProducts);
531-
} catch (error) {
532-
setCheckoutLinkDataError(true);
533-
}
534-
}
535-
};
536-
537-
prepareCheckout();
538-
}, [subscriptionDataLoaded, userCount]);
539-
540-
return {
541-
customer,
542-
isCreatingCustomer,
543-
customerDataError,
544-
subscriptions,
545-
subscriptionDataLoaded,
546-
subscriptionDataError,
547-
checkoutLinkDataLoaded,
548-
checkoutLinkDataError,
549-
products,
550-
admin,
551-
};
552-
};
553-
554-
export enum SubscriptionProducts {
555-
SUPPORT = "QW8L3WPMiNjQjI",
556-
MEDIAPACKAGE = 'QW8MpIBHxieKXd',
557-
AZUREAPIS = 'premium',
558-
GOOGLEAPIS = 'enterprise',
559-
AWSAPIS = 'enterprise-global',
560-
PRIVATECLOUD = 'private-cloud',
561-
MATRIXCLOUD = 'matrix-cloud',
562-
AGORATOKENSERVER = 'agora-tokenserver',
563-
SIGNALSERVER = 'signal-server',
564-
DATABASE = 'database',
565-
STORAGE = 'storage',
566-
IOSAPP = 'ios-app',
567-
ANDROIDAPP = 'android-app',
568-
AUDITLOG = 'audit-log',
569-
APPLOG = 'app-log',
570-
ENVIRONMENTS = 'environments',
571-
GITREPOS = 'git-repos',
572-
}
573-
574-
export const CheckSubscriptions = () => {
575-
const [subscriptions, setSubscriptions] = useState<Subscription[]>([]);
576-
const [subscriptionDataLoaded, setSubscriptionDataLoaded] = useState<boolean>(false);
577-
const [subscriptionDataError, setSubscriptionDataError] = useState<boolean>(false);
578-
const [loading, setLoading] = useState<boolean>(true);
579-
580-
const user = useSelector(getUser);
581-
const currentUser = useSelector(getCurrentUser);
582-
const deploymentId = useSelector(getDeploymentId);
583-
const orgID = user.currentOrgId;
584-
const domain = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port : '');
585-
586-
const subscriptionSearchCustomer: LowcoderSearchCustomer = {
587-
hostname: domain,
588-
hostId: deploymentId,
589-
email: currentUser.email,
590-
orgId: orgID,
591-
userId: user.id,
592-
};
593-
594-
useEffect(() => {
595-
const fetchCustomerAndSubscriptions = async () => {
596-
try {
597-
const subs = await searchCustomersSubscriptions(subscriptionSearchCustomer);
598-
setSubscriptions(subs);
599-
setSubscriptionDataLoaded(true);
600-
} catch (error) {
601-
setSubscriptionDataError(true);
602-
} finally {
603-
setLoading(false);
604-
}
605-
};
606-
if (
607-
Boolean(currentUser.email)
608-
&& Boolean(orgID)
609-
&& Boolean(user.id)
610-
&& Boolean(deploymentId)
611-
)
612-
fetchCustomerAndSubscriptions();
613-
}, [subscriptionSearchCustomer]);
614-
615-
return {
616-
subscriptions,
617-
subscriptionDataLoaded,
618-
subscriptionDataError,
619-
loading,
620-
};
621-
};
622-
623271
export default SubscriptionApi;

client/packages/lowcoder/src/constants/reduxActionConstants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,8 @@ export const ReduxActionErrorTypes = {
227227
CREATE_APP_SNAPSHOT_ERROR: "CREATE_APP_SNAPSHOT_ERROR",
228228
FETCH_APP_SNAPSHOTS_ERROR: "FETCH_APP_SNAPSHOTS_ERROR",
229229
FETCH_APP_SNAPSHOT_DSL_ERROR: "FETCH_APP_SNAPSHOT_DSL_ERROR",
230+
231+
FETCH_DATASOURCE_ERROR: "FETCH_DATASOURCE_ERROR",
230232
};
231233

232234
export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes];

0 commit comments

Comments
 (0)