Created a webhook to allow the transaction to be completed
This commit is contained in:
@@ -1,101 +0,0 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type {NextApiRequest, NextApiResponse} from "next";
|
||||
import {app} from "@/firebase";
|
||||
import {getFirestore, collection, getDocs, setDoc, doc} from "firebase/firestore";
|
||||
import {withIronSessionApiRoute} from "iron-session/next";
|
||||
import {sessionOptions} from "@/lib/session";
|
||||
import {Group} from "@/interfaces/user";
|
||||
import {Payment} from "@/interfaces/paypal";
|
||||
import {v4} from "uuid";
|
||||
import ShortUniqueId from "short-unique-id";
|
||||
import axios from "axios";
|
||||
|
||||
const db = getFirestore(app);
|
||||
|
||||
export default withIronSessionApiRoute(handler, sessionOptions);
|
||||
|
||||
interface BillingData {
|
||||
apartment: string;
|
||||
email: string;
|
||||
floor: string;
|
||||
first_name: string;
|
||||
street: string;
|
||||
building: string;
|
||||
phone_number: string;
|
||||
shipping_method: string;
|
||||
postal_code: string;
|
||||
city: string;
|
||||
country: string;
|
||||
last_name: string;
|
||||
state: string;
|
||||
}
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (!req.session.user) {
|
||||
res.status(401).json({ok: false});
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === "GET") await get(req, res);
|
||||
if (req.method === "POST") await post(req, res);
|
||||
}
|
||||
|
||||
async function get(req: NextApiRequest, res: NextApiResponse) {
|
||||
const snapshot = await getDocs(collection(db, "payments"));
|
||||
|
||||
res.status(200).json(
|
||||
snapshot.docs.map((doc) => ({
|
||||
id: doc.id,
|
||||
...doc.data(),
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
async function post(req: NextApiRequest, res: NextApiResponse) {
|
||||
const body = req.body as Payment;
|
||||
|
||||
const shortUID = new ShortUniqueId();
|
||||
await setDoc(doc(db, "payments", shortUID.randomUUID(8)), body);
|
||||
res.status(200).json({ok: true});
|
||||
}
|
||||
|
||||
const authenticatePaymob = async () => {
|
||||
const response = await axios.post<{token: string}>(
|
||||
"https://oman.paymob.com/api/auth/tokens",
|
||||
{
|
||||
api_key: process.env.PAYMOB_API_KEY,
|
||||
},
|
||||
{headers: {Authorization: `Bearer ${process.env.PAYMOB_SECRET_KEY}`}},
|
||||
);
|
||||
|
||||
return response.data.token;
|
||||
};
|
||||
|
||||
const createOrder = async (token: string) => {
|
||||
const response = await axios.post<{id: number}>(
|
||||
"https://oman.paymob.com/api/ecommerce/orders",
|
||||
{auth_token: token, delivery_needed: "false", currency: "OMR", amount_cents: "100", items: []},
|
||||
{headers: {Authorization: `Bearer ${token}`}},
|
||||
);
|
||||
|
||||
return response.data.id;
|
||||
};
|
||||
|
||||
const createTransactionIFrame = async (token: string, orderID: number, billingData: BillingData) => {
|
||||
const response = await axios.post<{token: string}>(
|
||||
"https://oman.paymob.com/api/acceptance/payment_keys",
|
||||
{
|
||||
auth_token: token,
|
||||
amount_cents: "100",
|
||||
order_id: orderID,
|
||||
currency: "OMR",
|
||||
expiration: 3600,
|
||||
integration_id: 1540,
|
||||
lock_order_when_paid: "true",
|
||||
billing_data: billingData,
|
||||
},
|
||||
{headers: {Authorization: `Bearer ${token}`}},
|
||||
);
|
||||
|
||||
return response.data.token;
|
||||
};
|
||||
52
src/pages/api/paymob/index.ts
Normal file
52
src/pages/api/paymob/index.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type {NextApiRequest, NextApiResponse} from "next";
|
||||
import {app} from "@/firebase";
|
||||
import {getFirestore, collection, getDocs, setDoc, doc} from "firebase/firestore";
|
||||
import {withIronSessionApiRoute} from "iron-session/next";
|
||||
import {sessionOptions} from "@/lib/session";
|
||||
import {Group} from "@/interfaces/user";
|
||||
import {Payment} from "@/interfaces/paypal";
|
||||
import {v4} from "uuid";
|
||||
import ShortUniqueId from "short-unique-id";
|
||||
import axios from "axios";
|
||||
import {IntentionResult, PaymentIntention} from "@/interfaces/paymob";
|
||||
|
||||
const db = getFirestore(app);
|
||||
|
||||
export default withIronSessionApiRoute(handler, sessionOptions);
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (!req.session.user) {
|
||||
res.status(401).json({ok: false});
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === "GET") await get(req, res);
|
||||
if (req.method === "POST") await post(req, res);
|
||||
}
|
||||
|
||||
async function get(req: NextApiRequest, res: NextApiResponse) {
|
||||
const snapshot = await getDocs(collection(db, "payments"));
|
||||
|
||||
res.status(200).json(
|
||||
snapshot.docs.map((doc) => ({
|
||||
id: doc.id,
|
||||
...doc.data(),
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
async function post(req: NextApiRequest, res: NextApiResponse) {
|
||||
const intention = req.body as PaymentIntention;
|
||||
|
||||
const response = await axios.post<IntentionResult>(
|
||||
"https://oman.paymob.com/v1/intention/",
|
||||
{...intention, payment_methods: [1540], items: []},
|
||||
{headers: {Authorization: `Token ${process.env.PAYMOB_SECRET_KEY}`}},
|
||||
);
|
||||
const intentionResult = response.data;
|
||||
|
||||
res.status(200).json({
|
||||
iframeURL: `https://oman.paymob.com/unifiedcheckout/?publicKey=${process.env.PAYMOB_PUBLIC_KEY}&clientSecret=${intentionResult.client_secret}`,
|
||||
});
|
||||
}
|
||||
82
src/pages/api/paymob/webhook.ts
Normal file
82
src/pages/api/paymob/webhook.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type {NextApiRequest, NextApiResponse} from "next";
|
||||
import {app} from "@/firebase";
|
||||
import {getFirestore, collection, getDocs, setDoc, doc, getDoc, query, where} from "firebase/firestore";
|
||||
import {withIronSessionApiRoute} from "iron-session/next";
|
||||
import {sessionOptions} from "@/lib/session";
|
||||
import {Group, User} from "@/interfaces/user";
|
||||
import {Package, Payment} from "@/interfaces/paypal";
|
||||
import {v4} from "uuid";
|
||||
import ShortUniqueId from "short-unique-id";
|
||||
import axios from "axios";
|
||||
import {IntentionResult, PaymentIntention, TransactionResult} from "@/interfaces/paymob";
|
||||
import moment from "moment";
|
||||
|
||||
const db = getFirestore(app);
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (req.method === "POST") await post(req, res);
|
||||
}
|
||||
|
||||
async function post(req: NextApiRequest, res: NextApiResponse) {
|
||||
const transactionResult = req.body as TransactionResult;
|
||||
const authToken = await authenticatePaymob();
|
||||
|
||||
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});
|
||||
|
||||
const {userID, packageID} = transactionResult.intention.extras.creation_extras;
|
||||
|
||||
const userSnapshot = await getDoc(doc(db, "users", userID));
|
||||
const packageSnapshot = await getDoc(doc(db, "packages", packageID));
|
||||
|
||||
if (!userSnapshot.exists() || !packageSnapshot.exists()) return res.status(404).json({ok: false});
|
||||
|
||||
const user = {...userSnapshot.data(), id: userSnapshot.id} as User;
|
||||
const pack = {...packageSnapshot.data(), id: packageSnapshot.id} as Package;
|
||||
|
||||
const subscriptionExpirationDate = user.subscriptionExpirationDate;
|
||||
if (!subscriptionExpirationDate) return res.status(200).json({ok: false});
|
||||
|
||||
const initialDate = moment(subscriptionExpirationDate).isAfter(moment()) ? moment(subscriptionExpirationDate) : moment();
|
||||
|
||||
const updatedSubscriptionExpirationDate = moment(initialDate).add(pack.duration, pack.duration_unit).toISOString();
|
||||
|
||||
await setDoc(userSnapshot.ref, {subscriptionExpirationDate: updatedSubscriptionExpirationDate}, {merge: true});
|
||||
|
||||
if (user.type === "corporate") {
|
||||
const groupsSnapshot = await getDocs(query(collection(db, "groups"), where("admin", "==", user.id)));
|
||||
const groups = groupsSnapshot.docs.map((g) => ({...g.data(), id: g.id})) as Group[];
|
||||
|
||||
const participants = (await Promise.all(
|
||||
groups.flatMap((x) => x.participants).map(async (x) => ({...(await getDoc(doc(db, "users", x))).data(), id: x})),
|
||||
)) as User[];
|
||||
const sameExpiryDateParticipants = participants.filter((x) => x.subscriptionExpirationDate === subscriptionExpirationDate);
|
||||
|
||||
for (const participant of sameExpiryDateParticipants) {
|
||||
await setDoc(doc(db, "users", participant.id), {subscriptionExpirationDate: updatedSubscriptionExpirationDate}, {merge: true});
|
||||
}
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
ok: true,
|
||||
});
|
||||
}
|
||||
|
||||
const authenticatePaymob = async () => {
|
||||
const response = await axios.post<{token: string}>(
|
||||
"https://oman.paymob.com/api/auth/tokens",
|
||||
{
|
||||
api_key: process.env.PAYMOB_API_KEY,
|
||||
},
|
||||
{headers: {Authorization: `Bearer ${process.env.PAYMOB_SECRET_KEY}`}},
|
||||
);
|
||||
|
||||
return response.data.token;
|
||||
};
|
||||
|
||||
const checkTransaction = async (token: string, orderID: number) => {
|
||||
const response = await axios.post("https://oman.paymob.com/api/ecommerce/orders/transaction_inquiry", {auth_token: token, order_id: orderID});
|
||||
|
||||
return response.status === 200;
|
||||
};
|
||||
Reference in New Issue
Block a user