add booking and payment schema
This commit is contained in:
+136
-42
@@ -8,38 +8,39 @@ datasource db {
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
email String @unique
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
email String @unique
|
||||
/// Hash bcrypt. Null untuk user yang sign-in via OAuth (mis. Google).
|
||||
password String?
|
||||
image String?
|
||||
password String?
|
||||
image String?
|
||||
/// Diisi PrismaAdapter NextAuth saat email diverifikasi provider OAuth (Google selalu sudah verified).
|
||||
emailVerified DateTime?
|
||||
emailVerified DateTime?
|
||||
/// Apakah user telah menyetujui Syarat & Ketentuan dan Kebijakan Privasi
|
||||
acceptedTermsAndPrivacy Boolean @default(false)
|
||||
acceptedTermsAndPrivacy Boolean @default(false)
|
||||
/// Waktu user menyetujui Syarat & Ketentuan dan Kebijakan Privasi
|
||||
acceptedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
acceptedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
accounts Account[]
|
||||
trips Trip[]
|
||||
participations TripParticipant[]
|
||||
tripReviews TripReview[]
|
||||
bookings Booking[]
|
||||
|
||||
organizerVerification OrganizerVerification? @relation("OrganizerVerificationOwner")
|
||||
reviewedVerifications OrganizerVerification[] @relation("OrganizerVerificationReviewer")
|
||||
organizerVerification OrganizerVerification? @relation("OrganizerVerificationOwner")
|
||||
reviewedVerifications OrganizerVerification[] @relation("OrganizerVerificationReviewer")
|
||||
|
||||
profile UserProfile?
|
||||
profile UserProfile?
|
||||
}
|
||||
|
||||
/// Profil sosial publik. Berisi info yang user pilih untuk dibagikan ke peserta lain
|
||||
/// (bio, kota, minat, vibe). Tidak menyimpan data sensitif — KYC tetap di OrganizerVerification.
|
||||
model UserProfile {
|
||||
id String @id @default(cuid())
|
||||
userId String @unique
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
id String @id @default(cuid())
|
||||
userId String @unique
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
/// Bio singkat, teks bebas
|
||||
bio String?
|
||||
@@ -89,13 +90,13 @@ model OrganizerVerification {
|
||||
user User @relation("OrganizerVerificationOwner", fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
/// Nama lengkap sesuai KTP
|
||||
fullName String
|
||||
fullName String
|
||||
/// NIK terenkripsi (AES-256-GCM, base64). Plaintext tidak disimpan.
|
||||
nikEncrypted String
|
||||
/// HMAC-SHA256(NIK + pepper) untuk uniqueness lookup tanpa membuka plaintext.
|
||||
nikHash String @unique
|
||||
birthDate DateTime
|
||||
address String
|
||||
nikHash String @unique
|
||||
birthDate DateTime
|
||||
address String
|
||||
|
||||
/// Storage key foto KTP (mis. `ktp/<id>.jpg`). File disimpan terenkripsi di luar /public.
|
||||
ktpImageKey String
|
||||
@@ -111,7 +112,7 @@ model OrganizerVerification {
|
||||
rejectionReason String?
|
||||
reviewedAt DateTime?
|
||||
reviewedById String?
|
||||
reviewedBy User? @relation("OrganizerVerificationReviewer", fields: [reviewedById], references: [id])
|
||||
reviewedBy User? @relation("OrganizerVerificationReviewer", fields: [reviewedById], references: [id])
|
||||
verifiedAt DateTime?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
@@ -125,7 +126,7 @@ enum VerificationStatus {
|
||||
}
|
||||
|
||||
model Trip {
|
||||
id String @id @default(cuid())
|
||||
id String @id @default(cuid())
|
||||
title String
|
||||
description String?
|
||||
/// Kategori aktivitas.
|
||||
@@ -147,16 +148,17 @@ model Trip {
|
||||
price Int
|
||||
/// Ritme/energi trip — dipakai untuk matching dengan vibe user.
|
||||
vibe Vibe?
|
||||
status TripStatus @default(OPEN)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
status TripStatus @default(OPEN)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
organizerId String
|
||||
organizer User @relation(fields: [organizerId], references: [id])
|
||||
organizer User @relation(fields: [organizerId], references: [id])
|
||||
|
||||
participants TripParticipant[]
|
||||
images TripImage[]
|
||||
reviews TripReview[]
|
||||
bookings Booking[]
|
||||
|
||||
@@index([category, status, date])
|
||||
@@index([vibe, status, date])
|
||||
@@ -170,38 +172,42 @@ model TripReview {
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
tripId String
|
||||
trip Trip @relation(fields: [tripId], references: [id], onDelete: Cascade)
|
||||
trip Trip @relation(fields: [tripId], references: [id], onDelete: Cascade)
|
||||
|
||||
userId String
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([tripId, userId])
|
||||
}
|
||||
|
||||
model TripImage {
|
||||
id String @id @default(cuid())
|
||||
url String
|
||||
caption String?
|
||||
order Int @default(0)
|
||||
id String @id @default(cuid())
|
||||
url String
|
||||
caption String?
|
||||
order Int @default(0)
|
||||
|
||||
tripId String
|
||||
trip Trip @relation(fields: [tripId], references: [id], onDelete: Cascade)
|
||||
trip Trip @relation(fields: [tripId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
model TripParticipant {
|
||||
id String @id @default(cuid())
|
||||
status ParticipantStatus @default(PENDING)
|
||||
createdAt DateTime @default(now())
|
||||
/// Peserta menekan "Saya sudah bayar" (pembayaran manual)
|
||||
markedPaidAt DateTime?
|
||||
/// Organizer mengonfirmasi uang sudah masuk
|
||||
paymentConfirmedAt DateTime?
|
||||
id String @id @default(cuid())
|
||||
status ParticipantStatus @default(PENDING)
|
||||
createdAt DateTime @default(now())
|
||||
/// @deprecated — sumber kebenaran pindah ke Booking/Payment. Tetap di-update
|
||||
/// untuk backward-compat selama transisi UI lama. Akan dihapus PR berikutnya.
|
||||
markedPaidAt DateTime?
|
||||
/// @deprecated — sumber kebenaran pindah ke Booking/Payment. Tetap di-update
|
||||
/// untuk backward-compat selama transisi UI lama. Akan dihapus PR berikutnya.
|
||||
paymentConfirmedAt DateTime?
|
||||
|
||||
tripId String
|
||||
trip Trip @relation(fields: [tripId], references: [id])
|
||||
trip Trip @relation(fields: [tripId], references: [id])
|
||||
|
||||
userId String
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
|
||||
booking Booking?
|
||||
|
||||
@@unique([tripId, userId])
|
||||
}
|
||||
@@ -231,3 +237,91 @@ enum ParticipantStatus {
|
||||
CONFIRMED
|
||||
CANCELLED
|
||||
}
|
||||
|
||||
/// Booking 1-1 ke TripParticipant. Lifecycle ikut peserta:
|
||||
/// - join → Booking PENDING (menunggu approve organizer)
|
||||
/// - organizer confirm → AWAITING_PAY (paid trip) atau PAID (free trip)
|
||||
/// - peserta + organizer rampungkan pembayaran → PAID
|
||||
/// - cancel/reject → CANCELLED
|
||||
/// `amount` adalah snapshot harga saat booking dibuat — protect dari perubahan trip.price.
|
||||
model Booking {
|
||||
id String @id @default(cuid())
|
||||
tripId String
|
||||
trip Trip @relation(fields: [tripId], references: [id])
|
||||
userId String
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
participantId String @unique
|
||||
participant TripParticipant @relation(fields: [participantId], references: [id], onDelete: Cascade)
|
||||
|
||||
amount Int
|
||||
currency String @default("IDR")
|
||||
status BookingStatus @default(PENDING)
|
||||
|
||||
payments Payment[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([tripId, status])
|
||||
@@index([userId])
|
||||
}
|
||||
|
||||
enum BookingStatus {
|
||||
PENDING
|
||||
AWAITING_PAY
|
||||
PAID
|
||||
CANCELLED
|
||||
REFUNDED
|
||||
EXPIRED
|
||||
}
|
||||
|
||||
/// Satu attempt pembayaran. Satu Booking bisa punya banyak Payment kalau retry
|
||||
/// (di Phase MIDTRANS nanti). Untuk MANUAL biasanya cukup 1 Payment.
|
||||
model Payment {
|
||||
id String @id @default(cuid())
|
||||
bookingId String
|
||||
booking Booking @relation(fields: [bookingId], references: [id], onDelete: Cascade)
|
||||
|
||||
provider PaymentProvider
|
||||
/// order_id eksternal (unik per attempt). Format MANUAL: `manual-<bookingId>`.
|
||||
/// Format MIDTRANS nanti: `midtrans-<bookingId>-<retry>`.
|
||||
externalOrderId String @unique
|
||||
/// transaction_id dari gateway. Kosong untuk MANUAL atau sebelum first callback.
|
||||
externalTxId String?
|
||||
/// Metode konkret: bca_va, gopay, qris, manual_transfer, dst.
|
||||
method String?
|
||||
amount Int
|
||||
status PaymentStatus @default(PENDING)
|
||||
|
||||
/// Snapshot mentah callback gateway (untuk audit & dispute).
|
||||
rawCallback Json?
|
||||
/// Snap token Midtrans / redirect URL.
|
||||
snapToken String?
|
||||
/// Kapan attempt ini kadaluarsa (Midtrans default 24 jam).
|
||||
expiresAt DateTime?
|
||||
|
||||
paidAt DateTime?
|
||||
failedAt DateTime?
|
||||
rejectionReason String?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([bookingId, status])
|
||||
@@index([provider, status])
|
||||
}
|
||||
|
||||
enum PaymentProvider {
|
||||
MANUAL
|
||||
MIDTRANS
|
||||
}
|
||||
|
||||
enum PaymentStatus {
|
||||
PENDING
|
||||
AWAITING
|
||||
PAID
|
||||
FAILED
|
||||
EXPIRED
|
||||
CANCELLED
|
||||
REFUNDED
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user