File: /var/www/console.fixgini.com/app/Http/Controllers/BookingPaymentController.php
<?php
namespace App\Http\Controllers;
use Carbon\Carbon;
use App\Models\Gig;
use App\Models\User;
use App\Models\BookingForm;
use App\Models\Transaction;
use Illuminate\Http\Request;
use App\Models\BookingPayment;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Auth;
class BookingPaymentController extends Controller
{
// new method
public function store(Request $request)
{
// validate input
$request->validate([
'booking_id' => 'required|integer|exists:booking_forms,id',
'amount' => 'required|numeric|min:1',
]);
// generate unique transaction reference
$tx_ref = Str::uuid();
// create payment record in DB
$payment = BookingPayment::create([
'tx_ref' => $tx_ref,
'booking_id' => $request->booking_id,
'amount' => $request->amount,
'payment_status' => 'pending',
]);
return response()->json([
'tx_ref' => $tx_ref,
'amount' => $payment->amount,
'currency' => 'NGN',
'public_key' => config('flutterwave.public_key'), // send to frontend for checkout
]);
}
// new method
public function FluttewaveWebhook(Request $request)
{
$flutterwaveSignature = $request->header('verif-hash');
//$localSignature = env('FLUTTERWAVE_SECRET_HASH');
$localSignature = config('flutterwave.secret_hash');
// Check if the signature matches to prevent unauthorized requests
if ($flutterwaveSignature !== $localSignature) {
return response()->json(['status' => 'error', 'message' => 'Unauthorized request'], 403);
}
$payload = $request->all();
Log::info('Incoming flutterwave webhook :', $payload);
// Check if the payment status is successful
if (isset($payload['data']['status']) && $payload['data']['status'] === 'successful') {
$tx_ref = $payload['data']['tx_ref'];
$amount = $payload['data']['amount'];
// Find the booking payment record using the tx_ref
$bookingPayment = BookingPayment::where('tx_ref', $tx_ref)->first();
if ($bookingPayment) {
$bookingPayment->update([
'payment_status' => 'completed',
'amount' => $amount,
]);
// Retrieve the associated booking details using the booking_id
$booking = BookingForm::where('id', $bookingPayment->booking_id)->first();
if (!$booking) {
return response()->json(['status' => 'error', 'message' => 'Booking not found'], 404);
}
// Update the booking status to 'ongoing'
$booking->update([
'status' => 'ongoing', // Set the status to ongoing meaning payment is completed
]);
//update the transaction using tx_ref
$transx = Transaction::where('tx_ref', $tx_ref)->first();
$transx->update([
'status' => 'completed',
'amount' => $amount,
'description' => "Booking payment for {$booking->gig->title} is completed successfully",
]);
// Get the service provider and customer details
$serviceProvider = User::where('id', $booking->service_provider_id)->first(); // Assuming service_provider_id exists
$customer = User::where('id', $booking->customer_id)->first();
// Ensure the wallet relationships are loaded for both the service provider and customer
$serviceProviderWallet = $serviceProvider->wallet;
$customerWallet = $customer->wallet;
if (!$serviceProvider || !$customer) {
info('Service provider or customer not found');
return response()->json(['status' => 'error', 'message' => 'Service provider or customer not found'], 404);
}
if (!$serviceProviderWallet || !$customerWallet) {
info('Wallet not found for provider or customer');
return response()->json(['status' => 'error', 'message' => 'Wallet not found for provider or customer'], 404);
}
// Update the customer's withdraw wallet amount
$customerWallet->withdraw += $amount; // Deduct from the pending balance (add to withdraw of the customer)
$customerWallet->save(); // Save the changes
// Update the service provider's pending wallet amount
// $serviceProviderWallet->pending += $amount; // Add the payment amount to the wallet
// $serviceProviderWallet->save(); // Don't forget to save the changes
$this->updateFirestore($booking, $tx_ref);
info('Booking form and payment status updated. task is now ongoing');
return response()->json(['status' => 'success', 'message' => 'Payment updated successfully'], 200);
} else {
return response()->json(['status' => 'error', 'message' => 'Booking payment not found'], 404);
}
}
return response()->json(['status' => 'error', 'message' => 'Payment failed or incomplete'], 400);
}
private function updateFirestore($booking, $tx_ref)
{
try {
// Retrieve the chatRoomId and chatId from the session
$chatRoomId = $booking->chat_room_id;
$chatId = $booking->chat_id;
$currentTime = Carbon::now()->format('Y-m-d\TH:i:s.u\Z');
// Prepare the update data with chatId added to the fields (Optional, might not need the same data again)
$updateChatData = [
'fields' => [
'chatId' => [
'stringValue' => $chatId, // Adding the chatId to the payload
],
'lastMessage' => [
'stringValue' => 'Payment successful. Engagement ongoing!',
],
'lastMessageAt' => [
'stringValue' => $currentTime,
],
'lastSenderId' => [
'stringValue' => (string) $booking->customer_id,
],
'chatRoomStatus' => [
'stringValue' => 'active',
],
'participants' => [
'arrayValue' => [
'values' => [
[
'mapValue' => [
'fields' => [
'firstName' => [
'stringValue' => $booking->customer->name ?? '',
],
'userPhoto' => [
'stringValue' => $booking->customer->profile_photo_url,
],
'userUID' => [
'stringValue' => (string) $booking->customer_id,
],
],
],
],
[
'mapValue' => [
'fields' => [
'firstName' => [
'stringValue' => $booking->gig->user->name ?? '',
],
'userPhoto' => [
'stringValue' => $booking->gig->user->profile_photo_url,
],
'userUID' => [
'stringValue' => (string) $booking->service_provider_id,
],
],
],
],
],
],
],
'participantsUIDs' => [
'arrayValue' => [
'values' => [
[
'stringValue' => (string) $booking->customer_id,
],
[
'stringValue' => (string) $booking->service_provider_id,
],
],
],
],
],
];
// Send the data to Firestore using PATCH to update the existing document with chatId
$client = new \GuzzleHttp\Client();
$updateChatId = 'https://firestore.googleapis.com/v1/projects/fixgini/databases/(default)/documents/chats/' . $chatId;
$client->patch($updateChatId, [
'json' => $updateChatData,
]);
$updateChatRoomData = [
'fields' => [
'chatId' => [
'stringValue' => $chatId, // The actual chat ID
],
'message' => [
'stringValue' => 'Payment successful. Engagement ongoing!',
],
'messageId' => [
'stringValue' => $chatRoomId,
],
'sentBy' => [
'stringValue' => (string) $booking->customer_id, // The ID of the user who sent the message
],
'time' => [
'stringValue' => $currentTime,
],
'participants' => [
'arrayValue' => [
'values' => [
[
'stringValue' => (string) $booking->customer_id, // User's ID
],
[
'stringValue' => (string) $booking->service_provider_id, // Provider's ID
],
],
],
],
'chatHire' => [
'mapValue' => [
'fields' => [
'acceptTime' => [
'stringValue' => $currentTime
],
'paid' => [
'booleanValue' => true,
],
'paidTime' => [
'stringValue' => $currentTime,
],
'trxRef' => [
'stringValue' => $tx_ref,
],
'accepted' => [
'booleanValue' => true, // Use booleanValue for boolean
],
'completed' => [
'booleanValue' => false, // Use booleanValue for boolean
],
'completedTime' => [
'nullValue' => null, // Use nullValue instead of an empty string
],
'confirmCompleted' => [
'booleanValue' => false, // Use booleanValue for boolean
],
'reasonForConfirmDecline' => [
'nullValue' => null, // Use nullValue for empty fields
],
'reasonForDecline' => [
'nullValue' => null, // Use nullValue for empty fields
],
'rejected' => [
'booleanValue' => false, // Use booleanValue for boolean
],
'bookingId' => [
'stringValue' => (string) $booking->id, // Keep as stringValue if it's not numeric
],
'price' => [
'doubleValue' => (float) $booking->agreed_amount, // Use doubleValue for decimal values
],
'gigId' => [
'stringValue' => (string) $booking->gig_id, // Keep as stringValue
],
'gigName' => [
'stringValue' => (string) $booking->gig->title, // Gig title
],
'gigCurrency' => [
'stringValue' => (string) $booking->gig->currency, // Gig currency
],
'time' => [
'stringValue' => $currentTime,
],
'paymentLink' => [
'nullValue' => null, // Use nullValue instead of an empty string
],
'start' => [
'stringValue' => Carbon::parse($booking->start_time)->format('Y-m-d\TH:i:s.u\Z'), // Use stringValue
],
'end' => [
'stringValue' => Carbon::parse($booking->end_time)->format('Y-m-d\TH:i:s.u\Z'), // Use stringValue
],
'sentBy' => [
'stringValue' => (string) $booking->customer_id, // Keep as stringValue
],
],
],
],
],
];
// Firestore URL to create a new message in the chat room
$updatedChatRoomID = "https://firestore.googleapis.com/v1/projects/fixgini/databases/(default)/documents/chats/{$chatId}/chat_room/{$chatRoomId}";
// Send the message data to Firestore using PATCH to update the message
$client = new \GuzzleHttp\Client();
$client->patch($updatedChatRoomID, [
'json' => $updateChatRoomData,
]);
// update the booking form table
info('Chat room message updated with payment info successfully with MessageId in chat_room.');
// $this->sendFcmNotification($receiver, $validatedData['message']);
return response()->json([
'status' => 'success',
'message' => 'Chat room message id recorded successfully.',
], 200);
} catch (\Throwable $th) {
Log::error("Failed to record chat room message in Firestore: {$th->getMessage()}");
return response()->json(['status' => 'error', 'message' => 'Failed to record chat room in Firebase.'], 500);
}
}
public function sellerPaymentHistory()
{
try {
$user = Auth::user();
$gigIds = Gig::where('user_id', $user->id)->pluck('id');
$bookingIds = BookingForm::whereIn('gig_id', $gigIds)->pluck('id');
$payments = BookingPayment::with(['booking.gig.user'])->whereIn('booking_id', $bookingIds)->latest()->limit(10)->get();
$transactions = Transaction::where('user_id', $user->id)->latest()->limit(10)->get();
if ($payments->isEmpty()) {
return response()->json(['status' => 'error', 'message' => 'Payments not found'], 404);
} else {
return response()->json(['status' => 'success', 'message' => 'Payments are found', 'data' => $payments, 'transactions' => $transactions], 200);
}
} catch (\Throwable $th) {
return response()->json(['status' => 'error', 'message' => 'An error occurred', 'data' => $th->getMessage()], 500);
}
}
public function buyerPaymentHistory()
{
try {
$user = Auth::user();
$payments = BookingPayment::with(['booking.gig.user'])->where('customer_id', $user->id)->latest()->limit(10)->get();
if ($payments->isEmpty()) {
return response()->json(['status' => 'error', 'message' => 'Payments not found'], 404);
} else {
return response()->json(['status' => 'success', 'message' => 'Payments are found', 'data' => $payments], 200);
}
} catch (\Throwable $th) {
return response()->json(['status' => 'error', 'message' => 'An error occurred', 'data' => $th->getMessage()], 400);
}
}
}