import type { Metadata } from "next"; import Link from "next/link"; import { notFound, redirect } from "next/navigation"; import { getServerSession } from "next-auth"; import { authOptions } from "@/lib/auth"; import { tripService } from "@/server/services/trip.service"; import { bookingService } from "@/server/services/booking.service"; import { paymentService } from "@/server/services/payment.service"; import { formatRupiah } from "@/lib/utils"; import { formatTripCalendarDateRangeLong } from "@/lib/trip-dates"; import { isFreeTrip } from "@/lib/trip-pricing"; import { categoryMeta } from "@/lib/activity-category"; import { MidtransPayButton } from "@/features/booking/components/midtrans-pay-button"; import { ArrowLeft, CalendarDays, MapPin, PartyPopper, CircleCheck, Clock, Check, } from "lucide-react"; export const metadata: Metadata = { title: "Detail Pembayaran", robots: { index: false, follow: false }, }; interface PageProps { params: Promise<{ id: string }>; searchParams: Promise>; } export default async function PaymentPage({ params, searchParams }: PageProps) { const { id } = await params; const session = await getServerSession(authOptions); if (!session?.user) { redirect(`/login?callbackUrl=/trips/${id}/payment`); } let trip; try { trip = await tripService.getTripById(id); } catch { notFound(); } if (trip.organizerId === session.user.id) { redirect(`/trips/${id}`); } // Saat user kembali dari Snap, Midtrans append `order_id` (+ status_code + // transaction_status) ke finishUrl. Tarik status terkini dari Core API // sebelum render supaya UI sinkron tanpa menunggu webhook — penting di dev // (localhost) dan saat webhook tertunda. const sp = await searchParams; const orderIdParam = sp.order_id; const orderId = Array.isArray(orderIdParam) ? orderIdParam[0] : orderIdParam; if (orderId) { try { await paymentService.reconcileFromGateway(orderId, session.user.id); } catch { // Jangan blokir render kalau gateway tidak responsif — webhook tetap // sumber kebenaran jangka panjang. Status di UI akan apa adanya dari DB. } } const booking = await bookingService.getByTripAndUser( trip.id, session.user.id ); if (!booking || booking.status === "CANCELLED") { return ; } const tripIsFree = isFreeTrip(trip); const catMeta = categoryMeta(trip.category); const dateRange = formatTripCalendarDateRangeLong(trip.date, trip.endDate); const tripHeader = (
{catMeta.icon}

{catMeta.label}

{trip.title}

{dateRange} {trip.location}

Organizer:{" "} {trip.organizer.name}

); return (
Kembali ke trip

Detail Pembayaran

{tripIsFree ? "Trip ini gratis — kamu tidak perlu transfer apa-apa." : "Bayar lewat Midtrans untuk mengamankan slot kamu. Pembayaran akan ter-konfirmasi otomatis."}

{tripHeader} {tripIsFree ? ( ) : ( )}
); } function NotJoinedNotice({ tripId, title }: { tripId: string; title: string }) { return (

Kamu belum terdaftar di trip ini

Halaman pembayaran hanya tersedia untuk peserta trip{" "} {title}.

Lihat detail trip
); } type BookingStatus = | "PENDING" | "AWAITING_PAY" | "PAID" | "CANCELLED" | "REFUNDED" | "PARTIALLY_REFUNDED" | "EXPIRED"; function FreeTripSection({ tripId, bookingStatus, }: { tripId: string; bookingStatus: BookingStatus; }) { return (

Trip ini gratis

Tidak ada biaya yang perlu kamu transfer.

Status keikutsertaan

{bookingStatus === "PAID" ? ( <> Terkonfirmasi sebagai peserta ) : ( <> Menunggu persetujuan organizer )}

Kembali ke detail trip
); } function PaidTripSection({ tripId, organizerName, price, bookingStatus, }: { tripId: string; organizerName: string; price: number; bookingStatus: BookingStatus; }) { const isApproved = bookingStatus === "AWAITING_PAY" || bookingStatus === "PAID"; const isPendingApproval = bookingStatus === "PENDING"; const isFullyPaid = bookingStatus === "PAID"; const canPay = bookingStatus === "AWAITING_PAY"; return (

Total Pembayaran

{formatRupiah(price)}

Pembayaran diproses oleh Midtrans (BCA VA, GoPay, QRIS, kartu, dll). Dana ditahan SeTrip sampai trip selesai — bukan transfer langsung ke organizer.

{isPendingApproval && (
Kamu belum disetujui organizer untuk ikut trip ini. Tunggu persetujuan dulu sebelum bayar — supaya tidak perlu refund kalau ditolak.
)} {canPay && } {isFullyPaid && (

Pembayaran kamu sudah terkonfirmasi. Sampai jumpa di trip bareng{" "} {organizerName}!

)}
Kembali ke detail trip
); } function PaymentTimeline({ approved, confirmedPaid, }: { approved: boolean; confirmedPaid: boolean; }) { const steps = [ { label: "Disetujui organizer", done: approved }, { label: "Pembayaran terkonfirmasi Midtrans", done: confirmedPaid }, ]; return (

Status pembayaran

    {steps.map((s, i) => (
  1. {s.done ? ( ) : ( i + 1 )} {s.label}
  2. ))}
); }