feat: secure KYC storage, Google OAuth, terms gating
This commit is contained in:
@@ -10,8 +10,8 @@ export const organizerRepo = {
|
||||
return prisma.organizerVerification.findUnique({ where: { id } });
|
||||
},
|
||||
|
||||
async findByNik(nik: string) {
|
||||
return prisma.organizerVerification.findUnique({ where: { nik } });
|
||||
async findByNikHash(nikHash: string) {
|
||||
return prisma.organizerVerification.findUnique({ where: { nikHash } });
|
||||
},
|
||||
|
||||
async upsertForUser(
|
||||
|
||||
@@ -27,4 +27,11 @@ export const userRepo = {
|
||||
async create(data: Prisma.UserCreateInput) {
|
||||
return prisma.user.create({ data });
|
||||
},
|
||||
|
||||
async markAcceptedTerms(id: string) {
|
||||
return prisma.user.update({
|
||||
where: { id },
|
||||
data: { acceptedTermsAndPrivacy: true, acceptedAt: new Date() },
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { organizerRepo } from "@/server/repositories/organizer.repo";
|
||||
import { decryptString, encryptString, hmacHex } from "@/lib/crypto";
|
||||
|
||||
type SubmitInput = {
|
||||
fullName: string;
|
||||
nik: string;
|
||||
birthDate: Date;
|
||||
address: string;
|
||||
ktpImageUrl: string;
|
||||
selfieUrl: string;
|
||||
ktpImageKey: string;
|
||||
selfieKey: string;
|
||||
bankName: string;
|
||||
bankAccountNumber: string;
|
||||
bankAccountName: string;
|
||||
@@ -22,18 +23,20 @@ export const organizerService = {
|
||||
throw new Error("Pengajuan kamu masih dalam proses review");
|
||||
}
|
||||
|
||||
const dupNik = await organizerRepo.findByNik(data.nik);
|
||||
const nikHash = hmacHex(data.nik);
|
||||
const dupNik = await organizerRepo.findByNikHash(nikHash);
|
||||
if (dupNik && dupNik.userId !== userId) {
|
||||
throw new Error("NIK ini sudah dipakai akun lain");
|
||||
}
|
||||
|
||||
return organizerRepo.upsertForUser(userId, {
|
||||
fullName: data.fullName,
|
||||
nik: data.nik,
|
||||
nikEncrypted: encryptString(data.nik),
|
||||
nikHash,
|
||||
birthDate: data.birthDate,
|
||||
address: data.address,
|
||||
ktpImageUrl: data.ktpImageUrl,
|
||||
selfieUrl: data.selfieUrl,
|
||||
ktpImageKey: data.ktpImageKey,
|
||||
selfieKey: data.selfieKey,
|
||||
bankName: data.bankName,
|
||||
bankAccountNumber: data.bankAccountNumber,
|
||||
bankAccountName: data.bankAccountName,
|
||||
@@ -74,4 +77,9 @@ export const organizerService = {
|
||||
const v = await organizerRepo.findByUserId(userId);
|
||||
return v?.status === "APPROVED";
|
||||
},
|
||||
|
||||
/** Reveal NIK plaintext. Caller must enforce authorization (owner or admin). */
|
||||
decryptNik(nikEncrypted: string): string {
|
||||
return decryptString(nikEncrypted);
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user