Created a webhook to allow the transaction to be completed
This commit is contained in:
@@ -1,38 +1,73 @@
|
||||
import {PaymentIntention} from "@/interfaces/paymob";
|
||||
import {DurationUnit} from "@/interfaces/paypal";
|
||||
import {User} from "@/interfaces/user";
|
||||
import axios from "axios";
|
||||
import {useState} from "react";
|
||||
import Button from "./Low/Button";
|
||||
import Input from "./Low/Input";
|
||||
import Modal from "./Modal";
|
||||
|
||||
interface Props {
|
||||
user: User;
|
||||
currency: string;
|
||||
price: number;
|
||||
title: string;
|
||||
description: string;
|
||||
paymentID: string;
|
||||
packageID: string;
|
||||
setIsPaymentLoading: (v: boolean) => void;
|
||||
duration: number;
|
||||
duration_unit: DurationUnit;
|
||||
setIsLoading: (isLoading: boolean) => void;
|
||||
onSuccess: (duration: number, duration_unit: DurationUnit) => void;
|
||||
}
|
||||
|
||||
export default function PaymobPayment({
|
||||
user,
|
||||
price,
|
||||
currency,
|
||||
title,
|
||||
description,
|
||||
paymentID,
|
||||
duration,
|
||||
duration_unit,
|
||||
setIsLoading,
|
||||
onSuccess,
|
||||
}: Props) {
|
||||
const [iframeURL, setIFrameURL] = useState<string>();
|
||||
export default function PaymobPayment({user, price, packageID, setIsPaymentLoading, currency, duration, duration_unit, onSuccess}: Props) {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
|
||||
const [firstName, setFirstName] = useState(user.name.split(" ")[0]);
|
||||
const [lastName, setLastName] = useState([...user.name.split(" ")].pop());
|
||||
const [street, setStreet] = useState("");
|
||||
const [apartment, setApartment] = useState("");
|
||||
const [building, setBuilding] = useState("");
|
||||
const [state, setState] = useState("");
|
||||
const [floor, setFloor] = useState("");
|
||||
|
||||
const handleCardPayment = async () => {
|
||||
try {
|
||||
setIsPaymentLoading(true);
|
||||
|
||||
const paymentIntention: PaymentIntention = {
|
||||
amount: price * 1000,
|
||||
currency: "OMR",
|
||||
items: [],
|
||||
payment_methods: [1540],
|
||||
customer: {
|
||||
email: user.email,
|
||||
first_name: user.name.split(" ")[0],
|
||||
last_name: [...user.name.split(" ")].pop() || "N/A",
|
||||
extras: {
|
||||
re: user.id,
|
||||
},
|
||||
},
|
||||
billing_data: {
|
||||
apartment: apartment || "N/A",
|
||||
building: building || "N/A",
|
||||
country: user.demographicInformation?.country || "N/A",
|
||||
email: user.email,
|
||||
first_name: user.name.split(" ")[0],
|
||||
last_name: [...user.name.split(" ")].pop() || "N/A",
|
||||
floor: floor || "N/A",
|
||||
phone_number: user.demographicInformation?.phone || "N/A",
|
||||
state: state || "N/A",
|
||||
street: street || "N/A",
|
||||
},
|
||||
extras: {
|
||||
userID: user.id,
|
||||
packageID: packageID,
|
||||
},
|
||||
};
|
||||
|
||||
const response = await axios.post<{iframeURL: string}>(`/api/paymob`, paymentIntention);
|
||||
|
||||
window.open(response.data.iframeURL, "_blank", "noopener,noreferrer");
|
||||
} catch (error) {
|
||||
console.error("Error starting card payment process:", error);
|
||||
}
|
||||
@@ -40,8 +75,29 @@ export default function PaymobPayment({
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={handleCardPayment}>Pay</Button>
|
||||
{iframeURL}
|
||||
<Modal isOpen={isModalOpen} title="Billing Data" onClose={() => setIsModalOpen(false)}>
|
||||
<div className="flex flex-col gap-4 mt-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<Input label="First Name" value={firstName} onChange={setFirstName} type="text" name="firstName" />
|
||||
<Input label="Last Name" value={lastName} onChange={setLastName} type="text" name="lastName" />
|
||||
</div>
|
||||
<div className="grid grid-cols-3 -md:grid-cols-1 gap-4">
|
||||
<Input label="State" value={state} onChange={setState} type="text" name="state" />
|
||||
<Input label="Street" value={street} onChange={setStreet} type="text" name="street" />
|
||||
<Input label="Building" value={building} onChange={setBuilding} type="text" name="building" />
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<Input label="Floor" value={floor} onChange={setFloor} type="text" name="floor" />
|
||||
<Input label="Apartment" value={apartment} onChange={setApartment} type="text" name="apartment" />
|
||||
</div>
|
||||
<Button className="w-full max-w-[200px] self-end mt-4" disabled={!firstName || !lastName} onClick={handleCardPayment}>
|
||||
Complete Payment
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
<Button isLoading={isLoading} onClick={() => setIsModalOpen(true)}>
|
||||
Select
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
118
src/interfaces/paymob.ts
Normal file
118
src/interfaces/paymob.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
export interface PaymentIntention {
|
||||
amount: number;
|
||||
currency: string;
|
||||
payment_methods: number[];
|
||||
items: any[];
|
||||
billing_data: BillingData;
|
||||
customer: Customer;
|
||||
extras: IntentionExtras;
|
||||
}
|
||||
|
||||
interface BillingData {
|
||||
apartment: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
street: string;
|
||||
building: string;
|
||||
phone_number: string;
|
||||
country: string;
|
||||
email: string;
|
||||
floor: string;
|
||||
state: string;
|
||||
}
|
||||
|
||||
interface Customer {
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
email: string;
|
||||
extras: IntentionExtras;
|
||||
}
|
||||
|
||||
type IntentionExtras = {[key: string]: string};
|
||||
|
||||
export interface IntentionResult {
|
||||
payment_keys: PaymentKeysItem[];
|
||||
id: string;
|
||||
intention_detail: IntentionDetail;
|
||||
client_secret: string;
|
||||
payment_methods: PaymentMethodsItem[];
|
||||
special_reference: null;
|
||||
extras: Extras;
|
||||
confirmed: boolean;
|
||||
status: string;
|
||||
created: string;
|
||||
card_detail: null;
|
||||
object: string;
|
||||
}
|
||||
|
||||
interface PaymentKeysItem {
|
||||
integration: number;
|
||||
key: string;
|
||||
gateway_type: string;
|
||||
iframe_id: null;
|
||||
}
|
||||
|
||||
interface IntentionDetail {
|
||||
amount: number;
|
||||
items: ItemsItem[];
|
||||
currency: string;
|
||||
}
|
||||
|
||||
interface ItemsItem {
|
||||
name: string;
|
||||
amount: number;
|
||||
description: string;
|
||||
quantity: number;
|
||||
}
|
||||
|
||||
interface PaymentMethodsItem {
|
||||
integration_id: number;
|
||||
alias: null;
|
||||
name: null;
|
||||
method_type: string;
|
||||
currency: string;
|
||||
live: boolean;
|
||||
use_cvc_with_moto: boolean;
|
||||
}
|
||||
|
||||
interface Extras {
|
||||
creation_extras: IntentionExtras;
|
||||
confirmation_extras: null;
|
||||
}
|
||||
|
||||
export interface TransactionResult {
|
||||
paymob_request_id: null;
|
||||
intention: IntentionResult;
|
||||
hmac: string;
|
||||
transaction: Transaction;
|
||||
}
|
||||
|
||||
interface Transaction {
|
||||
amount_cents: number;
|
||||
created_at: string;
|
||||
currency: string;
|
||||
error_occured: boolean;
|
||||
has_parent_transaction: boolean;
|
||||
id: number;
|
||||
integration_id: number;
|
||||
is_3d_secure: boolean;
|
||||
is_auth: boolean;
|
||||
is_capture: boolean;
|
||||
is_refunded: boolean;
|
||||
is_standalone_payment: boolean;
|
||||
is_voided: boolean;
|
||||
order: Order;
|
||||
owner: number;
|
||||
pending: boolean;
|
||||
source_data: Source_data;
|
||||
success: boolean;
|
||||
receipt: string;
|
||||
}
|
||||
interface Order {
|
||||
id: number;
|
||||
}
|
||||
interface Source_data {
|
||||
pan: string;
|
||||
sub_type: string;
|
||||
type: string;
|
||||
}
|
||||
@@ -107,44 +107,37 @@ export default function PaymentDue({user, hasExpired = false, clientID, reload}:
|
||||
To add to your use of EnCoach, please purchase one of the time packages available below:
|
||||
</span>
|
||||
<div className="flex w-full flex-wrap justify-center gap-8">
|
||||
<PayPalScriptProvider
|
||||
options={{
|
||||
clientId: clientID,
|
||||
currency: "USD",
|
||||
intent: "capture",
|
||||
commit: true,
|
||||
}}>
|
||||
{packages.map((p) => (
|
||||
<div key={p.id} className={clsx("flex flex-col items-start gap-6 rounded-xl bg-white p-4")}>
|
||||
<div className="mb-2 flex flex-col items-start">
|
||||
<img src="/logo_title.png" alt="EnCoach's Logo" className="w-32" />
|
||||
<span className="text-xl font-semibold">
|
||||
EnCoach - {p.duration}{" "}
|
||||
{capitalize(
|
||||
p.duration === 1 ? p.duration_unit.slice(0, p.duration_unit.length - 1) : p.duration_unit,
|
||||
)}
|
||||
{packages.map((p) => (
|
||||
<div key={p.id} className={clsx("flex flex-col items-start gap-6 rounded-xl bg-white p-4")}>
|
||||
<div className="mb-2 flex flex-col items-start">
|
||||
<img src="/logo_title.png" alt="EnCoach's Logo" className="w-32" />
|
||||
<span className="text-xl font-semibold">
|
||||
EnCoach - {p.duration}{" "}
|
||||
{capitalize(
|
||||
p.duration === 1 ? p.duration_unit.slice(0, p.duration_unit.length - 1) : p.duration_unit,
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex w-full flex-col items-start gap-2">
|
||||
{!appliedDiscount && (
|
||||
<span className="text-2xl">
|
||||
{p.price}
|
||||
{getSymbolFromCurrency(p.currency)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex w-full flex-col items-start gap-2">
|
||||
{!appliedDiscount && (
|
||||
<span className="text-2xl">
|
||||
)}
|
||||
{appliedDiscount && (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-2xl line-through">
|
||||
{p.price}
|
||||
{getSymbolFromCurrency(p.currency)}
|
||||
</span>
|
||||
)}
|
||||
{appliedDiscount && (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-2xl line-through">
|
||||
{p.price}
|
||||
{getSymbolFromCurrency(p.currency)}
|
||||
</span>
|
||||
<span className="text-2xl text-mti-red-light">
|
||||
{(p.price - p.price * (appliedDiscount / 100)).toFixed(2)}
|
||||
{getSymbolFromCurrency(p.currency)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{/* <PayPalPayment
|
||||
<span className="text-2xl text-mti-red-light">
|
||||
{(p.price - p.price * (appliedDiscount / 100)).toFixed(2)}
|
||||
{getSymbolFromCurrency(p.currency)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{/* <PayPalPayment
|
||||
key={clientID}
|
||||
clientID={clientID}
|
||||
setIsLoading={setIsLoading}
|
||||
@@ -162,33 +155,31 @@ export default function PaymentDue({user, hasExpired = false, clientID, reload}:
|
||||
).toFixed(2)
|
||||
}
|
||||
/> */}
|
||||
<PaymobPayment
|
||||
key={clientID}
|
||||
user={user}
|
||||
description="Description"
|
||||
paymentID="123"
|
||||
title="Title"
|
||||
setIsLoading={setIsLoading}
|
||||
onSuccess={() => {
|
||||
setTimeout(reload, 500);
|
||||
}}
|
||||
currency={p.currency}
|
||||
duration={p.duration}
|
||||
duration_unit={p.duration_unit}
|
||||
price={+(p.price - p.price * (appliedDiscount / 100)).toFixed(2)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-1">
|
||||
<span>This includes:</span>
|
||||
<ul className="flex flex-col items-start text-sm">
|
||||
<li>- Train your abilities for the IELTS exam</li>
|
||||
<li>- Gain insights into your weaknesses and strengths</li>
|
||||
<li>- Allow yourself to correctly prepare for the exam</li>
|
||||
</ul>
|
||||
</div>
|
||||
<PaymobPayment
|
||||
key={clientID}
|
||||
user={user}
|
||||
setIsPaymentLoading={setIsLoading}
|
||||
packageID={p.id}
|
||||
onSuccess={() => {
|
||||
setTimeout(reload, 500);
|
||||
}}
|
||||
currency={p.currency}
|
||||
duration={p.duration}
|
||||
duration_unit={p.duration_unit}
|
||||
price={+(p.price - p.price * (appliedDiscount / 100)).toFixed(2)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</PayPalScriptProvider>
|
||||
<div className="flex flex-col items-start gap-1">
|
||||
<span>This includes:</span>
|
||||
<ul className="flex flex-col items-start text-sm">
|
||||
<li>- Train your abilities for the IELTS exam</li>
|
||||
<li>- Gain insights into your weaknesses and strengths</li>
|
||||
<li>- Allow yourself to correctly prepare for the exam</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
z
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -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