refund roadmap pr-1 and pr-2

This commit is contained in:
2026-05-11 13:04:20 +07:00
parent d2b0a780d5
commit 54f4569107
36 changed files with 5750 additions and 19 deletions
+108
View File
@@ -32,6 +32,8 @@ model User {
organizerVerification OrganizerVerification? @relation("OrganizerVerificationOwner")
reviewedVerifications OrganizerVerification[] @relation("OrganizerVerificationReviewer")
reviewedRefunds Refund[] @relation("RefundReviewer")
profile UserProfile?
}
@@ -258,6 +260,7 @@ model Booking {
status BookingStatus @default(PENDING)
payments Payment[]
refunds Refund[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -275,6 +278,7 @@ enum BookingStatus {
PAID
CANCELLED
REFUNDED
PARTIALLY_REFUNDED
EXPIRED
}
@@ -307,6 +311,8 @@ model Payment {
failedAt DateTime?
rejectionReason String?
refunds Refund[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -328,3 +334,105 @@ enum PaymentStatus {
CANCELLED
REFUNDED
}
/// Refund = financial event terpisah dari Booking. Satu Booking bisa punya
/// banyak Refund (partial, multi-tahap). Setiap row auditable: kapan dibuat,
/// siapa melaporkan, siapa approve, kapan SUCCEEDED. Never delete — kalau
/// gagal, set status=FAILED + alasan.
///
/// Di MVP refund dimasukkan admin secara manual berdasarkan laporan dari
/// peserta atau organizer (via WhatsApp/email). Phase berikutnya akan
/// menambah self-service flow dari user dan organizer.
model Refund {
id String @id @default(cuid())
bookingId String
booking Booking @relation(fields: [bookingId], references: [id], onDelete: Restrict)
/// Payment yang di-refund. Opsional di MVP (manual transfer bisa tidak
/// terikat ke Payment row tertentu); wajib saat integrasi Midtrans (R-4).
paymentId String?
payment Payment? @relation(fields: [paymentId], references: [id], onDelete: Restrict)
/// Nominal refund dalam satuan terkecil (IDR rupiah, integer). Boleh < total
/// payment untuk partial. Service layer enforce SUM(SUCCEEDED) <= payment.amount.
amount Int
currency String @default("IDR")
reason RefundReason
/// Siapa yang melaporkan kebutuhan refund ini ke admin.
reportedBy RefundReporter
/// Isi laporan dari peserta/organizer yang admin terima (mis. WA, email).
reportNote String
/// Pihak yang membuat record di sistem. Di MVP selalu ADMIN; saat self-service
/// nanti USER/ORGANIZER, dan SYSTEM untuk auto-trigger dari trip dibatalkan.
initiatedBy RefundInitiator @default(ADMIN)
status RefundStatus @default(PENDING)
/// Idempotency key, dipakai saat panggil Midtrans Refund API di R-4. Generate
/// sekali saat create supaya retry gateway tidak double-refund.
idempotencyKey String @unique
/// Catatan admin: alasan tolak, referensi transfer manual, dst. Bebas teks.
adminNote String?
/// Admin yang terakhir mengubah status (approve/reject/mark-succeeded/failed).
reviewedById String?
reviewedBy User? @relation("RefundReviewer", fields: [reviewedById], references: [id], onDelete: SetNull)
reviewedAt DateTime?
succeededAt DateTime?
failedAt DateTime?
/// ID refund di gateway (mis. Midtrans refund_id). Kosong untuk manual transfer.
externalRefundId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([bookingId, status])
@@index([status, createdAt])
}
enum RefundReason {
/// Peserta cancel booking sendiri (mengikuti refund window policy).
USER_CANCELLATION
/// Organizer membatalkan trip — peserta dapat full refund.
ORGANIZER_CANCELLED
/// Masalah saat/setelah trip (mis. itinerary tidak sesuai).
TRIP_ISSUE
/// Penyesuaian dari admin (kompensasi, koreksi nominal, dll.).
ADMIN_ADJUSTMENT
/// Hasil resolusi dispute / chargeback bank.
DISPUTE_RESOLVED
OTHER
}
enum RefundStatus {
/// Baru dilaporkan, menunggu review admin.
PENDING
/// Admin sudah setujui, siap dieksekusi (manual transfer / gateway).
APPROVED
/// Admin tolak (alasan di `adminNote`).
REJECTED
/// (R-4) Request sudah dikirim ke gateway, menunggu callback.
PROCESSING
/// Uang sudah keluar dari kas Setrip / merchant gateway.
SUCCEEDED
/// Eksekusi gagal (alasan di `adminNote`). Record tidak dihapus.
FAILED
}
enum RefundInitiator {
USER
ORGANIZER
SYSTEM
ADMIN
}
enum RefundReporter {
PARTICIPANT
ORGANIZER
}