sarlink-portal/actions/payment.ts
i701 0a63e4337e Enhance user management and payment processing features
- Updated `package.json` to include a new script for launching Prisma Studio.
- Modified `signup` function in `auth-actions.ts` to include account number in user data.
- Refactored `createPayment` function in `payment.ts` to improve error handling and return structured responses.
- Updated UI components in the dashboard to improve layout and responsiveness, including changes to `UserDevices` and `UserPayments` pages.
- Introduced new `AdminDevicesTable` and `UsersPaymentsTable` components for better admin functionalities.
- Enhanced `DeviceCartDrawer` to provide user feedback during payment processing.
- Added account number input to the signup form and updated validation schema accordingly.
- Updated Prisma schema to include a new `ninja_user_id` field for user management.

These changes improve the overall functionality, maintainability, and user experience of the application, particularly in user management and payment processing.
2025-01-06 12:49:13 +05:00

199 lines
4.4 KiB
TypeScript

"use server";
import prisma from "@/lib/db";
import type { PaymentType } from "@/lib/types";
import { formatMacAddress } from "@/lib/utils";
import type { Prisma } from "@prisma/client";
import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
import { addDevicesToGroup } from "./omada-actions";
export async function createPayment(data: PaymentType) {
try {
console.log("data", data);
const payment = await prisma.payment.create({
data: {
amount: data.amount,
numberOfMonths: data.numberOfMonths,
paid: data.paid,
userId: data.userId,
devices: {
connect: data.deviceIds.map((id) => {
return {
id,
};
}),
},
},
});
return { success: true, paymentId: payment.id };
} catch (error) {
console.error("Error creating payment:", error);
return { success: false, error: "Failed to create payment" };
}
}
type VerifyPaymentType = {
userId: string;
paymentId?: string;
benefName: string;
accountNo?: string;
absAmount: string;
time: string;
type?: "TRANSFER" | "WALLET";
};
type PaymentWithDevices = Prisma.PaymentGetPayload<{
include: {
devices: true;
};
}>;
class InsufficientFundsError extends Error {
constructor() {
super("Insufficient funds in wallet");
this.name = "InsufficientFundsError";
}
}
async function processWalletPayment(
user: { id: string; walletBalance: number } | null,
payment: PaymentWithDevices | null,
amount: number,
) {
if (!user || !payment) {
throw new Error("User or payment not found");
}
const walletBalance = user.walletBalance ?? 0;
if (walletBalance < amount) {
throw new InsufficientFundsError();
}
const expiryDate = new Date();
expiryDate.setMonth(expiryDate.getMonth() + payment.numberOfMonths);
await prisma.$transaction([
prisma.payment.update({
where: { id: payment.id },
data: {
paid: true,
paidAt: new Date(),
devices: {
updateMany: {
where: { paymentId: payment.id },
data: { isActive: true, expiryDate: expiryDate },
},
},
},
}),
prisma.user.update({
where: { id: user.id },
data: { walletBalance: walletBalance - amount },
}),
]);
}
type VerifyPaymentResponse =
| {
success: boolean;
message: string;
}
| {
success: boolean;
message: string;
transaction: {
ref: string;
sourceBank: string;
trxDate: string;
};
};
async function verifyExternalPayment(
data: VerifyPaymentType,
payment: PaymentWithDevices | null,
): Promise<VerifyPaymentResponse> {
console.log("payment verify data ->", data);
const response = await fetch(
"https://verifypaymentsapi.baraveli.dev/verify-payment",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
},
);
const json = await response.json();
console.log(json);
if (!payment) {
throw new Error("Payment verification failed or payment not found");
}
if (json.success) {
const expiryDate = new Date();
expiryDate.setMonth(expiryDate.getMonth() + payment.numberOfMonths);
await prisma.payment.update({
where: { id: payment.id },
data: {
paid: true,
paidAt: new Date(),
devices: {
updateMany: {
where: { paymentId: payment.id },
data: { isActive: true, expiryDate: expiryDate },
},
},
},
});
}
return json;
}
async function updateDevices(payment: PaymentWithDevices | null) {
if (!payment) return;
const newDevices = payment.devices.map((d) => ({
name: d.name,
macAddress: formatMacAddress(d.mac),
}));
return await addDevicesToGroup({
groupId: process.env.OMADA_GROUP_ID,
siteId: process.env.OMADA_SITE_ID,
newDevices,
});
}
export async function verifyPayment(data: VerifyPaymentType) {
try {
const [payment, user] = await Promise.all([
prisma.payment.findUnique({
where: { id: data.paymentId },
include: { devices: true },
}),
prisma.user.findUnique({
where: { id: data.userId },
}),
]);
if (data.type === "WALLET") {
await processWalletPayment(user, payment, Number(data.absAmount));
redirect("/payments");
}
const verificationResult = await verifyExternalPayment(data, payment);
await updateDevices(payment);
revalidatePath("/payment[paymentId]");
return verificationResult;
} catch (error) {
console.error("Payment verification failed:", error);
throw error; // Re-throw to handle at a higher level
}
}
export async function addDevicesToOmada() {
console.log("hi");
}