MIF_E31221305/TA_API/app/Http/Controllers/Api/MidtransController.php

331 lines
14 KiB
PHP

<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Booking;
use Illuminate\Support\Facades\Auth;
use Midtrans\Config;
use Midtrans\Snap;
use Midtrans\Notification;
use Midtrans\Transaction;
use App\Models\Wallet;
class MidtransController extends BaseController
{
public function __construct()
{
// Konfigurasi Midtrans
Config::$serverKey = env('MIDTRANS_SERVER_KEY', 'SB-Mid-server-m_cMr-8mOqJoaorKXpXoWBoQ');
Config::$isProduction = env('MIDTRANS_IS_PRODUCTION', false);
Config::$isSanitized = true;
Config::$is3ds = true;
}
/**
* Inisiasi pembayaran dengan Midtrans
*/
public function initiatePayment(Request $request, Booking $booking)
{
try {
// Periksa otentikasi dan otorisasi
if (Auth::id() !== $booking->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);
}
}
}