File "WebhookController.php"

Full Path: /home/trinadezambia/public_html/admin_panel/app/Http/Controllers/WebhookController.php
File size: 65.91 KB
MIME-type: text/x-php
Charset: utf-8

<?php

namespace App\Http\Controllers;

use App\Models\CompulsoryFee;
use App\Models\Fee;
use App\Models\FeesAdvance;
use App\Models\FeesInstallment;
use App\Models\FeesPaid;
use App\Models\OptionalFee;
use App\Models\PaymentConfiguration;
use App\Models\PaymentTransaction;
use App\Models\School;
use App\Models\User;
use App\Models\TransportationFee;
use App\Models\TransportationPayment;
use App\Repositories\User\UserInterface;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Http;
use Razorpay\Api\Api;
use Stripe\Exception\SignatureVerificationException;
use Stripe\Webhook;
use Throwable;
use UnexpectedValueException;
use Carbon\Carbon;

class WebhookController extends Controller
{

    public function __construct(UserInterface $user) {}

    public function stripe()
    {
        $payload = @file_get_contents('php://input');
        Log::info(PHP_EOL . "----------------------------------------------------------------------------------------------------------------------");
        try {
            // Verify webhook signature and extract the event.
            // See https://stripe.com/docs/webhooks/signatures for more information.
            $data = json_decode($payload, false, 512, JSON_THROW_ON_ERROR);

            $sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];

            $school_id = $data->data->object->metadata->school_id;
            $school = School::on('mysql')->where('id', $school_id)->first();

            Config::set('database.connections.school.database', $school->database_name);
            DB::purge('school');
            DB::connection('school')->reconnect();
            DB::setDefaultConnection('school');

            // You can find your endpoint's secret in your webhook settings
            $paymentConfiguration = PaymentConfiguration::select('webhook_secret_key')->where('payment_method', 'stripe')->where('school_id', $data->data->object->metadata->school_id ?? null)->first();
            $endpoint_secret = $paymentConfiguration['webhook_secret_key'];
            $event = Webhook::constructEvent(
                $payload,
                $sig_header,
                $endpoint_secret
            );

            $metadata = $event->data->object->metadata;
            // Log::info("School ID : ", $metadata['school_id']);




            // Use this lines to Remove Signature verification for debugging purpose
            //    $event = json_decode($payload, false, 512, JSON_THROW_ON_ERROR);
            //    $metadata = (array)$event->data->object->metadata;


            //get the current today's date
            $current_date = date('Y-m-d');

            Log::info("Stripe Webhook : ", [$event->type]);

            // handle the events
            switch ($event->type) {
                case 'payment_intent.succeeded':
                    $paymentTransactionData = PaymentTransaction::where('id', $metadata['payment_transaction_id'])->first();
                    if ($paymentTransactionData == null) {
                        Log::error("Stripe Webhook : Payment Transaction id not found");
                        break;
                    }

                    if ($paymentTransactionData->status == "succeed") {
                        Log::info("Stripe Webhook : Transaction Already Successes");
                        break;
                    }
                    if ($metadata['fees_type'] != "transportation_fee") {
                        $fees = Fee::where('id', $metadata['fees_id'])->with(['fees_class_type', 'fees_class_type.fees_type'])->firstOrFail();
                    }

                    DB::beginTransaction();
                    try {
                        // Update payment transaction status
                        PaymentTransaction::find($metadata['payment_transaction_id'])->update(['payment_status' => "succeed"]);

                        if ($metadata['fees_type'] == "transportation_fee") {
                            $fee_id = Transportationpayment::where('payment_transaction_id', $paymentTransactionData->id)->first();
                            $transportationFee = TransportationFee::where('id', $fee_id->transportation_fee_id)->first();
                            $expiryDate = null;
                            if ($transportationFee) {
                                if (!empty($transportationFee->duration)) {
                                    $expiryDate = now()->addDays($transportationFee->duration);
                                }
                            }
                            TransportationPayment::where('payment_transaction_id', $paymentTransactionData->id)
                                ->update([
                                    'status' => "paid",
                                    'paid_at' => Carbon::now()->format('Y-m-d H:i:s'),
                                    'expiry_date' => $expiryDate
                                ]);
                        } else {

                            // Get or create fees_paid record
                            $feesPaidDB = FeesPaid::where([
                                'fees_id' => $metadata['fees_id'],
                                'student_id' => $metadata['student_id'],
                                'school_id' => $metadata['school_id']
                            ])
                                ->with(['compulsory_fee' => function ($query) {
                                    $query->whereNull('deleted_at');
                                }])->first();

                            // Calculate total amount including any existing payments
                            $feesAmount = $paymentTransactionData->amount - ($metadata['dueChargesAmount'] ?? 0);
                            $totalAmount = !empty($feesPaidDB) ? $feesPaidDB->amount + $feesAmount : $feesAmount;

                            // Prepare fees_paid data
                            $feesPaidData = array(
                                'amount' => $totalAmount,
                                'date' => date('Y-m-d', strtotime($current_date)),
                                "school_id" => $metadata['school_id'],
                                'fees_id' => $metadata['fees_id'],
                                'student_id' => $metadata['student_id'],
                                'is_used_installment' => !empty($metadata['installment'])
                            );

                            // Update or create fees_paid record
                            $feesPaidResult = FeesPaid::updateOrCreate(
                                ['id' => $feesPaidDB->id ?? null],
                                $feesPaidData
                            );

                            if ($metadata['fees_type'] == "compulsory") {
                                $installments = json_decode($metadata['installment'], true);
                                // dd('here');
                                if (!empty($installments)) {
                                    foreach ($installments as $installment) {
                                        CompulsoryFee::create([
                                            'student_id' => $metadata['student_id'],
                                            'payment_transaction_id' => $paymentTransactionData->id,
                                            'type' => 'Installment Payment',
                                            'installment_id' => $installment['id'],
                                            'mode' => 'Online',
                                            'cheque_no' => null,
                                            'amount' => $installment['amount'],
                                            'due_charges' => $installment['dueChargesAmount'],
                                            'fees_paid_id' => $feesPaidResult->id,
                                            'status' => "Success",
                                            'date' => date('Y-m-d'),
                                            'school_id' => $metadata['school_id'],
                                        ]);
                                    }
                                } else {
                                    // Full payment
                                    CompulsoryFee::create([
                                        'student_id' => $metadata['student_id'],
                                        'payment_transaction_id' => $paymentTransactionData->id,
                                        'type' => 'Full Payment',
                                        'installment_id' => null,
                                        'mode' => 'Online',
                                        'cheque_no' => null,
                                        'amount' => $feesAmount,
                                        'due_charges' => $metadata['dueChargesAmount'] ?? 0,
                                        'fees_paid_id' => $feesPaidResult->id,
                                        'status' => "Success",
                                        'date' => date('Y-m-d'),
                                        'school_id' => $metadata['school_id'],
                                    ]);
                                }

                                // Handle advance payment if any
                                if (!empty($metadata['advance_amount']) && $metadata['advance_amount'] > 0) {
                                    \Log::info("Advance Amount : ", [$metadata['advance_amount']]);


                                    // 'fees_id', $data->meta_data->fees_id
                                    // 'student_id' => $data->meta_data->student_id,
                                    // 'school_id' => $data->meta_data->school_id,
                                    // 'fees_paid_id' => $feesPaidResult->id,
                                    // $advanceAmount = $data->meta_data->advance_amount;
                                    // 'payment_transaction_id' => $paymentTransactionData->id,
                                    // string to float
                                    $metadata['advance_amount'] = floatval($metadata['advance_amount']);
                                    $this->advanceInstalmentAmount($metadata['fees_id'], $metadata['student_id'], $metadata['school_id'], $feesPaidResult->id, $metadata['advance_amount'], $paymentTransactionData->id);




                                    // $updateCompulsoryFees = CompulsoryFee::where('student_id', $metadata['student_id'])
                                    //     ->with('fees_paid')
                                    //     ->whereHas('fees_paid', function ($q) use ($metadata) {
                                    //         $q->where('fees_id', $metadata['fees_id']);
                                    //     })
                                    //     ->orderBy('id', 'DESC')
                                    //     ->first();

                                    // if ($updateCompulsoryFees) {
                                    //     $updateCompulsoryFees->amount += $metadata['advance_amount'];
                                    //     $updateCompulsoryFees->save();

                                    //     FeesAdvance::create([
                                    //         'compulsory_fee_id' => $updateCompulsoryFees->id,
                                    //         'student_id' => $metadata['student_id'],
                                    //         'parent_id' => $metadata['parent_id'],
                                    //         'amount' => $metadata['advance_amount']
                                    //     ]);
                                    // }
                                }

                                $totalCompulsoryFees = 0;
                                $feesPaidDB = FeesPaid::where([
                                    'fees_id' => $metadata['fees_id'],
                                    'student_id' => $metadata['student_id'],
                                    'school_id' => $metadata['school_id']
                                ])
                                    ->with(['compulsory_fee' => function ($query) {
                                        $query->whereNull('deleted_at');
                                    }])->first();

                                if ($feesPaidDB && !empty($feesPaidDB->compulsory_fee)) {
                                    $totalCompulsoryFees = $feesPaidDB->compulsory_fee->sum('amount');
                                }

                                $feesPaidResult->update([
                                    'is_fully_paid' => $totalCompulsoryFees >= $fees->total_compulsory_fees
                                ]);
                            } else if ($metadata['fees_type'] == "optional") {
                                $optionalFees = json_decode($metadata['optional_fees_id'], true);
                                foreach ($optionalFees as $optionalFee) {
                                    OptionalFee::create([
                                        'student_id' => $metadata['student_id'],
                                        'class_id' => $metadata['class_id'],
                                        'payment_transaction_id' => $paymentTransactionData->id,
                                        'fees_class_id' => $optionalFee['id'],
                                        'amount' => $optionalFee['amount'],
                                        'fees_paid_id' => $feesPaidResult->id,
                                        'status' => "Success",
                                        'date' => date('Y-m-d'),
                                        'mode' => 'Online',
                                        'school_id' => $metadata['school_id'],
                                    ]);
                                }
                            }
                        }

                        // Send success notification
                        \Log::info("Success Notification in Stripe");
                        $user = User::where('id', $metadata['parent_id'])->first();
                        $body = 'Amount :- ' . $paymentTransactionData->amount;
                        $type = 'payment';
                        DB::commit();

                        send_notification([$user->id], 'Fees Payment Successful', $body, $type, ['is_payment_success' => "true"]);

                        Log::info("Payment processed successfully for transaction ID: " . $metadata['payment_transaction_id']);
                    } catch (\Exception $e) {
                        DB::rollBack();
                        Log::error("Error processing payment: " . $e->getMessage());
                        throw $e;
                    }
                    break;
                case
                'payment_intent.payment_failed':
                    $paymentTransactionData = PaymentTransaction::find($metadata['payment_transaction_id']);
                    if (!$paymentTransactionData) {
                        Log::error("Stripe Webhook : Payment Transaction id not found --->");
                        break;
                    }

                    PaymentTransaction::find($metadata['payment_transaction_id'])->update(['payment_status' => "0"]);

                    if ($metadata['fees_type'] == "transportation_fee") {
                        TransportationPayment::where('payment_transaction_id', $paymentTransactionData->id)
                            ->update([
                                'status' => "cancelled"
                            ]);
                    } else {
                        if ($metadata['fees_type'] == "compulsory") {
                            CompulsoryFee::where('payment_transaction_id', $paymentTransactionData->id)->update([
                                'status' => "failed",
                            ]);
                        } else if ($metadata['fees_type'] == "optional") {
                            OptionalFee::where('payment_transaction_id', $paymentTransactionData->id)->update([
                                'status' => "failed",
                            ]);
                        }
                    }

                    http_response_code(400);
                    $user = User::where('id', $metadata['parent_id'])->first();
                    $body = 'Amount :- ' . $paymentTransactionData->amount;
                    $type = 'payment';

                    DB::commit();

                    send_notification([$user->id], 'Fees Payment Failed', $body, $type, ['is_payment_success' => "false"]);
                    break;
                default:
                    Log::error('Stripe Webhook : Received unknown event type');
            }
        } catch (UnexpectedValueException) {
            // Invalid payload
            echo "Stripe Webhook : Payload Mismatch";
            Log::error("Stripe Webhook : Payload Mismatch");
            http_response_code(400);
            exit();
        } catch (SignatureVerificationException) {
            // Invalid signature
            echo "Stripe Webhook : Signature Verification Failed";
            Log::error("Stripe Webhook : Signature Verification Failed");
            http_response_code(400);
            exit();
        } catch (Throwable $e) {
            DB::rollBack();
            Log::error("Stripe Webhook : Error occurred", [$e->getMessage() . ' --> ' . $e->getFile() . ' At Line : ' . $e->getLine()]);
            http_response_code(400);
            exit();
        }
    }

    public function razorpay()
    {
        $webhookBody = file_get_contents('php://input');
        Log::info(PHP_EOL . "----------------------------------------------------------------------------------------------------------------------");
        try {
            // Parse webhook data
            $data = json_decode($webhookBody);
            Log::info("Razorpay Webhook Data:", ['data' => $data]);

            if (!$data || !isset($data->payload->payment->entity)) {
                throw new \Exception('Invalid webhook payload structure');
            }

            // Extract transaction data from the correct path in payload
            $webhookData = $data->payload->payment->entity;
            $metadata = $webhookData->notes;

            if (!$metadata || !isset($metadata->school_id)) {
                throw new \Exception('Invalid metadata in webhook payload');
            }

            $schoolId = $metadata->school_id;
            $school = School::on('mysql')->where('id', $schoolId)->first();
            if (!$school) {
                throw new \Exception('School not found for ID: ' . $schoolId);
            }

            // Switch to school database
            Config::set('database.connections.school.database', $school->database_name);
            DB::purge('school');
            DB::connection('school')->reconnect();
            DB::setDefaultConnection('school');

            // Get payment configuration
            $paymentConfiguration = PaymentConfiguration::select(['webhook_secret_key', 'api_key'])
                ->where('payment_method', 'Razorpay')
                ->where('school_id', $schoolId)
                ->first();

            if (!$paymentConfiguration) {
                throw new \Exception('Payment configuration not found');
            }

            // Find payment transaction using order_id or payment_id
            $paymentTransaction = PaymentTransaction::where('order_id', $webhookData->order_id)
                ->orWhere('payment_id', $webhookData->id)
                ->first();

            if (!$paymentTransaction) {
                throw new \Exception('Payment transaction not found for order: ' . $webhookData->order_id);
            }

            Log::info("Payment Transaction:", ['transaction' => $paymentTransaction]);

            // Verify webhook signature using Razorpay SDK
            $webhookSignature = $_SERVER['HTTP_X_RAZORPAY_SIGNATURE'] ?? null;
            if (!$webhookSignature) {
                throw new \Exception('Webhook signature not found in request headers');
            }

            $api = new Api($paymentConfiguration->api_key, $paymentConfiguration->webhook_secret_key);
            $api->utility->verifyWebhookSignature($webhookBody, $webhookSignature, $paymentConfiguration->webhook_secret_key);

            // Process based on transaction status
            $status = $webhookData->status ?? '';
            Log::info("Transaction Status:", ['status' => $status]);

            if ($status === 'captured' || $status === 'authorized') {
                if ($paymentTransaction->payment_status == 'succeed') {
                    return response()->json(['status' => 'success'], 200);
                }
                $result = $this->handleRazorpaySuccess($paymentTransaction, $webhookData, $metadata);

                // Send success notification
                $user = User::find($metadata->parent_id ?? $paymentTransaction->user_id);
                if ($user) {
                    $body = 'Payment successful. Amount: ' . ($webhookData->amount / 100);
                    send_notification([$user->id], 'Payment Successful', $body, 'payment', ['is_payment_success' => true]);
                }

                return $result;
            } else if ($status === 'failed') {
                return $this->handleRazorpayFailed($paymentTransaction, $webhookData, $metadata);
            }

            Log::info("Unhandled transaction status:", ['status' => $status]);
            return response()->json(['status' => 'unhandled_status'], 200);
        } catch (\Exception $e) {
            Log::error("Razorpay Webhook Error:", [
                'message' => $e->getMessage(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString()
            ]);
            return response()->json(['error' => $e->getMessage()], 400);
        }
    }

    public function paystack()
    {
        $webhookBody = file_get_contents('php://input');
        Log::info(PHP_EOL . "----------------------------------------------------------------------------------------------------------------------");
        try {
            $data = json_decode($webhookBody, false, 512, JSON_THROW_ON_ERROR);
            Log::info("Paystack Webhook : ", [$data]);

            // Get metadata from the webhook payload
            $metadata = $data->data->metadata;
            $school_id = $metadata->school_id;
            $school = School::on('mysql')->where('id', $school_id)->first();

            // Set up database connection for the school
            Config::set('database.connections.school.database', $school->database_name);
            DB::purge('school');
            DB::connection('school')->reconnect();
            DB::setDefaultConnection('school');

            // Get payment configuration
            $paymentConfiguration = PaymentConfiguration::select('secret_key')
                ->where('payment_method', 'Paystack')
                ->where('school_id', $school_id)
                ->first();

            $webhookSecret = $paymentConfiguration['secret_key'];

            // Verify webhook signature
            $expectedSignature = $_SERVER['HTTP_X_PAYSTACK_SIGNATURE'];
            $calculatedSignature = hash_hmac('sha512', $webhookBody, $webhookSecret);

            $paymentTransactionData = PaymentTransaction::where('order_id', $data->data->reference)->first();
            if ($expectedSignature !== $calculatedSignature) {

                // send notification
                $user = User::where('id', $metadata->parent_id)->first();
                $body = 'Amount :- ' . $paymentTransactionData->amount;
                $type = 'payment';
                send_notification([$user->id], 'Fees Payment Failed', $body, $type, ['is_payment_success' => 'false']);
                throw new SignatureVerificationException('Invalid signature');
            }

            // Get the payment transaction dat

            if (!$paymentTransactionData) {
                // Create a new payment transaction
                // $paymentTransactionData = new PaymentTransaction();
                $paymentTransactionData = PaymentTransaction::create([
                    'user_id' => $data->data->metadata->parent_id,
                    'amount' => $data->data->metadata->total_amount,
                    'payment_gateway' => 'Paystack',
                    'order_id' => $data->data->reference,
                    'payment_status' => 'pending',
                ]);
            }

            $current_date = date('Y-m-d');

            if ($data->event === 'charge.success') {
                Log::info('Payment successful');
                \Log::info("Payment reference :- " . $data->data->reference);
                $paymentTransactionData = PaymentTransaction::where('order_id', $data->data->reference)->first();

                if (!$paymentTransactionData) {
                    Log::error("Paystack Webhook : Payment Transaction id not found");
                    return response()->json(['error' => 'Transaction not found'], 404);
                }

                if ($paymentTransactionData->payment_status === "succeed") {
                    Log::info("Paystack Webhook : Transaction Already Succeed");
                    return response()->json(['status' => 'success'], 200);
                }
                if ($metadata->fees_type != "transportation_fee") {
                    $fees = Fee::where('id', $metadata->fees_id)
                        ->with(['fees_class_type', 'fees_class_type.fees_type'])
                        ->firstOrFail();
                }

                DB::beginTransaction();
                // Update payment transaction status
                PaymentTransaction::where('order_id', $data->data->reference)
                    ->update(['payment_status' => "succeed"]);

                if ($metadata->fees_type == "transportation_fee") {
                    $fee_id = Transportationpayment::where('payment_transaction_id', $paymentTransactionData->id)->first();
                    $transportationFee = TransportationFee::where('id', $fee_id->transportation_fee_id)->first();
                    $expiryDate = null;
                    if ($transportationFee) {
                        if (!empty($transportationFee->duration)) {
                            $expiryDate = now()->addDays($transportationFee->duration);
                        }
                    }
                    TransportationPayment::where('payment_transaction_id', $paymentTransactionData->id)
                        ->update([
                            'status' => "paid",
                            'paid_at' => Carbon::now()->format('Y-m-d H:i:s'),
                            'expiry_date' => $expiryDate
                        ]);
                } else {

                    // Get or create fees paid record
                    $feesPaidDB = FeesPaid::where([
                        'fees_id' => $metadata->fees_id,
                        'student_id' => $metadata->student_id,
                        'school_id' => $metadata->school_id
                    ])->first();

                    $feesAmount = $paymentTransactionData->amount - ($metadata->dueChargesAmount ?? 0);

                    $totalAmount = !empty($feesPaidDB)
                        ? $feesPaidDB->amount + $feesAmount
                        : $feesAmount;

                    $feesPaidData = [
                        'amount' => $totalAmount,
                        'date' => $current_date,
                        'school_id' => $metadata->school_id,
                        'fees_id' => $metadata->fees_id,
                        'student_id' => $metadata->student_id,
                    ];

                    $feesPaidResult = FeesPaid::updateOrCreate(
                        ['id' => $feesPaidDB->id ?? null],
                        $feesPaidData
                    );

                    if ($metadata->fees_type === "compulsory") {
                        $installments = json_decode($metadata->installment, true, 512, JSON_THROW_ON_ERROR);

                        \Log::info("Installments : ", [$installments]);

                        if (count($installments) > 0) {
                            foreach ($installments as $installment) {
                                CompulsoryFee::updateOrCreate(
                                    [
                                        'payment_transaction_id' => $paymentTransactionData->id,
                                        'fees_paid_id' => $feesPaidResult->id,
                                        'installment_id' => $installment['id'],
                                    ],
                                    [
                                        'student_id' => $metadata->student_id,
                                        'type' => 'Installment Payment',
                                        'installment_id' => $installment['id'],
                                        'mode' => 'Online',
                                        'cheque_no' => null,
                                        'amount' => $installment['amount'],
                                        'due_charges' => $installment['dueChargesAmount'] ?? 0,
                                        'fees_paid_id' => $feesPaidResult->id,
                                        'status' => 'Success',
                                        'date' => $current_date,
                                        'school_id' => $metadata->school_id,
                                    ]
                                );
                                // CompulsoryFee::create([
                                //     'student_id' => $metadata->student_id,
                                //     'payment_transaction_id' => $paymentTransactionData->id,
                                //     'type' => 'Installment Payment',
                                //     'installment_id' => $installment['id'],
                                //     'mode' => 'Online',
                                //     'cheque_no' => null,
                                //     'amount' => $installment['amount'],
                                //     'due_charges' => $installment['dueChargesAmount'],
                                //     'fees_paid_id' => $feesPaidResult->id,
                                //     'status' => "Success",
                                //     'date' => $current_date,
                                //     'school_id' => $metadata->school_id,
                                // ]);
                            }
                        } else if ($metadata->advance_amount == 0) {


                            CompulsoryFee::create([
                                'student_id' => $metadata->student_id,
                                'payment_transaction_id' => $paymentTransactionData->id,
                                'type' => 'Full Payment',
                                'installment_id' => null,
                                'mode' => 'Online',
                                'cheque_no' => null,
                                'amount' => $feesAmount,
                                'due_charges' => $metadata->dueChargesAmount,
                                'fees_paid_id' => $feesPaidResult->id,
                                'status' => "Success",
                                'date' => $current_date,
                                'school_id' => $metadata->school_id,
                            ]);
                        }

                        // Add advance amount in installment
                        if ($metadata->advance_amount > 0) {

                            $this->advanceInstalmentAmount($metadata->fees_id, $metadata->student_id, $metadata->school_id, $feesPaidResult->id, $metadata->advance_amount, $paymentTransactionData->id);

                            // $updateCompulsoryFees = CompulsoryFee::where('student_id', $metadata->student_id)
                            //     ->with('fees_paid')
                            //     ->whereHas('fees_paid', function ($q) use ($metadata) {
                            //         $q->where('fees_id', $metadata->fees_id);
                            //     })
                            //     ->orderBy('id', 'DESC')
                            //     ->first();

                            // $updateCompulsoryFees->amount += $metadata->advance_amount;
                            // $updateCompulsoryFees->save();

                            // FeesAdvance::create([
                            //     'compulsory_fee_id' => $updateCompulsoryFees->id,
                            //     'student_id' => $metadata->student_id,
                            //     'parent_id' => $metadata->parent_id,
                            //     'amount' => $metadata->advance_amount
                            // ]);
                        }

                        $feesPaidDB = FeesPaid::where([
                            'fees_id' => $metadata->fees_id,
                            'student_id' => $metadata->student_id,
                            'school_id' => $metadata->school_id
                        ])
                            ->with(['compulsory_fee' => function ($query) {
                                $query->whereNull('deleted_at');
                            }])->first();

                        $totalCompulsoryFees = 0;
                        if ($feesPaidDB && !empty($feesPaidDB->compulsory_fee)) {
                            $totalCompulsoryFees = $feesPaidDB->compulsory_fee->sum('amount');
                        }

                        FeesPaid::where('id', $feesPaidDB->id)->update([
                            'is_fully_paid' => $totalCompulsoryFees >= $fees->total_compulsory_fees,
                            'is_used_installment' => !empty($metadata->installment)
                        ]);

                        // $feesPaidResult->is_fully_paid = $totalCompulsoryFees >= $fees->total_compulsory_fees;
                        // $feesPaidResult->is_used_installment = !empty($metadata->installment);
                        // $feesPaidResult->save();
                    } else if ($metadata->fees_type === "optional") {
                        $optional_fees = json_decode($metadata->optional_fees_id, false, 512, JSON_THROW_ON_ERROR);
                        foreach ($optional_fees as $optional_fee) {
                            OptionalFee::create([
                                'student_id' => $metadata->student_id,
                                'class_id' => $metadata->class_id,
                                'payment_transaction_id' => $paymentTransactionData->id,
                                'fees_class_id' => $optional_fee->id,
                                'mode' => 'Online',
                                'cheque_no' => null,
                                'amount' => $optional_fee->amount,
                                'fees_paid_id' => $feesPaidResult->id,
                                'date' => $current_date,
                                'school_id' => $metadata->school_id,
                                'status' => "Success",
                            ]);
                        }
                    }
                }

                // Send notification
                $user = User::where('id', $metadata->parent_id)->first();
                $body = 'Amount :- ' . $paymentTransactionData->amount;
                $type = 'payment';

                DB::commit();

                send_notification([$user->id], 'Fees Payment Successful', $body, $type, ['is_payment_success' => "true"]);
                return response()->json(['status' => 'success'], 200);
            } else if ($data->event === 'charge.failed') {
                $paymentTransactionData = PaymentTransaction::where('order_id', $data->data->reference)->first();

                if (!$paymentTransactionData) {
                    Log::error("Paystack Webhook : Payment Transaction id not found");
                    return response()->json(['error' => 'Transaction not found'], 404);
                }

                DB::beginTransaction();

                PaymentTransaction::find($metadata->payment_transaction_id)
                    ->update(['payment_status' => "failed"]);

                if ($metadata->fees_type == "transportation_fee") {
                    TransportationPayment::where('payment_transaction_id', $paymentTransactionData->id)
                        ->update([
                            'status' => "cancelled"
                        ]);
                } else {

                    if ($metadata->fees_type === "compulsory") {
                        CompulsoryFee::where('payment_transaction_id', $paymentTransactionData->id)
                            ->update(['status' => "failed"]);
                    } else if ($metadata->fees_type === "optional") {
                        OptionalFee::where('payment_transaction_id', $paymentTransactionData->id)
                            ->update(['status' => "failed"]);
                    }
                }

                // Send notification
                $user = User::where('id', $metadata->parent_id)->first();
                DB::commit();
                if ($user) {
                    $body = 'Amount: ' . $paymentTransactionData->amount;
                    send_notification([$user->id], 'Fees Payment Failed', $body, 'payment', ['is_payment_success' => 'false']);
                }


                return response()->json(['status' => 'failed'], 400);
            }

            return response()->json(['status' => 'ignored'], 200);
        } catch (UnexpectedValueException $e) {
            Log::error("Paystack Webhook : Invalid payload", [$e->getMessage()]);
            return response()->json(['error' => 'Invalid payload'], 400);
        } catch (SignatureVerificationException $e) {
            Log::error("Paystack Webhook : Invalid signature", [$e->getMessage()]);
            return response()->json(['error' => 'Invalid signature'], 400);
        } catch (Throwable $e) {
            DB::rollBack();
            Log::error("Paystack Webhook Error: " . $e->getMessage(), [
                'file' => $e->getFile(),
                'line' => $e->getLine()
            ]);
            return response()->json(['error' => 'Internal server error'], 500);
        }
    }

    public function flutterwave()
    {
        $webhookBody = file_get_contents('php://input');
        Log::info(PHP_EOL . "----------------------------------------------------------------------------------------------------------------------");

        try {

            $data = json_decode($webhookBody, false, 512, JSON_THROW_ON_ERROR);

            $school_id = $data->meta_data->school_id;
            $school = School::on('mysql')->where('id', $school_id)->first();

            Config::set('database.connections.school.database', $school->database_name);
            DB::purge('school');
            DB::connection('school')->reconnect();
            DB::setDefaultConnection('school');

            // You can find your endpoint's secret in your webhook settings
            $paymentConfiguration = PaymentConfiguration::select(['secret_key', 'api_key'])->where('payment_method', 'flutterwave')->where('school_id', $school_id ?? null)->first();


            $webhookSecret = $paymentConfiguration['secret_key'];
            $webhookPublic = $paymentConfiguration['api_key'];


            $api = new Api($webhookPublic, $webhookSecret);


            //get the current today's date
            $current_date = date('Y-m-d');

            if (isset($data->event) && $data->event == 'charge.completed') {

                Log::info('Payment completed');

                //checks the signature
                $expectedSignature = hash_hmac("SHA256", $webhookBody, $webhookSecret);

                $api->utility->verifyWebhookSignature($webhookBody, $expectedSignature, $webhookSecret);
                $paymentTransactionData = PaymentTransaction::where('order_id', $data->data->tx_ref)->first();

                if ($paymentTransactionData == null) {
                    Log::error("Flutterwave Webhook : Payment Transaction id not found");
                }

                if ($paymentTransactionData->payment_status == "succeed") {
                    Log::info("Flutterwave Webhook : Transaction Already Succeed");
                    return response()->json(['status' => 'succeed'], 200);
                }
                $fees = Fee::where('id', $data->meta_data->fees_id)->with(['fees_class_type', 'fees_class_type.fees_type'])->firstOrFail();

                DB::beginTransaction();
                PaymentTransaction::where('id', $paymentTransactionData->id)->update(['payment_status' => "succeed"]);

                if ($data->meta_data->fees_type == "transportation_fee") {
                    $fee_id = Transportationpayment::where('payment_transaction_id', $paymentTransactionData->id)->first();
                    $transportationFee = TransportationFee::where('id', $fee_id->transportation_fee_id)->first();
                    $expiryDate = null;
                    if ($transportationFee) {
                        if (!empty($transportationFee->duration)) {
                            $expiryDate = now()->addDays($transportationFee->duration);
                        }
                    }
                    TransportationPayment::where('payment_transaction_id', $paymentTransactionData->id)
                        ->update([
                            'status' => "paid",
                            'paid_at' => Carbon::now()->format('Y-m-d H:i:s'),
                            'expiry_date' => $expiryDate
                        ]);
                } else {
                    $feesPaidDB = FeesPaid::where([
                        'fees_id' => $data->meta_data->fees_id,
                        'student_id' => $data->meta_data->student_id,
                        'school_id' => $data->meta_data->school_id
                    ])->first();

                    // Check if Fees Paid Exists Then Add The optional Fees Amount with Fess Paid Amount
                    $feesAmount = $data->data->amount - ($data->meta_data->dueChargesAmount ?? 0);
                    $totalAmount = !empty($feesPaidDB) ? $feesPaidDB->amount + $feesAmount : $feesAmount;
                    // Fees Paid Array
                    $feesPaidData = array(
                        'amount' => $totalAmount,
                        'date' => date('Y-m-d', strtotime($current_date)),
                        "school_id" => $data->meta_data->school_id,
                        'fees_id' => $data->meta_data->fees_id,
                        'student_id' => $data->meta_data->student_id,
                    );

                    $feesPaidResult = FeesPaid::updateOrCreate(['id' => $feesPaidDB->id ?? null], $feesPaidData);

                    if ($data->meta_data->fees_type == "compulsory") {
                        $installments = json_decode($data->meta_data->installment, true, 512, JSON_THROW_ON_ERROR);
                        if (count($installments) > 0) {
                            \Log::info("Installments: ", [$installments]);
                            foreach ($installments as $installment) {

                                CompulsoryFee::create([
                                    'student_id' => $data->meta_data->student_id,
                                    'payment_transaction_id' => $paymentTransactionData->id,
                                    'type' => 'Installment Payment',
                                    'installment_id' => $installment['id'],
                                    'mode' => 'Online',
                                    'cheque_no' => null,
                                    'amount' => $installment['amount'],
                                    'due_charges' => $installment['dueChargesAmount'] ?? 0,
                                    'fees_paid_id' => $feesPaidResult->id,
                                    'status' => "Success",
                                    'date' => date('Y-m-d'),
                                    'school_id' => $data->meta_data->school_id,
                                ]);
                            }
                        } else if ($data->meta_data->advance_amount == 0) {


                            CompulsoryFee::create([
                                'student_id' => $data->meta_data->student_id,
                                'payment_transaction_id' => $paymentTransactionData->id,
                                'type' => 'Full Payment',
                                'installment_id' => null,
                                'mode' => 'Online',
                                'cheque_no' => null,
                                'amount' => $feesAmount,
                                'due_charges' => $data->meta_data->dueChargesAmount ?? 0,
                                'fees_paid_id' => $feesPaidResult->id,
                                'status' => "Success",
                                'date' => date('Y-m-d'),
                                'school_id' => $data->meta_data->school_id,
                            ]);
                        }

                        // Add advance amount in installment
                        if ($data->meta_data->advance_amount > 0) {

                            // 'fees_id', $data->meta_data->fees_id
                            // 'student_id' => $data->meta_data->student_id,
                            // 'school_id' => $data->meta_data->school_id,
                            // 'fees_paid_id' => $feesPaidResult->id,
                            // $advanceAmount = $data->meta_data->advance_amount;
                            // 'payment_transaction_id' => $paymentTransactionData->id,

                            $this->advanceInstalmentAmount($data->meta_data->fees_id, $data->meta_data->student_id, $data->meta_data->school_id, $feesPaidResult->id, $data->meta_data->advance_amount, $paymentTransactionData->id);


                            // $updateCompulsoryFees = CompulsoryFee::where('student_id', $data->meta_data->student_id)->with('fees_paid')->whereHas('fees_paid', function ($q) use ($data) {
                            //     $q->where('fees_id', $data->meta_data->fees_id);
                            // })->orderBy('id', 'DESC')->first();

                            // $updateCompulsoryFees->amount += $data->meta_data->advance_amount;
                            // $updateCompulsoryFees->save();

                            // FeesAdvance::create([
                            //     'compulsory_fee_id' => $updateCompulsoryFees->id,
                            //     'student_id' => $data->meta_data->student_id,
                            //     'parent_id' => $data->meta_data->parent_id,
                            //     'amount' => $data->meta_data->advance_amount
                            // ]);
                        }


                        $feesPaidDB = FeesPaid::where([
                            'fees_id' => $data->meta_data->fees_id,
                            'student_id' => $data->meta_data->student_id,
                            'school_id' => $data->meta_data->school_id
                        ])
                            ->with(['compulsory_fee' => function ($query) {
                                $query->whereNull('deleted_at');
                            }])->first();

                        $totalCompulsoryFees = 0;
                        if ($feesPaidDB && !empty($feesPaidDB->compulsory_fee)) {
                            $totalCompulsoryFees = $feesPaidDB->compulsory_fee->sum('amount');
                        }

                        FeesPaid::where('id', $feesPaidDB->id)->update([
                            'is_fully_paid' => $totalCompulsoryFees >= $fees->total_compulsory_fees,
                            'is_used_installment' => !empty($metadata->installment)
                        ]);


                        // $feesPaidResult->is_fully_paid = $totalAmount >= $fees->total_compulsory_fees;
                        // $feesPaidResult->is_used_installment = !empty($data->meta_data->installment);
                        // $feesPaidResult->save();
                    } else if ($data->meta_data->fees_type == "optional") {
                        $optional_fees = json_decode($data->meta_data->optional_fees_id, false, 512, JSON_THROW_ON_ERROR);
                        foreach ($optional_fees as $optional_fee) {
                            OptionalFee::create([
                                'student_id' => $data->meta_data->student_id,
                                'class_id' => $data->meta_data->class_id,
                                'payment_transaction_id' => $paymentTransactionData->id,
                                'fees_class_id' => $optional_fee->id,
                                'mode' => 'Online',
                                'cheque_no' => null,
                                'amount' => $optional_fee->amount,
                                'fees_paid_id' => $feesPaidResult->id,
                                'date' => date('Y-m-d'),
                                'school_id' => $data->meta_data->school_id,
                                'status' => "Success",
                            ]);
                        }
                    }
                }

                Log::info("payment_intent.succeeded called successfully");
                $user = User::where('id', $data->meta_data->parent_id)->first();
                $body = 'Amount :- ' . $paymentTransactionData->amount;
                $type = 'payment';
                DB::commit();

                send_notification([$user->id], 'Fees Payment Successful', $body, $type, ['is_payment_success' => 'true']);
                http_response_code(200);
            } elseif (isset($data->event) && $data->event == 'charge.failed') {
                $paymentTransactionData = PaymentTransaction::find($data->data->id);
                if (!$paymentTransactionData) {
                    Log::error("Flutterwave Webhook : Payment Transaction id not found --->");
                }

                PaymentTransaction::find($data->data->id)->update(['payment_status' => "failed"]);
                if ($data->meta_data->fees_type == "transportation_fee") {
                    TransportationPayment::where('payment_transaction_id', $paymentTransactionData->id)
                        ->update([
                            'status' => "cancelled"
                        ]);
                } else {
                    if ($data->data->meta_data->fees_type == "compulsory") {
                        CompulsoryFee::where('payment_transaction_id', $paymentTransactionData->id)
                            ->update([
                                'status' => "failed",
                            ]);
                    } else if ($data->data->meta_data->fees_type == "optional") {
                        OptionalFee::where('payment_transaction_id', $paymentTransactionData->id)
                            ->update([
                                'status' => "failed",
                            ]);
                    }
                }

                http_response_code(400);
                $user = User::where('id', $data->data->meta_data->parent_id)->first();
                $body = 'Amount :- ' . $paymentTransactionData->amount;
                $type = 'payment';
                DB::commit();
                send_notification([$user->id], 'Fees Payment Failed', $body, $type, ['is_payment_success' => 'false']);
            } elseif (isset($data->event) && $data->event == 'charge.authorized') {
                http_response_code(200);
            } else {
                Log::error('Flutterwave Webhook : Received unknown event type');
            }
        } catch (UnexpectedValueException) {
            // Invalid payload
            echo "Flutterwave Webhook : Payload Mismatch";
            Log::error("Flutterwave  : Payload Mismatch");
            http_response_code(400);
            exit();
        } catch (SignatureVerificationException) {
            // Invalid signature
            echo "Flutterwave  Webhook : Signature Verification Failed";
            Log::error("Flutterwave  Webhook : Signature Verification Failed");
            http_response_code(400);
            exit();
        } catch (Throwable $e) {
            DB::rollBack();
            Log::error("Flutterwave Webhook : Error occurred", [$e->getMessage() . ' --> ' . $e->getFile() . ' At Line : ' . $e->getLine()]);
            http_response_code(400);
            exit();
        }
    }

    private function advanceInstalmentAmount($feesId, $studentId, $schoolId, $feesPaidId, $advanceAmount, $paymentTransactionId)
    {
        // 'fees_id', $data->meta_data->fees_id
        // 'student_id' => $data->meta_data->student_id,
        // 'school_id' => $data->meta_data->school_id,
        // 'fees_paid_id' => $feesPaidResult->id,
        // $advanceAmount = $data->meta_data->advance_amount;
        // 'payment_transaction_id' => $paymentTransactionData->id,


        $feeInstallments = FeesInstallment::where('fees_id', $feesId)
            ->whereDoesntHave('compulsory_fees', function ($query) use ($studentId) {
                $query->where('student_id', $studentId);
            })
            ->orderBy('due_date', 'ASC')->get();

        foreach ($feeInstallments as $feeInstallment) {

            if ($advanceAmount > $feeInstallment->installment_amount) {
                CompulsoryFee::create([
                    'student_id' => $studentId,
                    'payment_transaction_id' => $paymentTransactionId,
                    'type' => 'Installment Payment',
                    'installment_id' => $feeInstallment->id,
                    'mode' => 'Online',
                    'cheque_no' => null,
                    'amount' => $feeInstallment->installment_amount,
                    'due_charges' => 0,
                    'fees_paid_id' => $feesPaidId,
                    'status' => "Success",
                    'date' => date('Y-m-d'),
                    'school_id' => $schoolId,
                ]);

                $advanceAmount -= $feeInstallment->installment_amount;
            } else {
                CompulsoryFee::create([
                    'student_id' => $studentId,
                    'payment_transaction_id' => $paymentTransactionId,
                    'type' => 'Installment Payment',
                    'installment_id' => $feeInstallment->id,
                    'mode' => 'Online',
                    'cheque_no' => null,
                    'amount' => $advanceAmount,
                    'due_charges' => 0,
                    'fees_paid_id' => $feesPaidId,
                    'status' => "Success",
                    'date' => date('Y-m-d'),
                    'school_id' => $schoolId,
                ]);
                break;
            }
        }
    }


    private function handleRazorpaySuccess($paymentTransaction, $webhookData, $metadata)
    {
        if ($paymentTransaction->status === "succeed") {
            Log::info("Transaction already processed successfully");
            return response()->json(['status' => 'success', 'message' => 'Transaction already processed']);
        }

        DB::beginTransaction();
        try {
            // Update payment transaction status
            $paymentTransaction->payment_status = "succeed";
            $paymentTransaction->save();

            if ($metadata->fees_type == "transportation_fee") {
                $fee_id = Transportationpayment::where('payment_transaction_id', $metadata->payment_transaction_id)->first();
                $transportationFee = TransportationFee::where('id', $fee_id->transportation_fee_id)->first();
                $expiryDate = null;
                if ($transportationFee) {
                    if (!empty($transportationFee->duration)) {
                        $expiryDate = now()->addDays($transportationFee->duration);
                    }
                }
                TransportationPayment::where('payment_transaction_id', $metadata->payment_transaction_id)
                    ->update([
                        'status' => "paid",
                        'paid_at' => Carbon::now()->format('Y-m-d H:i:s'),
                        'expiry_date' => $expiryDate
                    ]);
                $amount = (int) $webhookData->amount / 100;
            } else {

                Log::info("Metadata here : ", [$metadata]);
                Log::info("Webhook Data here : ", [$webhookData]);
                // Get fees details

                // Update fees paid record
                $feesPaidDB = FeesPaid::where([
                    'fees_id' => $metadata->fees_id,
                    'student_id' => $metadata->student_id,
                    'school_id' => $metadata->school_id
                ])->first();

                // Convert amount to integer
                $amount = (int) $webhookData->amount / 100; // Razorpay amount is in paise

                $amount = $amount - ($metadata->dueChargesAmount ?? 0);
                Log::info("Amount here : ", [$amount]);

                $totalAmount = !empty($feesPaidDB) ? $feesPaidDB->amount + $amount : $amount;

                $feesPaidData = [
                    'amount' => $totalAmount,
                    'date' => date('Y-m-d'),
                    'school_id' => $metadata->school_id,
                    'fees_id' => $metadata->fees_id,
                    'student_id' => $metadata->student_id,
                    'is_used_installment' => !empty($metadata->installment)
                ];

                $feesPaidResult = FeesPaid::updateOrCreate(
                    ['id' => $feesPaidDB->id ?? null],
                    $feesPaidData
                );

                // Process fees based on type
                if ($metadata->fees_type == "compulsory") {
                    $this->processCompulsoryFees($paymentTransaction, $feesPaidResult, $metadata);
                } else if ($metadata->fees_type == "optional") {
                    $this->processOptionalFees($paymentTransaction, $feesPaidResult, $metadata);
                }
            }
            DB::commit();
            // Send success notification
            $user = User::find($metadata->parent_id);
            if ($user) {
                $body = 'Amount: ' . $amount;
                send_notification([$user->id], 'Fees Payment Successful', $body, 'payment', ['is_payment_success' => 'true']);
            }

            return response()->json(['status' => 'success'], 200);
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    private function handleRazorpayFailed($paymentTransaction, $webhookData, $metadata)
    {
        DB::beginTransaction();
        try {
            $paymentTransaction->payment_status = "failed";
            $paymentTransaction->save();

            if ($metadata->fees_type == "transportation_fee") {
                TransportationPayment::where('payment_transaction_id', $metadata->payment_transaction_id)
                    ->update([
                        'status' => "cancelled"
                    ]);
            } else {

                if ($metadata->fees_type == "compulsory") {
                    CompulsoryFee::where('payment_transaction_id', $paymentTransaction->id)
                        ->update(['status' => "failed"]);
                } else if ($metadata->fees_type == "optional") {
                    OptionalFee::where('payment_transaction_id', $paymentTransaction->id)
                        ->update(['status' => "failed"]);
                }
            }
            DB::commit();
            // Send failure notification
            $user = User::find($metadata->parent_id);
            if ($user) {
                $body = 'Amount: ' . ((int) $webhookData->amount / 100);
                send_notification([$user->id], 'Fees Payment Failed', $body, 'payment', ['is_payment_success' => 'false']);
            }
            return response()->json(['status' => 'failed'], 400);
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    public function processCompulsoryFees($paymentTransaction, $feesPaidResult, $metadata)
    {
        $installments = json_decode($metadata->installment ?? '[]', true);
        $current_date = date('Y-m-d');

        if (!empty($installments)) {
            // Process installment payments
            foreach ($installments as $installment) {
                // CompulsoryFee::create([
                //     'student_id' => $metadata->student_id,
                //     'payment_transaction_id' => $paymentTransaction->id,
                //     'type' => 'Installment Payment',
                //     'installment_id' => $installment['id'],
                //     'mode' => 'Online',
                //     'cheque_no' => null,
                //     'amount' => $installment['amount'],
                //     'due_charges' => $installment['dueChargesAmount'] ?? 0,
                //     'fees_paid_id' => $feesPaidResult->id,
                //     'status' => "Success",
                //     'date' => $current_date,
                //     'school_id' => $metadata->school_id,
                // ]);


                CompulsoryFee::updateOrCreate(
                    [
                        'payment_transaction_id' => $paymentTransaction->id,
                        'fees_paid_id' => $feesPaidResult->id,
                        'installment_id' => $installment['id'],
                    ],
                    [
                        'student_id' => $metadata->student_id,
                        'type' => 'Installment Payment',
                        'installment_id' => $installment['id'],
                        'mode' => 'Online',
                        'cheque_no' => null,
                        'amount' => $installment['amount'],
                        'due_charges' => $installment['dueChargesAmount'] ?? 0,
                        'fees_paid_id' => $feesPaidResult->id,
                        'status' => 'Success',
                        'date' => $current_date,
                        'school_id' => $metadata->school_id,
                    ]
                );
            }
        } else if ($metadata->advance_amount == 0) {
            // Process full payment
            // CompulsoryFee::create([
            //     'student_id' => $metadata->student_id,
            //     'payment_transaction_id' => $paymentTransaction->id,
            //     'type' => 'Full Payment',
            //     'installment_id' => null,
            //     'mode' => 'Online',
            //     'cheque_no' => null,
            //     'amount' => $paymentTransaction->amount,
            //     'due_charges' => $metadata->dueChargesAmount ?? 0,
            //     'fees_paid_id' => $feesPaidResult->id,
            //     'status' => "Success",
            //     'date' => $current_date,
            //     'school_id' => $metadata->school_id,
            // ]);

            CompulsoryFee::updateOrCreate(
                [
                    'payment_transaction_id' => $paymentTransaction->id,
                    'fees_paid_id' => $feesPaidResult->id,
                ],
                [
                    'student_id' => $metadata->student_id,
                    'type' => 'Full Payment',
                    'installment_id' => null,
                    'mode' => 'Online',
                    'cheque_no' => null,
                    'amount' => $paymentTransaction->amount - ($metadata->dueChargesAmount ?? 0),
                    'due_charges' => $metadata->dueChargesAmount ?? 0,
                    'status' => 'Success',
                    'date' => $current_date,
                    'school_id' => $metadata->school_id,
                ]
            );
        }

        // Handle advance payment if any
        if ($metadata->advance_amount > 0) {

            $this->advanceInstalmentAmount($metadata->fees_id, $metadata->student_id, $metadata->school_id, $feesPaidResult->id, $metadata->advance_amount, $paymentTransaction->id);


            // $updateCompulsoryFees = CompulsoryFee::where('student_id', $metadata->student_id)
            //     ->with('fees_paid')
            //     ->whereHas('fees_paid', function ($q) use ($metadata) {
            //         $q->where('fees_id', $metadata->fees_id);
            //     })
            //     ->orderBy('id', 'DESC')
            //     ->first();

            // if ($updateCompulsoryFees) {
            //     $updateCompulsoryFees->amount += $metadata->advance_amount;
            //     $updateCompulsoryFees->save();

            //     FeesAdvance::create([
            //         'compulsory_fee_id' => $updateCompulsoryFees->id,
            //         'student_id' => $metadata->student_id,
            //         'parent_id' => $metadata->parent_id,
            //         'amount' => $metadata->advance_amount
            //     ]);
            // }
        }


        $fees = Fee::where('id', $metadata->fees_id)
            ->with(['fees_class_type', 'fees_class_type.fees_type'])
            ->firstOrFail();

        $feesPaidDB = FeesPaid::where([
            'fees_id' => $metadata->fees_id,
            'student_id' => $metadata->student_id,
            'school_id' => $metadata->school_id
        ])
            ->with(['compulsory_fee' => function ($query) {
                $query->whereNull('deleted_at');
            }])
            ->first();

        $totalCompulsoryFees = 0;
        if ($feesPaidDB && !empty($feesPaidDB->compulsory_fee)) {
            $totalCompulsoryFees = $feesPaidDB->compulsory_fee->sum('amount');
        }

        FeesPaid::where([
            'id' => $feesPaidDB->id,
        ])->update([
            'is_fully_paid' => $totalCompulsoryFees >= $fees->total_compulsory_fees,
        ]);
    }

    public function processOptionalFees($paymentTransaction, $feesPaidResult, $metadata)
    {
        $optional_fees = json_decode($metadata->optional_fees_id ?? '[]', true);
        $current_date = date('Y-m-d');

        foreach ($optional_fees as $optional_fee) {
            OptionalFee::create([
                'student_id' => $metadata->student_id,
                'class_id' => $metadata->class_id,
                'payment_transaction_id' => $paymentTransaction->id,
                'fees_class_id' => $optional_fee['id'],
                'mode' => 'Online',
                'cheque_no' => null,
                'amount' => $optional_fee['amount'],
                'fees_paid_id' => $feesPaidResult->id,
                'date' => $current_date,
                'school_id' => $metadata->school_id,
                'status' => "Success",
            ]);
        }
    }
}