kyc user and upload partial update encrypt nik and picture

This commit is contained in:
2026-04-27 21:48:24 +07:00
parent b31fe675ae
commit a92b4a8fd9
51 changed files with 5180 additions and 452 deletions
+8 -1
View File
@@ -2,7 +2,12 @@ import bcrypt from "bcryptjs";
import { userRepo } from "@/server/repositories/user.repo";
export const authService = {
async register(data: { name: string; email: string; password: string }) {
async register(data: {
name: string;
email: string;
password: string;
acceptedTermsAndPrivacy: boolean;
}) {
const existing = await userRepo.findByEmail(data.email);
if (existing) {
throw new Error("Email sudah terdaftar");
@@ -14,6 +19,8 @@ export const authService = {
name: data.name,
email: data.email,
password: hashedPassword,
acceptedTermsAndPrivacy: data.acceptedTermsAndPrivacy,
acceptedAt: data.acceptedTermsAndPrivacy ? new Date() : null,
});
return { id: user.id, name: user.name, email: user.email };
+77
View File
@@ -0,0 +1,77 @@
import { organizerRepo } from "@/server/repositories/organizer.repo";
type SubmitInput = {
fullName: string;
nik: string;
birthDate: Date;
address: string;
ktpImageUrl: string;
selfieUrl: string;
bankName: string;
bankAccountNumber: string;
bankAccountName: string;
};
export const organizerService = {
async submitVerification(userId: string, data: SubmitInput) {
const existing = await organizerRepo.findByUserId(userId);
if (existing && existing.status === "APPROVED") {
throw new Error("Akun kamu sudah terverifikasi");
}
if (existing && existing.status === "PENDING") {
throw new Error("Pengajuan kamu masih dalam proses review");
}
const dupNik = await organizerRepo.findByNik(data.nik);
if (dupNik && dupNik.userId !== userId) {
throw new Error("NIK ini sudah dipakai akun lain");
}
return organizerRepo.upsertForUser(userId, {
fullName: data.fullName,
nik: data.nik,
birthDate: data.birthDate,
address: data.address,
ktpImageUrl: data.ktpImageUrl,
selfieUrl: data.selfieUrl,
bankName: data.bankName,
bankAccountNumber: data.bankAccountNumber,
bankAccountName: data.bankAccountName,
status: "PENDING",
rejectionReason: null,
reviewedAt: null,
reviewedById: null,
verifiedAt: null,
});
},
async reviewVerification(input: {
verificationId: string;
decision: "APPROVED" | "REJECTED";
rejectionReason?: string;
reviewerId: string;
}) {
const verification = await organizerRepo.findById(input.verificationId);
if (!verification) {
throw new Error("Pengajuan tidak ditemukan");
}
if (verification.status !== "PENDING") {
throw new Error("Pengajuan ini sudah diproses");
}
return organizerRepo.updateReview(input.verificationId, {
status: input.decision,
rejectionReason: input.decision === "REJECTED" ? input.rejectionReason ?? null : null,
reviewedById: input.reviewerId,
});
},
async getStatusForUser(userId: string) {
return organizerRepo.findByUserId(userId);
},
async isApproved(userId: string) {
const v = await organizerRepo.findByUserId(userId);
return v?.status === "APPROVED";
},
};
+6 -6
View File
@@ -11,11 +11,7 @@ export type OrganizerTrust = {
export const trustService = {
async getOrganizerTrust(organizerId: string): Promise<OrganizerTrust> {
const [user, tripsCreated, reviewAgg] = await Promise.all([
prisma.user.findUnique({
where: { id: organizerId },
select: { isVerified: true },
}),
const [tripsCreated, reviewAgg, organizerVerification] = await Promise.all([
prisma.trip.count({ where: { organizerId } }),
prisma.tripReview.aggregate({
where: {
@@ -24,11 +20,15 @@ export const trustService = {
_avg: { rating: true },
_count: { _all: true },
}),
prisma.organizerVerification.findUnique({
where: { userId: organizerId },
select: { status: true },
}),
]);
const avg = reviewAgg._avg.rating;
return {
isVerified: user?.isVerified ?? false,
isVerified: organizerVerification?.status === "APPROVED",
tripsCreated,
avgRating:
avg != null ? Math.round(Number(avg) * 10) / 10 : null,