feat: secure KYC storage, Google OAuth, terms gating

This commit is contained in:
arifal
2026-04-28 23:10:21 +07:00
parent 58da4608ac
commit 05d0929f7a
41 changed files with 3087 additions and 262 deletions
+14 -6
View File
@@ -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);
},
};