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 {DurationUnit} from "@/interfaces/paypal";
|
||||||
import {User} from "@/interfaces/user";
|
import {User} from "@/interfaces/user";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
import Button from "./Low/Button";
|
import Button from "./Low/Button";
|
||||||
|
import Input from "./Low/Input";
|
||||||
|
import Modal from "./Modal";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
user: User;
|
user: User;
|
||||||
currency: string;
|
currency: string;
|
||||||
price: number;
|
price: number;
|
||||||
title: string;
|
packageID: string;
|
||||||
description: string;
|
setIsPaymentLoading: (v: boolean) => void;
|
||||||
paymentID: string;
|
|
||||||
duration: number;
|
duration: number;
|
||||||
duration_unit: DurationUnit;
|
duration_unit: DurationUnit;
|
||||||
setIsLoading: (isLoading: boolean) => void;
|
|
||||||
onSuccess: (duration: number, duration_unit: DurationUnit) => void;
|
onSuccess: (duration: number, duration_unit: DurationUnit) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PaymobPayment({
|
export default function PaymobPayment({user, price, packageID, setIsPaymentLoading, currency, duration, duration_unit, onSuccess}: Props) {
|
||||||
user,
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
price,
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
currency,
|
|
||||||
title,
|
const [firstName, setFirstName] = useState(user.name.split(" ")[0]);
|
||||||
description,
|
const [lastName, setLastName] = useState([...user.name.split(" ")].pop());
|
||||||
paymentID,
|
const [street, setStreet] = useState("");
|
||||||
duration,
|
const [apartment, setApartment] = useState("");
|
||||||
duration_unit,
|
const [building, setBuilding] = useState("");
|
||||||
setIsLoading,
|
const [state, setState] = useState("");
|
||||||
onSuccess,
|
const [floor, setFloor] = useState("");
|
||||||
}: Props) {
|
|
||||||
const [iframeURL, setIFrameURL] = useState<string>();
|
|
||||||
|
|
||||||
const handleCardPayment = async () => {
|
const handleCardPayment = async () => {
|
||||||
try {
|
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) {
|
} catch (error) {
|
||||||
console.error("Error starting card payment process:", error);
|
console.error("Error starting card payment process:", error);
|
||||||
}
|
}
|
||||||
@@ -40,8 +75,29 @@ export default function PaymobPayment({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button onClick={handleCardPayment}>Pay</Button>
|
<Modal isOpen={isModalOpen} title="Billing Data" onClose={() => setIsModalOpen(false)}>
|
||||||
{iframeURL}
|
<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,13 +107,6 @@ 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:
|
To add to your use of EnCoach, please purchase one of the time packages available below:
|
||||||
</span>
|
</span>
|
||||||
<div className="flex w-full flex-wrap justify-center gap-8">
|
<div className="flex w-full flex-wrap justify-center gap-8">
|
||||||
<PayPalScriptProvider
|
|
||||||
options={{
|
|
||||||
clientId: clientID,
|
|
||||||
currency: "USD",
|
|
||||||
intent: "capture",
|
|
||||||
commit: true,
|
|
||||||
}}>
|
|
||||||
{packages.map((p) => (
|
{packages.map((p) => (
|
||||||
<div key={p.id} className={clsx("flex flex-col items-start gap-6 rounded-xl bg-white p-4")}>
|
<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">
|
<div className="mb-2 flex flex-col items-start">
|
||||||
@@ -165,10 +158,8 @@ export default function PaymentDue({user, hasExpired = false, clientID, reload}:
|
|||||||
<PaymobPayment
|
<PaymobPayment
|
||||||
key={clientID}
|
key={clientID}
|
||||||
user={user}
|
user={user}
|
||||||
description="Description"
|
setIsPaymentLoading={setIsLoading}
|
||||||
paymentID="123"
|
packageID={p.id}
|
||||||
title="Title"
|
|
||||||
setIsLoading={setIsLoading}
|
|
||||||
onSuccess={() => {
|
onSuccess={() => {
|
||||||
setTimeout(reload, 500);
|
setTimeout(reload, 500);
|
||||||
}}
|
}}
|
||||||
@@ -188,7 +179,7 @@ export default function PaymentDue({user, hasExpired = false, clientID, reload}:
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</PayPalScriptProvider>
|
z
|
||||||
</div>
|
</div>
|
||||||
</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