Paypal integration improvements

This commit is contained in:
Joao Ramos
2024-03-07 11:18:48 +00:00
parent 0cff310354
commit 330c177ff9
4 changed files with 125 additions and 85 deletions

View File

@@ -70,18 +70,26 @@ export default function PayPalPayment({
throw new Error("trackingId is not set"); throw new Error("trackingId is not set");
} }
const request = await axios.post<{ ok: boolean; reason?: string }>( axios
"/api/paypal/approve", .post<{ ok: boolean; reason?: string }>("/api/paypal/approve", {
{ id: data.orderID, duration, duration_unit, trackingId } id: data.orderID,
); duration,
duration_unit,
trackingId,
})
.then((request) => {
if (request.status !== 200) {
toast.error("Something went wrong, please try again later");
return;
}
if (request.status !== 200) { toast.success("Your account has been credited more time!");
toast.error("Something went wrong, please try again later"); return onSuccess(duration, duration_unit);
return; })
} .catch((err) => {
console.error(err);
toast.success("Your account has been credited more time!"); toast.error("Something went wrong, please try again later");
return onSuccess(duration, duration_unit); });
}; };
const onError = async (data: Record<string, unknown>) => { const onError = async (data: Record<string, unknown>) => {

View File

@@ -15,6 +15,8 @@ import InviteCard from "@/components/Medium/InviteCard";
import {useRouter} from "next/router"; import {useRouter} from "next/router";
import {PayPalScriptProvider} from "@paypal/react-paypal-js"; import {PayPalScriptProvider} from "@paypal/react-paypal-js";
import { usePaypalTracking } from "@/hooks/usePaypalTracking"; import { usePaypalTracking } from "@/hooks/usePaypalTracking";
import {ToastContainer} from "react-toastify";
interface Props { interface Props {
user: User; user: User;
@@ -47,6 +49,7 @@ export default function PaymentDue({user, hasExpired = false, clientID, reload}:
return ( return (
<> <>
<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 animate-pulse flex-col items-center gap-8 text-white">

View File

@@ -49,79 +49,89 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
"PayPal-Client-Metadata-Id": trackingId, "PayPal-Client-Metadata-Id": trackingId,
}, },
}; };
const request = await axios.post(url, {}, headers); axios
.post(url, {}, headers)
.then(async (request) => {
if (request.data.status === "COMPLETED") {
const user = req.session.user;
const subscriptionExpirationDate =
user!.subscriptionExpirationDate;
const today = moment(new Date());
const dateToBeAddedTo = !subscriptionExpirationDate
? today
: moment(subscriptionExpirationDate).isAfter(today)
? moment(subscriptionExpirationDate)
: today;
if (request.data.status === "COMPLETED") { const updatedExpirationDate = dateToBeAddedTo.add(
const user = req.session.user; duration,
const subscriptionExpirationDate = duration_unit
req.session.user.subscriptionExpirationDate; );
const today = moment(new Date()); await setDoc(
const dateToBeAddedTo = !subscriptionExpirationDate doc(db, "users", req.session.user!.id),
? today {
: moment(subscriptionExpirationDate).isAfter(today) subscriptionExpirationDate: updatedExpirationDate.toISOString(),
? moment(subscriptionExpirationDate) status: "active",
: today; },
{ merge: true }
);
const updatedExpirationDate = dateToBeAddedTo.add(duration, duration_unit); try {
await setDoc( await setDoc(doc(db, "paypalpayments", v4()), {
doc(db, "users", req.session.user.id), orderId: id,
{ userId: req.session.user!.id,
subscriptionExpirationDate: updatedExpirationDate.toISOString(), status: request.data.status,
status: "active", createdAt: new Date().toISOString(),
}, value:
{ merge: true } request.data.purchase_units[0].payments.captures[0].amount.value,
); currency:
request.data.purchase_units[0].payments.captures[0].amount
.currency_code,
subscriptionDuration: duration,
subscriptionDurationUnit: duration_unit,
subscriptionExpirationDate: updatedExpirationDate.toISOString(),
});
} catch (err) {
console.error("Failed to insert paypal payment!", err);
}
try { if (user!.type === "corporate") {
await setDoc(doc(db, "paypalpayments", v4()), { const snapshot = await getDocs(collection(db, "groups"));
orderId: id, const groups: Group[] = (
userId: req.session.user.id, snapshot.docs.map((doc) => ({
status: request.data.status, id: doc.id,
createdAt: new Date().toISOString(), ...doc.data(),
value: request.data.purchase_units[0].payments.captures[0].amount.value, })) as Group[]
currency: ).filter((x) => x.admin === user!.id);
request.data.purchase_units[0].payments.captures[0].amount
.currency_code,
subscriptionDuration: duration,
subscriptionDurationUnit: duration_unit,
subscriptionExpirationDate: updatedExpirationDate.toISOString(),
});
} catch (err) {
console.error("Failed to insert paypal payment!", err);
}
if (user.type === "corporate") { await Promise.all(
const snapshot = await getDocs(collection(db, "groups")); groups
const groups: Group[] = ( .flatMap((x) => x.participants)
snapshot.docs.map((doc) => ({ .map(
id: doc.id, async (x) =>
...doc.data(), await setDoc(
})) as Group[] doc(db, "users", x),
).filter((x) => x.admin === user.id); {
subscriptionExpirationDate:
await Promise.all( updatedExpirationDate.toISOString(),
groups status: "active",
.flatMap((x) => x.participants) },
.map( { merge: true }
async (x) => )
await setDoc(
doc(db, "users", x),
{
subscriptionExpirationDate:
updatedExpirationDate.toISOString(),
status: "active",
},
{ merge: true }
) )
) );
); }
}
return res.status(200).json({ ok: true }); return res.status(200).json({ ok: true });
} }
res.status(404).json({ res.status(404).json({
ok: false, ok: false,
reason: "Order ID not found or purchase was not approved!", reason: "Order ID not found or purchase was not approved!",
}); });
})
.catch((err) => {
console.error(err.response.status, err.response.data);
res.status(err.response.status).json(err.response.data);
});
} }

View File

@@ -32,16 +32,29 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
return res.status(401).json({ ok: false, reason: "Missing tracking id!" }); return res.status(401).json({ ok: false, reason: "Missing tracking id!" });
const url = `${process.env.PAYPAL_ACCESS_TOKEN_URL}/v2/checkout/orders`; const url = `${process.env.PAYPAL_ACCESS_TOKEN_URL}/v2/checkout/orders`;
const amount = {
currency_code: currencyCode,
value: price.toString(),
};
const data = { const data = {
purchase_units: [ purchase_units: [
{ {
invoice_id: `INV-${v4()}`, invoice_id: `INV-${v4()}`,
amount: { amount: {
currency_code: currencyCode, ...amount,
value: price.toString(), breakdown: {
item_total: amount,
},
}, },
reference_id: v4(), items: [
{
name: "Encoach Subscription",
quantity: "1",
category: "DIGITAL_GOODS",
unit_amount: amount,
},
],
}, },
], ],
payment_source: { payment_source: {
@@ -74,7 +87,13 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
}) })
); );
const request = await axios.post<OrderResponseBody>(url, data, headers); axios
.post<OrderResponseBody>(url, data, headers)
res.status(request.status).json(request.data); .then((request) => {
res.status(request.status).json(request.data);
})
.catch((err) => {
console.error(err.response.status, err.response.data);
res.status(err.response.status).json(err.response.data);
});
} }