Skip to content

Commit

Permalink
fix: display Payment Details verification modal when profile already …
Browse files Browse the repository at this point in the history
…has some balance
  • Loading branch information
amnambiar committed Apr 16, 2024
1 parent db7c63f commit 1861909
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 42 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -2,7 +2,7 @@

| Statements | Branches | Functions | Lines |
| --------------------------- | ----------------------- | ------------------------- | ----------------- |
| ![Statements](https://img.shields.io/badge/statements-6.38%25-red.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-1.42%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-4.45%25-red.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-6.07%25-red.svg?style=flat) |
| ![Statements](https://img.shields.io/badge/statements-6.34%25-red.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-1.39%25-red.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-4.43%25-red.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-6.03%25-red.svg?style=flat) |

Front-end repository for Certification Service integration

Expand Down
Expand Up @@ -11,8 +11,8 @@ import './PaymentDetailsVerification.css'
interface Props {
content?: any;
data: {
fee: BigNum,
subscription: ISubscription,
subscription: any,
balance: number,
profile: any
} | undefined | null;
onAccept: () => void;
Expand Down Expand Up @@ -46,8 +46,8 @@ const PaymentDetailsVerification = ( props: Props ) => {
{props?.data?.subscription && (<>
<ListItem><ListItemText primary="Subscription Type" secondary={formatToTitleCase(props?.data?.subscription.type)} /></ListItem>
<ListItem><ListItemText primary="Tier" secondary={"Tier " + props?.data?.subscription.tierId}/></ListItem>
<ListItem><ListItemText primary="Amount in USD" secondary={`$${Math.round(props?.data?.subscription.price/1000000*props?.data?.subscription.adaUsdPrice*100)/100}/year`} /></ListItem>
<ListItem><ListItemText primary="Amount in ADA" secondary={`₳ ${Math.round(props?.data?.subscription.price*100/1000000)/100}`} /></ListItem>
<ListItem><ListItemText primary="Amount in USD" secondary={`$${props?.data?.subscription.usdPrice}/year`} /></ListItem>
<ListItem><ListItemText primary="Expected Amount in ADA" secondary={`₳ ${props?.data?.subscription.adaPrice} ${props?.data?.balance ? `- Will be deducted from the available balance ₳${props?.data?.balance} in your profile` : ''}`} /></ListItem>
</>)}
</List>
</>
Expand Down
5 changes: 3 additions & 2 deletions src/pages/landing/components/RegisterSection.tsx
Expand Up @@ -16,7 +16,8 @@ const REFRESH_TIME = 30;

interface Props {
tier: Tier,
onSubmit: (form: RegisterForm) => void
onClickPay: (form: RegisterForm) => void,
submitForm: boolean
}

const RegisterSection = (props: Props) => {
Expand Down Expand Up @@ -65,7 +66,7 @@ const RegisterSection = (props: Props) => {
);

const onSubmit = (form: RegisterForm) => {
props.onSubmit({
props.onClickPay({
...form,
linkedin: form.linkedin!.length > 0 ? form.linkedin : undefined,
twitter: form.twitter!.length > 0 ? form.twitter : undefined,
Expand Down
41 changes: 29 additions & 12 deletions src/pages/landing/index.tsx
Expand Up @@ -9,7 +9,7 @@ import RegisterModal from "./components/RegisterModal";

import { useAppDispatch, useAppSelector } from "store/store";
import { fetchActiveSubscription } from "store/slices/auth.slice";
import { register, clear, payForRegister } from "store/slices/register.slice";
import { register, clear, payForRegister, calculateFee } from "store/slices/register.slice";
import type { Tier } from "store/slices/tiers.slice";
import type { RegisterForm } from "store/slices/register.slice";

Expand All @@ -35,12 +35,16 @@ export default function LandingPage() {
const dispatch = useAppDispatch();
const { wallet } = useAppSelector((state) => state.walletConnection);
const { transactionId, processing, success } = useAppSelector((state) => state.register);
const { price } = useAppSelector((state) => state.price);
const { profileBalance } = useAppSelector((state) => state.profile);

const [step, setStep] = useState<string>('connect');
const [selectedTier, setSelectedTier] = useState<Tier|null>(null);
const [showVerificationModal, setShowVerificationModal] = useState<boolean>(false);
const [registerResponse, setRegisterResponse] = useState<any>(null);
const [detailsToBeVerified, setDetailsToBeVerified] = useState<any>(null)
const [detailsToBeVerified, setDetailsToBeVerified] = useState<any>(null);
const [submitForm, setSubmitForm] = useState<boolean>(false);
const [form, setForm] = useState<RegisterForm>({ address: "", companyName: "", contactEmail: "", email: "", fullName: ""});

useEffect(() => {
if (wallet !== null && step === 'connect') {
Expand All @@ -54,22 +58,35 @@ export default function LandingPage() {


const handleRegistration = async (form: RegisterForm) => {
const response: any = await dispatch(register({ form, tierId: selectedTier!.id }));
if (typeof response.payload !== 'string') {
setRegisterResponse(response.payload);
setDetailsToBeVerified({
...response.payload,
if (selectedTier?.id) {
setForm(form)
const adaPrice: number = selectedTier.usdPrice / price;
const lovelacesPrice: number = Math.round(adaPrice * 1000000);
const { lessBalance } = await calculateFee(lovelacesPrice, profileBalance);
await setDetailsToBeVerified({
subscription: {
type: selectedTier.type,
tierId: selectedTier.id,
adaPrice: adaPrice.toFixed(2),
usdPrice: selectedTier.usdPrice
},
balance: !lessBalance ? Math.round(profileBalance*100/1000000) / 100 : null,
profile: form
})
setShowVerificationModal(true);
} else {
// do nothin;
await setShowVerificationModal(true);
}
};

const onPaymentDetailsVerified = async () => {
setShowVerificationModal(false)
await dispatch(payForRegister(registerResponse));
const response: any = await dispatch(register({ form, tierId: selectedTier!.id }));
if (response?.payload?.subscription) {
setRegisterResponse(response.payload);
await dispatch(payForRegister(response.payload));
setSubmitForm(true)
} else {
// do nothing;
}
}

const handleContinue = () => {
Expand All @@ -83,7 +100,7 @@ export default function LandingPage() {
<Box className="flex flex-row h-screen bg-cover bg-center bg-landing">
{ step === 'connect' && <ConnectSection /> }
{ step === 'subscription' && <SubscriptionSection onSelectTier={setSelectedTier}/> }
{ selectedTier !== null && <RegisterSection tier={selectedTier} onSubmit={handleRegistration} /> }
{ selectedTier !== null && <RegisterSection tier={selectedTier} onClickPay={handleRegistration} submitForm={submitForm} /> }
<RegisterModal show={processing||success} onClose={handleContinue} success={success} transactionId={transactionId} />

{showVerificationModal ?
Expand Down
2 changes: 2 additions & 0 deletions src/store/slices/auth.slice.ts
@@ -1,6 +1,7 @@
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "store/rootReducer";
import { fetch } from "../../api";
import { fetchProfileBalance } from "./profile.slice";

interface AuthState {
isSessionFetched: boolean;
Expand All @@ -18,6 +19,7 @@ export const fetchActiveSubscription = createAsyncThunk('fetchActiveSubscription
try {
const {impersonate, retainId} = (thunkApi.getState() as RootState).profile;
const response: any = await fetch<string[]>(thunkApi, { method: 'GET', url: `/profile/${impersonate ? retainId : "current"}/subscriptions/active-features` });
thunkApi.dispatch(fetchProfileBalance({}))
if (response.status !== 200) throw new Error();
return response.data;
} catch (error) {
Expand Down
23 changes: 22 additions & 1 deletion src/store/slices/profile.slice.ts
Expand Up @@ -84,6 +84,7 @@ interface ProfileState {
runHistory: Run[];
impersonate: boolean;
retainId: number | null;
profileBalance: number;
}

export interface IUpdateProfile {
Expand Down Expand Up @@ -123,7 +124,8 @@ const initialState: ProfileState = {
loadingHistory: false,
runHistory: [],
impersonate: false,
retainId: null
retainId: null,
profileBalance: 0
};

// Add this export to your profileSlice file
Expand Down Expand Up @@ -235,6 +237,16 @@ export const fetchProfileRunHistory = createAsyncThunk("fetchProfileRunHistory",
}
})

export const fetchProfileBalance = createAsyncThunk("fetchProfileBalance", async( payload: any, thunkApi) => {
try {
const res: any = await fetch<number>(thunkApi, { method: 'GET', url: '/profile/current/balance' });
if (res.status !== 200) throw { message: res.data };
return res.data;
} catch (e: any) {
return thunkApi.rejectWithValue(e.response.data);
}
})

export const profileSlice = createSlice({
name: "profile",
initialState,
Expand Down Expand Up @@ -351,6 +363,15 @@ export const profileSlice = createSlice({
state.loadingHistory = false;
state.runHistory = [];
})
.addCase(fetchProfileBalance.pending, (state) => {
state.profileBalance = 0;
})
.addCase(fetchProfileBalance.fulfilled, (state, actions) => {
state.profileBalance = actions.payload;
})
.addCase(fetchProfileBalance.rejected, (state, actions) => {
state.profileBalance = 0;
})
},
});

Expand Down
39 changes: 17 additions & 22 deletions src/store/slices/register.slice.ts
Expand Up @@ -64,14 +64,10 @@ const getUserSubscriptionById = async (thunkApi: any, subscriptionId: string) =>
return res.data.find(item => item.id === subscriptionId);
}

const getUserBalance = async (thunkApi: any) => {
const res = await fetch<number>(thunkApi, { method: 'GET', url: '/profile/current/balance' });
if (res.status !== 200) throw { message: res.data };
return BigNum.from_str(res.data.toString());
}

const calculateFee = async (subscriptionPrice: BigNum, balance: BigNum) => {
export const calculateFee = async (subscriptionPriceNum: number, balanceNum: number) => {
let lessBalance = false;
const subscriptionPrice: BigNum = BigNum.from_str(subscriptionPriceNum.toString())
const balance: BigNum = BigNum.from_str(balanceNum.toString())
if (balance.less_than(subscriptionPrice)) {
lessBalance = true;
} else {
Expand Down Expand Up @@ -130,19 +126,14 @@ export const register = createAsyncThunk("register", async (request: RegisterReq
if (!subscription) throw { message: 'There\'s no subscription registered' };
if (subscription.status !== 'pending') throw { message: 'The subscription it\'s not pending' };

const subscriptionPrice = BigNum.from_str(subscription.price.toString());
const balance = await getUserBalance(thunkApi);
const { profileBalance } = (thunkApi.getState() as RootState).profile;

const { lessBalance, fee } = await calculateFee(subscriptionPrice, balance);
if (!lessBalance) {
// do nothing; auto pay from balance
return '';
} else {
return {
fee: fee,
subscription: subscription
};
}
const { lessBalance, fee } = await calculateFee(subscription.price, profileBalance);
return {
fee: fee,
subscription: subscription,
balance: !lessBalance ? profileBalance : null
};
}
else {
const currentSub = await getUserSubscriptionById(thunkApi, pendingSubscription.id);
Expand All @@ -157,11 +148,15 @@ export const register = createAsyncThunk("register", async (request: RegisterReq
}
});

export const payForRegister = createAsyncThunk("payForRegister", async (payload: {fee: BigNum, subscription: any}, thunkApi) => {
export const payForRegister = createAsyncThunk("payForRegister", async (payload: {fee: BigNum, subscription: any, balance: number}, thunkApi) => {
const { wallet, walletAddress, stakeAddress } = (thunkApi.getState() as RootState).walletConnection;
let transactionId: string | null;
let transactionId: string | null = '';
try {
transactionId = await doPayment(thunkApi.dispatch, wallet, walletAddress!, stakeAddress!, payload.fee);
if (payload.balance) {
// prevent triggering wallet payments as account has balance
} else {
transactionId = await doPayment(thunkApi.dispatch, wallet, walletAddress!, stakeAddress!, payload.fee);
}

await hasActiveSubscription(thunkApi, payload.subscription.id).catch(error => {
return thunkApi.rejectWithValue(error.message.toString());
Expand Down
3 changes: 3 additions & 0 deletions src/store/slices/tiers.slice.ts
Expand Up @@ -12,6 +12,9 @@ export interface Tier {
features: TierFeature[];
usdPrice: number;
enabled: boolean;
type: string;
duration: number;
description: string;
}

interface TiersState {
Expand Down

0 comments on commit 1861909

Please sign in to comment.