From 1861909936029fd20d025344ec5c5b5546766308 Mon Sep 17 00:00:00 2001 From: amnambiar Date: Tue, 16 Apr 2024 15:20:28 +0530 Subject: [PATCH] fix: display Payment Details verification modal when profile already has some balance --- README.md | 2 +- .../PaymentDetailsVerification.tsx | 8 ++-- .../landing/components/RegisterSection.tsx | 5 ++- src/pages/landing/index.tsx | 41 +++++++++++++------ src/store/slices/auth.slice.ts | 2 + src/store/slices/profile.slice.ts | 23 ++++++++++- src/store/slices/register.slice.ts | 39 ++++++++---------- src/store/slices/tiers.slice.ts | 3 ++ 8 files changed, 81 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 652d3bd2..506d66f0 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/components/PaymentConfirmation/PaymentDetailsVerification.tsx b/src/components/PaymentConfirmation/PaymentDetailsVerification.tsx index 8d1cd445..160706df 100644 --- a/src/components/PaymentConfirmation/PaymentDetailsVerification.tsx +++ b/src/components/PaymentConfirmation/PaymentDetailsVerification.tsx @@ -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; @@ -46,8 +46,8 @@ const PaymentDetailsVerification = ( props: Props ) => { {props?.data?.subscription && (<> - - + + )} diff --git a/src/pages/landing/components/RegisterSection.tsx b/src/pages/landing/components/RegisterSection.tsx index 3a3c1725..61a77fea 100644 --- a/src/pages/landing/components/RegisterSection.tsx +++ b/src/pages/landing/components/RegisterSection.tsx @@ -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) => { @@ -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, diff --git a/src/pages/landing/index.tsx b/src/pages/landing/index.tsx index 896e758e..39f15b06 100644 --- a/src/pages/landing/index.tsx +++ b/src/pages/landing/index.tsx @@ -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"; @@ -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('connect'); const [selectedTier, setSelectedTier] = useState(null); const [showVerificationModal, setShowVerificationModal] = useState(false); const [registerResponse, setRegisterResponse] = useState(null); - const [detailsToBeVerified, setDetailsToBeVerified] = useState(null) + const [detailsToBeVerified, setDetailsToBeVerified] = useState(null); + const [submitForm, setSubmitForm] = useState(false); + const [form, setForm] = useState({ address: "", companyName: "", contactEmail: "", email: "", fullName: ""}); useEffect(() => { if (wallet !== null && step === 'connect') { @@ -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 = () => { @@ -83,7 +100,7 @@ export default function LandingPage() { { step === 'connect' && } { step === 'subscription' && } - { selectedTier !== null && } + { selectedTier !== null && } {showVerificationModal ? diff --git a/src/store/slices/auth.slice.ts b/src/store/slices/auth.slice.ts index 302340fc..179746b0 100644 --- a/src/store/slices/auth.slice.ts +++ b/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; @@ -18,6 +19,7 @@ export const fetchActiveSubscription = createAsyncThunk('fetchActiveSubscription try { const {impersonate, retainId} = (thunkApi.getState() as RootState).profile; const response: any = await fetch(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) { diff --git a/src/store/slices/profile.slice.ts b/src/store/slices/profile.slice.ts index c0f4d4f8..64fc268e 100644 --- a/src/store/slices/profile.slice.ts +++ b/src/store/slices/profile.slice.ts @@ -84,6 +84,7 @@ interface ProfileState { runHistory: Run[]; impersonate: boolean; retainId: number | null; + profileBalance: number; } export interface IUpdateProfile { @@ -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 @@ -235,6 +237,16 @@ export const fetchProfileRunHistory = createAsyncThunk("fetchProfileRunHistory", } }) +export const fetchProfileBalance = createAsyncThunk("fetchProfileBalance", async( payload: any, thunkApi) => { + try { + const res: any = await fetch(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, @@ -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; + }) }, }); diff --git a/src/store/slices/register.slice.ts b/src/store/slices/register.slice.ts index 65ca9b77..ed429a3c 100644 --- a/src/store/slices/register.slice.ts +++ b/src/store/slices/register.slice.ts @@ -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(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 { @@ -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); @@ -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()); diff --git a/src/store/slices/tiers.slice.ts b/src/store/slices/tiers.slice.ts index 38ef9542..862893ab 100644 --- a/src/store/slices/tiers.slice.ts +++ b/src/store/slices/tiers.slice.ts @@ -12,6 +12,9 @@ export interface Tier { features: TierFeature[]; usdPrice: number; enabled: boolean; + type: string; + duration: number; + description: string; } interface TiersState {