Skip to content

Commit e1eff97

Browse files
authored
fix(sdk): check payment method before plan upgrade and downgrade (#530)
* chore: move payment method to frontier context * fix: check for payment method in plan change * fix: flicker in plan list loading state * chore: check for plan pricing for checkout
1 parent 8c6c961 commit e1eff97

File tree

3 files changed

+52
-49
lines changed

3 files changed

+52
-49
lines changed

sdks/js/packages/core/react/components/organization/billing/index.tsx

Lines changed: 9 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -142,16 +142,16 @@ const PaymentMethod = ({
142142

143143
export default function Billing() {
144144
const {
145-
billingAccount: activeBillingAccount,
145+
billingAccount,
146+
isBillingAccountLoading,
146147
client,
147148
config,
148149
activeSubscription,
149-
isActiveSubscriptionLoading
150+
isActiveSubscriptionLoading,
151+
paymentMethod
150152
} = useFrontier();
151153
const navigate = useNavigate({ from: '/billing' });
152-
const [billingAccount, setBillingAccount] = useState<V1Beta1BillingAccount>();
153-
const [paymentMethod, setPaymentMethod] = useState<V1Beta1PaymentMethod>();
154-
const [isBillingAccountLoading, setBillingAccountLoading] = useState(false);
154+
155155
const [invoices, setInvoices] = useState<V1Beta1Invoice[]>([]);
156156
const [isInvoicesLoading, setIsInvoicesLoading] = useState(false);
157157

@@ -175,39 +175,10 @@ export default function Billing() {
175175
);
176176

177177
useEffect(() => {
178-
async function getPaymentMethod(orgId: string, billingId: string) {
179-
setBillingAccountLoading(true);
180-
try {
181-
const resp = await client?.frontierServiceGetBillingAccount(
182-
orgId,
183-
billingId,
184-
{ with_payment_methods: true }
185-
);
186-
if (resp?.data) {
187-
const paymentMethods = resp?.data?.payment_methods || [];
188-
setBillingAccount(resp.data.billing_account);
189-
setPaymentMethod(paymentMethods[0]);
190-
}
191-
} catch (err: any) {
192-
toast.error('Something went wrong', {
193-
description: err.message
194-
});
195-
console.error(err);
196-
} finally {
197-
setBillingAccountLoading(false);
198-
}
199-
}
200-
201-
if (activeBillingAccount?.id && activeBillingAccount?.org_id) {
202-
getPaymentMethod(activeBillingAccount?.org_id, activeBillingAccount?.id);
203-
fetchInvoices(activeBillingAccount?.org_id, activeBillingAccount?.id);
178+
if (billingAccount?.id && billingAccount?.org_id) {
179+
fetchInvoices(billingAccount?.org_id, billingAccount?.id);
204180
}
205-
}, [
206-
activeBillingAccount?.id,
207-
activeBillingAccount?.org_id,
208-
client,
209-
fetchInvoices
210-
]);
181+
}, [billingAccount?.id, billingAccount?.org_id, client, fetchInvoices]);
211182

212183
const onAddDetailsClick = useCallback(() => {
213184
if (billingAccount?.id) {
@@ -248,7 +219,7 @@ export default function Billing() {
248219
isLoading={isLoading}
249220
/>
250221
<BillingDetails
251-
billingAccount={activeBillingAccount}
222+
billingAccount={billingAccount}
252223
onAddDetailsClick={onAddDetailsClick}
253224
isLoading={isLoading}
254225
/>

sdks/js/packages/core/react/components/organization/plans/index.tsx

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ const PlanPricingColumn = ({
9696
currentPlan?: IntervalPricingWithPlan;
9797
allowAction: boolean;
9898
}) => {
99-
const { config } = useFrontier();
99+
const { config, paymentMethod } = useFrontier();
100100

101101
const navigate = useNavigate({ from: '/plans' });
102102

@@ -144,16 +144,18 @@ const PlanPricingColumn = ({
144144
}, [currentPlan, selectedIntervalPricing]);
145145

146146
const isAlreadySubscribed = !_.isEmpty(currentPlan);
147+
const isCheckoutRequired =
148+
_.isEmpty(paymentMethod) && selectedIntervalPricing.amount > 0;
147149

148150
const onPlanActionClick = useCallback(() => {
149-
if (action?.showModal) {
151+
if (action?.showModal && !isCheckoutRequired) {
150152
navigate({
151153
to: '/plans/confirm-change/$planId',
152154
params: {
153155
planId: selectedIntervalPricing?.planId
154156
}
155157
});
156-
} else if (isAlreadySubscribed) {
158+
} else if (isAlreadySubscribed && !isCheckoutRequired) {
157159
const planId = selectedIntervalPricing?.planId;
158160
changePlan({
159161
planId,
@@ -184,6 +186,7 @@ const PlanPricingColumn = ({
184186
action?.immediate,
185187
action?.btnLabel,
186188
isAlreadySubscribed,
189+
isCheckoutRequired,
187190
navigate,
188191
selectedIntervalPricing?.planId,
189192
changePlan,
@@ -378,8 +381,14 @@ const PlansList = ({
378381
};
379382

380383
export default function Plans() {
381-
const { config, client, activeSubscription, activeOrganization } =
382-
useFrontier();
384+
const {
385+
config,
386+
client,
387+
activeSubscription,
388+
activeOrganization,
389+
isActiveSubscriptionLoading,
390+
isActiveOrganizationLoading
391+
} = useFrontier();
383392
const [isPlansLoading, setIsPlansLoading] = useState(false);
384393
const [plans, setPlans] = useState<V1Beta1Plan[]>([]);
385394
const [features, setFeatures] = useState<V1Beta1Feature[]>([]);
@@ -436,7 +445,11 @@ export default function Plans() {
436445
getPlansAndFeatures();
437446
}, [client]);
438447

439-
const isLoading = isPlansLoading || isPermissionsFetching;
448+
const isLoading =
449+
isPlansLoading ||
450+
isPermissionsFetching ||
451+
isActiveSubscriptionLoading ||
452+
isActiveOrganizationLoading;
440453

441454
return (
442455
<Flex direction="column" style={{ width: '100%', overflow: 'hidden' }}>

sdks/js/packages/core/react/contexts/FrontierContext.tsx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
V1Beta1BillingAccount,
2020
V1Beta1Group,
2121
V1Beta1Organization,
22+
V1Beta1PaymentMethod,
2223
V1Beta1Plan,
2324
V1Beta1Subscription,
2425
V1Beta1User
@@ -76,6 +77,8 @@ interface FrontierContextProviderProps {
7677
setIsActivePlanLoading: Dispatch<SetStateAction<boolean>>;
7778

7879
fetchActiveSubsciption: () => Promise<V1Beta1Subscription | undefined>;
80+
81+
paymentMethod: V1Beta1PaymentMethod | undefined;
7982
}
8083

8184
const defaultConfig: FrontierClientOptions = {
@@ -134,7 +137,9 @@ const initialValues: FrontierContextProviderProps = {
134137
isActivePlanLoading: false,
135138
setIsActivePlanLoading: () => false,
136139

137-
fetchActiveSubsciption: async () => undefined
140+
fetchActiveSubsciption: async () => undefined,
141+
142+
paymentMethod: undefined
138143
};
139144

140145
export const FrontierContext =
@@ -160,6 +165,7 @@ export const FrontierContextProvider = ({
160165
const [isUserLoading, setIsUserLoading] = useState(false);
161166

162167
const [billingAccount, setBillingAccount] = useState<V1Beta1BillingAccount>();
168+
const [paymentMethod, setPaymentMethod] = useState<V1Beta1PaymentMethod>();
163169
const [isBillingAccountLoading, setIsBillingAccountLoading] = useState(false);
164170

165171
const [isActiveSubscriptionLoading, setIsActiveSubscriptionLoading] =
@@ -297,10 +303,22 @@ export const FrontierContextProvider = ({
297303
const {
298304
data: { billing_accounts = [] }
299305
} = await frontierClient.frontierServiceListBillingAccounts(orgId);
300-
if (billing_accounts.length > 0) {
301-
const billing_account = billing_accounts[0];
302-
setBillingAccount(billing_account);
303-
await getSubscription(orgId, billing_account?.id || '');
306+
const billingAccountId = billing_accounts[0]?.id || '';
307+
if (billingAccountId) {
308+
const [resp] = await Promise.all([
309+
frontierClient?.frontierServiceGetBillingAccount(
310+
orgId,
311+
billingAccountId,
312+
{ with_payment_methods: true }
313+
),
314+
getSubscription(orgId, billingAccountId)
315+
]);
316+
317+
if (resp?.data) {
318+
const paymentMethods = resp?.data?.payment_methods || [];
319+
setBillingAccount(resp.data.billing_account);
320+
setPaymentMethod(paymentMethods[0]);
321+
}
304322
} else {
305323
setBillingAccount(undefined);
306324
setActiveSubscription(undefined);
@@ -354,6 +372,7 @@ export const FrontierContextProvider = ({
354372
setIsUserLoading,
355373
billingAccount,
356374
setBillingAccount,
375+
paymentMethod,
357376
isBillingAccountLoading,
358377
setIsBillingAccountLoading,
359378
isActiveSubscriptionLoading,

0 commit comments

Comments
 (0)