Updated the payment to work with Paymob
This commit is contained in:
@@ -86,7 +86,7 @@ export default function Navbar({user, path, navDisabled = false, focusMode = fal
|
|||||||
|
|
||||||
{showExpirationDate() && (
|
{showExpirationDate() && (
|
||||||
<Link
|
<Link
|
||||||
href={user.subscriptionExpirationDate && disablePaymentPage ? "/payment" : ""}
|
href={!!user.subscriptionExpirationDate && !disablePaymentPage ? "/payment" : ""}
|
||||||
data-tip="Expiry date"
|
data-tip="Expiry date"
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"flex w-fit cursor-pointer justify-center rounded-full border px-6 py-2 text-sm font-normal focus:outline-none",
|
"flex w-fit cursor-pointer justify-center rounded-full border px-6 py-2 text-sm font-normal focus:outline-none",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {PaymentIntention} from "@/interfaces/paymob";
|
|||||||
import {DurationUnit} from "@/interfaces/paypal";
|
import {DurationUnit} from "@/interfaces/paypal";
|
||||||
import {User} from "@/interfaces/user";
|
import {User} from "@/interfaces/user";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import {useRouter} from "next/router";
|
||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
import Button from "./Low/Button";
|
import Button from "./Low/Button";
|
||||||
import Input from "./Low/Input";
|
import Input from "./Low/Input";
|
||||||
@@ -11,14 +12,13 @@ interface Props {
|
|||||||
user: User;
|
user: User;
|
||||||
currency: string;
|
currency: string;
|
||||||
price: number;
|
price: number;
|
||||||
packageID: string;
|
|
||||||
setIsPaymentLoading: (v: boolean) => void;
|
setIsPaymentLoading: (v: boolean) => void;
|
||||||
duration: number;
|
duration: number;
|
||||||
duration_unit: DurationUnit;
|
duration_unit: DurationUnit;
|
||||||
onSuccess: (duration: number, duration_unit: DurationUnit) => void;
|
onSuccess: (duration: number, duration_unit: DurationUnit) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PaymobPayment({user, price, packageID, setIsPaymentLoading, currency, duration, duration_unit, onSuccess}: Props) {
|
export default function PaymobPayment({user, price, setIsPaymentLoading, currency, duration, duration_unit, onSuccess}: Props) {
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
|
||||||
@@ -30,6 +30,8 @@ export default function PaymobPayment({user, price, packageID, setIsPaymentLoadi
|
|||||||
const [state, setState] = useState("");
|
const [state, setState] = useState("");
|
||||||
const [floor, setFloor] = useState("");
|
const [floor, setFloor] = useState("");
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const handleCardPayment = async () => {
|
const handleCardPayment = async () => {
|
||||||
try {
|
try {
|
||||||
setIsPaymentLoading(true);
|
setIsPaymentLoading(true);
|
||||||
@@ -61,13 +63,15 @@ export default function PaymobPayment({user, price, packageID, setIsPaymentLoadi
|
|||||||
},
|
},
|
||||||
extras: {
|
extras: {
|
||||||
userID: user.id,
|
userID: user.id,
|
||||||
packageID: packageID,
|
duration,
|
||||||
|
duration_unit,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await axios.post<{iframeURL: string}>(`/api/paymob`, paymentIntention);
|
const response = await axios.post<{iframeURL: string}>(`/api/paymob`, paymentIntention);
|
||||||
|
|
||||||
window.open(response.data.iframeURL, "_blank", "noopener,noreferrer");
|
router.push(response.data.iframeURL);
|
||||||
|
setIsModalOpen(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error starting card payment process:", error);
|
console.error("Error starting card payment process:", error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ interface Customer {
|
|||||||
extras: IntentionExtras;
|
extras: IntentionExtras;
|
||||||
}
|
}
|
||||||
|
|
||||||
type IntentionExtras = {[key: string]: string};
|
type IntentionExtras = {[key: string]: string | number};
|
||||||
|
|
||||||
export interface IntentionResult {
|
export interface IntentionResult {
|
||||||
payment_keys: PaymentKeysItem[];
|
payment_keys: PaymentKeysItem[];
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/* eslint-disable @next/next/no-img-element */
|
/* eslint-disable @next/next/no-img-element */
|
||||||
import Layout from "@/components/High/Layout";
|
import Layout from "@/components/High/Layout";
|
||||||
import PayPalPayment from "@/components/PayPalPayment";
|
|
||||||
import useGroups from "@/hooks/useGroups";
|
import useGroups from "@/hooks/useGroups";
|
||||||
import usePackages from "@/hooks/usePackages";
|
import usePackages from "@/hooks/usePackages";
|
||||||
import useUsers from "@/hooks/useUsers";
|
import useUsers from "@/hooks/useUsers";
|
||||||
@@ -13,8 +12,6 @@ import useInvites from "@/hooks/useInvites";
|
|||||||
import {BsArrowRepeat} from "react-icons/bs";
|
import {BsArrowRepeat} from "react-icons/bs";
|
||||||
import InviteCard from "@/components/Medium/InviteCard";
|
import InviteCard from "@/components/Medium/InviteCard";
|
||||||
import {useRouter} from "next/router";
|
import {useRouter} from "next/router";
|
||||||
import {PayPalScriptProvider} from "@paypal/react-paypal-js";
|
|
||||||
import {usePaypalTracking} from "@/hooks/usePaypalTracking";
|
|
||||||
import {ToastContainer} from "react-toastify";
|
import {ToastContainer} from "react-toastify";
|
||||||
import useDiscounts from "@/hooks/useDiscounts";
|
import useDiscounts from "@/hooks/useDiscounts";
|
||||||
import PaymobPayment from "@/components/PaymobPayment";
|
import PaymobPayment from "@/components/PaymobPayment";
|
||||||
@@ -37,7 +34,6 @@ export default function PaymentDue({user, hasExpired = false, clientID, reload}:
|
|||||||
const {users} = useUsers();
|
const {users} = useUsers();
|
||||||
const {groups} = useGroups();
|
const {groups} = useGroups();
|
||||||
const {invites, isLoading: isInvitesLoading, reload: reloadInvites} = useInvites({to: user?.id});
|
const {invites, isLoading: isInvitesLoading, reload: reloadInvites} = useInvites({to: user?.id});
|
||||||
const trackingId = usePaypalTracking();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const userDiscounts = discounts.filter((x) => user.email.endsWith(`@${x.domain}`));
|
const userDiscounts = discounts.filter((x) => user.email.endsWith(`@${x.domain}`));
|
||||||
@@ -65,9 +61,15 @@ export default function PaymentDue({user, hasExpired = false, clientID, reload}:
|
|||||||
<ToastContainer />
|
<ToastContainer />
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<div className="absolute left-0 top-0 z-[999] h-screen w-screen overflow-hidden bg-black/60">
|
<div className="absolute left-0 top-0 z-[999] h-screen w-screen overflow-hidden bg-black/60">
|
||||||
<div className="absolute left-1/2 top-1/2 flex h-fit w-fit -translate-x-1/2 -translate-y-1/2 animate-pulse flex-col items-center gap-8 text-white">
|
<div className="absolute left-1/2 top-1/2 flex h-fit w-fit -translate-x-1/2 -translate-y-1/2 flex-col items-center gap-8 text-white">
|
||||||
<span className={clsx("loading loading-infinity w-48")} />
|
<span className={clsx("loading loading-infinity w-48 animate-pulse")} />
|
||||||
<span className={clsx("text-2xl font-bold")}>Completing your payment...</span>
|
<span className={clsx("text-2xl font-bold animate-pulse")}>Completing your payment...</span>
|
||||||
|
<span>If you canceled your payment or it failed, please click the button below to restart</span>
|
||||||
|
<button
|
||||||
|
onClick={() => setIsLoading(false)}
|
||||||
|
className="border border-white rounded-full px-4 py-2 hover:bg-white/80 hover:text-black cursor-pointer transition ease-in-out duration-300">
|
||||||
|
Cancel Payment
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -137,29 +139,10 @@ export default function PaymentDue({user, hasExpired = false, clientID, reload}:
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* <PayPalPayment
|
|
||||||
key={clientID}
|
|
||||||
clientID={clientID}
|
|
||||||
setIsLoading={setIsLoading}
|
|
||||||
onSuccess={() => {
|
|
||||||
setTimeout(reload, 500);
|
|
||||||
}}
|
|
||||||
trackingId={trackingId}
|
|
||||||
currency={p.currency}
|
|
||||||
duration={p.duration}
|
|
||||||
duration_unit={p.duration_unit}
|
|
||||||
price={
|
|
||||||
+(
|
|
||||||
p.price -
|
|
||||||
p.price * (appliedDiscount / 100)
|
|
||||||
).toFixed(2)
|
|
||||||
}
|
|
||||||
/> */}
|
|
||||||
<PaymobPayment
|
<PaymobPayment
|
||||||
key={clientID}
|
key={clientID}
|
||||||
user={user}
|
user={user}
|
||||||
setIsPaymentLoading={setIsLoading}
|
setIsPaymentLoading={setIsLoading}
|
||||||
packageID={p.id}
|
|
||||||
onSuccess={() => {
|
onSuccess={() => {
|
||||||
setTimeout(reload, 500);
|
setTimeout(reload, 500);
|
||||||
}}
|
}}
|
||||||
@@ -179,7 +162,6 @@ export default function PaymentDue({user, hasExpired = false, clientID, reload}:
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
z
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -198,10 +180,10 @@ export default function PaymentDue({user, hasExpired = false, clientID, reload}:
|
|||||||
{user.corporateInformation.payment.value}
|
{user.corporateInformation.payment.value}
|
||||||
{getSymbolFromCurrency(user.corporateInformation.payment.currency)}
|
{getSymbolFromCurrency(user.corporateInformation.payment.currency)}
|
||||||
</span>
|
</span>
|
||||||
<PayPalPayment
|
<PaymobPayment
|
||||||
key={clientID}
|
key={clientID}
|
||||||
clientID={clientID}
|
user={user}
|
||||||
setIsLoading={setIsLoading}
|
setIsPaymentLoading={setIsLoading}
|
||||||
currency={user.corporateInformation.payment.currency}
|
currency={user.corporateInformation.payment.currency}
|
||||||
price={user.corporateInformation.payment.value}
|
price={user.corporateInformation.payment.value}
|
||||||
duration={user.corporateInformation.monthlyDuration}
|
duration={user.corporateInformation.monthlyDuration}
|
||||||
@@ -210,8 +192,6 @@ export default function PaymentDue({user, hasExpired = false, clientID, reload}:
|
|||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
setTimeout(reload, 500);
|
setTimeout(reload, 500);
|
||||||
}}
|
}}
|
||||||
loadScript
|
|
||||||
trackingId={trackingId}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-start gap-1">
|
<div className="flex flex-col items-start gap-1">
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {getFirestore, collection, getDocs, setDoc, doc, getDoc, query, where} fr
|
|||||||
import {withIronSessionApiRoute} from "iron-session/next";
|
import {withIronSessionApiRoute} from "iron-session/next";
|
||||||
import {sessionOptions} from "@/lib/session";
|
import {sessionOptions} from "@/lib/session";
|
||||||
import {Group, User} from "@/interfaces/user";
|
import {Group, User} from "@/interfaces/user";
|
||||||
import {Package, Payment} from "@/interfaces/paypal";
|
import {DurationUnit, Package, Payment} from "@/interfaces/paypal";
|
||||||
import {v4} from "uuid";
|
import {v4} from "uuid";
|
||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
@@ -25,24 +25,37 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
if (!checkTransaction(authToken, transactionResult.transaction.order.id)) return res.status(404).json({ok: false});
|
if (!checkTransaction(authToken, transactionResult.transaction.order.id)) return res.status(404).json({ok: false});
|
||||||
if (!transactionResult.transaction.success) return res.status(200).json({ok: false});
|
if (!transactionResult.transaction.success) return res.status(200).json({ok: false});
|
||||||
|
|
||||||
const {userID, packageID} = transactionResult.intention.extras.creation_extras;
|
const {userID, duration, duration_unit} = transactionResult.intention.extras.creation_extras as {
|
||||||
|
userID: string;
|
||||||
|
duration: number;
|
||||||
|
duration_unit: DurationUnit;
|
||||||
|
};
|
||||||
|
|
||||||
const userSnapshot = await getDoc(doc(db, "users", userID));
|
const userSnapshot = await getDoc(doc(db, "users", userID as string));
|
||||||
const packageSnapshot = await getDoc(doc(db, "packages", packageID));
|
|
||||||
|
|
||||||
if (!userSnapshot.exists() || !packageSnapshot.exists()) return res.status(404).json({ok: false});
|
if (!userSnapshot.exists() || !duration || !duration_unit) return res.status(404).json({ok: false});
|
||||||
|
|
||||||
const user = {...userSnapshot.data(), id: userSnapshot.id} as User;
|
const user = {...userSnapshot.data(), id: userSnapshot.id} as User;
|
||||||
const pack = {...packageSnapshot.data(), id: packageSnapshot.id} as Package;
|
|
||||||
|
|
||||||
const subscriptionExpirationDate = user.subscriptionExpirationDate;
|
const subscriptionExpirationDate = user.subscriptionExpirationDate;
|
||||||
if (!subscriptionExpirationDate) return res.status(200).json({ok: false});
|
if (!subscriptionExpirationDate) return res.status(200).json({ok: false});
|
||||||
|
|
||||||
const initialDate = moment(subscriptionExpirationDate).isAfter(moment()) ? moment(subscriptionExpirationDate) : moment();
|
const initialDate = moment(subscriptionExpirationDate).isAfter(moment()) ? moment(subscriptionExpirationDate) : moment();
|
||||||
|
|
||||||
const updatedSubscriptionExpirationDate = moment(initialDate).add(pack.duration, pack.duration_unit).toISOString();
|
const updatedSubscriptionExpirationDate = moment(initialDate).add(duration, duration_unit).toISOString();
|
||||||
|
|
||||||
await setDoc(userSnapshot.ref, {subscriptionExpirationDate: updatedSubscriptionExpirationDate}, {merge: true});
|
await setDoc(userSnapshot.ref, {subscriptionExpirationDate: updatedSubscriptionExpirationDate}, {merge: true});
|
||||||
|
await setDoc(doc(db, "paypalpayments", v4()), {
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
currency: transactionResult.transaction.currency,
|
||||||
|
orderId: transactionResult.transaction.id,
|
||||||
|
status: "COMPLETED",
|
||||||
|
subscriptionDuration: duration,
|
||||||
|
subscriptionDurationUnit: duration_unit,
|
||||||
|
subscriptionExpirationDate: updatedSubscriptionExpirationDate,
|
||||||
|
userId: userID,
|
||||||
|
value: transactionResult.transaction.amount_cents / 1000,
|
||||||
|
});
|
||||||
|
|
||||||
if (user.type === "corporate") {
|
if (user.type === "corporate") {
|
||||||
const groupsSnapshot = await getDocs(query(collection(db, "groups"), where("admin", "==", user.id)));
|
const groupsSnapshot = await getDocs(query(collection(db, "groups"), where("admin", "==", user.id)));
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user