File: /var/www/console.fixgini.com/app/Http/Controllers/BookFormController.php
<?php
namespace App\Http\Controllers;
use Carbon\Carbon;
use App\Models\Gig;
use App\Models\Wallet;
use GuzzleHttp\Client;
use App\Mail\BookingAction;
use App\Models\BookingForm;
use App\Models\Transaction;
use Illuminate\Support\Str;
use App\Jobs\SendDelayedSms;
use Illuminate\Http\Request;
use App\Mail\PaymentLinkMail;
use App\Models\BookingPayment;
use App\Jobs\CompleteBookingJob;
use App\Services\ActivityLogger;
use Google\Client as GoogleClient;
use Illuminate\Support\Facades\Log;
use App\Mail\TriggerCompleteBooking;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
use App\Notifications\NewBookingNotification;
class BookFormController extends Controller
{
public function store(Request $request)
{
try {
$user = Auth::user();
$validatedData = $request->validate([
'gig_id' => 'required|exists:gigs,id',
'agreed_amount' => 'required|numeric|min:0',
'gig_price' => 'required|numeric|min:0',
'start_time' => 'required|date|after_or_equal:today',
'end_time' => 'required|date|after_or_equal:start_time',
'message' => 'nullable|string|max:255',
'app_type' => 'nullable',
]);
if (($validatedData['app_type'] ?? null) === 'web') {
$formattedStartTime = $validatedData['start_time'];
$formattedEndTime = $validatedData['end_time'];
} else {
$formattedStartTime = Carbon::createFromFormat('m/d/Y H:i:s', $validatedData['start_time'])->format('Y-m-d H:i:s');
$formattedEndTime = Carbon::createFromFormat('m/d/Y H:i:s', $validatedData['end_time'])->format('Y-m-d H:i:s');
}
$gig = Gig::where('id', $validatedData['gig_id'])->first();
$gig_provider = $gig->user_id;
// Create the booking
$booking = BookingForm::create([
'service_provider_id' => $gig_provider,
'customer_id' => $user->id,
'gig_id' => $validatedData['gig_id'],
'gig_price' => $validatedData['gig_price'],
'agreed_amount' => $validatedData['agreed_amount'],
'start_time' => $formattedStartTime,
'end_time' => $formattedEndTime,
]);
$providerEmail = $gig->user->email;
$providerName = $gig->user->name;
$providerPhone = $gig->user->phone;
$gigName = $gig->title;
$gigSlug = $gig->slug;
$message = $validatedData['message'] ?? 'Hello, I am interested in booking your service. Please let me know if this proposal works for you.';
// Send notification to the provider
Notification::route('mail', $providerEmail)
->notify(new NewBookingNotification($providerEmail, $providerName, $gigName, $gigSlug, $message));
// create the record on firebase
$this->recordOnFirestore($validatedData, $user, $booking, $gig);
// Schedule SMS after 12 hours
SendDelayedSms::dispatch($providerPhone, $message)->delay(now()->addHours(12));
//send Firestore notification to user
// Determine the receiver
if ($user->id === $gig->provider->id) {
// Authenticated user is the provider, send notification to the customer
$receiver = $gig->user ?? '';
} else {
// Authenticated user is not the provider, send notification to the provider
$receiver = $gig->provider ?? '';
}
info('receiver info ' . $receiver);
// Ensure the receiver is set and send the notification
if ($receiver) {
$this->sendFcmNotification($receiver, $message);
}
return response()->json(['status' => 'success', 'message' => 'Booking engagement sent ', 'data' => $booking], 200);
} catch (\Throwable $th) {
Log::error($th->getMessage());
return response()->json(['status' => 'error', 'message' => 'Booking engagement not sent', 'data' => $th->getMessage()], 401);
}
}
public function bookingAction(Request $request)
{
try {
$user = Auth::user();
$validatedData = $request->validate([
'gig_id' => 'required|exists:gigs,id',
'message' => 'nullable|string|max:255',
'action' => 'nullable|string|in:Accepted,declined',
'booking_id' => 'required|exists:booking_forms,id',
]);
// info($validatedData);
$gig = Gig::where('id', $validatedData['gig_id'])->first();
$booking = $gig->bookings->where('id', $validatedData['booking_id'])->first();
$bookingAmount = $booking ? $booking->agreed_amount : null;
$gigName = $gig->title;
$gigSlug = $gig->slug;
$trxRef = strtoupper('FIXGINI-' . Str::random(16));
$message = $validatedData['message'] ?? 'You got a new notification.';
// Send Firestore notification to user
$receiver = $gig->provider ?? '';
$this->sendFcmNotification($receiver, $message);
if ($validatedData['action'] === 'Accepted') {
$message = 'Your booking request has been accepted. Please proceed to payment to confirm the booking.';
// Call Flutterwave to generate a payment link
$paymentLink = $this->generatePaymentLink($trxRef, $bookingAmount, $validatedData['booking_id'], $booking->customer, $gig);
$this->initiateBookingPayment($trxRef, $booking);
// send to firebase for notification
$this->updateFirestore($paymentLink, $trxRef, $validatedData, $user, $booking, $gig);
// Send the payment link via email to the user
Mail::to($booking->customer->email)->send(new PaymentLinkMail($paymentLink, $booking->customer->name));
} elseif ($validatedData['action'] === 'declined') {
info('incoming decline');
$message = $validatedData['message'] ?? 'Hello, This enagement is rejected.';
$this->cancelBooking($trxRef, $message, $validatedData['booking_id']);
$this->updateFirestoreRejected($validatedData, $user, $booking, $gig, $validatedData['booking_id']);
Mail::to($booking->customer->email)->send(new BookingAction($booking, $gigName, $gigSlug, $message));
// Log the user activity
$device = '';
$activityLogger = app(ActivityLogger::class);
$activityLogger->log('Provider rejected engagement from customer', $user->id, $user->role, $device);
}
return response()->json(['status' => 'success', 'message' => 'Booking engagement sent', 'data' => $trxRef], 200);
} catch (\Throwable $th) {
info($th->getMessage());
return response()->json(['status' => 'error', 'message' => 'Response not successfully', 'data' => $th->getMessage()], 401);
}
}
private function initiateBookingPayment($trxRef, $booking)
{
BookingPayment::create([
'tx_ref' => $trxRef,
'booking_id' => $booking->id,
'customer_id' => $booking->customer_id,
'amount' => $booking->agreed_amount,
'payment_status' => 'pending',
'cancel_reason' => 'no',
]);
Transaction::create([
'tx_ref' => $trxRef,
'booking_id' => $booking->id,
'user_id' => $booking->customer_id,
'type' => 'booking',
'amount' => $booking->agreed_amount,
'description' => "Booking payment for {$booking->gig->title} is pending",
'status' => 'pending',
]);
}
private function cancelBooking($trxRef, $message, $bookingId)
{
// Find the existing booking payment by tx_ref
$bookingPayment = BookingPayment::where('tx_ref', $trxRef)->first();
if ($bookingPayment) {
// Update the existing booking payment
$bookingPayment->update([
'payment_status' => 'cancelled',
'cancel_reason' => $message ?? '',
]);
}
// Find the existing booking form by id
$booking = BookingForm::where('id', $bookingId)->first();
if ($booking) {
// Update the existing booking payment
$booking->update([
'status' => 'cancelled',
]);
}
}
private function generatePaymentLink($trxRef, $amount, $bookId, $user, $gig)
{
$currency = ($gig->currency == '₦') ? 'NGN' : $gig->currency;
// Create the HTTP client
$client = new Client();
//FLWSECK-4414d62ee9f096de8a542b988d954b7c-198be0428d9vt-X
//FLWSECK_TEST-6cf1359026621655dea637381728ef05-X
try {
// Make the POST request
$response = $client->post('https://api.flutterwave.com/v3/payments', [
'headers' => [
'Authorization' => 'Bearer FLWSECK-4414d62ee9f096de8a542b988d954b7c-198be0428d9vt-X',
'Content-Type' => 'application/json',
],
'json' => [
'amount' => $amount,
'currency' => $currency,
'tx_ref' => strtoupper($trxRef),
'order_id' => $bookId,
'redirect_url' => 'https://fixgini.com/account/payment/history',
'customer' => [
'email' => $user->email,
'name' => $user->name,
'phone_number' => $user->phone,
],
'meta' => [
'fullname' => "{$user->name} {$user->lastname}",
'customer_id' => $user->id,
'booking_id' => $bookId,
'service_paid_for' => "https://fixgini.com/service/{$gig->slug}",
'service_price' => "{$currency}{$gig->price}",
]
]
]);
// Get the response body as a string
$responseBody = $response->getBody()->getContents();
// Decode the JSON response into an array
$responseData = json_decode($responseBody, true);
// Check if the response is successful
if ($responseData['status'] === 'success') {
return $responseData['data']['link']; // Return the payment link
}
// If not successful, throw an exception
throw new \Exception('Error generating payment link: ' . $responseData['message']);
} catch (\Exception $e) {
// Log the error message
info('Error in generating payment link', ['error' => $e->getMessage()]);
throw $e; // Rethrow the exception
}
}
private function recordOnFirestore($validatedData, $user, $booking, $gig)
{
try {
// Firebase Firestore URL (without chatId, Firestore will auto-generate it)
$firestoreUrl = 'https://firestore.googleapis.com/v1/projects/fixgini/databases/(default)/documents/chats';
$currentTime = Carbon::now()->format('Y-m-d\TH:i:s.u\Z');
// Prepare the initial chat data for creation
$chatData = [
'fields' => [
'createdAt' => [
'stringValue' => $currentTime,
],
'lastMessage' => [
'stringValue' => $validatedData['message'] ?? 'Hey there, are you available for this engagement?',
],
'lastMessageAt' => [
'stringValue' => $currentTime,
],
'lastSenderId' => [
'stringValue' => (string) $user->id,
],
'chatRoomStatus' => [
'stringValue' => 'active',
],
'participants' => [
'arrayValue' => [
'values' => [
[
'mapValue' => [
'fields' => [
'firstName' => [
'stringValue' => $user->name,
],
'userPhoto' => [
'stringValue' => $user->profile_photo_url,
],
'userUID' => [
'stringValue' => (string) $user->id, // Ensure it's a string
],
],
],
],
[
'mapValue' => [
'fields' => [
'firstName' => [
'stringValue' => $gig->user->name,
],
'userPhoto' => [
'stringValue' => $gig->user->profile_photo_url,
],
'userUID' => [
'stringValue' => (string) $booking->service_provider_id,
],
],
],
],
],
],
],
'participantsUIDs' => [
'arrayValue' => [
'values' => [
[
'stringValue' => (string) $user->id, // Ensure it's a string
],
[
'stringValue' => (string) $gig->user->id,
],
],
],
],
],
];
// Send the data to Firestore using POST to create a new document with an auto-generated ID
$client = new \GuzzleHttp\Client();
$response = $client->post($firestoreUrl, [
'json' => $chatData,
]);
// Check if the response was successful
if ($response->getStatusCode() === 200) {
// Decode the response to get the document ID (chatId)
$responseBody = json_decode($response->getBody(), true);
$chatId = $responseBody['name']; // This is the document ID
$chatId = substr($chatId, strrpos($chatId, '/') + 1);
info("Chat created with ID: {$chatId}");
// Prepare the update data with chatId added to the fields (Optional, might not need the same data again)
$updateData = [
'fields' => [
'chatId' => [
'stringValue' => $chatId, // Adding the chatId to the payload
],
'lastMessage' => [
'stringValue' => $validatedData['message'] ?? 'Hey there, are you available for this engagement?',
],
'lastMessageAt' => [
'stringValue' => $currentTime,
],
'lastSenderId' => [
'stringValue' => (string) $user->id,
],
'chatRoomStatus' => [
'stringValue' => 'active',
],
'participants' => [
'arrayValue' => [
'values' => [
[
'mapValue' => [
'fields' => [
'firstName' => [
'stringValue' => $user->name ?? '',
],
'userPhoto' => [
'stringValue' => $user->profile_photo_url,
],
'userUID' => [
'stringValue' => (string) $user->id,
],
],
],
],
[
'mapValue' => [
'fields' => [
'firstName' => [
'stringValue' => $gig->user->name ?? '',
],
'userPhoto' => [
'stringValue' => $gig->user->profile_photo_url,
],
'userUID' => [
'stringValue' => (string) $gig->user->id,
],
],
],
],
],
],
],
'participantsUIDs' => [
'arrayValue' => [
'values' => [
[
'stringValue' => (string) $user->id,
],
[
'stringValue' => (string) $gig->user->id,
],
],
],
],
],
];
// Send the data to Firestore using PATCH to update the existing document with chatId
$firestoreUpdateUrl = 'https://firestore.googleapis.com/v1/projects/fixgini/databases/(default)/documents/chats/' . $chatId;
$updateResponse = $client->patch($firestoreUpdateUrl, [
'json' => $updateData,
]);
if ($updateResponse->getStatusCode() === 200) {
// Prepare the chat room message data for creation
$messageData = [
'fields' => [
'chatId' => [
'stringValue' => $chatId, // The actual chat ID
],
'message' => [
'stringValue' => $validatedData['message'] ?? 'Hello, I am interested in booking your service. Please let me know if this proposal works for you.', // Your message
],
'sentBy' => [
'stringValue' => (string) $user->id, // The ID of the user who sent the message
],
'time' => [
'stringValue' => $currentTime, // Timestamp when the message was sent
],
'chatReceipt' => [
'nullValue' => null, // If not yet received
],
'paymentLink' => [
'nullValue' => null, // If not yet received
],
'participants' => [
'arrayValue' => [
'values' => [
[
'stringValue' => (string) $user->id, // User's ID
],
[
'stringValue' => (string) $gig->user->id, // Provider's ID
],
],
],
],
'chatHire' => [
'mapValue' => [
'fields' => [
'price' => [
'doubleValue' => (string) $booking->agreed_amount, // Price for the gig
],
'gigId' => [
'stringValue' => (string) $validatedData['gig_id'], // Gig ID
],
'gigName' => [
'stringValue' => (string) $booking->gig->title, // Gig title
],
'gigCurrency' => [
'stringValue' => (string) $booking->gig->currency, // Gig currency
],
'gigName' => [
'stringValue' => (string) $booking->gig->title, // Gig title
],
'gigCurrency' => [
'stringValue' => (string) $booking->gig->currency, // Gig currency
],
'time' => [
'stringValue' => $currentTime, // Current time
],
'bookingId' => [
'stringValue' => (string) $booking->id, // Booking ID
],
'start' => [
'stringValue' => $booking->start_time, // Start time
],
'end' => [
'stringValue' => $booking->end_time, // End time
],
'sentBy' => [
'stringValue' => (string) $user->id, // User's ID
],
],
],
],
],
];
// Firestore URL to create a new message in the chat room
$firestoreMessageUrl = "https://firestore.googleapis.com/v1/projects/fixgini/databases/(default)/documents/chats/{$chatId}/chat_room";
// Send the message data to Firestore using POST to create a new message
$client = new \GuzzleHttp\Client();
$messageResponse = $client->post($firestoreMessageUrl, [
'json' => $messageData,
]);
// Check if the message was successfully added to Firestore
if ($messageResponse->getStatusCode() === 200) {
info('Chat message created successfully in chat_room.');
$responseBody = json_decode($messageResponse->getBody(), true);
$chatRoomId = $responseBody['name']; // This is the document ID
$chatRoomId = substr($chatRoomId, strrpos($chatRoomId, '/') + 1);
$updatedMessageData = [
'fields' => [
'chatId' => [
'stringValue' => $chatId, // Adding the chatId to the payload
],
'messageId' => [
'stringValue' => $chatRoomId,
],
'message' => [
'stringValue' => $validatedData['message'] ?? 'Hey there, are you available for this engagement?',
],
'sentBy' => [
'stringValue' => (string) $user->id, // The ID of the user who sent the message
],
'time' => [
'stringValue' => $currentTime, // Timestamp when the message was sent
],
'chatReceipt' => [
'nullValue' => null, // If not yet received
],
'paymentLink' => [
'nullValue' => null, // If not yet received
],
'participants' => [
'arrayValue' => [
'values' => [
[
'stringValue' => (string) $user->id, // User's ID
],
[
'stringValue' => (string) $gig->user->id, // Provider's ID
],
],
],
],
'chatHire' => [
'mapValue' => [
'fields' => [
'accepted' => [
'booleanValue' => false, // Use booleanValue for boolean
],
'paid' => [
'booleanValue' => false,
],
'price' => [
'doubleValue' => (string) $booking->agreed_amount, // Price for the gig
],
'gigId' => [
'stringValue' => (string) $validatedData['gig_id'], // Gig ID
],
'gigName' => [
'stringValue' => (string) $booking->gig->title, // Gig title
],
'gigCurrency' => [
'stringValue' => (string) $booking->gig->currency, // Gig currency
],
'bookingId' => [
'stringValue' => (string) $booking->id, // Booking ID
],
'time' => [
'stringValue' => $currentTime,
],
'start' => [
'stringValue' => Carbon::parse($booking->start_time)->format('Y-m-d\TH:i:s.u\Z'),
],
'end' => [
'stringValue' => Carbon::parse($booking->end_time)->format('Y-m-d\TH:i:s.u\Z'),
],
'sentBy' => [
'stringValue' => (string) $user->id, // User's ID
],
],
],
],
],
];
// Firestore URL to create a new message in the chat room
$updateFirestoreMessageUrl = "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();
$messageResponse = $client->patch($updateFirestoreMessageUrl, [
'json' => $updatedMessageData,
]);
// update the booking form table
BookingForm::where('id', $booking->id)->update(['chat_id' => $chatId, 'chat_room_id' => $chatRoomId]);
return response()->json([
'status' => 'success',
'message' => 'Chat room and message recorded successfully.',
], 200);
} else {
throw new \Exception('Failed to create message in chat_room');
}
}
} else {
throw new \Exception('Failed to record chat in Firebase');
}
} catch (\Throwable $th) {
Log::error("Failed to record chat in Firebase: {$th->getMessage()}");
return response()->json(['status' => 'error', 'message' => 'Failed to record chat in Firebase.'], 500);
}
}
private function updateFirestore($paymentLink, $trxRef, $validatedData, $user, $booking, $gig)
{
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' => $validatedData['message'] ?? 'Hey there, are you available for this engagement?',
],
'lastMessageAt' => [
'stringValue' => $currentTime,
],
'lastSenderId' => [
'stringValue' => (string) $user->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' => $gig->provider->name ?? '',
],
'userPhoto' => [
'stringValue' => $gig->provider->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' => $validatedData['message'], // Your message
],
'messageId' => [
'stringValue' => $chatRoomId,
],
'sentBy' => [
'stringValue' => (string) $user->id, // The ID of the user who sent the message
],
'time' => [
'stringValue' => $currentTime,
],
'chatReceipt' => [
'nullValue' => null, // If not yet received
],
'paymentLink' => [
'stringValue' => $paymentLink,
],
'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' => false,
],
'paidTime' => [
'nullValue' => null,
],
'trxRef' => [
'stringValue' => $trxRef,
],
'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) $validatedData['gig_id'], // Keep as stringValue
],
'gigName' => [
'stringValue' => (string) $booking->gig->title, // Gig title
],
'gigCurrency' => [
'stringValue' => (string) $booking->gig->currency, // Gig currency
],
'time' => [
'stringValue' => $currentTime,
],
'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) $user->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 successfully with MessageId in chat_room.');
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);
}
}
private function updateFirestoreRejected($validatedData, $user, $booking)
{
try {
$chatId = $booking->chat_id;
$chatRoomId = $booking->chat_room_id;
$currentTime = Carbon::now()->format('Y-m-d\TH:i:s.u\Z');
$updateData = [
'fields' => [
'chatId' => [
'stringValue' => $chatId, // Adding the chatId to the payload
],
'lastMessage' => [
'stringValue' => $validatedData['message'] ?? 'Hey there, are you available for this engagement?',
],
'lastMessageAt' => [
'stringValue' => $currentTime,
],
'lastSenderId' => [
'stringValue' => (string) $user->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();
$firestoreUpdateUrl = 'https://firestore.googleapis.com/v1/projects/fixgini/databases/(default)/documents/chats/' . $chatId;
$client->patch($firestoreUpdateUrl, [
'json' => $updateData,
]);
$updateChatRoomData = [
'fields' => [
'chatId' => [
'stringValue' => $chatId, // The actual chat ID
],
'message' => [
'stringValue' => $validatedData['message'], // Your message
],
'messageId' => [
'stringValue' => $chatRoomId,
],
'sentBy' => [
'stringValue' => (string) $user->id, // The ID of the user who sent the message
],
'time' => [
'stringValue' => $currentTime,
],
'chatReceipt' => [
'nullValue' => null, // Use nullValue instead of an empty string
],
'paymentLink' => [
'nullValue' => null, // Use nullValue instead of an empty string
],
'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
],
'accepted' => [
'booleanValue' => false, // Use booleanValue for boolean
],
'rejected' => [
'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' => true, // 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) $validatedData['gig_id'], // Keep as stringValue
],
'gigName' => [
'stringValue' => (string) $booking->gig->title, // Gig title
],
'gigCurrency' => [
'stringValue' => (string) $booking->gig->currency, // Gig currency
],
'time' => [
'stringValue' => $currentTime,
],
'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) $user->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
// BookingForm::where('id', $booking->id)->update(['chat_id' => $chatId, 'chat_room_id' => $chatRoomId]);
info('Chat room message updated successfully with MessageId in chat_room.');
info('Engagement rejected in chat_room.');
return response()->json([
'status' => 'success',
'message' => 'Chat room and message recorded successfully.',
], 200);
} catch (\Throwable $th) {
Log::error("Failed to record chat in Firebase: {$th->getMessage()}");
return response()->json(['status' => 'error', 'message' => 'Failed to record chat in Firebase.'], 500);
}
}
private function sendFcmNotification($receiver, $message)
{
try {
// Prepare the FCM notification payload
$fcm_token = $receiver->fcm_token;
if (empty($receiver->fcm_token)) {
return response()->json(['message' => 'No FCM token available for this user'], 400);
}
// Path to Firebase service account credentials
$credentialsFilePath = public_path("fixgini-168886924712.json");
$projectId = 'fixgini';
// Retrieve access token for FCM using Google Client
$client = new GoogleClient();
$client->setAuthConfig($credentialsFilePath);
$client->useApplicationDefaultCredentials();
$client->addScope('https://www.googleapis.com/auth/firebase.messaging');
$client->fetchAccessTokenWithAssertion();
$token = $client->getAccessToken();
$accessToken = $token['access_token'];
// Set up headers for FCM API
$headers = [
"Authorization: Bearer $accessToken",
'Content-Type: application/json',
];
$data = [
"title" => "Dear {$receiver->name}",
"message" => $message,
"type" => "single_chat",
];
$notification = [
"title" => "Dear {$receiver->name}",
"body" => $message,
];
$webpush = [
"fcm_options" => [
"link" => "https://fixgini.com/chat"
],
];
// FCM API payload for the single user
$data = [
"message" => [
"token" => $fcm_token,
"data" => $data,
"notification" => $notification,
"webpush" => $webpush,
],
];
$payload = json_encode($data);
// Send the notification via cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://fcm.googleapis.com/v1/projects/{$projectId}/messages:send");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$response = curl_exec($ch);
$err = curl_error($ch);
curl_close($ch);
// Check for errors
if ($err) {
Log::error('Curl Error: ', ['error' => $err]); // Use array structure for logging
return response()->json([
'message' => 'Curl Error: ' . $err,
], 500);
} else {
// Decode response for logging
$decodedResponse = json_decode($response, true);
Log::info('Notification sent successfully with response : ', $decodedResponse); // Log array response properly
return response()->json([
'message' => 'Notification sent successfully',
'response' => $decodedResponse, // Return decoded response as JSON
], 200);
}
} catch (\Throwable $th) {
Log::error($th->getMessage());
// Catch and handle errors
return response()->json([
'message' => $th->getMessage(),
], 500);
}
}
public function chatMessage(Request $request)
{
try {
$user = Auth::user();
$validatedData = $request->validate([
'booking_id' => 'required|exists:booking_forms,id',
'message' => 'required|string',
]);
$booking = BookingForm::where('id', $validatedData['booking_id'])->first();
// 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)
$updateExistingChatData = [
'fields' => [
'chatId' => [
'stringValue' => $chatId, // Adding the chatId to the payload
],
'lastMessage' => [
'stringValue' => $validatedData['message'] ?? 'Hey there, are you available for this engagement?',
],
'lastMessageAt' => [
'stringValue' => $currentTime,
],
'lastSenderId' => [
'stringValue' => (string) $user->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();
$updateExistingChatId = 'https://firestore.googleapis.com/v1/projects/fixgini/databases/(default)/documents/chats/' . $chatId;
$client->patch($updateExistingChatId, [
'json' => $updateExistingChatData,
]);
$createChatRoom = [
'fields' => [
'chatId' => [
'stringValue' => $chatId, // The actual chat ID
],
'message' => [
'stringValue' => $validatedData['message'], // Your message
],
'messageId' => [
'stringValue' => $chatRoomId,
],
'sentBy' => [
'stringValue' => (string) $user->id, // The ID of the user who sent the message
],
'time' => [
'stringValue' => $currentTime,
],
'chatReceipt' => [
'nullValue' => null, // If not yet received
],
'paymentLink' => [
'nullValue' => null, // If not yet received
],
'participants' => [
'arrayValue' => [
'values' => [
[
'stringValue' => (string) $booking->customer_id, // User's ID
],
[
'stringValue' => (string) $booking->service_provider_id, // Provider's ID
],
],
],
],
'chatHire' => [
'nullValue' => null, // If not yet received
],
'webHire' => [
'mapValue' => [
'fields' => [
'acceptTime' => [
'stringValue' => $currentTime
],
'paid' => [
'booleanValue' => true,
],
'paidTime' => [
'stringValue' => $currentTime,
],
'trxRef' => [
'nullValue' => null, // Use nullValue instead of an empty string
],
'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,
],
'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) $user->id, // Keep as stringValue
],
],
],
],
],
];
// Firestore URL to create a new message in the chat room
$createChatRoomID = "https://firestore.googleapis.com/v1/projects/fixgini/databases/(default)/documents/chats/{$chatId}/chat_room/";
// Send the message data to Firestore using PATCH to update the message
$client = new \GuzzleHttp\Client();
$client->post($createChatRoomID, [
'json' => $createChatRoom,
]);
// update the booking form table
info('new chat created in chat_room.');
$receiver = $booking->provider ?? '';
$this->sendFcmNotification($receiver, $validatedData['message']);
return response()->json([
'status' => 'success',
'message' => 'Chat room message 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);
}
}
// Get all bookings for the logged-in customer
public function customerBookings(Request $request)
{
try {
$user = Auth::user();
$customer_id = $user->id;
$allBookings = BookingForm::with('gig')->where('customer_id', $customer_id)->latest()->limit(10)->get();
if ($allBookings->isEmpty()) {
return response()->json(['status' => 'error', 'message' => 'Engagement not found'], 404);
} else {
$pendingBookings = $allBookings->where('status', 'pending')->values();
$ongoingBookings = $allBookings->where('status', 'ongoing')->values();
$completedBookings = $allBookings->where('status', 'completed')->values();
return response()->json([
'status' => 'success',
'message' => 'Engagement found',
'data' => [
'all' => $allBookings,
'pending' => $pendingBookings,
'ongoing' => $ongoingBookings,
'completed' => $completedBookings,
],
], 200);
}
} catch (\Throwable $th) {
return response()->json(['status' => 'error', 'message' => 'Engagement Booking not found', 'data' => $th->getMessage()], 401);
}
}
// Get all gigs in booking that below to the service provider user id
public function serviceProviderBookings(Request $request)
{
try {
$user = Auth::user();
$gig_ids = Gig::where('user_id', $user->id)->pluck('id');
// Retrieve bookings based on statuses
$allBookings = BookingForm::with('gig', 'customer')->whereIn('gig_id', $gig_ids)->latest()->limit(10)->get();
if ($allBookings->isEmpty()) {
return response()->json(['status' => 'error', 'message' => 'Engagement not found'], 404);
} else {
$pendingBookings = $allBookings->where('status', 'pending')->values();
$ongoingBookings = $allBookings->where('status', 'ongoing')->values();
$completedBookings = $allBookings->where('status', 'completed')->values();
// Return grouped data
return response()->json([
'status' => 'success',
'message' => 'Engagement found',
'data' => [
'all' => $allBookings,
'pending' => $pendingBookings,
'ongoing' => $ongoingBookings,
'completed' => $completedBookings,
],
], 200);
}
} catch (\Throwable $th) {
return response()->json(['status' => 'error', 'message' => 'Engagement Booking not found', 'data' => $th->getMessage()], 401);
}
}
// Show a specific booking by ID
public function show(Request $request)
{
try {
$validatedData = $request->validate([
'booking_id' => 'required|exists:booking_forms,id',
]);
$id = $validatedData['booking_id'];
$booking = BookingForm::findOrFail($id);
return response()->json(['status' => 'success', 'data' => $booking], 200);
} catch (\Throwable $th) {
return response()->json(['status' => 'error', 'message' => 'Booking not found', 'data' => $th->getMessage()], 401);
}
}
// Cancel a booking
public function cancel(Request $request)
{
try {
$validatedData = $request->validate([
'booking_id' => 'required|exists:booking_forms,id',
]);
$id = $validatedData['booking_id'];
$booking = BookingForm::findOrFail($id);
$booking->update(['status' => 'cancelled']);
return response()->json(['status' => 'success', 'message' => 'Booking cancelled successfully'], 200);
} catch (\Throwable $th) {
return response()->json(['status' => 'error', 'message' => 'Booking not found', 'data' => $th->getMessage()], 401);
}
}
// public function completeBooking(Request $request)
// {
// try {
// $validatedData = $request->validate([
// 'booking_id' => 'required|exists:booking_forms,id',
// ]);
// info('incoming confirm completion from customer');
// $id = $validatedData['booking_id'];
// $booking = BookingForm::findOrFail($id);
// // Update booking status
// $booking->update(['status' => 'completed']);
// // Get the amount paid
// $payment = BookingPayment::where('booking_id', $id)->firstOrFail();
// // Calculate amounts
// $toCredit = $payment->payment_status === 'completed' ? $payment->agreed_amount : 0;
// $toPending = $booking->status === 'pending' ? $booking->agreed_amount : 0;
// // Credit the service provider's wallet
// $provider_id = $booking->gig->user_id;
// $provider_wallet = Wallet::where('user_id', $provider_id)->first();
// info('before - provider_wallet ' . $provider_wallet);
// if ($provider_wallet) {
// // Update the wallet's balances
// $provider_wallet->update([
// 'available' => $provider_wallet->available + $toCredit,
// 'pending' => $provider_wallet->pending + $toPending,
// ]);
// }
// info('after - provider_wallet ' . $provider_wallet);
// // Update Firestore or any other related systems
// $this->updateCompleteFirestore($booking, $payment->tx_ref);
// return response()->json([
// 'status' => 'success',
// 'message' => 'Engagement completed',
// ], 200);
// } catch (\Throwable $th) {
// return response()->json([
// 'status' => 'error',
// 'message' => 'Engagement Booking not found',
// 'data' => $th->getMessage(),
// ], 401);
// }
// }
public function completeBooking(Request $request)
{
try {
$validatedData = $request->validate([
'booking_id' => 'required|exists:booking_forms,id',
]);
info('Incoming confirm completion from customer.');
$bookingId = $validatedData['booking_id'];
$booking = BookingForm::findOrFail($bookingId);
// Update booking status
$booking->update(['status' => 'completed']);
// Get the completed booking
$booking_id = BookingForm::where('id', $bookingId)->where('status', 'completed')->first();
// Get the amount paid of the completed booking
$payment = BookingPayment::where('booking_id', $booking_id->id)->where('payment_status', 'completed')->first();
// Calculate amount to credit based on payment status
$toCredit = $payment->amount;
// Credit the service provider's wallet
$provider_id = $booking->gig->user_id;
$provider_wallet = Wallet::where('user_id', $provider_id)->first();
if ($provider_wallet) {
// Update only the `available` balance if payment is completed
$provider_wallet->update([
'pending_withdraw' => $provider_wallet->pending_withdraw + $toCredit,
]);
} else {
// Log an error if the wallet is not found
info("Wallet not found for provider with ID: $provider_id.");
return response()->json([
'status' => 'error',
'message' => 'Service provider wallet not found.',
], 404);
}
info('Updated provider wallet: ' . json_encode($provider_wallet));
// Update Firestore or any other external system
$this->updateCompleteFirestore($booking, $payment->tx_ref);
return response()->json([
'status' => 'success',
'message' => 'Engagement completed successfully.',
], 200);
} catch (\Throwable $th) {
// Catch and log exceptions
info('Error completing booking: ' . $th->getMessage());
return response()->json([
'status' => 'error',
'message' => 'An error occurred while completing the booking.',
'data' => $th->getMessage(),
], 500);
}
}
public function triggerCompleteBooking(Request $request)
{
try {
$validatedData = $request->validate([
'booking_id' => 'required|exists:booking_forms,id',
]);
$id = $validatedData['booking_id'];
$booking = BookingForm::findOrFail($id);
if ($booking) {
$booking->status = 'awaiting';
$booking->save();
}
// mail the customer that the service provider is done with the project
$email = $booking->customer->email;
Mail::to($email)->send(new TriggerCompleteBooking($booking));
// Dispatch job to complete booking after 12 hours if booking status is still ongoing
if (($booking->status) === 'awaiting') {
CompleteBookingJob::dispatch($id)->delay(now()->addHours(12));
}
$this->triggerCompleteFirestore($booking);
return response()->json([
'status' => 'success',
'message' => 'Customer notified successfully',
], 200);
} catch (\Throwable $th) {
return response()->json([
'status' => 'error',
'message' => 'Booking not found',
'data' => $th->getMessage(),
], 401);
}
}
private function updateCompleteFirestore($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' => 'Engagement completed successfully',
],
'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' => 'Engagement completed successfully',
],
'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' => true, // Use booleanValue for boolean
],
'completedTime' => [
'stringValue' => $currentTime
],
'confirmCompleted' => [
'booleanValue' => true, // 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,
],
'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
// BookingForm::where('id', $booking->id)->update(['chat_id' => $chatId, 'chat_room_id' => $chatRoomId]);
info('Task completed successfully with MessageId in chat_room.');
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);
}
}
private function triggerCompleteFirestore($booking)
{
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' => 'Engagement completion awaiting customer approval',
],
'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' => 'Engagement completion awaiting customer approval',
],
'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' => [
'nullValue' => null, // Use nullValue for empty fields
],
'accepted' => [
'booleanValue' => true, // Use booleanValue for boolean
],
'completed' => [
'booleanValue' => true, // Use booleanValue for boolean
],
'completedTime' => [
'stringValue' => $currentTime
],
'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,
],
'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,
]);
info('Task triggered for completion.');
return response()->json([
'status' => 'success',
'message' => 'Task triggered for completion in chat room recorded successfully.',
], 200);
} catch (\Throwable $th) {
Log::error("Failed to Task triggered for completion in chat room in Firestore: {$th->getMessage()}");
return response()->json(['status' => 'error', 'message' => 'Failed to triggered for completion in chat room in Firebase.'], 500);
}
}
public function userWallet()
{
$user = Auth::user();
$userWallet = Wallet::where('user_id', $user->id)->first();
$available = BookingForm::where('service_provider_id', $user->id)->where('status', 'completed')->sum('agreed_amount');
// $pending = BookingForm::where('service_provider_id', $user->id)->where('status', 'ongoing')->orWhere('status', 'awaiting')->sum('agreed_amount');
$pending = BookingForm::where('service_provider_id', $user->id)
->where(function ($query) {
$query->where('status', 'ongoing')
->orWhere('status', 'awaiting');
})
->sum('agreed_amount');
$userWallet->update([
// 'available' => $available,
'pending' => $pending,
]);
if ($userWallet) {
return response()->json([
'data' => $userWallet,
'status' => 'success',
'message' => 'Transactions Found',
], 200);
}
}
}