feat: add age validation in signup and update payment verification logic
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 5m17s

This commit is contained in:
i701 2025-04-20 12:36:24 +05:00
parent f7122cb252
commit 0c093f1303
Signed by: i701
GPG Key ID: 54A0DA1E26D8E587
5 changed files with 31 additions and 193 deletions

View File

@ -121,6 +121,15 @@ export async function signup(_actionState: ActionState, formData: FormData) {
errors: parsedData.error.flatten(),
};
}
const age =
new Date().getFullYear() - new Date(parsedData.data.dob).getFullYear();
if (age < 18) {
return {
message: "You must be at least 18 years old to register.",
payload: formData,
db_error: "dob",
};
}
const idCardExists = await checkIdOrPhone({
id_card: parsedData.data.id_card,

View File

@ -115,20 +115,18 @@ export async function cancelPayment({ id }: { id: string }) {
return { message: "Payment successfully canceled." };
}
type UpdatePayment = Pick<
Payment,
"id" | "paid" | "paid_at" | "method" | "number_of_months"
>;
export async function updatePayment({
id,
method,
paid,
paid_at,
number_of_months,
}: UpdatePayment) {
type UpdatePayment = {
id: string;
method: "TRANSFER" | "WALLET";
benefName?: string;
accountNo?: string;
absAmount?: string;
time?: string;
};
export async function verifyPayment({ id, method }: UpdatePayment) {
const session = await getServerSession(authOptions);
const response = await fetch(
`${process.env.SARLINK_API_BASE_URL}/api/billing/payment/${id}/update/`,
`${process.env.SARLINK_API_BASE_URL}/api/billing/payment/${id}/verify/`,
{
method: "PUT",
headers: {
@ -137,58 +135,11 @@ export async function updatePayment({
},
body: JSON.stringify({
method,
paid,
paid_at,
number_of_months,
}),
},
);
if (!response.ok) {
const errorData = (await response.json()) as ApiError;
const errorMessage =
errorData.message || errorData.detail || "An error occurred.";
const error = new Error(errorMessage);
(error as ApiError & { details?: ApiError }).details = errorData; // Attach the errorData to the error object
throw error;
}
const payment = (await response.json()) as Payment;
return payment;
}
type TUpdateWalletBalance = Pick<User, "id" | "wallet_balance">;
export async function updateWalletBalance({
id,
wallet_balance,
}: TUpdateWalletBalance) {
const session = await getServerSession(authOptions);
console.log("wallet bal in server action", wallet_balance);
const response = await fetch(
`${process.env.SARLINK_API_BASE_URL}/api/auth/update-wallet/${id}/`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Token ${session?.apiToken}`,
},
body: JSON.stringify({
wallet_balance: Number.parseFloat(wallet_balance?.toFixed(2) ?? "0"),
}),
},
);
if (!response.ok) {
const errorData = (await response.json()) as ApiError;
const errorMessage =
errorData.message || errorData.detail || "An error occurred.";
const error = new Error(errorMessage);
(error as ApiError & { details?: ApiError }).details = errorData; // Attach the errorData to the error object
throw error;
}
const message = (await response.json()) as {
message: "Wallet balance updated successfully.";
};
return message;
revalidatePath("/payments/[paymentsId]");
return handleApiResponse<Payment>(response, "updatePayment");
}
type VerifyPaymentType = {
@ -217,43 +168,6 @@ export async function getProfile() {
return handleApiResponse<User>(response, "getProfile");
}
export async function processWalletPayment({
payment,
amount,
}: { payment: Payment | undefined; amount: number }) {
await checkSession();
const session = await getServerSession(authOptions);
const walletBalance = session?.user?.wallet_balance ?? 0;
console.log("processing wallet payment >>>", walletBalance);
if (!payment) return;
const [updatePaymentError, _] = await tryCatch(
updatePayment({
id: payment.id,
method: "WALLET",
paid: true,
paid_at: new Date().toISOString(),
number_of_months: payment.number_of_months,
}),
);
if (updatePaymentError) {
throw new Error(updatePaymentError.message);
}
console.log("Wallet balance before update:", walletBalance);
const updated_balance = walletBalance - amount;
if (!session?.user?.id) return;
const [walletUpdateError, response] = await tryCatch(
updateWalletBalance({
id: session?.user?.id,
wallet_balance: Number.parseFloat(updated_balance?.toFixed(2) ?? "0"),
}),
);
if (walletUpdateError) {
throw new Error(walletUpdateError.message);
}
revalidatePath("/payments/[paymentsId]", "page");
return response;
}
type VerifyPaymentResponse =
| {
success: boolean;

View File

@ -230,6 +230,11 @@ export default function SignUpForm() {
{actionState?.errors?.fieldErrors?.dob}
</span>
)}
{actionState?.db_error === "dob" && (
<span className="text-sm inline-block text-red-500">
{actionState?.message}
</span>
)}
</div>
<div>
<label htmlFor="accNo" className="text-sm">

View File

@ -19,7 +19,7 @@ export function DeviceCartDrawer() {
return (
<Button
size={"lg"}
className="bg-sarLinkOrange absolute bottom-10 w-fit z-20 left-1/2 transform -translate-x-1/2"
className="bg-sarLinkOrange fixed bottom-20 w-80 uppercase h-12 z-20 left-1/2 transform -translate-x-1/2"
onClick={() => router.push("/devices-to-pay")}
variant="outline"
>
@ -27,94 +27,4 @@ export function DeviceCartDrawer() {
Pay {devices.length > 0 && `(${devices.length})`} Device
</Button>
);
// <>
// <Drawer open={isOpen} onOpenChange={setIsOpen}>
// <DrawerTrigger asChild>
// <Button size={"lg"} className="bg-sarLinkOrange absolute bottom-10 w-fit z-20 left-1/2 transform -translate-x-1/2" onClick={() => setIsOpen(!isOpen)} variant="outline">
// <MonitorSmartphone />
// Pay {devices.length > 0 && `(${devices.length})`} Device
// </Button>
// </DrawerTrigger>
// <DrawerContent>
// <div className="mx-auto w-full max-w-sm">
// <DrawerHeader>
// <DrawerTitle>Selected Devices</DrawerTitle>
// <DrawerDescription>Selected devices pay.</DrawerDescription>
// </DrawerHeader>
// <div className="flex max-h-[calc(100svh-400px)] flex-col overflow-auto px-4 pb-4 gap-4">
// <pre>{JSON.stringify(isOpen, null, 2)}</pre>
// {devices.map((device) => (
// <DeviceCard key={device.id} device={device} />
// ))}
// </div>
// <div className="px-4 flex flex-col gap-4">
// <NumberInput
// label="Set No of Months"
// value={months}
// onChange={(value) => setMonths(value)}
// maxAllowed={12}
// isDisabled={devices.length === 0}
// />
// {message && (
// <span className="title-bg text-lime-800 bg-lime-100/50 dark:text-lime-100 rounded text-center p-2 w-full">
// {message}
// </span>
// )}
// </div>
// <DrawerFooter>
// <Button
// onClick={async () => {
// setDisabled(true);
// toast.promise(
// createPayment(data).then((result) => {
// if (result.success) {
// setDeviceCart([]);
// setMonths(1);
// setDisabled(false);
// if (isOpen) router.push(`/payments/${result.paymentId}`);
// setIsOpen(!isOpen);
// return "Payment created!";
// }
// }),
// {
// loading: "Processing payment...",
// success: "Payment created!",
// error: (err) => err.message || "Something went wrong.",
// }
// );
// }}
// className="w-full"
// disabled={devices.length === 0 || disabled}
// >
// {disabled ? (
// <>
// <Loader2 className="ml-2 animate-spin" />
// </>
// ) : (
// <>
// Go to payment
// <CircleDollarSign />
// </>
// )}
// </Button>
// <DrawerClose asChild>
// <Button variant="outline">Cancel</Button>
// </DrawerClose>
// <Button
// onClick={() => {
// setDeviceCart([]);
// setIsOpen(!isOpen);
// }}
// variant="outline"
// >
// Clear Selection
// </Button>
// </DrawerFooter>
// </div>
// </DrawerContent>
// </Drawer>
// </>
// );
}

View File

@ -1,5 +1,5 @@
"use client";
import { processWalletPayment } from "@/actions/payment";
import { verifyPayment } from "@/actions/payment";
import {
Table,
TableBody,
@ -84,9 +84,9 @@ export default function DevicesToPay({
disabled={verifying}
onClick={async () => {
setVerifying(true);
await processWalletPayment({
amount: payment?.amount ?? 0,
payment: payment,
await verifyPayment({
method: "WALLET",
id: payment?.id ?? "",
});
setVerifying(false);
}}