customer_id) { return $this->sendError('Unauthorized.', ['error' => 'Anda tidak memiliki akses untuk melakukan pembayaran ini'], 403); } // Periksa status booking if (!in_array($booking->status, ['reservasi', 'diproses', 'selesai'])) { return $this->sendError('Invalid status', ['error' => 'Pesanan tidak dapat dibayar dengan status saat ini'], 422); } // Periksa jika pembayaran sudah dilakukan if (in_array($booking->payment_status, ['paid', 'success'])) { return $this->sendError('Already paid', ['error' => 'Pesanan ini sudah dibayar'], 422); } // Pastikan harga telah diatur if (!$booking->total_price) { return $this->sendError('Invalid price', ['error' => 'Harga belum ditetapkan oleh penjahit'], 422); } // Set up parameter untuk Midtrans $params = [ 'transaction_details' => [ 'order_id' => $booking->transaction_code, // Gunakan transaction_code yang sudah ada 'gross_amount' => (int) ($booking->total_price + 4000), // Tambahkan biaya admin ], 'customer_details' => [ 'first_name' => $booking->customer->name, 'email' => $booking->customer->email, 'phone' => $booking->customer->phone_number ?? '', ], 'item_details' => [ [ 'id' => 'BOOKING-' . $booking->id, 'price' => (int) $booking->total_price, 'quantity' => 1, 'name' => 'Booking ' . $booking->service_type . ' - ' . $booking->category, ], [ 'id' => 'ADMIN-FEE', 'price' => 4000, 'quantity' => 1, 'name' => 'Biaya Admin (Payment Gateway + TailorHub)', ] ], // CATATAN PENTING: Jangan aktifkan baris di bawah ini jika ingin menampilkan semua metode pembayaran // Jika ingin membatasi metode pembayaran tertentu, uncomment baris di bawah ini // 'enabled_payments' => ['bank_transfer'], ]; // Buat token transaksi Snap $snapToken = Snap::getSnapToken($params); // Simpan informasi pembayaran $booking->update([ 'payment_method' => 'transfer_bank_midtrans', 'midtrans_snap_token' => $snapToken ]); return $this->sendResponse([ 'snap_token' => $snapToken, 'redirect_url' => 'https://app.sandbox.midtrans.com/snap/v2/vtweb/' . $snapToken, 'booking' => [ 'id' => $booking->id, 'transaction_code' => $booking->transaction_code, 'total_price' => $booking->total_price, 'status' => $booking->status, ] ], 'Token pembayaran berhasil dibuat'); } catch (\Exception $e) { \Log::error('Error initiating Midtrans payment: ' . $e->getMessage()); return $this->sendError('Error.', ['error' => 'Terjadi kesalahan saat memulai pembayaran: ' . $e->getMessage()], 500); } } /** * Callback notification handler dari Midtrans */ public function notificationHandler(Request $request) { try { $notificationBody = $request->all(); \Log::info('Midtrans Notification Received', $notificationBody); // Verifikasi signature menggunakan server key untuk keamanan $notification = new Notification(); $transactionStatus = $notification->transaction_status; $type = $notification->payment_type; $orderId = $notification->order_id; $fraud = $notification->fraud_status; // Cari booking berdasarkan transaction_code (order_id) $booking = Booking::where('transaction_code', $orderId)->first(); if (!$booking) { return response()->json(['status' => 'error', 'message' => 'Booking tidak ditemukan'], 404); } // Proses berdasarkan status transaksi if ($transactionStatus == 'capture') { if ($type == 'credit_card'){ if($fraud == 'challenge'){ $booking->payment_status = 'pending'; } else { $booking->payment_status = 'paid'; // Tambahkan dana ke wallet penjahit $tailorWallet = $booking->tailor->wallet; if (!$tailorWallet) { $tailorWallet = Wallet::create([ 'user_id' => $booking->tailor_id, 'balance' => 0 ]); } $tailorWallet->addBalance( $booking->total_price, 'Pembayaran booking #' . $booking->transaction_code, $booking ); } } } else if ($transactionStatus == 'settlement') { $booking->payment_status = 'paid'; // Tambahkan dana ke wallet penjahit $tailorWallet = $booking->tailor->wallet; if (!$tailorWallet) { $tailorWallet = Wallet::create([ 'user_id' => $booking->tailor_id, 'balance' => 0 ]); } $tailorWallet->addBalance( $booking->total_price, 'Pembayaran booking #' . $booking->transaction_code, $booking ); } else if ($transactionStatus == 'pending') { $booking->payment_status = 'pending'; } else if ($transactionStatus == 'deny') { $booking->payment_status = 'failed'; } else if ($transactionStatus == 'expire') { $booking->payment_status = 'expired'; } else if ($transactionStatus == 'cancel') { $booking->payment_status = 'cancelled'; } // Simpan perubahan $booking->save(); \Log::info('Payment status updated: ' . $booking->payment_status); return response()->json(['status' => 'success', 'message' => 'Notification processed']); } catch (\Exception $e) { \Log::error('Error processing Midtrans notification: ' . $e->getMessage()); return response()->json(['status' => 'error', 'message' => $e->getMessage()], 500); } } /** * Memeriksa status pembayaran */ public function checkPaymentStatus(Booking $booking) { try { // Periksa otentikasi dan otorisasi if (Auth::id() !== $booking->customer_id && Auth::id() !== $booking->tailor_id) { return $this->sendError('Unauthorized.', ['error' => 'Anda tidak memiliki akses untuk melihat status pembayaran ini'], 403); } return $this->sendResponse([ 'booking_id' => $booking->id, 'transaction_code' => $booking->transaction_code, 'payment_status' => $booking->payment_status, 'payment_method' => $booking->payment_method, 'total_price' => $booking->total_price, 'status' => $booking->status, 'midtrans_snap_token' => $booking->midtrans_snap_token ], 'Status pembayaran berhasil diambil'); } catch (\Exception $e) { \Log::error('Error checking payment status: ' . $e->getMessage()); return $this->sendError('Error.', ['error' => 'Terjadi kesalahan saat memeriksa status pembayaran'], 500); } } /** * Memeriksa dan memperbarui status pembayaran secara manual * Dapat digunakan sebagai alternatif notification handler Midtrans */ public function manualCheckStatus(Booking $booking) { try { // Periksa otentikasi dan otorisasi // User harus customer atau penjahit terkait atau admin $user = Auth::user(); if ($user->id !== $booking->customer_id && $user->id !== $booking->tailor_id && !$user->hasRole('admin')) { return $this->sendError('Unauthorized.', ['error' => 'Anda tidak memiliki akses untuk memeriksa status pembayaran ini'], 403); } // Periksa apakah booking memiliki transaction_code if (!$booking->transaction_code) { return $this->sendError('Invalid order', ['error' => 'Booking ini tidak memiliki kode transaksi'], 422); } // Periksa status transaksi di Midtrans $statusResponse = Transaction::status($booking->transaction_code); \Log::info('Midtrans Status Check Response', (array) $statusResponse); $transactionStatus = $statusResponse->transaction_status ?? null; $fraudStatus = $statusResponse->fraud_status ?? null; $paymentType = $statusResponse->payment_type ?? null; // Jika status sudah paid, tidak perlu update lagi if (in_array($booking->payment_status, ['paid', 'success']) && $transactionStatus == 'settlement') { return $this->sendResponse([ 'booking_id' => $booking->id, 'transaction_code' => $booking->transaction_code, 'payment_status' => $booking->payment_status, 'payment_method' => $booking->payment_method, 'midtrans_status' => $transactionStatus, ], 'Pembayaran sudah berhasil'); } // Update status pembayaran berdasarkan response $statusUpdated = false; if ($transactionStatus == 'capture') { if ($paymentType == 'credit_card') { if ($fraudStatus == 'challenge') { $booking->payment_status = 'pending'; } else { $booking->payment_status = 'paid'; $statusUpdated = true; } } } else if ($transactionStatus == 'settlement') { $booking->payment_status = 'paid'; $statusUpdated = true; } else if ($transactionStatus == 'pending') { $booking->payment_status = 'pending'; } else if ($transactionStatus == 'deny') { $booking->payment_status = 'failed'; } else if ($transactionStatus == 'expire') { $booking->payment_status = 'expired'; } else if ($transactionStatus == 'cancel') { $booking->payment_status = 'cancelled'; } // Jika status berubah menjadi paid, tambahkan dana ke wallet penjahit if ($statusUpdated && $booking->payment_status == 'paid') { $tailorWallet = $booking->tailor->wallet; if (!$tailorWallet) { $tailorWallet = Wallet::create([ 'user_id' => $booking->tailor_id, 'balance' => 0 ]); } $tailorWallet->addBalance( $booking->total_price, 'Pembayaran booking #' . $booking->transaction_code, $booking ); } // Simpan perubahan $booking->save(); \Log::info('Payment status manually updated: ' . $booking->payment_status); return $this->sendResponse([ 'booking_id' => $booking->id, 'transaction_code' => $booking->transaction_code, 'payment_status' => $booking->payment_status, 'payment_method' => $booking->payment_method, 'total_price' => $booking->total_price, 'status' => $booking->status, 'midtrans_status' => $transactionStatus, 'midtrans_snap_token' => $booking->midtrans_snap_token ], 'Status pembayaran berhasil diperbarui'); } catch (\Exception $e) { \Log::error('Error checking payment status manually: ' . $e->getMessage()); return $this->sendError('Error.', ['error' => 'Terjadi kesalahan saat memeriksa status pembayaran: ' . $e->getMessage()], 500); } } }