• File: ThirdPartController.php
  • Full Path: /var/www/imaliapi/app/Http/Controllers/ThirdPartController.php
  • Date Modified: 01/26/2026 6:40 PM
  • File size: 319.51 KB
  • MIME-type: text/x-php
  • Charset: utf-8
<?php

namespace App\Http\Controllers;

use  App\Events\PaymentChat;
use  App\Events\PaymentQrcode;
use  App\Events\PaymentPush;
use Illuminate\Support\Facades\Event;
use App\Bank\MasterAccount;
use App\Bank\Payment;
use App\Classes\Auth;
use App\Classes\ImageUploader;
use App\Classes\Kyc;
use App\Classes\PushNotification;
use App\Classes\QRCodeService;
use App\Classes\Record;
use App\Classes\SendResponse;
use App\Classes\StoreKyc;
use App\Classes\TransactionGeneration;
use App\Classes\UserKyc;
use App\Fee;
use App\Imali\BusinessAccount;
use App\Imali\ImaliAccount;
use App\Imali\RechargeImaliAccount;
use App\Imali\Transfer;
use App\ImaliSubAccount;
use App\ImaliWayRequest;
use App\Jobs\SendPushPaymentJob;
use App\Link;
use App\MkeshAskingDeposit;
use App\MobileTariff;
use App\MobileWallet;
use App\Operator;
use App\PaymentMethod;
use App\PaymentRequest;
use App\PaymentType;
use App\PushPaymentRequest;
use App\Qrcode;
use App\Rules\LinkExists;
use App\Store;
use App\User;
use App\Wallet;
use App\WalletFee;
use App\WithdrawalsRequest;
use DateTime;
use Error;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;
use phpDocumentor\Reflection\Types\Null_;
use Spatie\ArrayToXml\ArrayToXml;
use Illuminate\Support\Str;
use SimpleSoftwareIO\QrCode\Facades\QrCode as QrCodeGenenator;

class ThirdPartController extends Controller
{

    private $emola_url = NULL;
    private $mpesa_url = NULL;
    private $mkesh_url = NULL;
    // todo 31/07/2024
    private $timeout = 60;
    private $count_timeout = 0;


    // protected QRCodeService $qrCodeService;

    public function __construtor()
    {

        $this->emola_url = 'hddtddjukmfy,kfy,kgkhk,jk';
        $this->mpesa_url = env('MPESA_C2B_URL');
        $this->mkesh_url = env('MKESK_C2B_URL');
        // $this->qrCodeService = new QRCodeService;
        // dd($this->emola_url);
    }



    private function formatMPesaError($response)
    {
        Log::info('[Error Response]', [
            'content' => $response->json(),
        ]);

        $respObject = $response->json();
        return array('message' => $respObject['imaliMessage']);
    }

    // todo --- NOVOS METODOS CARREGAMENTO
    private function logWalletErrorAPI($response, $wallet_name)
    {

        Log::info('[ERRROR ' . strtoupper($wallet_name) . ' RESPONSE]', [
            //  'content' => $response->message(),
            'error' => $response->json(),
        ]);

        // return SendResponse::warningResp500serverError('Falha no pagamento do push, motivo - ' . $th->getMessage(), 'Payment push failed, reason - ' . $th->getMessage());

        if ($response->json(['partnerCode']) == 'INS-2006') {
            return SendResponse::errorResp400('O saldo da tua conta ' . strtoupper($wallet_name) . ' e insuficiente para efectuar a transacao', 'Your' . strtoupper($wallet_name) . ' account balance is insufficient to complete the transaction.');
        }

        if ($response->json(['partnerCode']) == '03') {
            return SendResponse::errorResp400('O saldo da tua conta ' . strtoupper($wallet_name) . ' e insuficiente para efectuar a transacao', 'Your' . strtoupper($wallet_name) . ' account balance is insufficient to complete the transaction.');
        }

        if ($response->json(['partnerCode']) == 'AUTHORIZATION_CURRENT_BALANCE_TOO_LOW') {
            return SendResponse::errorResp400('O saldo da tua conta ' . strtoupper($wallet_name) . ' e insuficiente para efectuar a transacao', 'Your' . strtoupper($wallet_name) . ' account balance is insufficient to complete the transaction.');
        }

        return SendResponse::errorResp400();
    }

    // todo --- NOVOS METODOS 10/12/2024
    public function check_pending_withdrawalls(Request $request, $wallet_name)
    {
        $this->validate(
            $request,
            [
                'transaction_id' => 'required',
            ],
            [
                'transaction_id.required' => 'Campo transaction_id é obrigatório',
            ]
        );

        $wallet = Operator::query()->where('acronym', $wallet_name)->first();
        if (!$wallet) return SendResponse::errorResp404notfound();

        return $this->call_b2c_c2b_status($request, $wallet, $request->transaction_id);
    }

    // todo --- NOVOS METODOS CARREGAMENTO - 10/12/2024
    public function call_b2c_c2b_status(Request $request, $wallet, $transactionId)
    {
        $url = $this->get_wallet_status_url($wallet->acronym);

        if (!$url) throw new Exception("variável de ambiente " . $this->get_wallet_status_env_param($wallet->acronym) . " não declarado no ficheiro .env");

        $data = $this->get_wallet_status_request_data($request, $transactionId, $wallet->acronym);

        //mkeshTransacReference

        if ($wallet->acronym === 'mpesa') return Http::timeout(2000)->get($url . '/?mpesaTransacReference=' . $transactionId);

        // $response = Http::get('http://localhost:3003/mpesa/customer-masked-name?phone=' . $request->phone);

        return Http::timeout(2000)->post($url, ["mkeshTransacReference" => "PTK_B2A40TN9QRXJ"]);
    }

    // todo --- NOVOS METODOS CARREGAMENTO - 17/07/2024
    private function get_wallet_status_url($wallet_name)
    {
        switch ($wallet_name) {
            case 'mpesa':
                return $_ENV['MPESA_TRANSACTION_STATUS_URL'];
                break;
            case 'emola':
                return $_ENV['EMOLA_TRANSACTION_STATUS_URL'];
                break;
            case 'mkesh':
                return $_ENV['MKESH_TRANSACTION_STATUS_URL'];
                break;

            default:
                throw new Exception("variáveis de ambiente (MPESA_TRANSACTION_STATUS_URL, EMOLA_TRANSACTION_STATUS_URL, MKESH_TRANSACTION_STATUS_URL) não definidos no ficheiro .env");
                break;
        }
    }

    // todo --- NOVOS METODOS CARREGAMENTO - 10/12/2024
    private function get_wallet_status_request_data($request, $transactionId, $wallet_name)
    {
        $data = [];

        switch ($wallet_name) {
            case 'mpesa':
                $data['mpesaTransacReference'] = $transactionId;
                return $data;
                break;
            case 'emola':
                $data['emolaTransacReference'] = $transactionId;
                $data['emolaTransacType'] = $request->emolaTransacType;
                return $data;
                break;
            case 'mkesh':
                $data['mkeshTransacReference'] = $transactionId;
                return $data;
                break;

            default:
                return null;
                break;
        }
    }

    //POST
    public function c2bPayment(Request $request)
    {

        $this->validate(
            $request,
            [
                'phone' => 'required|numeric|digits:9',
                'amount' => 'required',
                'imaliReference' => 'required'
            ],
            [
                'phone.required' => 'Campo Phone é obrigatório',
                'phone.numeric' => 'Campo Phone é númerico',
                'phone.digits' => 'Campo Phone deve ter 9 digitos',
                'amount.required' => 'Campo amount é obrigatório',
                // 'amount.min' => 'O valor minimo deve ser 100MT',
                'imaliReference.required' => 'Campo imaliReference é obrigatório'
            ]
        );

        if ($request->has('is_store') && $request->is_store) {
            // Buscar a loja onde se fara o pagamento
            $imali_account = Store::getStoreAccount($request->imaliReference);
            if (!$imali_account)

                return SendResponse::errorResp404notfound(
                    'Conta de pagamento invalido',
                    'Invalid Account',
                );

            $imali_account_2 = Store::getStoreAccount($request->imaliReference);
        } else {
            // Buscar dados do User que faz a transacao
            $userPayer = User::getUserDetails(auth()->user()->id);

            // Buscar dados da conta do User que faz a transacao
            $imali_account = User::getAccountByUser($userPayer->id, $userPayer->account_id);
            if (!$imali_account)

                return SendResponse::errorResp404notfound(
                    'Conta de depósito inválido',
                    'Invalid Account Number',
                );

            $imali_account_2 = User::getAccountByUser($userPayer->id, $userPayer->account_id);
        }

        try {

            $user = User::getUserDetails(auth('api')->user()->id);

            $userKyc = new UserKyc($user);

            $usrKycResp = $userKyc->checkUserKYC($request->amount, 404);
            if ($usrKycResp->getStatusCode() != 200) return $usrKycResp;

            $response = Http::post('http://localhost:3002/mpesa/c2b-payment', ['phone' => '258' . $request->phone, 'amount' => $request->amount, 'customerAccount' => $request->imaliReference]);
            // $response = Http::post('http://localhost:3002/mpesa/c2b-payment', ['phone' => '258' . $request->phone, 'amount' => $request->amount, 'customerAccount' => $request->imaliReference]);

            // $response->object()->
            // if (($response->status() != 200) && ($response->status() != 201)) return response()->json($response->object(), $response->status());
            // if (($response->status() != 200) && ($response->status() != 201)) return response()->json($this->formatMPesaError($response), $response->status() == 422 ? 400 : $response->status());
            // if (($response->status() != 200) && ($response->status() != 201)) return response()->json($this->formatMPesaError($response), 404);
            if (($response->status() != 200) && ($response->status() != 201)) return response()->json($this->formatMPesaError($response), $response->status());

            if (count($response->json()) > 0 && $response->json()['mpesaCode'] == 'INS-0') {

                // $imali_account = ImaliAccount::query()->where('user_id', auth('api')->user()->id)->first();
                // $imali_account_2 = ImaliAccount::query()->where('user_id', auth('api')->user()->id)->first();





                // $imali_account = ImaliAccount::query()->where('user_id', auth('api')->user()->id)->first();
                // $imali_account_2 = ImaliAccount::query()->where('user_id', auth('api')->user()->id)->first();


                $imali_account->balance += $request->amount;
                $imali_account->update();

                $transactionString = new TransactionGeneration();
                $masterAccount = MasterAccount::find(2);

                $recharge = RechargeImaliAccount::create([
                    'imali_account_id' => $imali_account->id,
                    'transaction_id' => $transactionString->generateTransaction(),
                    'bank_reference' => $response->json()['mpesaTransactionId'],
                    'bank_date' => $request->datatransaccao,
                    'account_reference' => $request->referenciaDoc,
                    // adicionado🔰🔽
                    'phone' => $request->phone,
                    'description' => 'Carregamento realtime via MPesa',
                    'amount' => $request->amount,
                    'last_balance' => $imali_account_2->balance,
                    'balance' => $imali_account->balance,
                    'recharge_way' => 'MPesa',
                    'estado' => 'sucesso',
                    'estado_color' => '#388E3C',
                    'master_account_id' => $masterAccount->id
                ]);

                $usr = auth('api')->user();


                $data = array(
                    'transaction' => $recharge->transaction_id,
                    'name' => $usr->name,
                    'description' => $recharge->description,
                    'amount' => (float)$recharge->amount,
                    // 'phone' => $usr->phone,
                    'phone' => $recharge->phone,
                    'reference' => $imali_account->reference,
                    'data' => date($recharge->created_at),
                    'estado' => $recharge->estado,
                    'route' => 'RECHARGE_DETAILS',
                    'recharge_way' => $recharge->recharge_way,
                    'account_number' => $imali_account->account_number,
                    'terminal' => 'firebase'
                );

                $p = new PushNotification(
                    'Carregamento ' . $recharge->amount . ' MT',
                    'Parabéns, ' . 'carregaste ' . $recharge->amount . ' MT ' . ' na tua conta ' . $imali_account->account_number,
                    $usr->firebase_token,
                    'com.imali.payapp.payment_RECHARGE_DETAILS'
                );

                $p->sendPush($data);
            }

            return response()->json(['message' => 'Carregamento enviado com sucesso']);
        } catch (\Throwable $th) {

            Log::info('Outgoing Response', [
                'content' => $request->url(),
                'params' => $request->all(),
            ]);

            return response()->json(['message' => 'Erro ao tentar efectuar o pagamento', 'exception' => $th->getMessage()], 500);
        }
    }

    //POST
    public function b2cPayment(Request $request)
    {

        $req = Request::create('/api/b2c/mpesa/withdraw', 'POST', $request->all());
        return Route::dispatch($req);

        $this->validate(
            $request,
            [
                'phone' => 'required|numeric|digits:9',
                'amount' => 'required',
                'mobile_wallets_id' => 'required',
                'imaliReference' => 'required'
            ],
            [
                'phone.required' => 'Campo Phone é obrigatório',
                'phone.numeric' => 'Campo Phone é númerico',
                'phone.digits' => 'Campo Phone deve ter 9 digitos',
                'amount.required' => 'Campo amount é obrigatório',
                'mobile_wallets_id.required' => 'Campo mobile_wallets_id é obrigatório',
                'imaliReference.required' => 'Campo imaliReference é obrigatório'
            ]
        );

        try {

            // $userKyc = new UserKyc(auth('api')->user()->id);
            // $usrKycResp = $userKyc->checkUserKYC($request->amount);
            // if ($usrKycResp->getStatusCode() != 200) return $usrKycResp;

            $b2c_data = $this->checkB2CTransaction(new Request([
                'phone' => $request->phone,
                'amount' => $request->amount,
                'mobile_wallets_id' => $request->mobile_wallets_id,
            ]), 'mpesa');

            // return $response;
            $usr = auth('api')->user();


            // se valida pin
            // if(has request->pin) valida pin .. pin vazio fingerprint
            // $usr = User::query()->join('imali_accounts', 'imali_accounts.user_id', 'users.id')->where('imali_accounts.account_number', $request->account_number)->first();

            //? teste
            $kyc = new Kyc();
            $request->request->add(['id' => auth()->user()->id, 'receiver_id' => $usr->id]);
            $resultKYC = $kyc->checkSender($request);
            //? teste

            // if (Hash::check($request->pin, auth()->user()->pin)) {

            $result = $resultKYC;

            if ($result) {

                $log = new Record();
                $log->createLog([
                    'description' => 'Transfer Money',
                    'details' => $result,
                    'operation' => 'Transfer Money',
                    'status' => 'Error',
                    'origin_ip' => $request->ip(),
                    'properties' => json_encode($request->except(['pin'])),
                    //                        'properties' => json_encode($request->all()),
                    'origin_request' => $request->url(),
                    'user_id' => auth()->user()->id
                ]);

                return $result;
            }

            $trasactionGeneration = new TransactionGeneration();
            $transaction_id = $trasactionGeneration->generateTransaction();

            if (($b2c_data->getStatusCode() != 200)) return $b2c_data;
            $response = Http::post('http://localhost:3002/mpesa/b2c-payment', ['phone' => $request->phone, 'amount' => $request->amount, 'customerAccount' => $request->imaliReference]);
            // $response = Http::post('http://localhost:3002/mpesa/b2c-payment', ['phone' => '258' . $request->phone, 'amount' => $request->amount, 'customerAccount' => $request->imaliReference]);

            if (($response->status() != 200) && ($response->status() != 201)) return response()->json($this->formatMPesaError($response), $response->status());
            // if (($response->status() != 200) && ($response->status() != 201)) return response()->json($this->formatMPesaError($response), 404);
            // if (($response->status() != 200) && ($response->status() != 201)) return response()->json($response->object(), $response->status());
            // if ($response->status() != 201) return response()->json($response->object(), $response->status());

            if (count($response->json()) > 0 && $response->json()['mpesaCode'] == 'INS-0') {

                $imali_account = ImaliAccount::query()->where('user_id', auth('api')->user()->id)->first();
                $new_imali_account = ImaliAccount::query()->where('user_id', auth('api')->user()->id)->first();

                //actualizacao do saldo principal
                $imali_account->balance = $imali_account->balance - $b2c_data->getData()->total;
                $imali_account->update();


                $withdrawalls = WithdrawalsRequest::create([
                    'imali_account' => $imali_account->account_number,
                    'account_type' => 'client',
                    'amount' => $request->amount,
                    'imali_fee' => $b2c_data->getData()->imali_fee,
                    'bank_fee' => $b2c_data->getData()->imali_cost,
                    'description' => 'TRF. MPesa',
                    'account_number' => $request->phone,
                    'wallets_id' => 2,
                    'operators_id' => $request->mobile_wallets_id,
                    'status' => 'success',
                    'old_balance' => $new_imali_account->balance,
                    'new_balance' => $imali_account->balance,
                    'total' => $b2c_data->getData()->total,
                    'transaction_id' => $transaction_id,
                    'commission' => $b2c_data->getData()->commission,
                    'stamp_tax' => $b2c_data->getData()->stamp_tax,
                    'user_id' => $imali_account->user_id,
                    'sender_name' => $usr->name,
                    'reciever_name' => $b2c_data->getData()->masked_name,
                    'imali_account_id' => $imali_account->id
                ]);


                // return $response;
                // $usr = auth('api')->user();


                $data = array(
                    'transaction' => $withdrawalls->transaction_id,
                    'name' => $usr->name,
                    'description' => $withdrawalls->description,
                    'amount' => (float)$withdrawalls->total,
                    'phone' => $usr->phone,
                    'reference' => $imali_account->reference,
                    'data' => date($withdrawalls->created_at),
                    'estado' => $withdrawalls->status,
                    'route' => 'RECHARGE_DETAILS',
                    'recharge_way' => $withdrawalls->description,
                    'account_number' => $imali_account->account_number,

                    'total' => $b2c_data->getData()->total,
                    'commission' => $b2c_data->getData()->commission,
                    'stamp_tax' => $b2c_data->getData()->stamp_tax,

                    'sender_name' => $usr->name,
                    'reciever_name' => $b2c_data->getData()->masked_name,

                    'terminal' => 'firebase'
                );

                $p = new PushNotification(
                    // 'Transferência de ' . $withdrawalls->amount . ' MT para MPesa',
                    'Transferência para MPesa',
                    'Transferência de ' . $withdrawalls->amount . ' MT ' . 'da conta ' . $imali_account->account_number . ' para o MPesa: ' . $request->phone,
                    $usr->firebase_token,
                    'com.imali.payapp.payment_RECHARGE_DETAILS'
                );

                $p->sendPush($data);
                // return $response;
            }

            return response()->json(['message' => 'A tua transferência para MPesa foi efectuada com sucesso!'], 200);
            // } else {
            //     return response()->json(['message' => 'Pin Incorrecto'], 400);
            // }
        } catch (\Throwable $th) {

            return response()->json(['message' => 'Erro ao tentar efectuar o pagamento', 'exception' => $th->getMessage()], 500);
        }
    }

    //GET
    // todo 17/07/2024 UPDATED
    public function maskedName(Request $request)
    {

        $this->validate(
            $request,
            [
                'phone' => 'required|numeric|digits:9'
            ],
            [
                'phone.required' => 'Campo telefone é obrigatório',
                'phone.numeric' => 'Campo telefone é númerico',
                'phone.digits' => 'Campo telefone deve ter 9 digitos'
            ]
        );

        try {
            $response = Http::get('http://localhost:3000/mpesa/customer-masked-name?phone=' . $request->phone);
            // $response = Http::get('http://localhost:3002/mpesa/customer-masked-name?phone=' . $request->phone);

            if (($response->status() != 200) && ($response->status() != 201)) return response()->json($this->formatMPesaError($response), $response->status());
            // if (($response->status() != 200) && ($response->status() != 201)) return response()->json($this->formatMPesaError($response), 404);
            // if (($response->status() != 200) && ($response->status() != 201)) return response()->json($response->object(), $response->status());

            if ($response->json()['mpesaCustomerMaskedName']) {

                return response()->json(['name' => $response->json()['mpesaCustomerMaskedName']], $response->status());
            }
            return response()->json(['name' => 'Indisponivel'], $response->status());
        } catch (\Throwable $th) {

            return response()->json(['message' => 'Erro ao tentar efectuar o pagamento', 'exception' => $th->getMessage()], 500);
        }
    }


    // todo 22/10/2025 UPDATED
    public function checkB2CTransaction(Request $request, $wallet_name = null, $can_get_masked_name = true)
    {
        try {
            $wallet = Operator::query()->where('acronym', $wallet_name)->first();
            if (!$wallet) throw new Exception('Carteira informada não existe....');
        } catch (\Throwable $th) {

            return SendResponse::errorResp404notfound('Carteira informada não existe.....', $th->getMessage());
        }

        $this->validate(
            $request,
            [
                'phone' => 'required|numeric|digits:9',
                'amount' => 'required|numeric',
                // 'amount' => 'required|numeric|min:100',
            ],
            [
                'amount.required' => 'Campo amount é obrigatório',
                'amount.numeric' => 'Campo amount é númerico',
                // 'amount.min'      => 'O valor mínimo permitido para a transação é 100',
                'phone.required' => 'Campo Phone é obrigatório',
                'phone.numeric' => 'Campo Phone é númerico',
                'phone.digits' => 'Campo Phone deve ter 9 digitos',
            ]
        );

        $regex = "/^(82|83|84|85|86|87)+[0-9]{7,7}$/";
        if (!preg_match($regex, $request->phone)) return response()->json(['message' => 'Número de telefone inválido'], 400);

        try {

            if ($can_get_masked_name)
                $maskedNameResponse = $this->getWalletCustomerName(new Request(['phone' => $request->phone]), $wallet->acronym);
            else
                $maskedNameResponse = response()->json([], 400);

            //? Taxa das carteiras moveis -- Integracao directa
            $mobileTarif = MobileTariff::query()
                ->where('mobile_wallets_id', $wallet->id)
                ->where('min', '<=', $request->amount)
                ->where('max', '>=', $request->amount)
                ->first();

            // if (!$mobileTarif) return SendResponse::errorResp404notfound('Tarifa da carteira ' . $wallet->acronym . 'não definida', $wallet->acronym . ' mobile tariff not defined');
            if (!$mobileTarif) return SendResponse::errorResp400('Tarifa da carteira ' . strtoupper($wallet->acronym) . ' não definida', $wallet->acronym . ' mobile tariff not defined');

            //? Taxa das carteiras moveis -- Via Moza Banco
            $mozaFee = WalletFee::query()
                ->where('wallets_id', 2)
                ->where('min_amount', '<=', $request->amount)
                ->where('max_amount', '>=', $request->amount)
                ->first();

            if (!$mozaFee) return SendResponse::errorResp404notfound('Tarifa da carteira ' . strtoupper($wallet->acronym) . ' no Moza Banco não definida', $wallet->acronym . ' mobile tariff at Moza Banco not defined');

            //? Custo da Transacao para o iMali/Paytek
            $user = User::getUserAccount();

            if ($mobileTarif->imali_cost <= $mozaFee->bank_fee) {
                return $this->get_wallets_fees_new25($request, $mobileTarif, $maskedNameResponse, $user);
            } elseif ($mozaFee->bank_fee <= $mobileTarif->imali_cost) {
                return $this->get_moza_fees_new25($request, $mozaFee, $maskedNameResponse, $user);
            } else {
                return SendResponse::errorResp400('Tarifa nao definida', 'Tariff not defined');
            }
        } catch (\Throwable $th) {
            return SendResponse::warningResp500serverError('Ocorreu um erro no servidor', $th->getMessage());
        }
    }

    // todo 2/10/2025 UPDATED
    private function get_wallets_fees_new25($request, $mobileTarif, $maskedNameResponse, $user)
    {
        $total = $request->amount + $mobileTarif->imali_fee;

        if ($user->profile == 'CLIENT') {
            $userKyc = new UserKyc($user);

            $usrKycResp = $userKyc->checkUserKYC($request->amount, 404);
            if ($usrKycResp->getStatusCode() != 200) return $usrKycResp;

            $usrKycRespBalance = $userKyc->checkUserBalance($total);
            if ($usrKycRespBalance->getStatusCode() != 200) return $usrKycRespBalance;
        } else {

            $profiles = [
                'BUSINESS' => 'EMPRESA',
                'CLIENT'   => 'CLIENTE',
            ];

            $profileKey  = strtoupper($user->profile);
            $profileName = $profiles[$profileKey] ?? $profileKey;

            if ($user->balance < $total) return response()->json(['message' => "Saldo da conta {$profileName} é insuficiente."], 400);
            // if ($user->balance < $total) return response()->json(['message' => 'Saldo da conta ' . strtoupper($user->profile) . ' é insuficiente'], 400);
            // if ($user->balance < $total) return response()->json(['message' => 'Saldo da conta business é insuficiente'], 400);
        }

        if ($maskedNameResponse->getStatusCode() != 200) {
            $data = [
                'phone' => $request->phone,
                'amount' => $request->amount,
                'total' => $total,
                'imali_fee' => $mobileTarif->imali_fee,
                'masked_name' => 'Indisponível',
                'imali_cost' => $mobileTarif->imali_cost,
                'commission' => $mobileTarif->commission,
                'stamp_tax' => $mobileTarif->stamp_tax,
            ];

            return response()->json($data, 200);
        }

        $maskedName = $maskedNameResponse->getData()->customerName;

        $data = [
            'phone' => $request->phone,
            'amount' => $request->amount,
            'total' => $total,
            'imali_fee' => $mobileTarif->imali_fee,
            'masked_name' => $maskedName,
            'imali_cost' => $mobileTarif->imali_cost,
            'commission' => $mobileTarif->commission,
            'stamp_tax' => $mobileTarif->stamp_tax,
        ];

        return response()->json($data, 200);
    }
    // todo 22/10/2025 UPDATED
    private function get_moza_fees_new25($request, $mozaFee, $maskedNameResponse, $user)
    {
        $total = $request->amount + $mozaFee->imali_fee;

        if ($user->profile == 'CLIENT') {
            $userKyc = new UserKyc($user);

            $usrKycResp = $userKyc->checkUserKYC($request->amount, 404);
            if ($usrKycResp->getStatusCode() != 200) return $usrKycResp;

            $usrKycRespBalance = $userKyc->checkUserBalance($total);
            if ($usrKycRespBalance->getStatusCode() != 200) return $usrKycRespBalance;
        } else {
            
            $profiles = [
                'BUSINESS' => 'EMPRESA',
                'CLIENT'   => 'CLIENTE',
            ];
            
            $profileKey  = strtoupper($user->profile);
            $profileName = $profiles[$profileKey] ?? $profileKey;
            
            if ($user->balance < $total) return response()->json(['message' => "Saldo da conta {$profileName} é insuficiente."], 400);
            // if ($user->balance < $total) return response()->json(['message' => 'Saldo da conta ' . strtoupper($user->profile) . ' é insuficiente'], 400);
            // if ($user->balance < $total) return response()->json(['message' => 'Saldo da conta business é insuficiente'], 400);
        }

        if ($maskedNameResponse->getStatusCode() != 200) {
            $data = [
                'phone' => $request->phone,
                'amount' => $request->amount,
                'total' => $total,
                'imali_fee' => $mozaFee->imali_fee,
                'masked_name' => 'Indisponível',
                'imali_cost' => $mozaFee->bank_fee,
                'commission' => $mozaFee->commission,
                'stamp_tax' => $mozaFee->stamp_tax,
            ];

            return response()->json($data, 200);
        }

        $maskedName = $maskedNameResponse->getData()->customerName;

        $data = [
            'phone' => $request->phone,
            'amount' => $request->amount,
            'total' => $total,
            'imali_fee' => $mozaFee->imali_fee,
            'masked_name' => $maskedName,
            'imali_cost' => $mozaFee->bank_fee,
            'commission' => $mozaFee->commission,
            'stamp_tax' => $mozaFee->stamp_tax,
        ];

        return response()->json($data, 200);
    }

    // todo 22/10/2025 UPDATED
    public function checkWithdrawallTransactionNew25ORIGINAL(Request $request, $transfer_to, $can_get_masked_name = true)
    {
        $this->validate(
            $request,
            [
                'amount' => 'required|numeric',
            ],
            [
                'amount.required' => 'Campo amount é obrigatório',
                'amount.numeric' => 'Campo amount é númerico',
            ]
        );

        if (strtoupper($transfer_to) == 'BANK') {
            $wallets = Wallet::query()->where('id', 1)->first();
            $request->request->add(['wallets_id' => 1]);
            return $this->check_bank_transaction_new25($request);
        } elseif (strtoupper($transfer_to) == 'WALLET') {
            $wallets = Wallet::query()->where('id', 2)->first();
            $request->request->add(['wallets_id' => 2]);
            return $this->check_wallet_transaction_new25($request, $can_get_masked_name);
        } else
            return response()->json(['message' => 'Opção de transferência inválida'], 400);

        if (!$wallets) return response()->json(['message' => 'Opção de transferência inválida'], 400);
    }

    public function checkWithdrawallTransactionNew25(Request $request, $can_get_masked_name = true)
    {
        $this->validate(
            $request,
            [
                'amount' => 'required|numeric',
                'transfer_to' => 'required|in:WALLET,BANK',
            ],
            [
                'transfer_to.required' => 'Campo transfer_to é obrigatório',
                'transfer_to.in' => 'Os valores permitidos são somente WALLET e BANK',
                'amount.required' => 'Campo amount é obrigatório',
                'amount.numeric' => 'Campo amount é númerico',
            ]
        );

        if (strtoupper($request->transfer_to) == 'BANK') {
            $wallets = Wallet::query()->where('id', 1)->first();
            $request->request->add(['wallets_id' => 1]);
            return $this->check_bank_transaction_new25($request);
        } elseif (strtoupper($request->transfer_to) == 'WALLET') {
            $wallets = Wallet::query()->where('id', 2)->first();
            $request->request->add(['wallets_id' => 2]);
            return $this->check_wallet_transaction_new25($request, $can_get_masked_name);
        } else
            return response()->json(['message' => 'Opção de transferência inválida'], 400);

        if (!$wallets) return response()->json(['message' => 'Opção de transferência inválida'], 400);
    }

    // todo 22/10/2025 UPDATED
    private function check_bank_transaction_new25($request)
    {
        $this->validate(
            $request,
            [
                'account_number' => 'required|numeric|digits:21',
            ],
            [
                'account_number.required' => 'Campo NIB é obrigatório',
                'account_number.numeric' => 'Campo NIB é númerico',
                'account_number.digits' => 'Campo NIB deve ter 21 digitos',
            ]
        );

        $ibanToValidate = "MZ59" . $request->account_number;
        if (!$this->validateIBAN($ibanToValidate)) return response()->json(['message' => 'Número de NIB inválido'], 400);

        $operator = Operator::query()->where('code', 'LIKE', '%' . substr($request->account_number, 0, 4) . '%')->first();
        if (!$operator) return response()->json(['message' => 'Número de NIB inválido'], 400);

        // if ($operator->code !== '0034') return response()->json(['message' => 'Serviço de Transferencias interbancária indisponivel. Caso tenha uma conta MOZA tente novamente com o teu NIB MOZA'], 400);

        //? PEGAR TAXAS A PAGAR ::..
        if ($operator->code == '0034') { //intrabancaria
            $walletsFee = WalletFee::query()
                ->where('id', 1)
                ->where('wallets_id', $request->wallets_id)
                ->first();
        } else { //interbancaria
            $walletsFee = WalletFee::query()
                ->where('id', 2)
                ->where('wallets_id', $request->wallets_id)
                ->first();
        }

        if (!$walletsFee) return response()->json(['message' => 'Operação não pode ser processada'], 400);

        // verificar saldo da conta iMali que esta a fazer a transferencia..
        $total = $request->amount + $walletsFee->imali_fee;
        // Limitando o valor para duas casas decimais
        $total = number_format($total, 2, '.', '');
        $total = floatval($total);

        // PEGAR A CONTA QUE SERA DEBITADA --- CONTA AUTENTICADA E VALIDAR O SALDO
        $user = User::getUserAccount();
        if ($user->profile == 'CLIENT') {
            $userKyc = new UserKyc($user);

            $usrKycResp = $userKyc->checkUserKYC($request->amount, 404);
            if ($usrKycResp->getStatusCode() != 200) return $usrKycResp;

            $usrKycRespBalance = $userKyc->checkUserBalance($total);
            if ($usrKycRespBalance->getStatusCode() != 200) return $usrKycRespBalance;
        } else {

            $profiles = [
                'BUSINESS' => 'EMPRESA',
                'CLIENT'   => 'CLIENTE',
            ];
            
            $profileKey  = strtoupper($user->profile);
            $profileName = $profiles[$profileKey] ?? $profileKey;
            
            if ($user->balance < $total) return response()->json(['message' => "Saldo da conta {$profileName} é insuficiente."], 400);
            // if ($user->balance < $total) return response()->json(['message' => 'Saldo da conta ' . strtoupper($user->profile) . ' é insuficiente'], 400);
            // if ($user->balance < $total) return response()->json(['message' => 'Saldo da conta business é insuficiente'], 400);
        }

        $data = [
            'account_number' => $request->account_number,
            'amount' => $request->amount,
            'total' => $total,
            'imali_fee' => $walletsFee->imali_fee,
            'masked_name' => 'Indisponível',
            'imali_cost' => $walletsFee->bank_fee,
            'commission' => $walletsFee->commission,
            'stamp_tax' => $walletsFee->stamp_tax,
        ];

        return response()->json($data, 200);
        // return response()->json(['data' => $data], 200);
    }

    // todo 22/10/2025 UPDATED
    private function check_wallet_transaction_new25($request, $can_get_masked_name)
    {

        $this->validate(
            $request,
            [
                'account_number' => 'required|numeric|digits:9',
            ],
            [
                'account_number.required' => 'Campo telefone é obrigatório',
                'account_number.numeric' => 'Campo telefone é numérico',
                'account_number.digits' => 'Campo telefone deve ter 9 digitos',
            ]
        );

        $request->request->add(['phone' => $request->account_number]);

        $regex = "/^(82|83|84|85|86|87)+[0-9]{7,7}$/";
        if (!preg_match($regex, $request->account_number)) return response()->json(['message' => 'Número de telefone inválido'], 400);

        $operator = Operator::query()->where('code', 'LIKE', '%' . substr($request->account_number, 0, 2) . '%')->first();
        if (!$operator) return response()->json(['message' => 'Número de telefone inválido'], 400);

        return $this->checkB2CTransaction($request, strtolower($operator->acronym), $can_get_masked_name);
    }

    // todo 24/10/2025 UPDATED
    public function b2cWithdraw_new25(Request $request, $wallet_name)
    {
        $wallet = Operator::query()->where('acronym', $wallet_name)->first();
        if (!$wallet) return SendResponse::errorResp404notfound();

        // todo 18/07/2024 UPDATED
        // $wallet = $this->getOperator($request, $wallet_name);

        $this->validate_parameters($request);

        if (!$this->check_wallet_number($request->phone, $wallet_name)) return SendResponse::errorResp400('Numero de telefone invalido', 'Invalid Phone Number');

        $payer = User::getUserAccount();
        $payer_account = User::getAccount($payer->account_number);
        $payer_account_2 = User::getAccount($payer->account_number);
        $userKyc = new UserKyc($payer);

        $usrKycResp = $userKyc->checkUserKYC($request->amount, 404);
        if ($usrKycResp->getStatusCode() != 200) return $usrKycResp;

        $usrKycRespBalance = $userKyc->checkUserBalance($request->amount);

        if ($usrKycRespBalance->getStatusCode() != 200) return $usrKycRespBalance;

        $this->validate(
            $request,
            [
                'mobile_wallets_id' => 'required|numeric'
            ],
            [
                'mobile_wallets_id.required' => 'Campo mobile_wallets_id é obrigatório',
                'mobile_wallets_id.numeric' => 'Campo mobile_wallets_id  deve ser númerico'
            ]
        );


        $b2c_data = $this->checkB2CTransaction(new Request([
            'phone' => $request->phone,
            'amount' => $request->amount,
            'mobile_wallets_id' => $request->mobile_wallets_id,
        ]), $wallet_name);

        if (($b2c_data->getStatusCode() != 200)) return $b2c_data;

        return $this->withdraw_by_mpesa_emola_mkesh($request, $payer, $payer_account, $payer_account_2, $wallet, $b2c_data);
    }

    // todo 24/10/2025 UPDATED
    private function withdraw_by_mpesa_emola_mkesh_new25($request, $payer, $payer_account, $payer_account_2, $wallet, $b2c_data)
    {
        // $response = Http::post('http://localhost:3000/mpesa/b2c-payment', ['phone' => '258' . $request->phone, 'amount' => $request->amount, 'customerAccount' => $request->imaliReference]);

        $transactionId = $this->generate_full_transaction_id($wallet->acronym);
        $response = $this->call_emola_mpesa_mkesh_b2c_api($request, $transactionId, $wallet);

        if (($response->status() != 200) && ($response->status() != 201)) return $this->logWalletErrorAPI($response, $wallet->acronym);

        //codigo para carregar a carteira
        if (!$this->is_wallet_transaction_successfully_done($wallet->acronym, $response)) return $this->logWalletErrorAPI($response, $wallet->acronym);

        //actualizacao do saldo principal
        $payer_account->balance = $payer_account->balance - $b2c_data->getData()->total;
        $payer_account->update();


        $withdrawalls = $this->create_withdraw_new25($request, $payer, $payer_account, $payer_account_2, $wallet, $b2c_data, $transactionId, $response->json()['partnerTransactionId']);

        Log::info('..:: B2C LOG :::..', ['B2C' => $withdrawalls]);

        $data = array(
            'transaction' => $withdrawalls->transaction_id,
            'name' => $payer->name,
            'description' => $withdrawalls->description,
            'amount' => (float)$withdrawalls->total,
            'phone' => $payer->phone,
            'reference' => $payer_account->reference,
            'data' => date($withdrawalls->created_at),
            'estado' => $withdrawalls->status,
            'route' => 'RECHARGE_DETAILS',
            'recharge_way' => $withdrawalls->description,
            'account_number' => $payer_account->account_number,

            'total' => $b2c_data->getData()->total,
            'commission' => $b2c_data->getData()->commission,
            'stamp_tax' => $b2c_data->getData()->stamp_tax,

            'sender_name' => $payer->name,
            'reciever_name' => $b2c_data->getData()->masked_name,

            'terminal' => 'firebase'
        );

        $p = new PushNotification(
            // 'Transferência de ' . $withdrawalls->amount . ' MT para MPesa',
            'Transferência de ' . $request->amount . 'MZN para ' . $wallet->name,
            'Transferência de ' . $withdrawalls->amount . ' MZN ' . 'da conta ' . $payer_account->account_number . ' para o ' . $wallet->name . ': ' . $request->phone,
            $payer->firebase_token,
            'com.imali.payapp.payment_RECHARGE_DETAILS'
        );

        Log::info([
            'content' => $response->json()
        ]);

        $p->sendPush($data);
        // return $response;
        return response()->json(['message' => 'A tua transferência para ' . $wallet->name . ' foi efectuada com sucesso!'], 200);
    }

    // todo 24/10/2025 UPDATED
    private function create_withdraw_new25($request, $payer, $payer_account, $payer_account_2, $wallet, $b2c_data, $transactionId, $partnerTransactionId)
    {
        $withdrall = WithdrawalsRequest::create([
            'imali_account' => $payer->account_number,
            'partner_transaction_id' => $partnerTransactionId,
            'account_type' => $payer->profile,
            'amount' => $request->amount,
            'imali_fee' => $b2c_data->getData()->imali_fee,
            'bank_fee' => $b2c_data->getData()->imali_cost, // nao temos !
            'description' => 'TRF.' . $wallet->name,
            'account_number' => $request->phone,
            'wallets_id' => 2,
            'operators_id' => $request->mobile_wallets_id,
            'status' => 'success',
            'old_balance' => $payer_account_2->balance,
            'new_balance' => $payer_account->balance,
            'total' => $b2c_data->getData()->total,
            'transaction_id' => $transactionId,
            'commission' => $b2c_data->getData()->commission, // nao temos !
            'stamp_tax' => $b2c_data->getData()->stamp_tax, // nao temos !
            'user_id' => $payer->id,
            'sender_name' => $payer->name,
            'reciever_name' => $b2c_data->getData()->masked_name,
            'imali_account_id' => $payer_account->id
        ]);

        return $withdrall;
    }

    private function validateIBAN($iban)
    {
        // Remover espaços e caracteres não numéricos do IBAN
        $iban = preg_replace('/\s+/', '', $iban);

        // Verificar se o IBAN tem o comprimento correto para Moçambique (25 caracteres)
        if (strlen($iban) !== 25) {
            return false;
        }

        // Mover os primeiros 4 caracteres para o final do IBAN
        $iban = substr($iban, 4) . substr($iban, 0, 4);

        // Substituir letras por números (A=10, B=11, ..., Z=35)
        $ibanNumeric = '';
        foreach (str_split($iban) as $char) {
            if (ctype_alpha($char)) {
                $ibanNumeric .= ord(strtoupper($char)) - ord('A') + 10;
            } else {
                $ibanNumeric .= $char;
            }
        }

        // Verificar se o IBAN é divisível por 97
        if (bcmod($ibanNumeric, '97') !== '1') {
            return false;
        }

        return true;
    }

    // todo 24/10/2025 UPDATED ....
    public function createWithDrawalls_new25(Request $request)
    {
        // Se o transfer-to for BANK vai para o MOZA
        // Se o transfer-to for WALLET e a taxa favoravel for do MOZA entao vai para o moza
        // Se o transfer-to for WALLET e a taxa favoravel for do WALLET entao vai para o WALLET de integracoes directas
        // Se o Wallet for de integracoes directas deve chamar APIS especificas e depois registar nos Withdrawalls

        // $this->validate(
        //     $request,
        //     [
        //         'account_number' => 'required|numeric|digits:9',
        //     ],
        //     [
        //         'account_number.required' => 'Campo telefone é obrigatório',
        //         'account_number.numeric' => 'Campo telefone é numérico',
        //         'account_number.digits' => 'Campo telefone deve ter 9 digitos',
        //     ]
        // );

        $this->validate(
            $request,
            [
                'transfer_to' => 'required|in:WALLET,BANK',
            ],
            [
                'transfer_to.required' => 'Campo transfer_to é obrigatório',
                'transfer_to.in' => 'Os valores permitidos são somente WALLET e BANK',
            ]
        );

        $user = User::getUserAccount();
        $imali = User::getAccount($user->account_number);
        $imali2 = User::getAccount($user->account_number);

        if (strtoupper($request->transfer_to) == 'BANK') {
            //verifica se pode efectuar a transacao
            $checkWithDrawall = $this->checkWithdrawallTransactionNew25($request, false);
            if ($checkWithDrawall->getStatusCode() >= 400 && $checkWithDrawall->getStatusCode() < 500)  return $checkWithDrawall->getData();
            // return $checkWithDrawall->getData();

            $operator = Operator::query()->where('code', 'LIKE', '%' . substr($request->account_number, 0, 4) . '%')->first();
            if (!$operator) return response()->json(['message' => 'NIB inválido'], 400);

            DB::beginTransaction();

            //actualizacao do saldo principal e do saldo cativo
            $imali->balance -= $checkWithDrawall->getData()->total;

            $imali->captive_balance += $checkWithDrawall->getData()->total;

            $imali->update();

            $request->request->add(['wallets_id' => 1]);

            if ($operator->code == '0034') { //intrabancaria
                $walletsFee = WalletFee::query()
                    ->where('id', 1)
                    ->where('wallets_id', $request->wallets_id)
                    ->first();
            } else { //interbancaria
                $walletsFee = WalletFee::query()
                    ->where('id', 2)
                    ->where('wallets_id', $request->wallets_id)
                    ->first();
            }

            //regista o pedido de transferencia
            $trasactionGeneration = new TransactionGeneration();
            $transaction_id = $trasactionGeneration->generateTransaction();

            WithdrawalsRequest::create([
                'imali_account' => $imali->account_number,
                'account_type' => $user->profile,
                'amount' => $request->amount,
                'imali_fee' => $walletsFee->imali_fee,
                'bank_fee' => $walletsFee->bank_fee, // nao temos
                'description' => 'TRF. ' . $operator->acronym,
                'account_number' => $request->account_number,
                'wallets_id' => $request->wallets_id, // nao temos
                'operators_id' => $operator->id,
                'status' => 'pending',
                'old_balance' => $imali2->balance,
                'new_balance' => $imali->balance,
                'total' => $checkWithDrawall->getData()->total,
                'transaction_id' => $transaction_id,
                'commission' => $walletsFee->commission, // nao temos
                'stamp_tax' => $walletsFee->stamp_tax, // nao temos
                'user_id' => $imali->user_id,
                'imali_account_id' => $imali->id
            ]);

            //? -----------------INICIO---------------------

            /**
             * gerar um ficheiro excel de transacoes para enviar para o MOZA Banco
             * Utilizando template localizado em storage/app/template.csv
             * Salvando em storage/app/downloads
             * */
            $data = new \App\Exports\WalletExport();

            // return $data->collection();

            $date = date('Y') . date('m') . date('d');
            $hours = date('H') . date('i') . date('s');
            $fileName = "Transac_iMali_" . $date . "-" . $hours  . '.csv';

            $this->generateMozaTransactionFile($data, $fileName);

            //encryptar o ficheiro e enviar no diretorio do moza banco

            $this->encryptGeneratedFileAndSendToMoza($fileName);

            DB::commit();

            return response()->json(['message' => 'Pedido de transferência efectuado com sucesso!'], 200);
            // if ($bool) {
            // }

            //actualizar o status da transacao para pending
            // $withdrawalls->status = 'pending';
            // $withdrawalls->update();

            // return response()->json(['message' => 'Ocorreu um erro ao gerar a transferencia'], 400);


            // $wallets = Wallet::query()->where('id', 1)->first();
            // $request->request->add(['wallets_id' => 1]);
            // return $this->check_bank_transaction_new25($request);
        } elseif (strtoupper($request->transfer_to) == 'WALLET') {
            // $wallets = Wallet::query()->where('id', 2)->first();

            $this->validate(
                $request,
                [
                    'account_number' => 'required|numeric|digits:9',
                    'amount' => 'required|numeric|min:10',
                    // 'imaliReference' => 'required'
                ],
                [
                    'account_number.required' => 'Campo account_number é obrigatório',
                    'account_number.numeric' => 'Campo account_number é númerico',
                    'account_number.digits' => 'Campo account_number deve ter 9 digitos',
                    'amount.required' => 'Campo amount é obrigatório',
                    'amount.min' => 'O valor minimo deve ser 10MT',
                    'amount.numeric' => 'Campo Montente deve ser númerico',
                    // 'imaliReference.required' => 'Campo store_account é obrigatório'
                ]
            );


            $regex = "/^(82|83|84|85|86|87)+[0-9]{7,7}$/";
            if (!preg_match($regex, $request->account_number)) return response()->json(['message' => 'Número de telefone inválido'], 400);

            $operator = Operator::query()->where('code', 'LIKE', '%' . substr($request->account_number, 0, 2) . '%')->first();
            if (!$operator) return response()->json(['message' => 'Número de telefone inválido'], 400);

            $request->request->add(['imaliReference' => $user->reference, 'mobile_wallets_id' => $operator->id]);
            // $userKyc = new UserKyc($user);

            // $usrKycResp = $userKyc->checkUserKYC($request->amount, 404);
            // if ($usrKycResp->getStatusCode() != 200) return $usrKycResp;

            // $usrKycRespBalance = $userKyc->checkUserBalance($request->amount);

            // if ($usrKycRespBalance->getStatusCode() != 200) return $usrKycRespBalance;

            $request->request->add(['wallets_id' => 2]);

            $fee = $this->check_wallet_transaction_new25($request, false);
            if (($fee->getStatusCode() != 200)) return $fee;

            if ($user->profile == 'CLIENT') {
                $userKyc = new UserKyc($user);

                $usrKycResp = $userKyc->checkUserKYC($request->amount, 404);
                if ($usrKycResp->getStatusCode() != 200) return $usrKycResp;

                $usrKycRespBalance = $userKyc->checkUserBalance($fee->getData()->total);
                if ($usrKycRespBalance->getStatusCode() != 200) return $usrKycRespBalance;
            } else {
                if ($user->balance < $fee->getData()->total) return response()->json(['message' => 'Saldo da conta business é insuficiente'], 400);
            }

            return $this->withdraw_by_mpesa_emola_mkesh_new25($request, $user, $imali, $imali2, $operator, $fee);
            // $user
            // $imali
        } else
            return response()->json(['message' => 'Opção de transferência inválida'], 400);
    }

    private function generateMozaTransactionFile($data, $fileName)
    {
        file_put_contents(storage_path('/app/template/' . $fileName), file_get_contents(storage_path('/app/template/template.csv')));

        $file = fopen(storage_path('/app/template/' . $fileName), 'a') or die('unable to open file');

        foreach ($data->collection() as $key => $value) {
            $text = $value['value' . $key] . "\n";
            fwrite($file, $text);
        }

        fclose($file);

        rename(storage_path('/app/template/' . $fileName), storage_path('/app/downloads/' . $fileName));
    }


    private function encryptGeneratedFileAndSendToMoza($fileName)
    {

        $filePath = "/downloads/" . $fileName;
        $uploadPath = storage_path('app' . $filePath);

        $filePath = str_replace('\\', '/', $uploadPath);

        if (!file_exists($filePath)) {
            Log::error(['MOZA ERROR', 'Ficheiro não existe, neste caminho', $filePath]);
            return false;
        }
        
        Log::info([
            'MOZA DEBUG',
            'Readable' => is_readable($filePath),
            'WritableDir' => is_writable(dirname($filePath)),
        ]);

        //OLD Encryption - ENCRIPTAR FICHEIRO
        //?? $commands = explode(",", "gpg --homedir /home/paytek/.gnupg --recipient einstein.bata@mozabanco.co.mz --encrypt " . $filePath . "," . "mv " . $filePath . ".gpg" . " /var/sftp/uploads_mozabanco/imali_transac_files/" . $fileName . ".gpg");
        //FIM Old Encryption

        $encryptedFile = $filePath . '.gpg';
        $destination   = "/var/sftp/uploads_mozabanco/imali_transac_files/$fileName.gpg";

        //"mv $encryptedFile $destination"
        $commands = [
            "gpg --homedir /var/www/.gnupg --batch --yes --trust-model always --recipient einstein.bata@mozabanco.co.mz --encrypt " . $filePath,
            "mv $encryptedFile $destination"
        ];

        try {

            foreach ($commands as $command) {
                exec($command . ' 2>&1', $output, $returnCode);
                Log::info(['MOZA CMD', $command]);
                Log::info(['MOZA OUTPUT', $output]);
                Log::info(['MOZA RETURNCODE', $returnCode]);

                if ($returnCode !== 0) {
                    Log::error('MOZA FALHA NO COMANDO');
                }
            }

            Log::info(['MOZA SUCCESS', 'Comando Executado com Sucesso']);
            return true;
        } catch (\Throwable $th) {
            Log::info(['MOZA ERRO', 'Erro na execucao do ficheiro' . $th->getMessage()]);
            return false;
        }
    }
    // todo 22/10/2025 UPDATED

    // todo 18/08/2025 UPDATED -- iMaliWay
    public function checkB2CFeesTransactionImaliway(Request $request)
    {

        if ($request->transaction_type !== 'B2C') return SendResponse::errorResp400('Tipo de transação invalida', 'Invalid transaction type');

        $this->general_validate_parameters_new25($request);

        $this->validate_push_parameters_new25($request);

        if ($request->payment_method == 'mpesa') {
            $request->request->add(['mobile_wallets_id' => 21]);
        } else if ($request->payment_method == 'emola') {
            $request->request->add(['mobile_wallets_id' => 22]);
        } else if ($request->payment_method == 'mkesh') {
            $request->request->add(['mobile_wallets_id' => 23]);
        } else if ($request->payment_method == 'imali') {
            $request->request->add(['mobile_wallets_id' => 28]);
        } else {
            return SendResponse::errorResp400('Carteira invalida', 'Invalid Wallet');
        }

        try {
            // $wallet = $this->getOperator($request, $wallet_name);
            $wallet = Operator::query()->where('id', $request->mobile_wallets_id)->orWhere('acronym', $request->payment_method)->first();
            if (!$wallet) throw new Exception('Carteira informada não existe....');
            // todo ADICIONADO 31/07/2024
            // $req = Request::create('check/{wallet_name}/b2c-transaction', 'GET', ['wallet_name' => $wallet->acronym]);
            // return Route::dispatch($req);
        } catch (\Throwable $th) {

            return SendResponse::errorResp404notfound('Carteira informada não existe.....', $th->getMessage());
        }

        if ($request->payment_method != 'imali') {

            $regex = "/^(82|83|84|85|86|87)+[0-9]{7,7}$/";
            if (!preg_match($regex, $request->client_account_number)) return response()->json(['message' => 'Número de telefone inválido'], 400);
        }

        // $user = User::getUserDetails(auth('api')->user()->id);

        // $userKyc = new UserKyc($user);

        // // $userKyc = new UserKyc(auth('api')->user()->id);
        // $usrKycResp = $userKyc->checkUserKYC($request->amount);
        // if ($usrKycResp->getStatusCode() != 200) return $usrKycResp;

        try {
            //code...

            if ($request->payment_method == 'imali') {


                $user_account = User::getAccount($request->client_account_number);
                $user = User::getUserDetails($user_account->user_id);
                // $payer_account = User::getAccount($payer->account_number);
                // $payer_account_2 = User::getAccount($payer->account_number);
                $userKyc = new UserKyc($user);

                $usrKycResp = $userKyc->checkUserKYC($request->amount, 404);
                if ($usrKycResp->getStatusCode() != 200) return $usrKycResp;

                $usrKycRespBalance = $userKyc->checkUserBalance($request->amount);
                if ($usrKycRespBalance->getStatusCode() != 200) return $usrKycRespBalance;
            }

            $mobileTarif = MobileTariff::query()
                ->where('mobile_wallets_id', $request->mobile_wallets_id)
                ->where('min', '<=', $request->amount)
                ->where('max', '>=', $request->amount)
                ->first();

            if (!$mobileTarif) return SendResponse::errorResp404notfound('Tarifa da carteira ' . $wallet->acronym . 'não definida', $wallet->acronym . ' mobile tariff not defined');

            $mozaFee = WalletFee::query()
                ->where('wallets_id', 2)
                ->where('min_amount', '<=', $request->amount)
                ->where('max_amount', '>=', $request->amount)
                ->first();

            if (!$mozaFee) return SendResponse::errorResp404notfound('Tarifa da carteira ' . $wallet->acronym . ' no Moza Banco não definida', $wallet->acronym . ' mobile tariff at Moza Banco not defined');

            // $imali_cost = ($request->amount * 0.0102);
            $imali_cost = round(($request->amount * 0.0102), 2);

            if ($imali_cost <= $mozaFee->bank_fee) {
                $total = $request->amount + $mobileTarif->imali_fee;

                // $imali_cost = (($request->amount * 0.01) + 0.02);

                if ($request->payment_method == 'imali') {

                    $kycRespBalance = $userKyc->checkUserBalance($total);
                    if ($kycRespBalance->getStatusCode() == 400) return $kycRespBalance;
                }

                // $maskedNameResponse = $this->maskedName(new Request(['phone' => '258' . $request->client_account_number]));
                if ($request->payment_method != 'imali') {

                    $maskedNameResponse = $this->getWalletCustomerName(new Request(['phone' => $request->client_account_number]), $wallet->acronym);
                } else {
                    $maskedNameResponse = $user->name;
                }

                if ($maskedNameResponse->getStatusCode() != 200) {
                    $data = [
                        'phone' => $request->client_account_number,
                        'amount' => $request->amount,
                        'total' => $total,
                        'imali_fee' => $mobileTarif->imali_fee,
                        'masked_name' => 'Indisponível',
                        'imali_cost' => $imali_cost,
                        // 'imali_cost' => $mobileTarif->imali_cost,
                        'commission' => $mobileTarif->commission,
                        'stamp_tax' => $mobileTarif->stamp_tax,
                    ];

                    return response()->json($data, 200);
                }

                $maskedName = $maskedNameResponse->getData()->customerName;

                $data = [
                    'phone' => $request->client_account_number,
                    'amount' => $request->amount,
                    'total' => $total,
                    'imali_fee' => $mobileTarif->imali_fee,
                    'masked_name' => $maskedName,
                    // 'imali_cost' => $mobileTarif->imali_cost,
                    'imali_cost' => $imali_cost,
                    'commission' => $mobileTarif->commission,
                    'stamp_tax' => $mobileTarif->stamp_tax,
                ];

                return response()->json($data, 200);
            }

            if ($mozaFee->bank_fee <= $imali_cost) {
                $total = $request->amount + $mozaFee->imali_fee;

                $kycRespBalance = $userKyc->checkUserBalance($total);
                if ($kycRespBalance->getStatusCode() == 400) return $kycRespBalance;

                // return $maskedNameResponse = $this->maskedName(new Request(['phone' => $request->client_account_number]));
                $maskedNameResponse = $this->getWalletCustomerName(new Request(['phone' => $request->client_account_number]), $wallet->acronym);

                if ($maskedNameResponse->getStatusCode() != 200) {
                    $data = [
                        'phone' => $request->client_account_number,
                        'amount' => $request->amount,
                        'total' => $total,
                        'imali_fee' => $mozaFee->imali_fee,
                        'masked_name' => 'Indisponível',
                        'imali_cost' => $mozaFee->bank_fee,
                        'commission' => $mozaFee->commission,
                        'stamp_tax' => $mozaFee->stamp_tax,
                    ];

                    return response()->json($data, 200);
                }

                $maskedName = $maskedNameResponse->getData()->customerName;

                $data = [
                    'phone' => $request->client_account_number,
                    'amount' => $request->amount,
                    'total' => $total,
                    'imali_fee' => $mozaFee->imali_fee,
                    'masked_name' => $maskedName,
                    'imali_cost' => $mozaFee->bank_fee,
                    'commission' => $mozaFee->commission,
                    'stamp_tax' => $mozaFee->stamp_tax,
                ];

                return response()->json($data, 200);
            }
        } catch (\Throwable $th) {
            //throw $th;
            return SendResponse::warningResp500serverError('Ocorreu um erro no servidor', $th->getMessage());
        }
    }



    //? mKesh Methods
    //POST
    public function c2bmKeshPayment(Request $request)
    {

        $this->validate(
            $request,
            [
                'phone' => 'required|numeric|digits:9',
                'amount' => 'required',
            ],
            [
                'phone.required' => 'Campo Phone é obrigatório',
                'phone.numeric' => 'Campo Phone é númerico',
                'phone.digits' => 'Campo Phone deve ter 9 digitos',
                'amount.required' => 'Campo amount é obrigatório',
            ]
        );

        // Buscar dados do User que faz a transacao
        $userPayer = User::getUserDetails(auth()->user()->id);

        $userKyc = new UserKyc($userPayer);

        $usrKycResp = $userKyc->checkUserKYC($request->amount, 404);
        if ($usrKycResp->getStatusCode() != 200) return $usrKycResp;

        // Buscar dados da conta do User que faz a transacao
        $imali_account = User::getAccountByUser($userPayer->id, $userPayer->account_id);

        try {

            // $response = Http::post('http://localhost:3002/mkesh/c2b-payment', ['phone' => '258' . $request->phone, 'amount' => $request->amount]);
            $response = Http::post('http://localhost:3000/mkesh/c2b-payment', ['phone' => '258' . $request->phone, 'amount' => $request->amount]);
            // return $response->json();
            if ($response->status() == 200) {

                MkeshAskingDeposit::create([
                    'account_number' => $imali_account->account_number,
                    'transaction_id' => $response->json()['imaliTransactionId'],
                    'partner_transaction_id' => $response->json()['partnerTransactionId'],
                    'phone' => $request->phone,
                    'amount' => $request->amount,
                    'recharge_way' => 'MKesh',
                    'user_id' => auth()->user()->id
                ]);

                return response()->json(['message' => 'O seu pedido de deposito foi enviado com sucesso. Em breve vais receber um pedido de pagamento do MKesh'], 200);
            } else {

                return response()->json($response->json(), 400);
            }
        } catch (\Throwable $th) {

            Log::info('Outgoing Response', [
                'content' => $request->url(),
                'params' => $request->all(),
            ]);

            return response()->json(['message' => 'Erro ao tentar efectuar o pagamento', 'exception' => $th->getMessage(), 'line' => $th->getLine()], 500);
        }
    }



    public function c2bMKeshPaymentResponse2(Request $request)
    {
        Log::info('Incoming Response', [
            'content' => $request->getContent(),
            'url' => $request->url(),
        ]);

        // $response = Http::post('http://localhost:3002/mkesh/c2b-payment-response', ['data' => $request->getContent()]);
        $response = Http::withBody($request->getContent(), 'text/xml')->post('http://localhost:3000/mkesh/c2b-payment-response');

        // return $response->json();

        $mKesh_resquest_deposit = MkeshAskingDeposit::query()
            ->where('partner_transaction_id', $response->json()['partnerTransactionId'])
            ->where('transaction_id', $response->json()['imaliTransactionId'])
            ->where('status', 'PENDING')
            // ->where('phone', substr($response->json()['msisdn'], 3, 12))
            ->first();

        if (!$mKesh_resquest_deposit) return response(ArrayToXml::convert(['message' => 'Not Found.']), 200)->header('Content-Type', 'text/xml');

        $mKesh_resquest_deposit->update([
            // 'status' => $response->json()['status'],
            'status' => $response->json()['partnerCode'],
        ]);

        $userPayer = User::getUserDetails($mKesh_resquest_deposit->user_id);

        // if ($response->json()['status'] != 'SUCCESSFUL') {
        if ($response->json()['partnerCode'] != 'SUCCESSFUL') {

            $p = new PushNotification(
                'Carregamento MKesh',
                'O teu carregamento mKesh expirou',
                $userPayer->firebase_token,
                'com.imali.payapp.payment_RECHARGE_DETAILS'
            );

            $p->sendPush(['sms' => 'O teu carregamento mKesh expirou']);

            return response(ArrayToXml::convert(['message' => 'UNSUCCESSFUL TRANSACTION UPDATED.']), 200)->header('Content-Type', 'text/xml');
        }


        // Buscar dados da conta do User que faz a transacao
        // $imali_account = User::getAccountByUser($userPayer->id, $userPayer->account_id);
        // $imali_account_2 = User::getAccountByUser($userPayer->id, $userPayer->account_id);

        // $imali_account->balance += $mKesh_resquest_deposit->amount;
        // $imali_account->update();

        // $masterAccount = MasterAccount::find(2);

        // $recharge = RechargeImaliAccount::create([
        //     'imali_account_id' => $imali_account->id,
        //     'transaction_id' => $mKesh_resquest_deposit->transaction_id,
        //     'bank_reference' => $mKesh_resquest_deposit->partner_transaction_id,
        //     'bank_date' => $mKesh_resquest_deposit->created_at,
        //     'account_reference' => $imali_account->reference,
        //     'phone' => $mKesh_resquest_deposit->phone,
        //     'description' => 'Carregamento realtime via MKesh',
        //     'amount' => $mKesh_resquest_deposit->amount,
        //     'last_balance' => $imali_account_2->balance,
        //     'balance' => $imali_account->balance,
        //     'recharge_way' => 'MKesh',
        //     'estado' => 'sucesso',
        //     'estado_color' => '#388E3C',
        //     'master_account_id' => $masterAccount->id,
        //     'user_id' => $mKesh_resquest_deposit->user_id,
        // ]);

        // $data = array(
        //     'transaction' => $recharge->transaction_id,
        //     'name' => $userPayer->name,
        //     'description' => $recharge->description,
        //     'amount' => (float)$recharge->amount,
        //     'phone' => $recharge->phone,
        //     'reference' => $imali_account->reference,
        //     'data' => date($recharge->created_at),
        //     'estado' => $recharge->estado,
        //     'route' => 'RECHARGE_DETAILS',
        //     'recharge_way' => $recharge->recharge_way,
        //     'account_number' => $imali_account->account_number,
        //     'terminal' => 'firebase'
        // );

        // // $p = new PushNotification(
        // //     'Carregamento ' . $recharge->amount . ' MT',
        // //     'Parabéns, ' . 'carregaste ' . $recharge->amount . ' MT ' . ' na tua conta ' . $imali_account->account_number,
        // //     $userPayer->firebase_token,
        // //     'com.imali.payapp.payment_RECHARGE_DETAILS'
        // // );

        // // $p->sendPush($data);

        // $this->send_push_notification($recharge, $userPayer, $imali_account);

        // return response()->json([], 200);
        // Converter JSON para XML

        Log::info('Outgoing Response', [
            'content' => 'mKesh Transaction successful',
        ]);

        return response(ArrayToXml::convert(['message' => 'Transaction successful.']), 200)->header('Content-Type', 'text/xml');
    }


    public function c2bMKeshPaymentResponse_OLD(Request $request)
    {
        Log::info('Incoming Response', [
            'content' => $request->getContent(),
            'url' => $request->url(),
        ]);

        $response = Http::withBody($request->getContent(), 'text/xml')->post('http://localhost:3000/mkesh/c2b-payment-response');

        $mKesh_resquest_deposit = MkeshAskingDeposit::query()
            ->where('partner_transaction_id', $response->json()['partnerTransactionId'])
            ->where('transaction_id', $response->json()['imaliTransactionId'])
            ->where('status', 'PENDING')
            ->first();

        if (!$mKesh_resquest_deposit) return response(ArrayToXml::convert(['message' => 'Not Found.']), 200)->header('Content-Type', 'text/xml');

        $mKesh_resquest_deposit->update([
            'status' => $response->json()['partnerCode'],
        ]);

        $userPayer = User::getUserDetails($mKesh_resquest_deposit->user_id);

        if ($response->json()['partnerCode'] != 'SUCCESSFUL') {

            $p = new PushNotification(
                'Carregamento MKesh',
                'O teu carregamento mKesh expirou',
                $userPayer->firebase_token,
                'com.imali.payapp.payment_RECHARGE_DETAILS'
            );

            $p->sendPush(['sms' => 'O teu carregamento mKesh expirou']);

            return response(ArrayToXml::convert(['message' => 'UNSUCCESSFUL TRANSACTION UPDATED.']), 200)->header('Content-Type', 'text/xml');
        }

        Log::info('Outgoing Response', [
            'content' => 'mKesh Transaction successful',
        ]);

        return response(ArrayToXml::convert(['message' => 'Transaction successful.']), 200)->header('Content-Type', 'text/xml');
    }

    public function c2bMKeshPaymentResponse(Request $request)
    {

        Log::info('Incoming Response', [
            'content' => $request->getContent(),
            'url' => $request->url(),
        ]);

        //$response = Http::withBody($request->getContent(), 'text/xml')->post('http://localhost:3002/mkesh/c2b-payment-response');
        $response = Http::withBody($request->getContent(), 'text/xml')->post($_ENV['MKESH_C2B_RESPONSE_URL']);

        // $mKesh_resquest_deposit = MkeshAskingDeposit::query()
        //     ->where('partner_transaction_id', $response->json()['partnerTransactionId'])
        //     ->where('transaction_id', $response->json()['imaliTransactionId'])
        //     ->where('status', 'PENDING')
        //     ->first();


        $mKesh_resquest_deposit = PaymentRequest::query()
            ->where('partner_transaction_id', $response->json()['partnerTransactionId'])
            ->where('transaction_id', $response->json()['imaliTransactionId'])
            ->where('status', 'PENDING')
            //->where('phone', $response->json()['msisdn'])
            //->where('phone', substr($response->json()['msisdn'], 3, 12))
            ->first();

        //if (!$mKesh_resquest_deposit) return;
        if (!$mKesh_resquest_deposit) {
            Log::info('[mKeshResponse]', ['message_iMalitoMkesh' => ArrayToXml::convert(['message' => 'Not Found.'])]);
            return response(ArrayToXml::convert(['message' => 'Not Found.']), 200)->header('Content-Type', 'text/xml');
        }

        $mKesh_resquest_deposit->update([
            //'status' => $response->json()['status'],
            'status' => $response->json()['partnerCode'],
        ]);

        $userPayer = User::getUserDetails($mKesh_resquest_deposit->user_id);

        //if ($response->json()['status'] != 'SUCCESSFUL') {
        if ($response->json()['partnerCode'] != 'SUCCESSFUL') {

            Log::info('[mKeshResponse]', ['message_iMalitoMkesh' => ArrayToXml::convert(['message' => 'UNSUCCESSFUL TRANSACTION UPDATED.'])]);

            $p = new PushNotification(
                'Carregamento MKesh',
                'O teu carregamento mKesh expirou',
                $userPayer->firebase_token,
                'com.imali.payapp.payment_RECHARGE_DETAILS'
            );

            $p->sendPush(['sms' => 'O teu carregamento mKesh expirou']);

            return response(ArrayToXml::convert(['message' => 'UNSUCCESSFUL TRANSACTION UPDATED.']), 200)->header('Content-Type', 'text/xml');
        }


        // Buscar dados da conta do User que faz a transacao
        $imali_account = User::getAccountByUser($userPayer->id, $userPayer->account_id);
        $imali_account_2 = User::getAccountByUser($userPayer->id, $userPayer->account_id);

        $imali_account->balance += $mKesh_resquest_deposit->amount;
        $imali_account->update();

        $masterAccount = MasterAccount::find(2);

        $recharge = RechargeImaliAccount::create([
            'imali_account_id' => $imali_account->id,
            'transaction_id' => $mKesh_resquest_deposit->transaction_id,
            'bank_reference' => $mKesh_resquest_deposit->partner_transaction_id,
            'bank_date' => $mKesh_resquest_deposit->created_at,
            'account_reference' => $imali_account->reference,
            'phone' => $mKesh_resquest_deposit->phone,
            'description' => 'Carregamento realtime via MKesh',
            'amount' => $mKesh_resquest_deposit->amount,
            'last_balance' => $imali_account_2->balance,
            'balance' => $imali_account->balance,
            'recharge_way' => 'MKesh',
            'estado' => 'sucesso',
            'estado_color' => '#388E3C',
            'master_account_id' => $masterAccount->id,
            'user_id' => $mKesh_resquest_deposit->user_id,
        ]);

        $data = array(
            'transaction' => $recharge->transaction_id,
            'name' => $userPayer->name,
            'description' => $recharge->description,
            'amount' => (float)$recharge->amount,
            'phone' => $recharge->phone,
            'reference' => $imali_account->reference,
            'data' => date($recharge->created_at),
            'estado' => $recharge->estado,
            'route' => 'RECHARGE_DETAILS',
            'recharge_way' => $recharge->recharge_way,
            'account_number' => $imali_account->account_number,
            'terminal' => 'firebase'
        );

        $this->send_push_notification($recharge, $userPayer, $imali_account);

        // Log::info('Outgoing Response', [
        //     'content' => 'mKesh Transaction successful',
        // ]);


        return response(ArrayToXml::convert(['message' => 'Transaction successful.']), 200)->header('Content-Type', 'text/xml');
    }


    //POST
    public function b2cmKeshPayment(Request $request)
    {

        $this->validate(
            $request,
            [
                'phone' => 'required|numeric|digits:9',
                'amount' => 'required',
                'mobile_wallets_id' => 'required',
            ],
            [
                'phone.required' => 'Campo Phone é obrigatório',
                'phone.numeric' => 'Campo Phone é númerico',
                'phone.digits' => 'Campo Phone deve ter 9 digitos',
                'amount.required' => 'Campo amount é obrigatório',
                'mobile_wallets_id.required' => 'Campo mobile_wallets_id é obrigatório',
            ]
        );

        $b2c_data = $this->checkB2CTransaction(new Request([
            'phone' => $request->phone,
            'amount' => $request->amount,
            'mobile_wallets_id' => $request->mobile_wallets_id,
        ]), 'mkesh');

        $userPayer = User::getUserDetails(auth()->user()->id);
        // $usr = auth('api')->user();

        // $userKyc = new UserKyc($userPayer);

        // $usrKycResp = $userKyc->checkUserKYC($request->amount, 404);
        // if ($usrKycResp->getStatusCode() != 200) return $usrKycResp;

        // //? teste
        // $kyc = new Kyc();
        // $request->request->add(['id' => auth()->user()->id, 'receiver_id' => $usr->id]);
        // $resultKYC = $kyc->checkSender($request);
        // //? teste

        // if (Hash::check($request->pin, auth()->user()->pin)) {

        // $result = $resultKYC;

        // if ($result) return $result;

        // Buscar dados da conta do User que faz a transacao
        // $imali_account = User::getAccountByUser($userPayer->id, $userPayer->account_id);

        try {

            if (($b2c_data->getStatusCode() != 200)) return $b2c_data;

            $response = Http::post('http://localhost:3000/mkesh/b2c-payment', ['phone' => '258' . $request->phone, 'amount' => $request->amount]);

            // return $response->json();

            if ($response->status() != 200) {

                Log::info('Outgoing Response', [
                    'content' => $response->json(),
                ]);

                return response()->json(['message' => 'Erro interno de servidor'], 500);
            }

            $imali_account = ImaliAccount::query()->where('user_id', auth('api')->user()->id)->first();
            $new_imali_account = ImaliAccount::query()->where('user_id', auth('api')->user()->id)->first();

            //actualizacao do saldo principal
            $imali_account->balance = $imali_account->balance - $b2c_data->getData()->total;
            $imali_account->update();

            // $trasactionGeneration = new TransactionGeneration();
            // $transaction_id = $trasactionGeneration->generateTransaction();

            $withdrawalls = WithdrawalsRequest::create([
                'imali_account' => $imali_account->account_number,
                'account_type' => 'client',
                'amount' => $request->amount,
                'imali_fee' => $b2c_data->getData()->imali_fee,
                'bank_fee' => $b2c_data->getData()->imali_cost,
                'description' => 'TRF. MKesh',
                'account_number' => $request->phone,
                'wallets_id' => 2,
                'operators_id' => $request->mobile_wallets_id,
                'status' => 'success',
                'old_balance' => $new_imali_account->balance,
                'new_balance' => $imali_account->balance,
                'total' => $b2c_data->getData()->total,
                'transaction_id' => $response->json()['imaliTransactionId'],
                'partner_transaction_id' => $response->json()['partnerTransactionId'],
                'commission' => $b2c_data->getData()->commission,
                'stamp_tax' => $b2c_data->getData()->stamp_tax,
                'user_id' => $imali_account->user_id,
                'sender_name' => $userPayer->name,
                'reciever_name' => $b2c_data->getData()->masked_name,
                'imali_account_id' => $imali_account->id
            ]);

            $data = array(
                'transaction' => $withdrawalls->transaction_id,
                'name' => $userPayer->name,
                'description' => $withdrawalls->description,
                'amount' => (float)$withdrawalls->total,
                'phone' => $userPayer->phone,
                'reference' => $imali_account->reference,
                'data' => date($withdrawalls->created_at),
                'estado' => $withdrawalls->status,
                'route' => 'RECHARGE_DETAILS',
                'recharge_way' => $withdrawalls->description,
                'account_number' => $imali_account->account_number,

                'total' => $b2c_data->getData()->total,
                'commission' => $b2c_data->getData()->commission,
                'stamp_tax' => $b2c_data->getData()->stamp_tax,

                'sender_name' => $userPayer->name,
                'reciever_name' => $b2c_data->getData()->masked_name,

                'terminal' => 'firebase'
            );

            $p = new PushNotification(
                'Transferência para MKesh',
                'Transferência de ' . $withdrawalls->amount . ' MT ' . 'da conta ' . $imali_account->account_number . ' para o MKesh: ' . $request->phone,
                $userPayer->firebase_token,
                'com.imali.payapp.payment_RECHARGE_DETAILS'
            );

            $p->sendPush($data);

            Log::info('Outgoing Response', [
                'content' => 'mKesh Transaction successful',
            ]);

            return response()->json(['message' => 'A tua transferência para MKesh foi efectuada com sucesso!'], 200);
        } catch (\Throwable $th) {

            return response()->json(['message' => 'Erro ao tentar efectuar o pagamento', 'exception' => $th->getMessage(), 'line' => $th->getLine()], 500);
        }
    }



    //? eMola Methods
    //POST
    // public function c2beMolaPayment(Request $request)
    // {
    //     return $response = Http::post('http://localhost:3008/api/emola/send/push', []);
    // }

    //POST
    public function c2beMolaPayment(Request $request)
    {

        $this->validate(
            $request,
            [
                'phone' => 'required|numeric|digits:9',
                'amount' => 'required',
                'imaliReference' => 'required'
            ],
            [
                'phone.required' => 'Campo Phone é obrigatório',
                'phone.numeric' => 'Campo Phone é númerico',
                'phone.digits' => 'Campo Phone deve ter 9 digitos',
                'amount.required' => 'Campo amount é obrigatório',
                // 'amount.min' => 'O valor minimo deve ser 100MT',
                'imaliReference.required' => 'Campo imaliReference é obrigatório'
            ]
        );

        if ($request->has('is_store') && $request->is_store) {
            // Buscar a loja onde se fara o pagamento
            $imali_account = Store::getStoreAccount($request->imaliReference);
            if (!$imali_account)

                return SendResponse::errorResp404notfound(
                    'Conta de pagamento invalido',
                    'Invalid Account',
                );

            $imali_account_2 = Store::getStoreAccount($request->imaliReference);
        } else {
            // Buscar dados do User que faz a transacao
            $userPayer = User::getUserDetails(auth()->user()->id);

            // Buscar dados da conta do User que faz a transacao
            $imali_account = User::getAccountByUser($userPayer->id, $userPayer->account_id);
            if (!$imali_account)

                return SendResponse::errorResp404notfound(
                    'Conta de depósito inválido',
                    'Invalid Account Number',
                );

            $imali_account_2 = User::getAccountByUser($userPayer->id, $userPayer->account_id);
        }

        try {

            $user = User::getUserDetails(auth('api')->user()->id);

            $userKyc = new UserKyc($user);

            $usrKycResp = $userKyc->checkUserKYC($request->amount, 404);
            if ($usrKycResp->getStatusCode() != 200) return $usrKycResp;

            return $response = Http::post('http://localhost:3002/emola/c2b-payment', ['phone' => '258' . $request->phone, 'amount' => $request->amount, 'customerAccount' => $request->imaliReference, 'customerName' => $user->name]);

            if (($response->status() != 200) && ($response->status() != 201)) return response()->json($this->formatMPesaError($response), $response->status());

            if (count($response->json()) > 0 && $response->json()['mpesaCode'] == 'INS-0') {

                $imali_account->balance += $request->amount;
                $imali_account->update();

                $transactionString = new TransactionGeneration();
                $masterAccount = MasterAccount::find(2);

                $recharge = RechargeImaliAccount::create([
                    'imali_account_id' => $imali_account->id,
                    'transaction_id' => $transactionString->generateTransaction(),
                    'bank_reference' => $response->json()['mpesaTransactionId'],
                    'bank_date' => $request->datatransaccao,
                    'account_reference' => $request->referenciaDoc,
                    // adicionado🔰🔽
                    'phone' => $request->phone,
                    'description' => 'Carregamento realtime via MPesa',
                    'amount' => $request->amount,
                    'last_balance' => $imali_account_2->balance,
                    'balance' => $imali_account->balance,
                    'recharge_way' => 'MPesa',
                    'estado' => 'sucesso',
                    'estado_color' => '#388E3C',
                    'master_account_id' => $masterAccount->id
                ]);

                $usr = auth('api')->user();


                $data = array(
                    'transaction' => $recharge->transaction_id,
                    'name' => $usr->name,
                    'description' => $recharge->description,
                    'amount' => (float)$recharge->amount,
                    // 'phone' => $usr->phone,
                    'phone' => $recharge->phone,
                    'reference' => $imali_account->reference,
                    'data' => date($recharge->created_at),
                    'estado' => $recharge->estado,
                    'route' => 'RECHARGE_DETAILS',
                    'recharge_way' => $recharge->recharge_way,
                    'account_number' => $imali_account->account_number,
                    'terminal' => 'firebase'
                );

                $p = new PushNotification(
                    'Carregamento ' . $recharge->amount . ' MT',
                    'Parabéns, ' . 'carregaste ' . $recharge->amount . ' MT ' . ' na tua conta ' . $imali_account->account_number,
                    $usr->firebase_token,
                    'com.imali.payapp.payment_RECHARGE_DETAILS'
                );

                $p->sendPush($data);
            }

            return response()->json(['message' => 'Carregamento enviado com sucesso']);
        } catch (\Throwable $th) {

            Log::info('Outgoing Response', [
                'content' => $request->url(),
                'params' => $request->all(),
            ]);

            return response()->json(['message' => 'Erro ao tentar efectuar o pagamento', 'exception' => $th->getMessage()], 500);
        }
    }

    //POST
    public function b2ceMolaPayment(Request $request)
    {

        $this->validate(
            $request,
            [
                'phone' => 'required|numeric|digits:9',
                'amount' => 'required',
                'mobile_wallets_id' => 'required',
                'imaliReference' => 'required'
            ],
            [
                'phone.required' => 'Campo Phone é obrigatório',
                'phone.numeric' => 'Campo Phone é númerico',
                'phone.digits' => 'Campo Phone deve ter 9 digitos',
                'amount.required' => 'Campo amount é obrigatório',
                'mobile_wallets_id.required' => 'Campo mobile_wallets_id é obrigatório',
                'imaliReference.required' => 'Campo imaliReference é obrigatório'
            ]
        );

        try {

            $b2c_data = $this->checkB2CTransaction(new Request([
                'phone' => $request->phone,
                'amount' => $request->amount,
                'mobile_wallets_id' => $request->mobile_wallets_id,
            ]), 'emola');

            $usr = auth('api')->user();

            //? teste
            $kyc = new Kyc();
            $request->request->add(['id' => auth()->user()->id, 'receiver_id' => $usr->id]);
            $resultKYC = $kyc->checkSender($request);
            //? teste

            $result = $resultKYC;

            if ($result) {

                $log = new Record();
                $log->createLog([
                    'description' => 'Transfer Money',
                    'details' => $result,
                    'operation' => 'Transfer Money',
                    'status' => 'Error',
                    'origin_ip' => $request->ip(),
                    'properties' => json_encode($request->except(['pin'])),
                    //                        'properties' => json_encode($request->all()),
                    'origin_request' => $request->url(),
                    'user_id' => auth()->user()->id
                ]);

                return $result;
            }


            if (($b2c_data->getStatusCode() != 200)) return $b2c_data;
            $response = Http::post('http://localhost:3002/emola/b2c-payment', ['phone' => '258' . $request->phone, 'amount' => $request->amount, 'customerAccount' => $request->imaliReference]);

            if (($response->status() != 200) && ($response->status() != 201)) return response()->json($this->formatMPesaError($response), $response->status());

            if (count($response->json()) > 0 && $response->json()['mpesaCode'] == 'INS-0') {

                $imali_account = ImaliAccount::query()->where('user_id', auth('api')->user()->id)->first();
                $new_imali_account = ImaliAccount::query()->where('user_id', auth('api')->user()->id)->first();

                //actualizacao do saldo principal
                $imali_account->balance = $imali_account->balance - $b2c_data->getData()->total;
                $imali_account->update();

                $trasactionGeneration = new TransactionGeneration();
                $transaction_id = $trasactionGeneration->generateTransaction();

                $withdrawalls = WithdrawalsRequest::create([
                    'imali_account' => $imali_account->account_number,
                    'account_type' => 'client',
                    'amount' => $request->amount,
                    'imali_fee' => $b2c_data->getData()->imali_fee,
                    'bank_fee' => $b2c_data->getData()->imali_cost,
                    'description' => 'TRF. MPesa',
                    'account_number' => $request->phone,
                    'wallets_id' => 2,
                    'operators_id' => $request->mobile_wallets_id,
                    'status' => 'success',
                    'old_balance' => $new_imali_account->balance,
                    'new_balance' => $imali_account->balance,
                    'total' => $b2c_data->getData()->total,
                    'transaction_id' => $transaction_id,
                    'commission' => $b2c_data->getData()->commission,
                    'stamp_tax' => $b2c_data->getData()->stamp_tax,
                    'user_id' => $imali_account->user_id,
                    'sender_name' => $usr->name,
                    'reciever_name' => $b2c_data->getData()->masked_name,
                    'imali_account_id' => $imali_account->id
                ]);

                $data = array(
                    'transaction' => $withdrawalls->transaction_id,
                    'name' => $usr->name,
                    'description' => $withdrawalls->description,
                    'amount' => (float)$withdrawalls->total,
                    'phone' => $usr->phone,
                    'reference' => $imali_account->reference,
                    'data' => date($withdrawalls->created_at),
                    'estado' => $withdrawalls->status,
                    'route' => 'RECHARGE_DETAILS',
                    'recharge_way' => $withdrawalls->description,
                    'account_number' => $imali_account->account_number,

                    'total' => $b2c_data->getData()->total,
                    'commission' => $b2c_data->getData()->commission,
                    'stamp_tax' => $b2c_data->getData()->stamp_tax,

                    'sender_name' => $usr->name,
                    'reciever_name' => $b2c_data->getData()->masked_name,

                    'terminal' => 'firebase'
                );

                $p = new PushNotification(
                    // 'Transferência de ' . $withdrawalls->amount . ' MT para MPesa',
                    'Transferência para MPesa',
                    'Transferência de ' . $withdrawalls->amount . ' MT ' . 'da conta ' . $imali_account->account_number . ' para o MPesa: ' . $request->phone,
                    $usr->firebase_token,
                    'com.imali.payapp.payment_RECHARGE_DETAILS'
                );

                $p->sendPush($data);
            }

            return response()->json(['message' => 'A tua transferência para MPesa foi efectuada com sucesso!'], 200);
        } catch (\Throwable $th) {

            return response()->json(['message' => 'Erro ao tentar efectuar o pagamento', 'exception' => $th->getMessage()], 500);
        }
    }


    public function c2beMolaPaymentResponse(Request $request)
    {
        // deve ser chamado com o pessoal do eMola...
        $response = Http::withBody($request->getContent(), 'text/xml')->post('http://localhost:3000/emola/c2b-payment-response');
    }



    // todo --- NOVOS METODOS CARREGAMENTO ATRAVES DAS CARTEIRAS MOVEIS EMOLA, MPESA....
    public function c2bDeposit(Request $request, $wallet_name)
    {
        $wallet = Operator::query()->where('acronym', $wallet_name)->first();
        if (!$wallet) return SendResponse::errorResp404notfound();

        $this->validate_parameters($request);

        if (!$this->check_wallet_number($request->phone, $wallet_name)) return SendResponse::errorResp400('Numero de telefone invalido', 'Invalid Phone Number');

        $payer = User::getUserAccount();
        $payer_account = User::getAccount($payer->account_number);
        $payer_account_2 = User::getAccount($payer->account_number);
        $userKyc = new UserKyc($payer);

        $usrKycResp = $userKyc->checkUserKYC($request->amount, 404);
        if ($usrKycResp->getStatusCode() != 200) return $usrKycResp;

        // $usrKycRespBalance = $userKyc->checkUserBalance($request->amount);
        // if ($usrKycRespBalance->getStatusCode() != 200) return $usrKycRespBalance;

        return $this->recharge_by_emola_mpesa_mkesh($request, $payer, $payer_account, $payer_account_2, $wallet);
    }

    // todo --- 06/08/2024 Store MOVEIS EMOLA, MPESA....
    private function check_mkesh_response($wallet_name, $request, $response,)
    {
        // if ($wallet_name != 'mkesh') return;

        $request_deposit = $this->create_request_deposit_store($request, $response);

        $status = $this->check_request_deposit_status($request_deposit->transaction_id);

        if ($status === 'NOTFOUND') return SendResponse::errorResp404notfound(
            'Transação invalida',
            'Invalid Transction'
        );

        if ($status === 'FAILED') return SendResponse::errorResp400(
            'Numero de telefone invalido',
            'Numero de telefone invalido'
        );

        if ($status === 'TIMEOUT') return SendResponse::warningResp500serverError(
            'Transacão expirou',
            'Transaction expired.'
        );

        if ($status === 'SUCCESSFUL') return SendResponse::successResp200(
            'Transacão feita com sucesso',
            'Transaction successfully.'
        );
    }


    // todo --- 06/08/2024 Store MOVEIS EMOLA, MPESA....
    //? NOVO METODO PAYMENTS IMALIWAY - 29/AUG/2024

    private function checkExpiredLink($link)
    {
        $expirationDate = (int)strtotime($link->expiration_datetime);
        $todayDate = (int)strtotime(date('Y-m-d H:i:s'));
        $remaningtime =  $todayDate > $expirationDate;
        if ($remaningtime && ($link->status == 'PENDING')) {
            $link->status = 'EXPIRED';
            $link->update();
        }
    }

    public function c2bDepositStore(Request $request, $wallet_name)
    {
        $wallet = Operator::query()->where('acronym', $wallet_name)->first();
        if (!$wallet) return SendResponse::errorResp404notfound();
        $request->request->add(['phone' => $request->account_number, 'imaliReference' => $request->store_account]);


        // todo -- codigo do link -- start

        // return $request->url();
        $app_exist = str_contains($request->url(), 'apps');
        if ($app_exist) {

            $this->validate_link_parameters($request);

            $link = Link::query()->where('link_id', $request->link_id)->orWhere('customer_link_id', $request->customer_link_id)->first();
            if (!$link) return SendResponse::errorResp404notfound('Link nao encontrado', 'Link not found');

            $this->checkExpiredLink($link);
            if ($link->status == 'EXPIRED') return SendResponse::errorResp400('Link expirado', 'Expired Link');
            if ($link->status == 'USED') return SendResponse::errorResp400('Link usado', 'Used Link');
            if ($link->status == 'INACTIVE') return SendResponse::errorResp400('Link inativo', 'Inactive Link');

            $store = Store::getStoreContractsAndConfigs($link->store_id);

            $request->request->add([
                'phone' => $request->account_number,
                'amount' => (string) $link->amount,
                'imaliReference' => $store->account_number,
                'link_id' => $link->id,
                'link_id_key' => $request->link_id
            ]);
        }

        // todo -- codigo do link -- end


        $this->validate_parameters($request);

        $store = Store::getStoreAccount($request->imaliReference);
        if (!$store) return SendResponse::errorResp404notfound(
            'Número de conta da Loja invalido',
            'Invalid Store Account',
        );

        $store_old = Store::getStoreAccount($request->imaliReference);

        $store_contract = Store::getStoreContractsAndConfigs($store->account_number);

        //validar KYC da Loja
        $storeKyc = new StoreKyc($store_contract);
        $respStoreKyc = $storeKyc->checkStoreKYC($request->amount);
        // if ($respStoreKyc->getStatusCode() != 200) return $respStoreKyc;
        if ($respStoreKyc->getStatusCode() != 200) return response()->json(['message' => $respStoreKyc->getData()->message, 'status' => 'FAILED'], 400);

        switch ($wallet_name) {
            case 'mpesa':
                return $this->make_wallet_payments($request, $wallet, $store, $store_old, $store_contract);
                break;
            case 'emola':
                return $this->make_wallet_payments($request, $wallet, $store, $store_old, $store_contract);
                break;
            case 'mkesh':
                return $this->make_wallet_payments($request, $wallet, $store, $store_old, $store_contract);
                break;
            case 'imali':
                return $this->make_imali_payments($request, $store);
                break;

            default:
                # code...
                break;
        }
    }


    //? ------------------------------------------------ todo Made By Mr Rodrigues Pagamento Com Parceiros----------------------------------------------

    private function make_push_payments_new25($request, $wallet_name, $payment_method, $payment_type)
    {

        $this->validate_push_parameters_new25($request);

        if ($request->has('expiration_datetime') && $request->expiration_datetime) {
            $expiration_date = (int)strtotime($request->expiration_datetime);
            $current_date = (int)strtotime(date('Y-m-d H:i:s'));

            if ($current_date > $expiration_date) return SendResponse::errorResp400('Data invalida', 'Invalid Date');
        }

        if (in_array($wallet_name, ['mpesa', 'emola', 'mkesh'])) {

            // todo -- TEMPORARIO 🚧
            // if ($wallet_name === 'emola') {
            //     return SendResponse::errorResp400('Serviço temporariamente indisponível', 'Service temporarily unavailable');
            // }
            // todo -- TEMPORARIO 🚧

            if (!$this->check_wallet_number($request->client_account_number, $wallet_name)) return SendResponse::errorResp400('Numero de telefone invalido', 'Invalid Phone Number');

            return $this->create_payment_requests_new25($request, $wallet_name, $payment_method, $payment_type);
        } else if ($wallet_name === 'imali') {

            // Contas a Pagar
            // $accountPayer = User::getAccount($request->client_account_number);
            // if (!$accountPayer) return SendResponse::errorResp404notfound(
            //     'Conta não encontrada',
            //     'Account Not Found',
            // );
            // if ($accountPayer->account_number == $request->payer_account_number) return SendResponse::errorResp404notfound(

            // $userPayer = User::getUserDetails($accountPayer->user_id);

            $userPayer = User::getUserDetails($request->client_account_number); # USER -- numero de telefone ✅ | numero de conta ❌
            Log::info(['DADOS DO $userPayer antes do IF'], ['data' => $userPayer]);

            if (!$userPayer) {
                $accountPayer = User::getAccount($request->client_account_number);
                Log::info(['DADOS DO $accountPayer'], ['data' => $accountPayer]);

                if (!$accountPayer) return SendResponse::errorResp404notfound(
                    'Conta não encontrada',
                    'Account Not Found',
                );
                $userPayer = User::getUserDetails($accountPayer->user_id);
                Log::info(['DADOS DO $userPayer dentro do IF'], ['data' => $userPayer]);
            }

            // return $userPayer;

            // Validar KYC do userPayer
            $kyc = new UserKyc($userPayer);
            $kycResp = $kyc->checkSenderKYC($request->amount);

            // Log::info(['DADOS DO kycResp'], ['data' => $kycResp]);
            // return response()->json($kycResp);

            if ($kycResp->getStatusCode() != 200) return $kycResp;

            // Validacao do saldo do User
            $kycRespBalance = $kyc->checkUserBalance($request->amount);
            // if ($kycRespBalance->getStatusCode() != 200) return $kycRespBalance; // todo ORIGINAL
            if ($kycRespBalance->getStatusCode() != 200) return response()->json(['message' => $kycRespBalance->getData()->message, 'status' => 'FAILED'], 400);

            return $this->create_payment_requests_new25($request, $wallet_name, $payment_method, $payment_type);
        } else {
            return response()->json(['message' => 'Método de Pagamento Invalido.', 'status' => 'FAILED']);
        }
    }


    private function make_qrcode_payments_new25($request, $wallet_name)
    {
        if ($wallet_name !== 'imali') return SendResponse::errorResp400('O metodo de Pagamento ' . $wallet_name . ' nao permite pagamento por QRCode');

        $this->validate_parameters_qrcode_new25($request);

        if ($request->has('expiration_datetime') && $request->expiration_datetime) {

            $expiration_date = (int)strtotime($request->expiration_datetime);
            $current_date = (int)strtotime(date('Y-m-d H:i:s'));

            if ($current_date > $expiration_date) return SendResponse::errorResp400('Data invalida', 'Invalid Date');
        }

        $url = $request->url();
        if (str_contains($url, 'apps')) {

            $this->validate(
                $request,
                [
                    'link_id' => ['required', new LinkExists()],
                ],
                [
                    'link_id.required' => 'Campo link_id é obrigatório',
                    'link_id.exists' => 'O link_id informado não existe na base de dados.',
                ]
            );
        }

        $secret_key = $_ENV['IMALI_SECRET_KEY'];

        $hasTimeout = $request->has('expiration_datetime') && $request->expiration_datetime;

        $qrcode_id = Str::uuid();

        $expiration_datetime = $hasTimeout ? $request->expiration_datetime : now()->addMinutes(2)->format('Y-m-d H:i:s');

        $request->request->add(['expiration_datetime' => $expiration_datetime]);

        $signature = hash_hmac('sha256', $qrcode_id . strtotime($expiration_datetime), $secret_key);

        $qrcode_payload = $qrcode_id . ':' . strtotime($expiration_datetime) . ':' . $signature;

        $request->request->add(['qrcode_id' => $qrcode_id, 'signature' => $signature, 'status' => 'PENDING']);

        Qrcode::create($request->all()); //cria o qrcode na base de dados


        $qrcode_img = QrCodeGenenator::format('png')->size(300)->merge("/var/www/imaliapistaging/public/images/logo/imali_logo_new25556.png", 0.30, true)->generate($qrcode_payload);


        $qrcode_data = [
            'qrcode_id' => $qrcode_id,
            'partner_transaction_id' => $request->partner_transaction_id,
            'expiration_datetime' => $expiration_datetime,
            'amount' => $request->amount,
            'qrcode_type' => $request->qrcode_type,
            'status' => $request->status
        ];

        return response()->json(['data' => $qrcode_data, 'qrcode_token' => $qrcode_payload, 'qrcode_image' => 'data:image/png;base64,' . base64_encode($qrcode_img)]);
    }


    // private fucit
    private function get_qrcode_data_new25($qrcode_token)
    {

        $qrcode_parts = explode(':', $qrcode_token);
        if (sizeof($qrcode_parts) != 3) throw new Error('QRCODE_INVALID');

        $qrcode_id = $qrcode_parts[0];
        $timestamp = $qrcode_parts[1];
        $signature = $qrcode_parts[2];

        $qrcode_data = array(
            'qrcode_id' => $qrcode_id,
            'timestamp' => $timestamp,
            'signature' => $signature
        );

        return json_encode($qrcode_data);
    }

    private function verify_qrcode_new25($qrcode_token)
    {

        if (strlen($qrcode_token) != 112) throw new Error('QRCODE_INVALID');

        $qrcode_data = null;

        try {

            $qrcode_data = json_decode($this->get_qrcode_data_new25($qrcode_token));
        } catch (\Throwable $th) {
            throw $th;
        }
        $secret_key = $_ENV['IMALI_SECRET_KEY'];

        $valid_signature = hash_hmac('sha256', $qrcode_data->qrcode_id . $qrcode_data->timestamp, $secret_key);

        $isValidQrcode = hash_equals($valid_signature, $qrcode_data->signature);

        if (!$isValidQrcode)  throw new Error('QRCODE_INVALID');

        if (now()->timestamp > $qrcode_data->timestamp) throw new Error('QRCODE_EXPIRED');
    }

    public function make_refresh_qrcode_payments_new25(Request $request)
    {
        $this->validate(
            $request,
            [
                'qrcode_token' => 'required|size:112'
            ],
            [
                'qrcode_token.required' => 'Campo qrcode_token é obrigatório',
                'qrcode_token.size' => 'Campo qrcode_token deve ter no máximo 112 caracteres',
            ]
        );

        $qrcode_data = null;

        try {

            $this->verify_qrcode_new25($request->qrcode_token);

            $qrcode_data = json_decode($this->get_qrcode_data_new25($request->qrcode_token));
        } catch (\Throwable $th) {

            if ($th->getMessage() == 'QRCODE_INVALID') return response()->json(['message' => 'Qrcode invalido.'], 400);
            $qrcode_data = json_decode($this->get_qrcode_data_new25($request->qrcode_token));
        }

        $qrcode = Qrcode::query()
            ->where('qrcode_id', $qrcode_data->qrcode_id)
            ->first();

        if (!$qrcode) return SendResponse::errorResp404notfound('QRCode nao encontrado', 'QRCode not found');

        $this->checkExpiredLink($qrcode);

        if ($qrcode->status == 'PENDING') return SendResponse::errorResp404notfound('QRCode ainda está valido.', 'QRCode it\'s still valid.');
        if ($qrcode->status !== 'EXPIRED') return SendResponse::errorResp404notfound('QRCode invalido.', 'QRCode Invalid.');
        if ($qrcode->qrcode_type !== 'DYNAMIC_TERMINAL') return SendResponse::errorResp404notfound('Este tipo de QRCode não pode ser actualizado.', 'This type of QRCode cannot be updated.');

        $qrcode->expiration_datetime = now()->addMinutes(2)->format('Y-m-d H:i:s');
        $qrcode->status = 'PENDING';
        $qrcode->update();

        $secret_key = $_ENV['IMALI_SECRET_KEY'];
        $signature = hash_hmac('sha256', $qrcode->qrcode_id . strtotime($qrcode->expiration_datetime), $secret_key);

        $qrcode_payload = $qrcode->qrcode_id . ':' . strtotime($qrcode->expiration_datetime) . ':' . $signature;
        $qrcode_img = QrCodeGenenator::format('png')->size(300)->merge("/var/www/imaliapistaging/public/images/logo/imali_logo_new25556.png", 0.30, true)->generate($qrcode_payload);

        $qrcode_data = [
            'qrcode_id' => $qrcode->qrcode_id,
            'partner_transaction_id' => $qrcode->partner_transaction_id,
            'expiration_datetime' => $qrcode->expiration_datetime,
            'amount' => $qrcode->amount,
            'qrcode_type' => $qrcode->qrcode_type,
            'status' => $qrcode->status
        ];

        return response()->json(['data' => $qrcode_data, 'qrcode_token' => $qrcode_payload, 'qrcode_image' => 'data:image/png;base64,' . base64_encode($qrcode_img)]);
    }

    private function make_link_payments_new25($request, $wallet_name, $payment_method, $payment_type_obj)
    {

        if ($wallet_name !== 'imali') return SendResponse::errorResp400('O metodo de Pagamento ' . $wallet_name . ' nao permite pagamento por links');
        $this->validate_parameters_link_new25($request);
        if (!$this->check_phone_number_new25($request->send_to_phone)) return SendResponse::errorResp400('Numero de telefone invalido', 'Invalid Phone Number');

        //buscando a Loja (Store)
        $store = Store::getStoreAccount($request->store_account_number);
        if (!$store) return SendResponse::errorResp404notfound(
            'Número da Loja invalido',
            'Invalid Store Account',
        );

        $business = BusinessAccount::query()->where('id', $store->business_account_id)->first();
        if (!$business) return SendResponse::errorResp404notfound(
            'Loja nao associada a conta Business',
            'Store not associated with Business account',
        );

        $store_contract = Store::getStoreContractsAndConfigs($store->account_number);

        //validar KYC da Loja
        $storeKyc = new StoreKyc($store_contract);
        $respStoreKyc = $storeKyc->checkStoreKYC($request->amount);
        // if ($respStoreKyc->getStatusCode() != 200) return $respStoreKyc;
        if ($respStoreKyc->getStatusCode() != 200) return response()->json(['message' => $respStoreKyc->getData()->message, 'status' => 'FAILED'], 400);

        $request->request->add(['store_id' => $store->id]);

        $payment_controller = new PaymentController;
        return $payment_controller->paymentLink($request);
    }


    // todo --  Consultar Saldo da subconta - Check Card Balance
    private function make_nfc_card_payments_new25($request)
    {
        $request->request->add([
            'card_number' => $request->client_account_number,
            'account_number' => $request->store_account_number,
            'is_real_payment' => 0
        ]);

        $payment_controller = new PaymentController;
        return $payment_controller->makePaymentStoreApp2024SubAccounts($request);
    }

    // todo -- Consultar Saldo da subconta - Check Card Balance
    public function check_card_balance_new25(Request $request)
    {
        $this->validate(
            $request,
            [
                'client_account_number' => 'required'
            ],
            [
                'client_account_number.required' => 'Campo client_account_number é obrigatório',
            ]
        );

        $card_number = $request->client_account_number;

        if (!$card_number) return response()->json(['message' => 'Número de cartão obrigatório!'], 400);

        $sub_account = ImaliSubAccount::query()->where('card_number', $card_number)->first();

        if (!$sub_account) return response()->json(['message' => 'Cartão invalido!'], 400);
        return response()->json(['balance' => $sub_account->balance], 200);
    }

    // todo -- Reembolso da subconta
    public function make_payment_refund_new25(Request $request)
    {

        $client_controller = new UserClientController;
        return $client_controller->makePaymentRefund($request);
    }


    private function check_phone_number_new25($phone)
    {
        $valid_number_prefix = [82, 83, 84, 85, 86, 87];

        $prefix = substr($phone, 0, 2);

        if (!in_array($prefix, $valid_number_prefix)) return false;

        return true;
    }

    // todo - ALTERADO NO DIA 01/07/2025
    private function general_validate_parameters_new25_OLD($request)
    {
        $request->merge([
            'amount' => str_replace(',', '.', $request->input('amount'))
        ]);

        return $this->validate(
            $request,
            [
                'store_account_number' => 'required|digits:9',
                'payment_type' => 'required|in:push,qrcode,link,nfc_card|exists:payment_types,name',
                'payment_method' => 'required|in:imali,mpesa,emola,mkesh|exists:payment_methods,name',
                'partner_transaction_id' => 'required|unique:payment_requests,partner_transaction_id',
                // 'amount' => 'required|numeric|min:10',
                'amount' => [
                    'required',
                    'regex:/^\d+(\.\d{1,2})?$/',
                    'min:10'
                ],
            ],
            [
                'amount.required' => 'Campo amount é obrigatório',
                'amount.min' => 'O valor minimo deve ser 10MT',
                'amount.regex' => 'Campo amount deve ser um número válido (ex: 10.50)',
                // 'amount.numeric' => 'Campo Montente deve ser númerico',
                'store_account_number.required' => 'Campo store_account_number é obrigatório',
                'store_account_number.digits' => 'Campo store_account_number deve ter no maximo 9 digitos',
                'payment_type.required' => 'Campo payment_type é obrigatório',
                'payment_type.in' => 'Tipos de pagamentos validos:push,qrcode,link,nfc_card',
                'payment_type.exists' => 'Tipo de pagamento nao encontrado',
                'payment_method.required' => 'Campo payment_method é obrigatório',
                'payment_method.in' => 'Metodos de pagamentos validos:imali,mpesa,emola,mkesh',
                'payment_method.exists' => 'Metodo de pagamento nao encontrado',
                'partner_transaction_id.required' => 'Campo partner_transaction_id é obrigatório',
                'partner_transaction_id.unique' => 'Campo partner_transaction_id ja existe',
            ]
        );
    }


    // todo - ALTERADO NO DIA 01/07/2025
    private function general_validate_parameters_new25(Request $request)
    {
        // Substituir vírgula por ponto em 'amount'
        // if ($request->has('amount')) {
        //     $request->merge([
        //         'amount' => str_replace(',', '.', $request->input('amount'))
        //     ]);
        // }

        if ($request->has('amount')) {
            $amount = preg_replace('/[^0-9\.]/', '', str_replace(',', '.', $request->input('amount')));
            $request->merge(['amount' => $amount]);
        }

        $request->validate([
            'amount' => [
                'required',
                'numeric',
                'regex:/^\d+\.\d{2}$/'
            ],
        ], [
            'amount.regex' => 'The value must have exactly 2 decimal places (example: 50.00).',
        ]);

        return $this->validate(
            $request,
            [
                'store_account_number' => 'required|digits:9',
                'payment_type' => 'required|in:push,qrcode,link,nfc_card|exists:payment_types,name',
                'payment_method' => 'required|in:imali,mpesa,emola,mkesh|exists:payment_methods,name',
                'partner_transaction_id' => 'required|unique:payment_requests,partner_transaction_id',
                'amount' => 'required|numeric|min:10',
            ],
            [
                'amount.required' => 'Campo amount é obrigatório',
                'amount.min' => 'O valor minimo deve ser 10MT',
                'amount.numeric' => 'Campo Montente deve ser númerico',
                'store_account_number.required' => 'Campo store_account_number é obrigatório',
                'store_account_number.digits' => 'Campo store_account_number deve ter no maximo 9 digitos',
                'payment_type.required' => 'Campo payment_type é obrigatório',
                'payment_type.in' => 'Tipos de pagamentos válidos: push, qrcode, link, nfc_card',
                'payment_type.exists' => 'Tipo de pagamento não encontrado',
                'payment_method.required' => 'Campo payment_method é obrigatório',
                'payment_method.in' => 'Métodos de pagamentos válidos: imali, mpesa, emola, mkesh',
                'payment_method.exists' => 'Método de pagamento não encontrado',
                'partner_transaction_id.required' => 'Campo partner_transaction_id é obrigatório',
                'partner_transaction_id.unique' => 'Campo partner_transaction_id já existe',
            ]
        );
    }

    private function general_validate_parameters_new25_b2c(Request $request)
    {
        // Substituir vírgula por ponto em 'amount'
        // if ($request->has('amount')) {
        //     $request->merge([
        //         'amount' => str_replace(',', '.', $request->input('amount'))
        //     ]);
        // }

        if ($request->has('amount')) {
            $amount = preg_replace('/[^0-9\.]/', '', str_replace(',', '.', $request->input('amount')));
            $request->merge(['amount' => $amount]);
        }

        $request->validate([
            'amount' => [
                'required',
                'numeric',
                'regex:/^\d+\.\d{2}$/'
            ],
        ], [
            'amount.regex' => 'The value must have exactly 2 decimal places (example: 50.00).',
        ]);


        return $this->validate(
            $request,
            [
                'store_account_number' => 'required|digits:9',
                // 'payment_type' => 'required|in:push,qrcode,link,nfc_card|exists:payment_types,name',
                'payment_method' => 'required|in:imali,mpesa,emola,mkesh|exists:payment_methods,name',
                'partner_transaction_id' => 'required|min:12|max:12|unique:payment_requests,partner_transaction_id',
                'amount' => 'required|numeric|min:1',
            ],
            [
                'amount.required' => 'Campo amount é obrigatório',
                'amount.numeric' => 'Campo Montente deve ser númerico',
                'amount.min' => 'O Montente mínimo deve ser 1',
                'store_account_number.required' => 'Campo store_account_number é obrigatório',
                'store_account_number.digits' => 'Campo store_account_number deve ter no máximo 9 digitos',
                // 'payment_type.required' => 'Campo payment_type é obrigatório',
                // 'payment_type.in' => 'Tipos de pagamentos válidos: push, qrcode, link, nfc_card',
                // 'payment_type.exists' => 'Tipo de pagamento não encontrado',
                'payment_method.required' => 'Campo payment_method é obrigatório',
                'payment_method.in' => 'Métodos de pagamentos válidos: imali, mpesa, emola, mkesh',
                'payment_method.exists' => 'Método de pagamento não encontrado',
                'partner_transaction_id.required' => 'Campo partner_transaction_id é obrigatório',
                'partner_transaction_id.unique' => 'Campo partner_transaction_id já existe',
                'partner_transaction_id.max' => 'Campo partner_transaction_id deve ter 12 caracteres no máximo',
                'partner_transaction_id.min' => 'Campo partner_transaction_id deve ter 12 caracteres no mínimo',
            ]
        );
    }

    private function general_validate_parameters_new25_b2c_check(Request $request)
    {
        // if ($request->has('amount')) {
        //     // Converte float em string com 2 casas decimais
        //     $request->merge([
        //         'amount' => number_format($request->input('amount'), 2, '.', '')
        //     ]);
        // }

        // // if ($request->has('amount')) {
        // //     $amount = preg_replace('/[^0-9\.]/', '', str_replace(',', '.', $request->input('amount')));
        // //     $request->merge(['amount' => $amount]);
        // // }

        // $request->validate([
        //     'amount' => [
        //         'required',
        //         'numeric',
        //         'regex:/^\d+\.\d{2}$/'
        //     ],
        // ], [
        //     'amount.regex' => 'The value must have exactly 2 decimal places (example: 50.00).',
        // ]);

        if ($request->has('amount')) {
            $amount = preg_replace('/[^0-9\.]/', '', str_replace(',', '.', $request->input('amount')));
            $request->merge(['amount' => $amount]);
        }

        $request->validate([
            'amount' => [
                'required',
                'numeric',
                'regex:/^\d+\.\d{2}$/'
            ],
        ], [
            'amount.regex' => 'The value must have exactly 2 decimal places (example: 50.00).',
        ]);


        return $this->validate(
            $request,
            [
                'store_account_number' => 'required|digits:9',
                // 'payment_type' => 'required|in:push,qrcode,link,nfc_card|exists:payment_types,name',
                'payment_method' => 'required|in:imali,mpesa,emola,mkesh|exists:payment_methods,name',
                'partner_transaction_id' => 'required|min:12|max:12|unique:payment_requests,partner_transaction_id',
                'amount' => 'required|numeric|min:1',
            ],
            [
                'amount.required' => 'Campo amount é obrigatório',
                'amount.numeric' => 'Campo Montente deve ser númerico',
                'amount.min' => 'O Montente mínimo deve ser 1',
                'store_account_number.required' => 'Campo store_account_number é obrigatório',
                'store_account_number.digits' => 'Campo store_account_number deve ter no máximo 9 digitos',
                // 'payment_type.required' => 'Campo payment_type é obrigatório',
                // 'payment_type.in' => 'Tipos de pagamentos válidos: push, qrcode, link, nfc_card',
                // 'payment_type.exists' => 'Tipo de pagamento não encontrado',
                'payment_method.required' => 'Campo payment_method é obrigatório',
                'payment_method.in' => 'Métodos de pagamentos válidos: imali, mpesa, emola, mkesh',
                'payment_method.exists' => 'Método de pagamento não encontrado',
                'partner_transaction_id.required' => 'Campo partner_transaction_id é obrigatório',
                'partner_transaction_id.unique' => 'Campo partner_transaction_id já existe',
                'partner_transaction_id.max' => 'Campo partner_transaction_id deve ter 12 caracteres no máximo',
                'partner_transaction_id.min' => 'Campo partner_transaction_id deve ter 12 caracteres no mínimo',
            ]
        );
    }



    private function validate_push_parameters_new25($request)
    {
        return $this->validate(
            $request,
            [
                'client_account_number' => 'required',
            ],
            [
                'client_account_number.required' => 'Campo client_account_number é obrigatório',
                'client_account_number.numeric' => 'Campo client_account_number é númerico',
            ]
        );
    }


    private function validate_check_status_parameters_new25($request)
    {
        return $this->validate(
            $request,
            [
                'payment_type' => 'required|in:push,qrcode,link|exists:payment_types,name',
                'partner_transaction_id' => 'required_if:payment_type,push|required_if:payment_type,link|prohibited_if:payment_type,qrcode',
                'qrcode_token' => 'required_if:payment_type,qrcode|size:112',
            ],
            [
                'payment_type.required' => 'Campo payment_type é obrigatório',
                'partner_transaction_id.required_if' => 'Campo partner_transaction_id é obrigatório',
                'qrcode_token.required_if' => 'Campo qrcode_token é obrigatório',
                'qrcode_token.size' => 'Campo qrcode_token deve ter no máximo 112 caracteres',
                'partner_transaction_id.prohibited_if' => 'Campo partner_transaction_id não é permitido',
                'payment_type.in' => 'Tipos de pagamentos validos:push,qrcode,link',
                'payment_type.exists' => 'Tipo de pagamento nao encontrado'
            ]
        );
    }

    private function validate_parameters_link_new25($request)
    {
        return $this->validate(
            $request,
            [
                'send_to_phone' => 'required|numeric',
                // 'store_account_number' => 'required|numeric',
            ],
            [
                'send_to_phone.required' => 'Campo send_to_phone é obrigatório',
                'send_to_phone.numeric' => 'Campo send_to_phone é númerico',
                // 'store_account_number.required' => 'Campo store_account_number é obrigatório',
                // 'store_account_number.numeric' => 'Campo store_account_number é númerico',
            ]
        );
    }

    private function validate_parameters_qrcode_new25($request)
    {

        return $this->validate(
            $request,
            [
                'qrcode_type' => 'required|in:DYNAMIC_TERMINAL,DYNAMIC_TICKET',
                'expiration_datetime' => 'required_if:qrcode_type,DYNAMIC_TICKET|date_format:Y-m-d H:i:s|prohibited_if:qrcode_type,DYNAMIC_TERMINAL',
                'title' => 'required_if:qrcode_type,DYNAMIC_TICKET|prohibited_if:qrcode_type,DYNAMIC_TERMINAL',
                'description' => 'required_if:qrcode_type,DYNAMIC_TICKET|prohibited_if:qrcode_type,DYNAMIC_TERMINAL'
            ],
            [
                'expiration_datetime.date_format' => 'expiration_datetime must be in the format Y-m-d H:i:s',
                'expiration_datetime.required_if' => 'o campo expiration_datetime é obrigatório',
                'expiration_datetime.prohibited_if' => 'O campo expiration_datetime não é permitido quando o tipo de QRCode for DYNAMIC_TERMINAL',
                'qrcode_type.in' => 'qrcode_type só permite os seguintes estados: DYNAMIC_TERMINAL, DYNAMIC_TICKET',
                'title.required_if' => 'o campo title é obrigatório',
                'description.required_if' => 'o campo description é obrigatório',
                'title.prohibited_if' => 'O campo title não é permitido quando o tipo de QRCode for DYNAMIC_TERMINAL',
                'description.prohibited_if' => 'O campo description não é permitido quando o tipo de QRCode for DYNAMIC_TERMINAL',
            ]
        );
    }

    public function imaliway_guard_new25(Request $request)
    {
        //
        $this->validate(
            $request,
            [
                'transaction_type' => 'required|in:C2B,B2C,B2B,C2C',
            ],
            [
                'transaction_type.required' => 'Campo transaction_type é obrigatório',
                'transaction_type.in' => 'O campo transaction_type deve ser C2B, B2C, B2B e C2C.',
            ]
        );

        if ($request->transaction_type == 'C2B') {
            return $this->c2bDepositStoreNew25($request);
        } elseif ($request->transaction_type == 'B2C') {
            return  $this->b2cDepositStoreNew25($request);
        }
    }

    public function c2bDepositStoreNew25(Request $request)
    {
        // PaymentChat::dispatch('Funck you...');
        // PaymentPush::dispatch('Payment Push...');

        // PaymentQrcode::dispatch('Payment Qrcode...');
        // broadcast(new PaymentPush('Payment Push...'))->toOthers();
        // broadcast(new PaymentQrcode('Payment Qrcode...'))->toOthers();
        // event(new PaymentQrcode('Payment Qrcode...'));
        // // event(new PaymentPush('Payment Push...'));
        // return  'done';

        $this->general_validate_parameters_new25($request);

        $wallet_name = $request->payment_method;
        $payment_type = $request->payment_type;

        // $wallet = Operator::query()->where('acronym', $wallet_name)->first();
        // if (!$wallet) return SendResponse::errorResp404notfound();

        $payment_method = PaymentMethod::query()->where('name', strtoupper($wallet_name))->first();
        if (!$payment_method) return SendResponse::errorResp404notfound('Metodo de pagamento nao encontrado', 'Payment Method not found');

        $payment_type_obj = PaymentType::query()->where('name', strtoupper($payment_type))->first();
        if (!$payment_type_obj) return SendResponse::errorResp404notfound('Tipo de pagamento nao encontrado', 'Payment Type not found');

        //buscando a Loja (Store)
        $store = Store::getStoreAccount($request->store_account_number);
        if (!$store) return SendResponse::errorResp404notfound(
            'Número da Loja invalido',
            'Invalid Store Account',
        );

        $business = BusinessAccount::query()->where('id', $store->business_account_id)->first();
        if (!$business) return SendResponse::errorResp404notfound(
            'Loja nao associada a conta Business',
            'Store not associated with Business account',
        );

        $store_contract = Store::getStoreContractsAndConfigs($store->account_number);

        //validar KYC da Loja
        $storeKyc = new StoreKyc($store_contract);
        $respStoreKyc = $storeKyc->checkStoreKYC($request->amount);
        // if ($respStoreKyc->getStatusCode() != 200) return $respStoreKyc;
        if ($respStoreKyc->getStatusCode() != 200) return response()->json(['message' => $respStoreKyc->getData()->message, 'status' => 'FAILED'], 400);

        $request->request->add(['business_account_number' => $business->account_number]);

        switch (strtoupper($payment_type_obj->name)) {
            case 'PUSH':
                return $this->make_push_payments_new25($request, $wallet_name, $payment_method, $payment_type_obj);
                break;
            case 'QRCODE':
                return $this->make_qrcode_payments_new25($request, $wallet_name);
                break;
            case 'LINK':
                return $this->make_link_payments_new25($request, $wallet_name, $payment_method, $payment_type_obj);
                break;
            case 'NFC_CARD':
                return $this->make_nfc_card_payments_new25($request);
                break;

            default:
                # code...
                break;
        }
    }

    public function b2cDepositStoreNew25(Request $request)
    {
        // Para efetuar uma transferencia para Credit = MamaSoko devemos verificar se 
        // no request esta ser enviado um credit_reference ou transaction_id do credito

        // se chamar pagamento directo entao ==  Route::post('imaliway/v2/payments', 
        // se chamar debito entao ==   Route::post('create_debit', 

        $this->general_validate_parameters_new25_b2c($request);

        $wallet_name = $request->payment_method;
        $payment_type = 'B2C';

        $payment_method = PaymentMethod::query()->where('name', strtoupper($wallet_name))->first();
        if (!$payment_method) return SendResponse::errorResp404notfound('Metodo de pagamento nao encontrado', 'Payment Method not found');

        $payment_type_obj = PaymentType::query()->where('name', strtoupper($payment_type))->first();
        if (!$payment_type_obj) return SendResponse::errorResp404notfound('Tipo de pagamento nao encontrado', 'Payment Type not found');

        $business = User::getAccount($request->store_account_number); // conta a debitar

        if (!$business) return SendResponse::errorResp404notfound('Conta nao encontrada', 'Account not found');
        if ($business->profile == 'BUSINESS') return SendResponse::errorResp404notfound('Conta nao encontrada', 'Account not found');

        // return $business

        // todo -- POR IMPLEMENTAR validar KYC de Business ...
        // todo -- pegar a taxa a cobrar do Business ...

        // if (strtolower($wallet_name) == 'mpesa') {

        //     $fee = Fee::query()
        //         ->where('code', 'TRF_MPESA_BUSINESS_B2C')
        //         ->where('commission_type', $business->commission_type)
        //         ->first();
        // }

        // if (strtolower($wallet_name) == 'imali') {
        //     $fee = Fee::query()
        //         ->where('code', 'TRF_IMALI_BUSINESS_B2C')
        //         ->where('commission_type', $business->commission_type)
        //         ->first();
        // }

        // if (strtolower($wallet_name) == 'emola') {
        //     $fee = Fee::query()
        //         ->where('code', 'TRF_EMOLA_BUSINESS_B2C')
        //         ->where('commission_type', $business->commission_type)
        //         ->first();
        // }

        // if (strtolower($wallet_name) == 'mkesh') {
        //     $fee = Fee::query()
        //         ->where('code', 'TRF_MKESH_BUSINESS_B2C')
        //         ->where('commission_type', $business->commission_type)
        //         ->first();
        // }

        $feeCodes = [
            'mpesa' => 'TRF_MPESA_BUSINESS_B2C',
            'imali' => 'TRF_IMALI_BUSINESS_B2C',
            'emola' => 'TRF_EMOLA_BUSINESS_B2C',
            'mkesh' => 'TRF_MKESH_BUSINESS_B2C',
        ];

        // todo -- TEMPORARIO 🚧
        // Verifica se o serviço emola está indisponível
        if ($wallet_name === 'emola') {
            return SendResponse::errorResp400('Serviço temporariamente indisponível', 'Service temporarily unavailable');
        }
        // todo -- TEMPORARIO 🚧

        $code = $feeCodes[strtolower($wallet_name)] ?? null;
        if (!$code) return SendResponse::errorResp400('Carteira inválida');

        $fee = Fee::query()
            ->where('code', $code)
            ->where('commission_type', $business->commission_type)
            ->first();


        if (!$fee) return SendResponse::errorResp404notfound('Taxa não encontrada', 'Fee Not found');

        if (($fee->commission_form == 'PERCENTAGE') && (($request->amount < ($fee->min_value / 1000)) || ($request->amount > ($fee->max_value / 1000)))) {
            return SendResponse::errorResp400('O montante ' . $request->amount . ' deve estar entre ' . $fee->min_value / 1000 . ' e ' . $fee->max_value / 1000);
        }

        $float_amount = floatval($request->amount);
        $percentage_fee = ($fee->value / 100); //valor em  percentagem
        $value_fee = ($fee->value / 100); //valor em metical

        $fee_val = $fee->commission_form == 'FIXED' ?  $value_fee : ($percentage_fee / 100);
        $total_value = $fee->commission_form == 'FIXED' ? $fee_val + $float_amount : ($fee_val * $float_amount) + $float_amount;

        // Validar saldo do Business Account 
        if ($business->balance < $total_value) {
            return SendResponse::errorResp400(
                'Saldo da conta ' . $business->account_number . ' insuficiente.'
            );
        }

        try {

            DB::beginTransaction();

            $business->balance -= $total_value;
            $business->captive_balance += $total_value;
            $business->update();

            $payment_request = PaymentRequest::create([
                'transaction_id' => $this->generate_full_transaction_id_new25($wallet_name),
                'partner_transaction_id' => $request->partner_transaction_id,
                'client_account_number' => $request->client_account_number,
                'transaction_direction' => $payment_type,
                'transaction_type' => 'TRANSFER',
                'store_account_number' => $request->store_account_number,
                'business_account_number' => $request->store_account_number,
                'amount' => $request->amount,
                'description' => 'Transferência ' . $payment_method->name,
                'payment_methods_id' => $payment_method->id,
                'payment_types_id' => $payment_type_obj->id,
                // 'expiration_datetime' => $request->expiration_datetime,
                'status' => 'PENDING',
            ]);


            //code...
            $request->request->add(['phone' => $request->client_account_number, 'imaliReference' => intval($business->account_number)]);
            // return $request->all();
            $response = $this->call_emola_mpesa_mkesh_b2c_api_v2($request, $payment_request->transaction_id, $wallet_name);
            if (($response->status() != 200) && ($response->status() != 201)) {

                Log::info('Json error', [$response->json()]);

                $respJson = $response->json();

                $payment_request->status = 'FAILED';
                $payment_request->status_reason = array_key_exists('partnerMessage', $respJson) ? $respJson['partnerMessage'] : $respJson['partnerCode']; // $response->json(),

                // Log::info('Json error', [$response->json()]);
                // $payment_request->status = 'FAILED';
                // $payment_request->status_reason = $response->json()['partnerMessage']; // $response->json(),
                $payment_request->update();

                $business->balance += $total_value;
                $business->captive_balance -= $total_value;
                $business->update();

                return $this->logWalletErrorAPI($response, $wallet_name);
            }

            $payment_request->status = 'SUCCESS';
            $payment_request->is_payment_confirmed = 1;
            $payment_request->update();

            $business->captive_balance -= $total_value;
            $business->update();

            // Codigo que vai pegar a referencia no status aprovado que tenha o "credit_reference" XYZ  que esteja preechido e mudar o status de APPROVED para IN_PROGRESS

            // 🚀 Verificar se há credit_reference enviado e atualizar status do crédito
            if (!empty($request->credit_reference)) {
                $creditRequest = \App\BusinessCreditRequest::query()
                    ->where('credit_reference', $request->credit_reference)
                    ->where('status', 'APPROVED')
                    ->first();

                if ($creditRequest) {
                    $creditRequest->status = 'IN_PROGRESS';
                    $creditRequest->save();

                    // saldo a creditar na conta "credit_balance"
                    $business->credit_balance += $total_value;
                    $business->update();

                    Log::info('Credit reference atualizado para IN_PROGRESS', [
                        'credit_reference' => $request->credit_reference,
                        'transaction_id' => $payment_request->transaction_id
                    ]);
                } else {
                    Log::info('Credit reference não encontrado ou não está aprovado', [
                        'credit_reference' => $request->credit_reference
                    ]);
                }
            }

            DB::commit();

            return SendResponse::successResp201('Transferência feita com sucesso', 'Success Transfer');
        } catch (\Throwable $th) {
            //throw $th;
            Log::info([
                'content' => $th
            ]);

            $payment_request->status = 'FAILED';
            $payment_request->status_reason = $th->getMessage();
            $payment_request->update();

            $business->captive_balance -= $total_value;
            $business->balance += $total_value;
            $business->update();

            DB::rollBack();

            return SendResponse::warningResp500serverError('Erro ao fazer a Transferência', 'Transfer Transaction error');
        }
    }

    // todo --- checkB2C
    public function checkB2CDepositNew25(Request $request)
    {
        if ($request->transaction_type != 'B2C') return SendResponse::errorResp400('Tipo de Trandacao Invalida', 'Invalid Transaction Type');
        // $this->general_validate_parameters_new25_b2c($request);
        $this->general_validate_parameters_new25_b2c_check($request);

        $wallet_name = $request->payment_method;
        $payment_type = 'B2C';

        $payment_method = PaymentMethod::query()->where('name', strtoupper($wallet_name))->first();
        if (!$payment_method) return SendResponse::errorResp404notfound('Metodo de pagamento nao encontrado', 'Payment Method not found');
        // return $payment_method;

        $payment_type_obj = PaymentType::query()->where('name', strtoupper($payment_type))->first();
        if (!$payment_type_obj) return SendResponse::errorResp404notfound('Tipo de pagamento nao encontrado', 'Payment Type not found');

        $business = User::getAccount($request->store_account_number); // conta a debitar

        if (!$business) return SendResponse::errorResp404notfound('Conta nao encontrada', 'Account not found');

        // return $business

        // todo -- POR IMPLEMENTAR validar KYC de Business ...
        // todo -- pegar a taxa a cobrar do Business ...

        if (strtolower($wallet_name) == 'mpesa') {

            $fee = Fee::query()
                ->where('code', 'TRF_MPESA_BUSINESS_B2C')
                ->where('commission_type', $business->commission_type)
                ->first();
        }

        if (strtolower($wallet_name) == 'imali') {
            $fee = Fee::query()
                ->where('code', 'TRF_IMALI_BUSINESS_B2C')
                ->where('commission_type', $business->commission_type)
                ->first();
        }

        if (strtolower($wallet_name) == 'emola') {
            $fee = Fee::query()
                ->where('code', 'TRF_EMOLA_BUSINESS_B2C')
                ->where('commission_type', $business->commission_type)
                ->first();
        }

        if (strtolower($wallet_name) == 'mkesh') {
            $fee = Fee::query()
                ->where('code', 'TRF_MKESH_BUSINESS_B2C')
                ->where('commission_type', $business->commission_type)
                ->first();
        }

        if (!$fee) return SendResponse::errorResp404notfound('Taxa não encontrada', 'Fee Not found');

        if (($fee->commission_form == 'PERCENTAGE') && (($request->amount < ($fee->min_value / 1000)) || ($request->amount > ($fee->max_value / 1000)))) {
            return SendResponse::errorResp400('O montante ' . $request->amount . ' deve estar entre ' . $fee->min_value / 1000 . ' e ' . $fee->max_value / 1000);
        }

        // $float_amount = floatval($request->amount);
        // $fee_value = $fee->commission_form == 'FIXED' ? $fee->value / 100 : ($fee->value / 100) / 100;
        // $total_value = $fee->commission_form == 'FIXED' ? $fee_value + $float_amount : ($fee_value * $float_amount) + $float_amount;

        $float_amount = floatval($request->amount);
        $percentage_fee = ($fee->value / 1000); //valor em  percentagem
        $value_fee = ($fee->value / 1000); //valor em metical

        $fee_value = $fee->commission_form == 'FIXED' ?  $value_fee : ($percentage_fee / 100);
        $total_value = $fee->commission_form == 'FIXED' ? $fee_value + $float_amount : ($fee_value * $float_amount) + $float_amount;

        // validar saldo do Business Account 
        if ($business->balance < $total_value) {
            return SendResponse::errorResp400(
                'Saldo da conta ' . $business->account_number . ' insuficiente.'
            );
        }

        // try {
        //     //code...
        //     $maskedNameResponse = $this->getWalletCustomerName(new Request(['phone' => $request->client_account_number]), $wallet_name);

        //     return $maskedNameResponse->json();
        // } catch (\Throwable $th) {
        //     //throw $th;

        //     Log::info([
        //         'content' => $th
        //     ]);
        // }

        // $data = [
        //     'client_account_number' => $request->client_account_number,
        //     'amount' => number_format($request->amount, 2, '.', ''), // string com 2 casas decimais
        //     'total' => round($total_value, 2), // float com 2 casas decimais
        //     'fee' => round($fee_value * $float_amount, 2), // float com 2 casas decimais
        //     'masked_name' => 'Indisponível',
        // ];

        $data = [
            'client_account_number' => $request->client_account_number,
            'amount' => number_format($request->amount, 2, '.', ''), // string
            'total' => $this->truncateFloat($total_value, 2), // truncado, float
            'fee' => $this->truncateFloat($fee_value * $float_amount, 2), // truncado, float
            'masked_name' => 'Indisponível',
        ];




        // $data = [
        //     'client_account_number' => $request->client_account_number,
        //     'amount' => $request->amount,
        //     'total' => $total_value,
        //     'fee' => $fee_value * $float_amount,
        //     // 'masked_name' => $maskedNameResponse ?? 'Indisponível',
        //     'masked_name' => 'Indisponível',
        //     // 'stamp_tax' => $fee_value * (2 / 100),
        //     // 'commission' => $fee_value - ($fee_value * (2 / 100)),
        // ];

        // retornar responde json
        return response()->json(['data' => $data]);
    }


    function truncateFloat($number, $decimals = 2)
    {
        $factor = pow(10, $decimals);
        return floor($number * $factor) / $factor;
    }


    private function create_payment_requests_new25($request, $wallet_name, $payment_method, $payment_type)
    {
        $payment_request = PaymentRequest::query()
            // ->where('amount', $request->amount)
            ->where('store_account_number', $request->store_account_number)
            ->where('payment_methods_id', $payment_method->id)
            ->where('client_account_number', $request->client_account_number)
            ->where('status', 'PENDING')
            ->first();

        if ($payment_request) return SendResponse::errorResp400('O cliente ' . $request->client_account_number . ' ja tem um ' . $payment_type->name . ' em curso.', 'The customer ' . $request->client_account_number . ' already has a ' . $payment_type->name . ' pending');

        // if ($payment_request && ($payment_request->status == 'PENDING')) return SendResponse::errorResp400('O cliente ja tem um ' . $payment_type->name . ' com o mesmo montante por pagar na sua loja', 'The customer already has a' . $payment_type->name . ' with the same amount to pay in your store');

        // Se o comerciante nao enviar o expiration_datetime a API por default adiciona + um dia de duracao do link
        if (!$request->has('expiration_datetime')) {
            $duration = new DateTime(); // Data atual
            (strtolower($payment_method->name) === 'mkesh') ? $duration->modify('+8 minutes') : $duration->modify('+3 minutes');
            $request->request->set('expiration_datetime', $duration->format('Y-m-d H:i:s'));
        }

        $payment_request = PaymentRequest::create([
            'transaction_id' => $this->generate_full_transaction_id_new25($wallet_name),
            'partner_transaction_id' => $request->partner_transaction_id,
            'client_account_number' => $request->client_account_number,
            'store_account_number' => $request->store_account_number,
            'business_account_number' => $request->business_account_number,
            'amount' => $request->amount,
            'description' => 'Pagamento ' . $payment_type->name . ' ' . $payment_method->name,
            'payment_methods_id' => $payment_method->id,
            'payment_types_id' => $payment_type->id,
            'link_id' => $request->link_id,
            'expiration_datetime' => $request->expiration_datetime,
            'status' => 'PENDING',
        ]);

        //executa o job para enviar push notification para o cliente final
        // if ($payment_type !== strtoupper('qrcode')) SendPushPaymentJob::dispatch($payment_request);
        // SendPushPaymentJob::dispatch($payment_request);
        // $this->send_push_payment_to_client_new25(strtolower($payment_method->name), strtolower($payment_type->name), $payment_request->transaction_id);
        if ($payment_type->name !== strtoupper('qrcode')) SendPushPaymentJob::dispatch($payment_request); // todo 29-09-25

        $data = [
            'transaction_id' => $payment_request->transaction_id,
            'partner_transaction_id' => $payment_request->partner_transaction_id,
            'amount' => $payment_request->amount,
            'expiration_datetime' => $payment_request->expiration_datetime,
            'status' => $payment_request->status,
        ];

        return response()->json(['data' => $data]);

        // return SendResponse::successResp200(
        //     'O teu pedido de pagamento com transação: '. $payment_request->transaction_id.' - foi feito com sucesso. Aguarde 10s vai receber um push de pagamento e confirme com teu PIN: ' . $wallet_name,
        //     'Your payment request with transaction: '. $payment_request->transaction_id.' - has been made successfully. Wait 10s you will receive a payment push and confirm with your PIN: ' . $wallet_name
        // );
    }


    // todo --- criar o metodo de envio de push atraves do transaction_id (imaliway/{wallet_name}/payment/{transaction_id})

    public function send_push_payment_to_client_new25($wallet_name, $payment_type, $transaction_id)
    {
        $payment_method = PaymentMethod::query()->where('name', strtoupper($wallet_name))->first();
        if (!$payment_method) return SendResponse::errorResp404notfound('Metodo de pagamento push nao encontrado', 'Push Payment Method not found');

        $payment_type_obj = PaymentType::query()->where('name', strtoupper($payment_type))->first();
        if (!$payment_type_obj) return SendResponse::errorResp404notfound('Tipo de pagamento push nao encontrado', 'Payment Type not found');

        $push_payment = PaymentRequest::query()
            ->where('transaction_id', $transaction_id)
            ->orWhere('partner_transaction_id', $transaction_id)
            ->first();

        $this->checkExpiredLink($push_payment);

        // todo -- REVER a terceira linha de validacao (push, qrcode, link -- por parametro)
        if (!$push_payment) return SendResponse::errorResp404notfound('Pedido de push nao encontrado', 'Push Payment not found');
        if ($push_payment->status === 'EXPIRED') return SendResponse::errorResp404notfound('Pedido de push expirado', 'Expired Push Payment');
        if ($push_payment->status === 'SUCCESS') return SendResponse::errorResp404notfound('Este push ja foi pago', 'This Push Already paid');
        if ($push_payment->status === 'REJECTED') return SendResponse::errorResp404notfound('Este push foi recusado', 'This Push was rejected');
        if ($push_payment->status === 'FAILED') return SendResponse::errorResp404notfound('Este push falhou', 'This Push failed');
        // if ($push_payment->status != 'PENDING') return SendResponse::errorResp404notfound('Pedido de push nao encontrado', 'Push Payment not found');
        if ($push_payment->payment_types_id != $payment_type_obj->id) return SendResponse::errorResp404notfound('Pedido de push nao encontrado', 'Push Payment not found');
        if ($push_payment->payment_methods_id != $payment_method->id) return SendResponse::errorResp404notfound('Pedido de push nao encontrado', 'Push Payment not found');

        $request = new Request([
            'client_account_number' => $push_payment->client_account_number,
            'store_account_number' => $push_payment->store_account_number,
            'payment_types_id' => $push_payment->payment_types_id,
            'partner_transaction_id' => $push_payment->partner_transaction_id,
            'amount' => (string)$push_payment->amount
        ]);

        try {
            $this->validate_push_parameters_new25($request);
        } catch (\Throwable $th) {
            Log::info('[ERROR_VALIDATING_PUSH_PARAMETERS]', ['error' => $th->getMessage(), 'line' => $th->getLine()]);
        }

        // $transaction_id = $this->generate_full_transaction_id_new25($wallet_name);
        $transaction_id = $push_payment->transaction_id;

        if (in_array($wallet_name, ['mpesa', 'emola', 'mkesh'])) {

            try {

                $response = $this->call_emola_mpesa_mkesh_c2b_api_store_new25($request, $transaction_id, $wallet_name);

                if (($response->status() != 200) && ($response->status() != 201)) {

                    Log::info('[RESPONSE_PUSH_DATA]', ['data' => $response->json()]);

                    $objRespFromThirdParty = json_decode($response->body());

                    if (is_object($objRespFromThirdParty) && property_exists($objRespFromThirdParty, 'error'))
                        throw new Error($wallet_name . ' - ' . $objRespFromThirdParty->error);
                    if (is_object($objRespFromThirdParty) && property_exists($objRespFromThirdParty, 'partnerMessageEn'))
                        throw new Error($wallet_name . ' - ' . $objRespFromThirdParty->partnerMessageEn);
                    else
                        throw new Error($wallet_name . ' - ' . $objRespFromThirdParty);
                }

                if (!$this->is_wallet_transaction_successfully_done($wallet_name, $response)) return $this->logWalletErrorAPI($response, $wallet_name);

                $push_payment->thirdparty_transaction_id = $response->json()['partnerTransactionId'];
                if ($wallet_name != 'mkesh') $push_payment->is_payment_confirmed = 1;
                $push_payment->update();

                Log::info('[SUCCESS_SENDING_PUSH]', ['message' => 'Push de pagamento: ' . $transaction_id . ' enviado com sucesso']);
                Log::info('[SUCCESS_SENDING_PUSH3]', ['message' => 'Push de pagamento: ' . $transaction_id . ' enviado com sucesso']);



                return SendResponse::successResp200('Push de pagamento: ' . $transaction_id . ' enviado com sucesso', 'Push payment: ' . $transaction_id . ' sent successfully');
            } catch (\Throwable $th) {

                Log::info('[ERROR_SENDING_PUSH]', ['error' => $th->getMessage(), 'line' => $th->getLine()]);

                $push_payment->update(['status' => 'FAILED', 'status_reason' => $th->getMessage()]);
                // todo ---- enviar email para notificar que o servico de pagamento por push das carteiras esta offline ******

                return SendResponse::warningResp500serverError('Falha no pagamento do push, motivo - ' . $th->getMessage(), 'Payment push failed, reason - ' . $th->getMessage());
            }
        } else if ($wallet_name === 'imali') {

            // $user_account = User::getAccount($request->client_account_number);
            // if (!$user_account) return SendResponse::errorResp404notfound('Conta de Cliente inválida', 'Invalid Client Account');
            // $user_payer = User::getUserDetails($user_account->user_id);

            $user_payer = User::getUserDetails($request->client_account_number); # USER -- numero de telefone ✅ | numero de conta ❌
            Log::info(['DADOS DO $user_payer antes do IF 3########'], ['data' => $user_payer]);

            if (!$user_payer) {
                $accountPayer = User::getAccount($request->client_account_number);
                Log::info(['DADOS DO $accountPayer 3########'], ['data' => $accountPayer]);

                if (!$accountPayer) return SendResponse::errorResp404notfound(
                    'Conta não encontrada',
                    'Account Not Found',
                );
                $user_payer = User::getUserDetails($accountPayer->user_id);
                Log::info(['DADOS DO $user_payer dentro do IF 3########'], ['data' => $user_payer]);
            }

            $store = Store::getStoreContractsAndConfigs($request->store_account_number);
            if (!$store) return SendResponse::errorResp404notfound('Conta da Loja inválida', 'Invalid Store Account');

            $push = new PushNotification(
                'Pagamento iMali : ' . $transaction_id,
                $user_payer->name . ' gerou um pagamento de ' . $request->amount . ' MT',
                $user_payer->firebase_token,
                'com.imali.payapp.payment_PUSH_NOTIFICATION'
            );

            $data = array(
                'transaction' => $transaction_id,
                'amount' => $request->amount,
                'account_number' => (string)$store->account_number,
                'name' => $store->name,
                'address' => $store->address,
                'mobile_phone' => $store->mobile_phone,
                'logo' => $store->logo,
                'logo_category' => $store->logo_category,
                'category' => $store->category,
                'description' => 'Pagamento Push',
                'route' => 'PUSH_NOTIFICATION',
                'created_at' => now()
            );

            $is_push_sent =  $push->sendPush($data);

            if ($is_push_sent) return SendResponse::successResp200('Push enviado com sucesso', 'Push Sent Successfull');



            return SendResponse::warningResp500serverError('Error ao enviar Push', 'Error sent Push');
        } else {
            return response()->json(['message' => 'Método de Pagamento Invalido.']);
        }
    }

    public function confirm_push_payment_new25($wallet_name, $payment_type, $transaction_id)
    {
        $payment_method = PaymentMethod::query()->where('name', strtoupper($wallet_name))->first();
        if (!$payment_method) return SendResponse::errorResp404notfound('Metodo de pagamento ' . $wallet_name . ' nao encontrado', ucfirst($wallet_name) . ' Payment Method not found');

        $payment_type_obj = PaymentType::query()->where('name', strtoupper($payment_type))->first();
        if (!$payment_type_obj) return SendResponse::errorResp404notfound('Tipo de pagamento ' . $wallet_name . ' nao encontrado', ucfirst($payment_type) . ' Payment Type not found');

        // todo -- Incluir codigo do txt aqui!!!

        $payment_check = PaymentRequest::where(function ($query) use ($transaction_id) {
            $query->where('transaction_id', $transaction_id)
                ->orWhere('partner_transaction_id', $transaction_id);
        })
            ->where('payment_types_id', $payment_type_obj->id)
            ->where('payment_methods_id', $payment_method->id)
            ->first();

        if (!$payment_check) return SendResponse::errorResp404notfound('Pedido de pagamento ' . $wallet_name . ' nao encontrado', ucfirst($wallet_name) . ' Payment Request not found');

        if ($payment_check->is_payment_confirmed) {

            $request_payment = new Request([
                'transaction' => $transaction_id,
                'payer_account_number' => $payment_check->client_account_number,
                'store_account_number' => $payment_check->store_account_number,
                'payment_type' => 'PAYMENT_STORE',
                'partner_transaction_id' => $payment_check->partner_transaction_id,
                'thirdparty_transaction_id' => $payment_check->thirdparty_transaction_id,
                'amount' => $payment_check->amount,
            ]);

            $pay_controller = new PaymentController;
            return $pay_controller->payment_guard($request_payment);
        }

        return response()->json(['data' => 'No Payments to do...']);
    }


    // todo --- 25
    public function check_payment_requests_status_new25(Request $request)
    {

        $this->validate_check_status_parameters_new25($request);

        if ($request->payment_type == 'push') {

            $payment_request = PaymentRequest::query()
                ->where('transaction_id', $request->partner_transaction_id)
                ->orWhere('partner_transaction_id', $request->partner_transaction_id)
                ->first();

            if (!$payment_request)  return SendResponse::errorResp404notfound('Pedido de pagamento por: ' . $request->payment_type . ' nao encontrado', ' Payment Request: ' . $request->payment_type . ' not found');
            $this->checkExpiredLink($payment_request);

            $data = [
                'status' => $payment_request->status,
                'left_time' => $this->get_left_time_new25($payment_request->expiration_datetime),
                'reason' => $payment_request->status_reason
            ];

            if (!$payment_request->status_reason) unset($data['reason']);

            return response()->json(['data' => $data]);
        } else if ($request->payment_type == 'link') {

            // todo --- metodo de teste -- updated

            // todo --- metodo de teste -- updated

            $payment_link = Link::query()
                ->where('link_id', $request->partner_transaction_id)
                ->orWhere('partner_transaction_id', $request->partner_transaction_id)
                ->first();

            if (!$payment_link)  return SendResponse::errorResp404notfound('Pedido de pagamento por: ' . $request->payment_type . ' nao encontrado', ' Payment Request: ' . $request->payment_type . ' not found');
            $this->checkExpiredLink($payment_link);

            $data = [
                'status' => $payment_link->status,
                'left_time' => $this->get_left_time_new25($payment_link->expiration_datetime)
            ];

            return response()->json(['data' => $data]);
        } else if ($request->payment_type == 'qrcode') {



            $qrcode_data = null;

            try {

                $this->verify_qrcode_new25($request->qrcode_token);
                $qrcode_data = json_decode($this->get_qrcode_data_new25($request->qrcode_token));
            } catch (\Throwable $th) {

                if ($th->getMessage() == 'QRCODE_INVALID') return response()->json(['message' => 'Qrcode invalido.'], 400);

                if ($th->getMessage() == 'QRCODE_EXPIRED') {

                    $data = [
                        'status' => 'EXPIRED',
                        'left_time' => 0
                    ];

                    return response()->json(['data' => $data]);
                }

                return response()->json(['message' => 'Erro inesperado no servidor. Message -' . $th->getMessage() . '- Line:' . $th->getLine()], 500);
            }

            $payment_qrcode = Qrcode::query()
                ->where('qrcode_id', $qrcode_data->qrcode_id)
                ->first();

            if (!$payment_qrcode)  return SendResponse::errorResp404notfound('Pedido de pagamento por: ' . $request->payment_type . ' nao encontrado', ' Payment Request: ' . $request->payment_type . ' not found');
            $this->checkExpiredLink($payment_qrcode);

            $data = [
                'status' => $payment_qrcode->status,
                'left_time' => $this->get_left_time_new25($payment_qrcode->expiration_datetime)
            ];

            return response()->json(['data' => $data]);
        } else {
            return SendResponse::errorResp404notfound('Tipo de pagamento: ' . $request->payment_type . ' nao encontrado', ' Payment Request: ' . $request->payment_type . ' not found');
        }
    }


    private function get_left_time_new25($expiration_datetime)
    {

        $request = new Request(['expiration_datetime' => $expiration_datetime]);
        $this->validate($request, ['expiration_datetime' => 'required|date_format:Y-m-d H:i:s']);

        $currentTimestamp = time();
        $expirationTimestamp = strtotime($request->expiration_datetime); // Converte para timestamp
        $diffInSeconds = max(0, $expirationTimestamp - $currentTimestamp); // Diferença em segundos
        $minutes = floor($diffInSeconds / 60);
        $seconds = $diffInSeconds % 60;

        if (($minutes == 0) && ($seconds == 0)) return 0;

        return $minutes . ':' . $seconds;
    }


    public function check_wallets_payment_status_new25(Request $request, $wallet_name, $thirdparty_transaction_id)
    {
        $payment_method = PaymentMethod::query()->where('name', strtoupper($wallet_name))->first();
        if (!$payment_method) return SendResponse::errorResp404notfound('Metodo de pagamento ' . $wallet_name . ' nao encontrado', ucfirst($wallet_name) . ' Payment Method not found');

        if ($wallet_name == 'emola') {
            $this->validate(
                $request,
                ['emolaTransacType' => 'required|in:C2B,B2C,QUERY_TXN,QUERY_CUS,QUERY_BEN'],
                ['emolaTransacType.required' => 'parametro emolaTransacType é obrigatório', 'emolaTransacType.in' => 'parametro emolaTransacType deve ser C2B,B2C,QUERY_TXN,QUERY_CUS,QUERY_BEN']
            );
        }

        return $this->call_b2c_c2b_status_new25($request, $wallet_name, $thirdparty_transaction_id);
    }

    // Check Client Account Balance

    public function check_imali_account_balance_new25(Request $request)
    {

        $this->validate(
            $request,
            [
                'account_number' => 'required|numeric|digits:9',
            ],
            [
                'account_number.required' => 'Campo account_number é obrigatório',
                'account_number.numeric' => 'Campo account_number é numérico',
                'account_number.digits' => 'Campo account_number deve ter 9 digitos',
            ]
        );

        $account = User::getAccount($request->account_number);
        if (!$account) return SendResponse::errorResp400('Número de Conta inválido', 'Invalid Account Number');

        return response()->json(['balance' => $account->balance]);
    }

    // Pending Payments
    public function get_imali_push_pendings_new25()
    {
        $account = User::getUserAccount();
        if (!$account) return SendResponse::errorResp400('Número de Conta inválido', 'Invalid Account Number');

        $pending_push = PaymentRequest::query()
            ->where('payment_methods_id', 1)
            ->where('client_account_number', $account->account_number)
            ->where('status', 'PENDING')
            ->orderByDesc('created_at')
            ->get();

        return response()->json(['data' => $pending_push]);
    }

    // Check Client Account Balance


    public function call_b2c_c2b_status_new25(Request $request, $wallet_name, $transactionId)
    {

        $url = $this->get_wallet_status_url_new25($wallet_name);

        if (!$url) throw new Exception("variável de ambiente " . $this->get_wallet_status_env_param($wallet_name) . " não declarado no ficheiro .env");

        $data = $this->get_wallet_status_request_data_new25($request, $transactionId, $wallet_name);

        //mkeshTransacReference
        // return $url . '?mpesaTransacReference=' . $transactionId;

        if ($wallet_name === 'mpesa') return Http::timeout(2000)->get($url . '?mpesaTransacReference=' . $transactionId);

        // $response = Http::get('http://localhost:3003/mpesa/customer-masked-name?phone=' . $request->phone);

        return Http::timeout(2000)->post($url, $data);
        // return Http::timeout(2000)->post($url, ["mkeshTransacReference" => "PTK_B2A40TN9QRXJ"]);
    }


    // todo --- NOVOS METODOS CARREGAMENTO - 17/07/2024
    private function get_wallet_status_url_new25($wallet_name)
    {
        switch ($wallet_name) {
            case 'mpesa':
                return $_ENV['MPESA_TRANSACTION_STATUS_URL'];
                break;
            case 'emola':
                return $_ENV['EMOLA_TRANSACTION_STATUS_URL'];
                break;
            case 'mkesh':
                return $_ENV['MKESH_TRANSACTION_STATUS_URL'];
                break;

            default:
                throw new Exception("variáveis de ambiente (MPESA_TRANSACTION_STATUS_URL, EMOLA_TRANSACTION_STATUS_URL, MKESH_TRANSACTION_STATUS_URL) não definidos no ficheiro .env");
                break;
        }
    }

    private function get_wallet_status_request_data_new25($request, $transactionId, $wallet_name)
    {
        $data = [];

        switch ($wallet_name) {
            case 'mpesa':
                $data['mpesaTransacReference'] = $transactionId;
                return $data;
                break;
            case 'emola':
                $data['emolaTransacReference'] = $transactionId;
                $data['emolaTransacType'] = $request->emolaTransacType;
                return $data;
                break;
            case 'mkesh':
                $data['mkeshTransacReference'] = $transactionId;
                return $data;
                break;

            default:
                return null;
                break;
        }
    }

    private function call_emola_mpesa_mkesh_c2b_api_store_new25($request, $transactionId, $wallet_name)
    {

        $url = $this->get_wallet_url($wallet_name);

        if (!$url) throw new Exception("variável de ambiente " . $this->get_wallet_env_param($wallet_name) . " não declarado no ficheiro .env");

        $data = $this->get_wallet_request_data_new25($request, $transactionId, $wallet_name, 'Cliente ' . $wallet_name);

        return Http::timeout(120)->post($url, $data);
    }


    private function generate_full_transaction_id_new25($wallet_name)
    {
        return $this->generate_wallet_prefix25($wallet_name) . $this->generate_transaction_id_new25();
    }

    private function generate_wallet_prefix25($wallet_name)
    {
        switch ($wallet_name) {
            case 'mpesa':
                return 'MPS' . substr(date('Y'), 2, 2);
                break;
            case 'emola':
                return 'EML' . substr(date('Y'), 2, 2);
                break;
            case 'mkesh':
                return 'PTK_' . substr(date('Y'), 2, 2);
                break;
            case 'imali':
                return 'IML' . substr(date('Y'), 2, 2);
                break;

            default:
                throw new Exception("Carteira selecionada invalida");
                break;
        }
    }

    private function generate_transaction_id_new25()
    {
        // return TransactionGeneration::generateTransactionSix();
        return TransactionGeneration::generateTransactionSeven();
    }

    // todo --- 06/08/2024 Store MOVEIS EMOLA, MPESA....
    private function check_mkesh_response_new25($wallet_name, $request, $response)
    {
        // if ($wallet_name != 'mkesh') return;

        $request_deposit = $this->create_request_deposit_store($request, $response);

        $status = $this->check_request_deposit_status($request_deposit->transaction_id);

        if ($status === 'NOTFOUND') return SendResponse::errorResp404notfound(
            'Transação invalida',
            'Invalid Transction'
        );

        if ($status === 'FAILED') return SendResponse::errorResp400(
            'Numero de telefone invalido',
            'Numero de telefone invalido'
        );

        if ($status === 'TIMEOUT') return SendResponse::warningResp500serverError(
            'Transacão expirou',
            'Transaction expired.'
        );

        if ($status === 'SUCCESSFUL') return SendResponse::successResp200(
            'Transacão feita com sucesso',
            'Transaction successfully.'
        );
    }


    private function get_wallet_request_data_new25($request, $transactionId, $wallet_name, $customer_name = 'iMali')
    {
        $data = [
            'phone' => $request->client_account_number,
            'amount' => $request->amount,
            'transactionId' => $transactionId
        ];

        switch ($wallet_name) {
            case 'mpesa':
                $data['customerAccount'] = $request->store_account_number;
                return $data;
                break;
            case 'emola':
                $data['customerName'] = $customer_name;
                $data['customerAccount'] = $request->store_account_number;
                return $data;
                break;
            case 'mkesh':
                return $data;
                break;

            default:
                return null;
                break;
        }
    }

    // Get All Payment Pushs exists in Database
    public function get_my_generated_push_payments_new25()
    {
        $user = User::getUserAccount();

        $request_payment = PaymentRequest::query()
            ->where('client_account_number', $user->account_number)
            ->where('payment_types_id', 2)
            ->where('payment_methods_id', 1)
            ->get();

        return response()->json(['data' => $request_payment], 200);
    }

    //? ------------------------------------------------ todo Made By Mr Rodrigues Pagamento Com Parceiros----------------------------------------------


    //? NOVO METODO PAYMENTS IMALIWAY - 29/AUG/2024

    private function make_wallet_payments($request, $wallet, $store, $store_old, $store_contract)
    {

        if (!$this->check_wallet_number($request->phone, $wallet->acronym)) return SendResponse::errorResp400('Numero de telefone invalido', 'Invalid Phone Number');


        if ($request->has('partner_transaction_id') && ($request->partner_transaction_id))
            $transaction_id = $request->partner_transaction_id;
        else
            $transaction_id = $this->generate_full_transaction_id($wallet->acronym);
        $response = $this->call_emola_mpesa_mkesh_c2b_api_store($request, $transaction_id, $wallet);


        if (($response->status() != 200) && ($response->status() != 201)) return $this->logWalletErrorAPI($response, $wallet->acronym);


        if (!$this->is_wallet_transaction_successfully_done($wallet->acronym, $response)) return $this->logWalletErrorAPI($response, $wallet->acronym);


        if ($wallet->acronym == 'mkesh') {

            $request_deposit = $this->check_mkesh_response($wallet->acronym, $request, $response);

            if ($request_deposit->getStatusCode() != 200) return $request_deposit->getData();
        }



        $imali_commission = $request->amount * ($store_contract->taxa) / 100;

        $business = BusinessAccount::getBusinessAccountByID($store->business_account_id);
        if (!$business) return SendResponse::errorResp404notfound(
            'Conta da loja não associada a conta Empresa',
            'Store account not associated with Company account',
        );

        // adicionar o valor retirado na conta do accountPayer no balance da Loja
        $store->balance += ($request->amount - $imali_commission);
        $store->update();

        // adicionar o valor retirado na conta do accountPayer no balance da Loja
        $business->balance += ($request->amount - $imali_commission);
        $business->update();

        /** **/
        $payment = $this->create_payment($request, $transaction_id, $response, $store, $store_old, $wallet, $imali_commission);

        // return SendResponse::successResp200([
        //     'transaction' => $payment->transaction_id,
        //     'partner_transaction_id' => $payment->partner_transaction_id,
        //     'created_at' => $payment->created_at,
        // ]);

        // return SendResponse::successResp200();

        return response()->json([
            'message' => trans('payment_done'),
            'transaction' => $payment->transaction_id,
            // 'partner_transaction_id' => $payment->partner_transaction_id,
            'partner_transaction_id' => $payment->service_transaction_id,
            'created_at' => $payment->created_at
        ]);

        // return SendResponse::successResp200([
        //     'message' => trans('payment_done'),
        //     'transaction' => $payment->transaction_id,
        //     'created_at' => $payment->created_at
        // ]);
    }

    //? NOVO METODO PAYMENTS IMALIWAY - 29/AUG/2024

    private function make_imali_payments($request, $store)
    {

        $request->request->add([
            'store_account_number' => $request->store_account,
            'client_account_number' => $request->account_number,
        ]);

        return $this->generatePushPaymentNew($request);

        // $this->validate(
        //     $request,
        //     [
        //         'account_number' => 'required|numeric|digits:9',
        //         'amount' => 'required|numeric|min:100',
        //         'imaliReference' => 'required'
        //     ],
        //     [
        //         'account_number.required' => 'Campo account_number é obrigatório',
        //         'account_number.numeric' => 'Campo account_number é númerico',
        //         'account_number.digits' => 'Campo account_number deve ter 9 digitos',
        //         'amount.required' => 'Campo amount é obrigatório',
        //         'amount.min' => 'O valor minimo deve ser 100MT',
        //         'amount.numeric' => 'Campo Montente deve ser númerico',
        //         'imaliReference.required' => 'Campo store_account é obrigatório'
        //     ]
        // );

        // $store = Store::getStoreAccount($request->imaliReference);
        // if (!$store) return SendResponse::errorResp404notfound(
        //     'Número de conta da Loja invalido',
        //     'Invalid Store Account',
        // );

        $business = BusinessAccount::getBusinessAccountByID($store->business_account_id);
        if (!$business) return SendResponse::errorResp404notfound(
            'Conta da loja não associada a conta Empresa',
            'Store account not associated with Company account',
        );
        // return "Chegou 2";

        // Contas a Pagar
        $accountPayer = User::getAccount($request->account_number);
        if (!$accountPayer) return SendResponse::errorResp404notfound(
            'Conta não encontrada',
            'Account Not Found',
        );

        // $payer = User::getAccount($this->request->clientAccountNumber);
        // $imali = User::getUserDetails($payer->id);


        $userPayer = User::getUserDetails($accountPayer->user_id);
        if (!$userPayer) return SendResponse::errorResp404notfound(
            'Conta não encontrada',
            'Account Not Found',
        );





        // Gerar o pagamento
        $tra = new TransactionGeneration();
        $transaction_id = $tra->generateTransaction();

        $created_payment = Payment::create([
            'transaction_id' => $transaction_id,
            'amount' => $request->amount,
            'estado' => 'pending',
            'status' => 'pending',
            'description' => $request->description,
            'terminalID' => $request->terminalID,
            'terminalChannel' => $request->terminalChannel,
            'terminalCompanyName' => $request->terminalCompanyName,
            'store_id' => $store->id,
            'category_id' => $store->industry_activity,
            // 'account_id' => $accountPayer->account_id,
            'business_account_id' => $business->id,
            'sender_id' => $userPayer->id,
            'created_at' => now(),
            'updated_at' => now(),
        ]);


        $paymentStore = Store::query()
            ->join('payments', 'payments.store_id', '=', 'stores.id')
            ->join('ramo_activities', 'ramo_activities.id', '=', 'stores.industry_activity')
            ->where('payments.transaction_id', '=', $transaction_id)
            ->select(
                'stores.name',
                'stores.logo',
                'stores.mobile_phone',
                'stores.account_number as storeAccountNumber',
                'stores.address',
                'payments.transaction_id as transaction',
                'payments.amount',
                'ramo_activities.nome as category',
                'ramo_activities.logo as logo_category'
            )
            ->first();


        $push = new PushNotification(
            'Pagamento iMali : ' . $request->transactionID,
            $userPayer->name . ' gerou um pagamento de ' . $request->amount . ' MT',
            $userPayer->firebase_token,
            'com.imali.payapp.payment_PUSH_NOTIFICATION'
        );

        $data = array(
            'transaction' => $request->transactionID,
            'amount' => $request->amount,
            'account_number' => (string)$paymentStore->storeAccountNumber,
            'name' => $paymentStore->name,
            'address' => $paymentStore->address,
            'mobile_phone' => $paymentStore->mobile_phone,
            'logo' => $paymentStore->logo,
            'logo_category' => $paymentStore->logo_category,
            'category' => $paymentStore->category,
            'description' => $request->description,
            'route' => 'PUSH_NOTIFICATION',
            'created_at' => now()
        );

        // $notification = array(
        //     'icon' => 'ic_imali_logo_verde_01',
        //     'title' => 'Pagamento iMali : ' . $transaction_id,
        //     'body' => $userPayer->name . ' gerou um pagamento de ' . $request->amount . ' MT',
        //     'click_action' => 'com.imali.payapp.payment_PUSH_NOTIFICATION',
        //     'color' => '#008577'
        // );

        // $data = array(
        //     'transaction' => $transaction_id,
        //     'amount' => $request->amount,
        //     'account_number' => $paymentStore->storeAccountNumber,
        //     'name' => $paymentStore->name,
        //     'address' => $paymentStore->address,
        //     'mobile_phone' => $paymentStore->mobile_phone,
        //     'logo' => $paymentStore->logo,
        //     'logo_category' => $paymentStore->logo_category,
        //     'category' => $paymentStore->category,
        //     'description' => 'Pagamento Push iMali',
        //     'route' => 'PUSH_NOTIFICATION',
        //     'created_at' => now()
        // );


        // $pushNotifi =  $this->pushNotifification($userPayer->firebase_token, $notification, $data);


        $is_push_sent =  $push->sendPush($data);

        // if ((int)$pushNotifi['success'] === 1) {
        if ($is_push_sent) {

            $payment = $this->checkTransactionStatus($request, $request->transactionID);

            $paymentStatus = $this->checkSuccessPayment($request, $payment);

            if ($paymentStatus === 'success') {

                $payment = $this->checkTransactionStatus($request, $request->transactionID);
                $formatedPayment = array(
                    "partnerTransactionID" => $payment->getData()->partner_transaction_id,
                    "storeAccountNumber" => $request->storeAccountNumber,
                    "customerAccountNumber" => $request->clientAccountNumber,
                    "amount" => $payment->getData()->amount,
                    "status" => $payment->getData()->status,
                    "description" => $payment->getData()->description,
                    "terminalID" => $payment->getData()->terminalID,
                    "terminalChannel" => $payment->getData()->terminalChannel,
                    "terminalCompanyName" => $payment->getData()->terminalCompanyName,
                    "createdAt" => $payment->getData()->created_at
                );

                $push = new PushNotification(
                    'Pagamento de ' . $request->amount . ' MZN',
                    'Pagamento de ' . $request->amount . ' MZN ' . ' feito para: ' . $paymentStore->name,
                    $userPayer->firebase_token
                );
                $push->sendPush($formatedPayment);

                //return response()->json(['message' => trans('payment_done'), 'status'=> 200, 'data'=> $formatedPayment]);
                return response()->json([
                    'cody' => trans('success')[0]['cody'],
                    'error' => trans('success')[0]['success'],
                    'type' => trans('success')[0]['type'],
                    'message' => 'Payment made successfully',
                    'data' => $formatedPayment
                ], trans('success')[0]['http_code']);
            };

            if ($paymentStatus === 'rejected') {
                $payment = $this->checkTransactionStatus($request, $request->transactionID);
                $formatedPayment = array(
                    "partnerTransactionID" => $payment->getData()->partner_transaction_id,
                    "storeAccountNumber" => $request->storeAccountNumber,
                    "customerAccountNumber" => $request->clientAccountNumber,
                    "amount" => $payment->getData()->amount,
                    "status" => $payment->getData()->status,
                    "description" => $payment->getData()->description,
                    "terminalID" => $payment->getData()->terminalID,
                    "terminalChannel" => $payment->getData()->terminalChannel,
                    "terminalCompanyName" => $payment->getData()->terminalCompanyName,
                    "createdAt" => $payment->getData()->created_at
                );

                $push = new PushNotification(
                    'Pagamento rejeitado!',
                    'Pagamento de ' . $request->amount . ' MZN ' . ' para loja ' . $paymentStore->name . ' foi rejeitado.',
                    $userPayer->firebase_token
                );
                $push->sendPush($formatedPayment);

                //return response()->json(['message' => trans('payment_rejected'), 'status'=> 401, 'data'=> $formatedPayment], 406);
                return response()->json([
                    'cody' => trans('error')[7]['cody'],
                    'error' => trans('error')[7]['error'],
                    'type' => trans('error')[7]['type'],
                    'message' => 'Payment Rejected by User',
                    'data' => $formatedPayment
                ], trans('error')[7]['http_code']);
            }

            if ($paymentStatus === 'expired') {

                $payment = $this->checkTransactionStatus($request, $request->transactionID);
                $formatedPayment = array(
                    "partnerTransactionID" => $payment->getData()->partner_transaction_id,
                    "storeAccountNumber" => $request->storeAccountNumber,
                    "customerAccountNumber" => $request->clientAccountNumber,
                    "amount" => $payment->getData()->amount,
                    "status" => $payment->getData()->status,
                    "description" => $payment->getData()->description,
                    "terminalID" => $payment->getData()->terminalID,
                    "terminalChannel" => $payment->getData()->terminalChannel,
                    "terminalCompanyName" => $payment->getData()->terminalCompanyName,
                    "createdAt" => $payment->getData()->created_at
                );

                $push = new PushNotification(
                    'Pagamento expirado!',
                    'Pagamento de ' . $request->amount . ' MZN ' . ' para loja ' . $paymentStore->name . ' expirou.',
                    $userPayer->firebase_token
                );
                $push->sendPush($formatedPayment);

                // $clientUser = User::find($request->user()->id);

                // $this->sendPush($clientUser->firebase_token, $data);

                //return response()->json(['message' => trans('payment_expired'), 'status'=> 401, 'data'=> $formatedPayment], 408);
                return response()->json([
                    'cody' => trans('error')[4]['cody'],
                    'error' => trans('error')[4]['error'],
                    'type' => trans('error')[4]['type'],
                    'message' => 'Expired Payment',
                    'data' => $formatedPayment
                ], trans('error')[4]['http_code']);
            }
            // } else if ((int)$pushNotifi['failure'] === 1) {

            //     //return response()->json(['message' => 'Erro ao tentar enviar push de pagamento', 'status'=> 500]);
            //     return response()->json([
            //         'cody' => trans('warning')[0]['cody'],
            //         'error' => trans('warning')[0]['warning'],
            //         'type' => trans('warning')[0]['type'],
            //         'message' => 'Error when trying to send payment push',
            //     ], trans('warning')[0]['http_code']);

            //? Colocar aqui uma mensagem a informar que o utilizador deve se autenticar na APP
        } else {
            //return response()->json(['message' => 'Erro desconhecido', 'status'=> 500]);
            return response()->json([
                'cody' => trans('warning')[0]['cody'],
                'error' => trans('warning')[0]['warning'],
                'type' => trans('warning')[0]['type'],
                'message' => 'Unknown error',
            ], trans('warning')[0]['http_code']);
        }

        // if (isset($pushNotifi['success']) && (int)$pushNotifi['success'] === 1) {

        //     $paymentStatus = $this->checkSuccessPayment($transaction_id);

        //     if ($paymentStatus === 'success') {

        //         return SendResponse::successResp200();
        //     };

        //     if ($paymentStatus === 'rejected') {

        //         return SendResponse::errorResp402rejected();
        //     }

        //     if ($paymentStatus === 'expired') {

        //         $this->sendPush($userPayer->firebase_token, $data);

        //         return SendResponse::errorResp408timeout();
        //     }
        // } else if (isset($pushNotifi['success']) && (int)$pushNotifi['failure'] === 1) {

        //     return SendResponse::warningResp500serverError('Erro ao tentar enviar push de pagamento, tente novamente', 'Error trying to send payment push, try again');
        // } else {
        //     return SendResponse::warningResp500serverError('Erro desconhecido', 'Unknown error');
        // }
    }



    public function checkTransactionStatus(Request $request, $transaction)
    {
        // $token = str_replace('Bearer ', '', $request->header('authorization'));

        // $userClient = UserClient::query()
        //     ->where('client_key', '=', $token)
        //     ->first();

        //if(!$userClient){
        //   return response()->json([
        //        'cody' => trans('error')[1]['cody'],
        //        'error' => trans('error')[1]['error'],
        //        'type' => trans('error')[1]['type'],
        //        'message' => "Invalid Client Key",
        //    ], trans('error')[1]['http_code']);
        // }

        $pay = Payment::query()
            ->with(['store', 'customer', 'account'])
            ->where('partner_transaction_id', '=', $transaction)
            ->first();


        if (!$pay) {

            $pay = Payment::query()
                ->with(['store', 'customer', 'account'])
                ->where('transaction_id', '=', $transaction)
                ->first();

            if ($pay) {

                $pay->makeHidden([
                    'id',
                    'qrcode',
                    'firebase_token',
                    'device_name',
                    'store_id',
                    'user_client_id',
                    'updated_at',
                    'imali_account_id',
                    'merchant_id',
                    'sender_id',
                    'store_amount_generation_id',
                    'category_id',
                    'estado',
                    'estado_color',
                    'transaction_id',
                    'token',
                    'token_sent',
                    'used_points',
                    'received_points',
                    'client_id',
                    'payment_type',
                    'amount_debited',
                    'comissao',
                    'payment_id'
                ]);

                if ($pay->customer) {
                    $pay->customer->makeHidden([
                        'id',
                        'status',
                        'profile',
                        'email',
                        'birthday',
                        'balance_visibility',
                        'country_code',
                        'email_verified_at',
                        'session_status',
                        'firebase_token',
                        'user_client_id',
                        'phone_reference',
                        'terminalCompanyName',
                        'terminalChannel',
                        'terminalID',
                        'client_id',
                        'created_at',
                        'updated_at',
                        'info_status',
                        'last_name',
                        'phone',
                        'bi',
                        'update_info_status',
                        'user_update_info_status',
                        'document_id',
                        'photo',
                        'user_id'
                    ]);
                }

                if ($pay->account) {
                    $pay->account->makeHidden([
                        'id',
                        'status',
                        'profile',
                        'email',
                        'birthday',
                        'balance_visibility',
                        'country_code',
                        'email_verified_at',
                        'session_status',
                        'firebase_token',
                        'user_client_id',
                        'phone_reference',
                        'terminalCompanyName',
                        'terminalChannel',
                        'terminalID',
                        'client_id',
                        'created_at',
                        'updated_at',
                        'info_status',
                        'bi',
                        'update_info_status',
                        'user_update_info_status',
                        'document_id',
                        'photo',
                        'user_id',
                        'imali_account_config',
                        'captive_balance',
                        'balance',
                        'points',
                        'reference'
                    ]);
                }

                $pay->store->makeHidden([
                    'id',
                    'qrcode',
                    'firebase_token',
                    'device_name',
                    'store_id',
                    'user_client_id',
                    'created_at',
                    'updated_at',
                    'updated_at',
                    'user_id',
                    'industry_activity',
                    'merchant_contract_id',
                    'merchant_account_id',
                    'longitude',
                    'latitude',
                    'balance',
                    'session_status',
                    'photo',
                    'logo',
                    'status',
                    'email',
                    'qrcode'
                ]);

                //     if ($userClient->id != $pay->client_id) {
                //         return response()->json(['message' => trans('not_allowed_on_store')], 400);

                //         //	return response()->json([
                //         //        'cody' => trans('error')[3]['cody'],
                //         //       'error' => trans('error')[3]['error'],
                //         //      'type' => trans('error')[3]['type'],
                //         //      'message' => "Not allowed to transact in this store",
                //         //  ], trans('error')[3]['http_code']);

                //     } else {
                //         return response()->json($pay);

                //         // return response()->json([
                //         //     'cody' => trans('success')[0]['cody'],
                //         //     'success' => trans('success')[0]['success'],
                //         //     'type' => trans('success')[0]['type'],
                //         //     'data' => $pay,
                //         // ], trans('success')[0]['http_code']);
                //     }
                // } else {

                //     return response()->json(['message' => trans('not_found_transaction')], 400);

                //     //return response()->json([
                //     //    'cody' => trans('error')[3]['cody'],
                //     //    'error' => trans('error')[3]['error'],
                //     //    'type' => trans('error')[3]['type'],
                //     //    'message' => "Transaction not found",
                //     // ], trans('error')[3]['http_code']);
                // }
            } else {

                $pay->makeHidden([
                    'id',
                    'qrcode',
                    'firebase_token',
                    'device_name',
                    'store_id',
                    'user_client_id',
                    'updated_at',
                    'imali_account_id',
                    'merchant_id',
                    'sender_id',
                    'store_amount_generation_id',
                    'category_id',
                    'estado',
                    'estado_color',
                    'transaction_id',
                    'token',
                    'token_sent',
                    'used_points',
                    'received_points',
                    'client_id',
                    'payment_type',
                    'amount_debited',
                    'comissao',
                    'payment_id'
                ]);

                if ($pay->customer) {
                    $pay->customer->makeHidden([
                        'id',
                        'status',
                        'profile',
                        'email',
                        'birthday',
                        'balance_visibility',
                        'country_code',
                        'email_verified_at',
                        'session_status',
                        'firebase_token',
                        'user_client_id',
                        'phone_reference',
                        'terminalCompanyName',
                        'terminalChannel',
                        'terminalID',
                        'client_id',
                        'created_at',
                        'updated_at',
                        'info_status',
                        'phone',
                        'last_name',
                        'bi',
                        'update_info_status',
                        'user_update_info_status',
                        'document_id',
                        'photo',
                        'user_id'
                    ]);
                }

                if ($pay->account) {
                    $pay->account->makeHidden([
                        'id',
                        'status',
                        'profile',
                        'email',
                        'birthday',
                        'balance_visibility',
                        'country_code',
                        'email_verified_at',
                        'session_status',
                        'firebase_token',
                        'user_client_id',
                        'phone_reference',
                        'terminalCompanyName',
                        'terminalChannel',
                        'terminalID',
                        'client_id',
                        'created_at',
                        'updated_at',
                        'info_status',
                        'bi',
                        'update_info_status',
                        'user_update_info_status',
                        'document_id',
                        'photo',
                        'user_id',
                        'imali_account_config',
                        'captive_balance',
                        'balance',
                        'points',
                        'reference'
                    ]);
                }

                $pay->store->makeHidden([
                    'id',
                    'qrcode',
                    'firebase_token',
                    'device_name',
                    'store_id',
                    'user_client_id',
                    'created_at',
                    'updated_at',
                    'updated_at',
                    'user_id',
                    'industry_activity',
                    'merchant_contract_id',
                    'merchant_account_id',
                    'longitude',
                    'latitude',
                    'balance',
                    'session_status',
                    'photo',
                    'logo',
                    'status',
                    'email',
                    'qrcode'
                ]);

                if ($userClient->id != $pay->client_id) {
                    return response()->json(['message' => trans('not_allowed_on_store')], 400);

                    //	return response()->json([
                    //            'cody' => trans('error')[3]['cody'],
                    //            'error' => trans('error')[3]['error'],
                    //            'type' => trans('error')[3]['type'],
                    //            'message' => "Not allowed to transact in this store.",
                    //        ], trans('error')[3]['http_code']);

                } else {
                    return response()->json($pay);

                    //	return response()->json([
                    //            'cody' => trans('success')[0]['cody'],
                    //            'success' => trans('success')[0]['success'],
                    //            'type' => trans('success')[0]['type'],
                    //            'message' => "Transaction found",
                    //            'data' => $pay,
                    //        ], trans('success')[0]['http_code']);

                }
            }
        }
    }


    // public function checkTransactionStatus($transaction_id)
    // {
    //     return response()->json(Payment::query()->where('transaction_id', $transaction_id)->first());
    // }

    //? NOVO METODO PAYMENTS IMALIWAY - 29/AUG/2024

    public function sendPush($firebase_token, $data)
    {
        $notification = array(
            'icon' => 'ic_imali_logo_verde_01',
            'title' => 'Pagamento iMali : ',
            'body' =>  ' gerou um pagamento de MT',
            'click_action' => 'com.imali.payapp.payment_TARGET_NOTIFICATION',
            'color' => '#008577'
        );


        $this->pushNotifification($firebase_token, $notification, $data);
    }

    //? NOVO METODO PAYMENTS IMALIWAY - 29/AUG/2024

    public function checkSuccessPayment($transaction_id)
    {
        $payment = Payment::query()->where('transaction_id', $transaction_id)->first();

        if ($payment->status === "pending") {

            $diffTime = (int) (time() - strtotime($payment->created_at));

            // Delay da transacao 40 secundos
            if ($diffTime >= 40) {
                $payment->update([
                    'estado' => 'expired',
                    'status' => 'expired',
                    'description' => 'Pagamento expirado',
                    'created_at' => now(),
                    'updated_at' => now()
                ]);
            }

            return $this->checkSuccessPayment($transaction_id);
        } else if ($payment->status === "rejected") {
            return "rejected";
        }
        if ($payment->status === "expired") {
            return "expired";
        } else {
            return "success";
        }
    }

    private function getGoogleAccessToken()
    {

        // $credentialsFilePath = 'server.json';
        $credentialsFilePath = public_path('server.json');
        $client = new \Google_Client();
        $client->setAuthConfig($credentialsFilePath);
        $client->addScope('https://www.googleapis.com/auth/firebase.messaging');
        // $client->fetchAccessTokenWithAssertion();
        $token = $client->fetchAccessTokenWithAssertion();
        return $token['access_token'];
    }


    public function pushNotifification($token, $notification = array(), $data = array())
    {
        $apiKey = 'AAAA8zVzEPQ:APA91bHl_DXB6UGb_6gZlmFnaLTQoANtX_OBjvl3nOy2bSlnFhxedvk6EhGj7cZoIvmlbKeCnqGxXbuyMH_rEPuhRXvuitXzo6Pfl2TMXLar1PlifXqEhYq6tS55UMrY2Kffzj-P_UH-';
        $fields = array('to' => $token, 'notification' => $notification, 'data' => $data);
        $headers = array('Authorization: key=' . $apiKey, 'Content-Type: application/json');
        $url = 'https://fcm.googleapis.com/fcm/send';

        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_POST, true);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($fields));
        $result = curl_exec($curl);
        curl_close($curl);

        return json_decode($result, true);
    }



    //? NOVO METODO PAYMENTS IMALIWAY - 29/AUG/2024
    public function pushNotifificationOld($token, $notification = array(), $data = array())
    {
        // $apiKey = 'AAAA8zVzEPQ:APA91bHl_DXB6UGb_6gZlmFnaLTQoANtX_OBjvl3nOy2bSlnFhxedvk6EhGj7cZoIvmlbKeCnqGxXbuyMH_rEPuhRXvuitXzo6Pfl2TMXLar1PlifXqEhYq6tS55UMrY2Kffzj-P_UH-';

        // return $token; $notification
        // $fields = array("message" => ['token' => $token, 'notification' => [ "title" => "Notificacao de pagamento", "body" => "Notificacao Serena"], 'data' => json_encode($data)]);
        $fields = [
            "message" => [
                "token" => $token,
                "notification" => [
                    'title' => 'Notificacao de Pagamento',
                    'body' => 'Sucesso',  // Mensagem simples
                    // 'icon' => 'ic_imali_logo_verde_01',
                    // 'click_action' => 'com.imali.payapp.payment_PUSH_NOTIFICATION',
                    // 'color' => '#008577',
                    // json_encode($notification)
                ],
                "data" => [
                    'transaction' => 'OSC5B76DSUWW',
                    'amount' => '100',
                    'account_number' => '220000247',  // Converta números para string
                    'name' => 'Loja Pinguim',
                    'address' => 'Rua Poeta Rui de Noronha, nº36, R/c, Maputo',
                    'mobile_phone' => '841266962',
                    'logo' => 'https://paytek-africa.com/imaliapi/public/images/comerciante/logo/202210261036135284534045_5534118063266235_2360707798148595146_n.jpg',
                    'logo_category' => 'https://www.paytek-africa.com/imaliapi/public/images/category/tecnologias.png',
                    'category' => 'Tecnologias',
                    'description' => 'Pagamento Push iMali',
                    'route' => 'PUSH_NOTIFICATION',
                    'created_at' => '2024-08-30T11:10:24.372921Z',
                ],
                // $data
                // json_encode()

            ]
        ];
        //   return $fields;
        // $headers = array('Authorization: key=' . $apiKey, 'Content-Type: application/json');
        $headers = ['Authorization: Bearer ' . $this->getGoogleAccessToken(), 'Content-Type: application/json',];


        // $url = 'https://fcm.googleapis.com/fcm/send';
        // return $headers;
        // $url = 'https://firebase.google.com/docs/cloud-messaging/migrate-v1';
        $project_code = 'imaliapp-5155e';
        // $project_number = '1044573786356';
        $url = 'https://fcm.googleapis.com/v1/projects/' . $project_code . '/messages:send';
        // return $url;

        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_POST, true);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($fields));
        $result = curl_exec($curl);
        return $result;
        curl_close($curl);


        return json_decode($result, true);
    }



    //     private function getGoogleAccessToken()
    // {
    //     // $credentialsFilePath = 'server.json';
    //     $credentialsFilePath = public_path('server.json');
    //     $client = new \Google_Client();
    //     $client->setAuthConfig($credentialsFilePath);
    //     $client->addScope('https://www.googleapis.com/auth/firebase.messaging');
    //     // $token = $client->fetchAccessTokenWithAssertion();
    //     $client->refreshTokenWithAssertion();
    //     return $token['access_token'];
    // }


    //? NOVO METODO PAYMENTS IMALIWAY - 29/AUG/2024
    private function create_payment($request, $transaction_id, $response, $store, $store_old, $wallet, $imali_commission)
    {
        $link_exist = ($request->has('link_id') && $request->link_id);

        $payment = Payment::create([
            'transaction_id' => $transaction_id,
            'service_transaction_id' => $response->json()['partnerTransactionId'],
            'partner_transaction_id' => $request->partner_transaction_id,
            'store_id' => $store->id,
            'source' => $wallet->acronym,
            'amount' => $request->amount,
            'amount_debited' => $request->amount,
            'amount_credited' => $request->amount - $imali_commission,
            'comissao' => $imali_commission,
            'description' => 'Pagamento via ' . $wallet->name,
            'estado' => 'success',
            'status' => 'success',
            'sender_account_number' => $request->account_number,
            'payment_type' => 'directo',
            'estado_color' => '#388E3C',
            'old_store_balance' => $store_old->balance,
            'new_store_balance' => $store->balance,
            'transaction_type' => 'debit',
            'transaction_name' => 'Pagamento',
            'category_id' => $store->industry_activity,
            'links_id' => $link_exist ? $request->link_id : null,
            'link_id_key' => $link_exist ? $request->link_id_key : null,
        ]);

        if ($link_exist) {
            $link = Link::query()->where('link_id', $request->link_id_key)->orWhere('customer_link_id', $request->link_id_key)->first();
            $link->status = 'USED';
            $link->update();
        }

        return $payment; // Retorna o pagamento criado
    }

    //? REVER O METODO DE PAGAMENTO SEM CONTA IMALI ASSOCIADO AO NUMERO DE TELEFONE....


    // todo ADICIONADO 31/07/2024
    // private function check_request_deposit_status($transactionId)
    // {
    //     $last_deposit = MkeshAskingDeposit::query()->where('transaction_id', $transactionId)->first();
    //     if (!$last_deposit) return false;

    //     if ($last_deposit->status === 'SUCCESSFUL') return true;

    //     sleep(10);

    //     $this->count_timeout += 10;

    //     if ($this->count_timeout == $this->timeout) return false;

    //     $this->check_request_deposit_status($transactionId);
    // }

    private function check_request_deposit_status($transactionId)
    {
        $last_deposit = MkeshAskingDeposit::query()->where('transaction_id', $transactionId)->first();

        if (!$last_deposit) return 'NOTFOUND';

        if ($last_deposit->status == 'PENDING') {
            sleep(5);
            $this->count_timeout += 5;
            if ($this->count_timeout == $this->timeout) return 'TIMEOUT';
            return $this->check_request_deposit_status($transactionId);
        }

        return $last_deposit->status;
    }


    // todo - 17/07/2024
    private function generate_transaction_id()
    {
        $transactionString = new TransactionGeneration();
        return $transactionString->generateTransaction();
    }

    // todo - 17/07/2024
    private function generate_wallet_prefix($wallet_name)
    {
        switch ($wallet_name) {
            case 'mpesa':
                return 'MPS';
                break;
            case 'emola':
                return 'EML';
                break;
            case 'mkesh':
                return 'PTK_';
                break;

            default:
                throw new Exception("Carteira selecionada invalida");
                break;
        }
    }



    // todo - 17/07/2024
    private function generate_full_transaction_id($wallet_name)
    {
        return $this->generate_wallet_prefix($wallet_name) . $this->generate_transaction_id();
    }

    // todo - 17/07/2024
    private function is_wallet_transaction_successfully_done($wallet_name, $response)
    {

        switch ($wallet_name) {
            case 'mpesa':
                return count($response->json()) > 0 && ($response->json()['partnerCode'] === 'INS-0');
                break;
            case 'emola':
                return count($response->json()) > 0 && ($response->json()['partnerCode'] === '0');
                break;
            case 'mkesh':
                return count($response->json()) > 0 && (($response->json()['partnerCode'] === 'PENDING') || ($response->json()['partnerCode'] === 'SUCCESSFUL'));
                break;

            default:
                return false;
                break;
        }
    }

    // todo - 17/07/2024
    private function successResponse($data_from_api, $wallet_name, $data_from_local = null)
    {
        Log::info('Outgoing Response Success', [
            'message' => 'Carregamento enviado com sucesso',
            'content_from_api' => $data_from_api,
            'content_from_local' => $data_from_local
        ]);

        if ($wallet_name == 'mkesh') {
            return SendResponse::successResp200(
                'O seu pedido de deposito foi enviado com sucesso. Em breve vais receber um pedido de pagamento do MKesh',
                'Your deposit request has been sent successfully. You will soon receive a payment request from MKesh'
            );
        } else  return SendResponse::successResp200('Carregamento via ' .  $wallet_name . ' efectuado com sucesso.');
    }

    // todo - 17/07/2024
    private function create_request_deposit($request, $response, $payer_account)
    {
        return MkeshAskingDeposit::create([
            'account_number' => $payer_account->account_number,
            'transaction_id' => $response->json()['imaliTransactionId'],
            'partner_transaction_id' => $response->json()['partnerTransactionId'],
            'phone' => $request->phone,
            'amount' => $request->amount,
            'recharge_way' => 'MKesh',
            'user_id' => $payer_account ? $payer_account->user_id : null
            // 'user_id' => auth()->user()->id
        ]);
    }

    // todo - 06/08/2024
    private function create_request_deposit_store($request, $response)
    {
        return MkeshAskingDeposit::create([
            'transaction_id' => $response->json()['imaliTransactionId'],
            'partner_transaction_id' => $response->json()['partnerTransactionId'],
            'phone' => $request->phone,
            'amount' => $request->amount,
            'recharge_way' => 'MKesh',
        ]);
    }


    // todo - 17/07/2024
    private function recharge_by_emola_mpesa_mkesh($request, $payer, $payer_account, $payer_account_2, $wallet)
    {
        try {

            $transactionId = $this->generate_full_transaction_id($wallet->acronym);

            $response = $this->call_emola_mpesa_mkesh_c2b_api($request, $payer, $transactionId, $wallet);

            if (($response->status() != 200) && ($response->status() != 201)) return $this->logWalletErrorAPI($response, $wallet->acronym);

            //codigo para carregar a carteira
            if (!$this->is_wallet_transaction_successfully_done($wallet->acronym, $response)) return $this->logWalletErrorAPI($response, $wallet->acronym);

            // if ($wallet->acronym == 'mkesh') {
            //     $request_deposit = $this->create_request_deposit($request, $response, $payer_account);
            //     return $this->successResponse($response->json(), $wallet->acronym, $request_deposit);
            // }

            // todo 01/08/2024
            if ($wallet->acronym == 'mkesh') {

                $request_deposit = $this->create_request_deposit($request, $response, $payer_account);

                // $status = $this->check_request_deposit_status($request_deposit->transaction_id);

                // Log::info(
                //     'Teste MKsh',
                //     ['content' => $status]
                // );

                // if ($status === 'NOTFOUND') return SendResponse::errorResp404notfound(
                //     'Transação invalida',
                //     'Invalid Transction'
                // );

                // if ($status === 'FAILED') return SendResponse::errorResp400(
                //     'Numero de telefone invalido',
                //     'Numero de telefone invalido'
                // );

                // if ($status === 'TIMEOUT') return SendResponse::warningResp500serverError(
                //     'Transacão expirou',
                //     'Transaction expired.'
                // );

                return $this->successResponse($response->json(), $wallet->acronym, $request_deposit);
            }

            $payer_account->balance += $request->amount;
            $payer_account->update();

            $recharge = $this->create_recharge($request, $payer, $payer_account, $payer_account_2, $wallet, $transactionId, $response);

            $this->send_push_notification($recharge, $payer, $payer_account);

            return $this->successResponse($response->json(),  $wallet->acronym, $recharge);
        } catch (\Throwable $th) {

            Log::info('Outgoing Response', [
                'content' => $th->getMessage(),
                'error' => $th
            ]);

            // sleep(15);
            // $this->walletTransacStatus();

            return SendResponse::warningResp500serverError();
        }
    }

    // todo --- NOVOS METODOS CARREGAMENTO - 17/07/2024
    private function get_wallet_url($wallet_name)
    {
        switch ($wallet_name) {
            case 'mpesa':
                return $_ENV['MPESA_C2B_URL'];
                // return env('MPESA_C2B_URL');
                break;
            case 'emola':
                return $_ENV['EMOLA_C2B_URL'];
                break;
            case 'mkesh':
                return $_ENV['MKESH_C2B_URL'];
                break;

            default:
                throw new Exception("variáveis de ambiente (MPESA_C2B_URL, EMOLA_C2B_URL, MKESH_C2B_URL) não definidos no ficheiro .env");
                break;
        }
    }

    // todo 18/07/2024
    private function get_wallet_b2c_url($wallet_name)
    {
        switch ($wallet_name) {
            case 'mpesa':
                return $_ENV['MPESA_B2C_URL'];
                // return env('MPESA_B2C_URL');
                break;
            case 'emola':
                return $_ENV['EMOLA_B2C_URL'];
                break;
            case 'mkesh':
                return $_ENV['MKESH_B2C_URL'];
                break;

            default:
                throw new Exception("variáveis de ambiente (MPESA_B2C_URL, EMOLA_B2C_URL, MKESH_B2C_URL) não definidos no ficheiro .env");
                break;
        }
    }

    // todo 17/07/2024 UPDATED
    private function get_wallet_customer_name_url($wallet_name)
    {
        switch ($wallet_name) {
            case 'mpesa':
                return $_ENV['MPESA_CUSTOMER_NAME_URL'];
                break;
            case 'emola':
                return $_ENV['EMOLA_CUSTOMER_NAME_URL'];
                break;
            case 'mkesh':
                return $_ENV['MKESH_CUSTOMER_NAME_URL'];
                break;

            default:
                throw new Exception("variáveis de ambiente (MPESA_CUSTOMER_NAME_URL, EMOLA_CUSTOMER_NAME_URL, MKESH_CUSTOMER_NAME_URL) não definidos no ficheiro .env");
                break;
        }
    }

    // todo --- NOVOS METODOS CARREGAMENTO - 17/07/2024
    private function get_wallet_env_param($wallet_name)
    {
        switch ($wallet_name) {
            case 'mpesa':
                return 'MPESA_C2B_URL';
                break;
            case 'emola':
                return 'EMOLA_C2B_URL';
                break;
            case 'mkesh':
                return 'MKESH_C2B_URL';
                break;

            default:
                return "(MPESA_C2B_URL, EMOLA_C2B_URL, MKESH_C2B_URL)";
                break;
        }
    }

    // todo --- NOVOS METODOS CARREGAMENTO - 17/07/2024
    private function get_wallet_request_data($request, $transactionId, $wallet_name, $customer_name = 'iMali')
    {
        $data = [
            'phone' => $request->phone,
            'amount' => $request->amount,
            'transactionId' => $transactionId
        ];

        switch ($wallet_name) {
            case 'mpesa':
                $data['customerAccount'] = $request->imaliReference;
                return $data;
                break;
            case 'emola':
                $data['customerName'] = $customer_name;
                $data['customerAccount'] = $request->imaliReference;
                return $data;
                break;
            case 'mkesh':
                return $data;
                break;

            default:
                return null;
                break;
        }
    }

    // todo 18/07/2024
    private function get_wallet_b2c_request_data($request, $transactionId, $wallet_name)
    {
        $data = [
            'phone' => $request->phone,
            'amount' => $request->amount,
            'transactionId' => $transactionId
        ];

        switch ($wallet_name) {
            case 'mpesa':
                $data['customerAccount'] = $request->imaliReference;
                return $data;
                break;
            case 'emola':
                return $data;
                break;
            case 'mkesh':
                return $data;
                break;

            default:
                return null;
                break;
        }
    }

    // todo --- NOVOS METODOS CARREGAMENTO - 17/07/2024
    private function call_emola_mpesa_mkesh_c2b_api($request, $payer, $transactionId, $wallet)
    {
        $url = $this->get_wallet_url($wallet->acronym);

        if (!$url) throw new Exception("variável de ambiente " . $this->get_wallet_env_param($wallet->acronym) . " não declarado no ficheiro .env");

        // $this->get_wallet_request_data($request, $transactionId, $wallet->acronym, $payer->name);

        $data = $this->get_wallet_request_data($request, $transactionId, $wallet->acronym, $payer ? $payer->name : 'iMali');

        // return Http::post($url, $data);
        return Http::timeout(2000)->post($url, $data);
    }

    // todo --- NOVOS METODOS CARREGAMENTO - 06/08/2024
    private function call_emola_mpesa_mkesh_c2b_api_store($request, $transactionId, $wallet)
    {

        $url = $this->get_wallet_url($wallet->acronym);

        if (!$url) throw new Exception("variável de ambiente " . $this->get_wallet_env_param($wallet->acronym) . " não declarado no ficheiro .env");

        $data = $this->get_wallet_request_data($request, $transactionId, $wallet->acronym, 'Cliente ' . $wallet->acronym);

        return Http::timeout(2000)->post($url, $data);
    }


    // todo --- NOVOS METODOS CARREGAMENTO
    private function create_recharge($request, $payer, $payer_account, $payer_account_2, $wallet, $transactionId, $response)
    {
        $masterAccount = MasterAccount::find(2);

        // return $payerAccount = User::getUserAccount();

        return RechargeImaliAccount::create([
            'imali_account_id' => $payer_account->id,
            'transaction_id' => $transactionId,
            'bank_reference' => $response->json()['partnerTransactionId'],
            'bank_date' => date('Y-m-d'),
            'account_reference' => $payer_account->reference,
            'phone' => $request->phone,
            'description' => 'Carregamento realtime via ' . $wallet->name,
            'amount' => $request->amount,
            'last_balance' => $payer_account_2->balance,
            'balance' => $payer_account->balance,
            'recharge_way' => $wallet->name,
            'estado' => 'sucesso',
            'estado_color' => '#388E3C',
            'master_account_id' => $masterAccount->id,
            'user_id' => $payer->id
        ]);
    }

    // todo --- NOVOS METODOS CARREGAMENTO
    private function send_push_notification($recharge, $payer, $payer_account)
    {
        $data = array(
            'transaction' => $recharge->transaction_id,
            'name' => $payer->name,
            'description' => $recharge->description,
            'amount' => (float)$recharge->amount,
            'phone' => $recharge->phone,
            'reference' => $payer_account->reference,
            'data' => date($recharge->created_at),
            'estado' => $recharge->estado,
            'route' => 'RECHARGE_DETAILS',
            'recharge_way' => $recharge->recharge_way,
            'account_number' => $payer_account->account_number,
            'terminal' => 'firebase'
        );

        $p = new PushNotification(
            // 'Carregamento ' . $recharge->amount . ' MZN',
            // 'Parabéns, ' . 'carregaste ' . $recharge->amount . ' MT ' . ' na tua conta ' . $payer_account->account_number,
            'Carregamento de ' . $recharge->amount . ' MZN via ' . $recharge->recharge_way,
            'Carregamento de ' . $recharge->amount . ' MZN na conta ' . $payer_account->account_number . ' feito com sucesso.',
            $payer->firebase_token,
            'com.imali.payapp.payment_RECHARGE_DETAILS'
        );

        $p->sendPush($data);
    }

    //? B2C Methods
    // todo 17/07/2024 UPDATED
    private function validate_parameters($request)
    {
        // acrescetar link_id = BWM4DS903JFM para permitir fazer pagamento via link...
        return $this->validate(
            $request,
            [
                'phone' => 'required|numeric|digits:9',
                'amount' => 'required|numeric|min:100',
                'imaliReference' => 'required'
            ],
            [
                'phone.required' => 'Campo account_number é obrigatório',
                'phone.numeric' => 'Campo account_number é númerico',
                'phone.digits' => 'Campo account_number deve ter 9 digitos',
                'amount.required' => 'Campo amount é obrigatório',
                'amount.min' => 'O valor minimo deve ser 100MT',
                'amount.numeric' => 'Campo Montente deve ser númerico',
                'imaliReference.required' => 'Campo store_account é obrigatório'
            ]
        );
    }



    private function validate_link_parameters($request)
    {
        // acrescetar link_id = BWM4DS903JFM para permitir fazer pagamento via link...
        return $this->validate(
            $request,
            [
                'account_number' => 'required|numeric',
                'link_id' => 'required|max:30'
            ],
            [
                'account_number.required' => 'Campo account_number é obrigatório',
                'account_number.numeric' => 'Campo Montente deve ser númerico',
                'link_id.required' => 'Campo link_id é obrigatório',
                'link_id.max' => 'Campo link_id tem maximo de 30 caracteres',
            ]
        );
    }

    // todo 18/07/2024 UPDATED
    private function getOperator($request, $wallet_name)
    {
        $wallet = Operator::query()->where('id', $request->mobile_wallets_id)->where('acronym', $wallet_name)->first();
        if (!$wallet) throw new Exception('Carteira informada não existe');
        return $wallet;
    }


    // todo 23/07/2024
    private function check_wallet_number($phone, $wallet_name)
    {
        $mkesh = [82, 83];
        $mpesa = [84, 85];
        $emola = [86, 87];

        $prefix = substr($phone, 0, 2);

        if (($wallet_name == 'mpesa') && !in_array($prefix, $mpesa)) return false;
        if (($wallet_name == 'mkesh') && !in_array($prefix, $mkesh)) return false;
        if (($wallet_name == 'emola') && !in_array($prefix, $emola)) return false;

        return true;
    }

    public function b2cWithdrawIMaliWay(Request $request, $wallet_name)
    {
        $wallet = Operator::query()->where('acronym', $wallet_name)->first();
        if (!$wallet) return SendResponse::errorResp404notfound();

        // todo 18/07/2024 UPDATED
        // $wallet = $this->getOperator($request, $wallet_name);

        $this->validate_parameters($request);

        if (!$this->check_wallet_number($request->phone, $wallet_name)) return SendResponse::errorResp400('Numero de telefone invalido', 'Invalid Phone Number');

        $payer = User::getUserAccount();
        $payer_account = User::getAccount($payer->account_number);
        $payer_account_2 = User::getAccount($payer->account_number);
        $userKyc = new UserKyc($payer);

        $usrKycResp = $userKyc->checkUserKYC($request->amount, 404);
        if ($usrKycResp->getStatusCode() != 200) return $usrKycResp;

        $usrKycRespBalance = $userKyc->checkUserBalance($request->amount);

        if ($usrKycRespBalance->getStatusCode() != 200) return $usrKycRespBalance;

        $this->validate(
            $request,
            [
                'mobile_wallets_id' => 'required|numeric'
            ],
            [
                'mobile_wallets_id.required' => 'Campo mobile_wallets_id é obrigatório',
                'mobile_wallets_id.numeric' => 'Campo mobile_wallets_id  deve ser númerico'
            ]
        );


        $b2c_data = $this->checkB2CTransaction(new Request([
            'phone' => $request->phone,
            'amount' => $request->amount,
            'mobile_wallets_id' => $request->mobile_wallets_id,
        ]), $wallet_name);

        if (($b2c_data->getStatusCode() != 200)) return $b2c_data;

        return $this->withdraw_by_mpesa_emola_mkesh($request, $payer, $payer_account, $payer_account_2, $wallet, $b2c_data);
    }

    // todo 17/07/2024 UPDATED
    public function b2cWithdraw(Request $request, $wallet_name)
    {
        $wallet = Operator::query()->where('acronym', $wallet_name)->first();
        if (!$wallet) return SendResponse::errorResp404notfound();

        // todo 18/07/2024 UPDATED
        // $wallet = $this->getOperator($request, $wallet_name);

        $this->validate_parameters($request); // passou

        if (!$this->check_wallet_number($request->phone, $wallet_name)) return SendResponse::errorResp400('Numero de telefone invalido', 'Invalid Phone Number'); // passou

        $payer = User::getUserAccount();
        $payer_account = User::getAccount($payer->account_number);
        $payer_account_2 = User::getAccount($payer->account_number);
        $userKyc = new UserKyc($payer);

        $usrKycResp = $userKyc->checkUserKYC($request->amount, 404);
        if ($usrKycResp->getStatusCode() != 200) return $usrKycResp;

        $usrKycRespBalance = $userKyc->checkUserBalance($request->amount);

        if ($usrKycRespBalance->getStatusCode() != 200) return $usrKycRespBalance;

        $this->validate(
            $request,
            [
                'mobile_wallets_id' => 'required|numeric'
            ],
            [
                'mobile_wallets_id.required' => 'Campo mobile_wallets_id é obrigatório',
                'mobile_wallets_id.numeric' => 'Campo mobile_wallets_id  deve ser númerico'
            ]
        );


        $b2c_data = $this->checkB2CTransaction(new Request([
            'phone' => $request->phone,
            'amount' => $request->amount,
            'mobile_wallets_id' => $request->mobile_wallets_id,
        ]), $wallet_name);

        if (($b2c_data->getStatusCode() != 200)) return $b2c_data;

        return $this->withdraw_by_mpesa_emola_mkesh($request, $payer, $payer_account, $payer_account_2, $wallet, $b2c_data);
    }

    // todo 17/07/2024 UPDATED
    private function withdraw_by_mpesa_emola_mkesh($request, $payer, $payer_account, $payer_account_2, $wallet, $b2c_data)
    {
        // $response = Http::post('http://localhost:3000/mpesa/b2c-payment', ['phone' => '258' . $request->phone, 'amount' => $request->amount, 'customerAccount' => $request->imaliReference]);

        $transactionId = $this->generate_full_transaction_id($wallet->acronym);
        $response = $this->call_emola_mpesa_mkesh_b2c_api($request, $transactionId, $wallet);

        if (($response->status() != 200) && ($response->status() != 201)) return $this->logWalletErrorAPI($response, $wallet->acronym);

        //codigo para carregar a carteira
        if (!$this->is_wallet_transaction_successfully_done($wallet->acronym, $response)) return $this->logWalletErrorAPI($response, $wallet->acronym);

        //actualizacao do saldo principal
        $payer_account->balance = $payer_account->balance - $b2c_data->getData()->total;
        $payer_account->update();


        $withdrawalls = $this->create_withdraw($request, $payer, $payer_account, $payer_account_2, $wallet, $b2c_data, $transactionId, $response->json()['partnerTransactionId']);

        Log::info('..:: B2C LOG :::..', ['B2C' => $withdrawalls]);

        $data = array(
            'transaction' => $withdrawalls->transaction_id,
            'name' => $payer->name,
            'description' => $withdrawalls->description,
            'amount' => (float)$withdrawalls->total,
            'phone' => $payer->phone,
            'reference' => $payer_account->reference,
            'data' => date($withdrawalls->created_at),
            'estado' => $withdrawalls->status,
            'route' => 'RECHARGE_DETAILS',
            'recharge_way' => $withdrawalls->description,
            'account_number' => $payer_account->account_number,

            'total' => $b2c_data->getData()->total,
            'commission' => $b2c_data->getData()->commission,
            'stamp_tax' => $b2c_data->getData()->stamp_tax,

            'sender_name' => $payer->name,
            'reciever_name' => $b2c_data->getData()->masked_name,

            'terminal' => 'firebase'
        );

        $p = new PushNotification(
            // 'Transferência de ' . $withdrawalls->amount . ' MT para MPesa',
            'Transferência de ' . $request->amount . 'MZN para ' . $wallet->name,
            'Transferência de ' . $withdrawalls->amount . ' MZN ' . 'da conta ' . $payer_account->account_number . ' para o ' . $wallet->name . ': ' . $request->phone,
            $payer->firebase_token,
            'com.imali.payapp.payment_RECHARGE_DETAILS'
        );

        Log::info([
            'content' => $response->json()
        ]);

        $p->sendPush($data);
        // return $response;
        return response()->json(['message' => 'A tua transferência para ' . $wallet->name . ' foi efectuada com sucesso!'], 200);
    }

    // todo 18/07/2024 UPDATED
    private function create_withdraw($request, $payer, $payer_account, $payer_account_2, $wallet, $b2c_data, $transactionId, $partnerTransactionId)
    {
        $withdrall = WithdrawalsRequest::create([
            'imali_account' => $payer->account_number,
            'partner_transaction_id' => $partnerTransactionId,
            'account_type' => $payer->profile,
            'amount' => $request->amount,
            'imali_fee' => $b2c_data->getData()->imali_fee,
            'bank_fee' => $b2c_data->getData()->imali_cost,
            'description' => 'TRF.' . $wallet->name,
            'account_number' => $request->phone,
            'wallets_id' => 2,
            'operators_id' => $request->mobile_wallets_id,
            'status' => 'success',
            'old_balance' => $payer_account_2->balance,
            'new_balance' => $payer_account->balance,
            'total' => $b2c_data->getData()->total,
            'transaction_id' => $transactionId,
            'commission' => $b2c_data->getData()->commission,
            'stamp_tax' => $b2c_data->getData()->stamp_tax,
            'user_id' => $payer->id,
            'sender_name' => $payer->name,
            'reciever_name' => $b2c_data->getData()->masked_name,
            'imali_account_id' => $payer_account->id
        ]);

        return $withdrall; // todo --- adicionado 10/06/2025
    }


    // todo 17/07/2024 UPDATED
    private function call_emola_mpesa_mkesh_b2c_api($request, $transactionId, $wallet)
    {
        $url = $this->get_wallet_b2c_url($wallet->acronym);

        Log::info([
            'content' => $url
        ]);

        if (!$url) throw new Exception("variável de ambiente " . $this->get_wallet_env_param($wallet->acronym) . " não declarado no ficheiro .env");

        // $this->get_wallet_request_data($request, $transactionId, $wallet->acronym, $payer->name);

        return Http::post($url, $this->get_wallet_b2c_request_data($request, $transactionId, $wallet->acronym));
    }

    private function call_emola_mpesa_mkesh_b2c_api_v2($request, $transactionId, $wallet_name)
    {
        $url = $this->get_wallet_b2c_url($wallet_name);

        Log::info([
            'content' => $url
        ]);

        if (!$url) throw new Exception("variável de ambiente " . $this->get_wallet_env_param($wallet_name) . " não declarado no ficheiro .env");

        // $this->get_wallet_request_data($request, $transactionId, $wallet_name, $payer->name);

        return Http::post($url, $this->get_wallet_b2c_request_data($request, $transactionId, $wallet_name));
    }



    public function walletTransacStatus(Request $request, $wallet_name) {}

    // todo 17/07/2024 UPDATED
    public function getWalletCustomerName(Request $request, $wallet_name)
    {
        $wallet = Operator::query()->where('acronym', $wallet_name)->first();
        if (!$wallet) return SendResponse::errorResp404notfound();

        $this->validate(
            $request,
            [
                'phone' => 'required|numeric|digits:9'
            ],
            [
                'phone.required' => 'Campo telefone é obrigatório',
                'phone.numeric' => 'Campo telefone é númerico',
                'phone.digits' => 'Campo telefone deve ter 9 digitos'
            ]
        );

        try {
            $response = $this->call_customer_name_api($request, $wallet);

            Log::info([
                'RESPONSE' => $response->json()
            ]);

            // $partnerCode = $response->json()['partnerCode'];
            // if (($partnerCode != 'INS-0') || ($partnerCode != 0) || ($partnerCode != 'SUCCESSFUL')) throw new Exception("Wallet desconhecido");

            return response()->json(['customerName' => $response->json()['partnerCustomerName']]);
        } catch (\Throwable $th) {
            //throw $th;
            return SendResponse::warningResp500serverError('Erro interno de servidor', $th->getMessage());
        }
    }

    // todo 17/07/2024 UPDATED
    public function call_customer_name_api($request, $wallet)
    {
        $url = $this->get_wallet_customer_name_url($wallet->acronym);

        Log::info([
            'content' => $url
        ]);

        if (!$url) throw new Exception("variável de ambiente " . $this->get_wallet_env_param($wallet->acronym) . " não declarado no ficheiro .env");
        if ($wallet->acronym == 'mpesa')
            return Http::timeout(2000)->get($url, ['phone' => $request->phone]);
        if ($wallet->acronym == 'emola')
            return Http::timeout(2000)->post($url, ['phone' => $request->phone]);
    }


    public function getEmpresaStoresMobile(Request $request)
    {
        $user = User::getUserAccount();
        // $user = User::getUserDetails(auth()->user()->user_id)->account_id;
        // $user = User::getUserDetails(auth()->user()->user_id);

        if ($user->profile === 'business') {


            $perPage = !!$request->input('per_page') ? $request->input('per_page') : 4;
            $orderType = $request->input('order_type') === 'ASC' ? 'ASC' : 'DESC';
            $orderBy = !!$request->input('order_by') && $request->input('order_by') !== 'null' ? $request->input('order_by') : 'stores.id';

            $stores = Store::query()
                ->select(
                    'id',
                    'name',
                    'account_number',
                    'balance',
                    'logo',
                )
                ->where('business_account_id', $user->account_id)
                // ->where('business_account_id', $user)
                ->orderBy($orderBy, $orderType)
                ->paginate($perPage);

            // if ($stores->isEmpty()) {

            //     return response()->json(['message' => 'Sem dados.'], 200);
            // }

            // return "Cheguei";
            return response()->json($stores, 200);
        } else {
            return response()->json([], 200);
        }
    }


    // todo 07-08-2024 Lojas Conta Empresa
    public function getEmpresaStores(Request $request)
    {
        // $user = User::getUserAccount();
        $user = User::getUserDetails(auth()->user()->user_id)->account_id;
        $user = User::getUserDetails(auth()->user()->user_id);

        if ($user->profile === 'business') {


            $perPage = !!$request->input('per_page') ? $request->input('per_page') : 4;
            $orderType = $request->input('order_type') === 'ASC' ? 'ASC' : 'DESC';
            $orderBy = !!$request->input('order_by') && $request->input('order_by') !== 'null' ? $request->input('order_by') : 'stores.id';

            $stores = Store::query()
                ->select(
                    'id',
                    'name',
                    'account_number',
                    'balance',
                    'logo',
                )
                ->where('business_account_id', $user->account_id)
                // ->where('business_account_id', $user)
                ->orderBy($orderBy, $orderType)
                ->paginate($perPage);

            // if ($stores->isEmpty()) {

            //     return response()->json(['message' => 'Sem dados.'], 200);
            // }

            // return "Cheguei";
            return response()->json($stores, 200);
        } else {
            return response()->json([], 200);
        }
    }

    // todo -- Get Numero de Lojas
    // Número de lojas
    public function getCountStores(Request $request)
    {
        // $user = User::getUserAccount();
        $user = User::getUserDetails(auth()->user()->user_id);

        if ($user->profile === 'business') {
            $storeCount = Store::where('business_account_id', $user->account_id)->count();

            return response()->json(['data' => $storeCount], 200);
        } else {
            return response()->json(['data' => 0], 200);
        }
    }

    // Número de transações totais (de todas as lojas)  
    public function getAllTransactionStoresWWWWW(Request $request)
    {
        $user = User::getUserAccount();

        if ($user->profile === 'business') {
            // Obter as lojas do usuário de perfil 'business'
            $storeIds = Store::where('business_account_id', $user->account_id)->pluck('id');

            // Contar o número total de transações feitas nas lojas
            $totalTransactions = Payment::whereIn('store_id', $storeIds)
                ->whereDate('created_at', DB::raw('CURDATE()')) // Filtra pela data atual
                ->count();

            return response()->json(['data' => $totalTransactions], 200);
        } else {
            // return response()->json(['data' => []], 200);
            return response()->json(['data' => 0], 200);
        }
    }

    public function getAllTransactionStores(Request $request)
    {
        // $user = User::getUserAccount();
        $user = User::getUserDetails(auth()->user()->user_id);

        if ($user->profile === 'business') {
            // Obter as lojas do usuário de perfil 'business'
            $storeIds = Store::where('business_account_id', $user->account_id)->pluck('id');

            // Obter o número total de transações de todas as lojas
            $totalTransactions = Payment::whereIn('store_id', $storeIds)
                ->whereDate('created_at', DB::raw('CURDATE()'))
                ->count();

            // Obter o número de transações para cada loja individualmente
            $transactionsByStore = Payment::whereIn('store_id', $storeIds)
                ->join('stores', 'stores.id', 'payments.store_id')
                ->whereDate('payments.created_at', DB::raw('CURDATE()'))
                ->select(
                    'store_id',
                    'stores.name',
                    'stores.account_number',
                    'stores.balance',
                    DB::raw('COUNT(*) as nr_transactions')
                )
                ->groupBy('store_id')
                ->get();

            return response()->json([
                'total_transactions' => $totalTransactions,
                'by_store' => $transactionsByStore
            ], 200);
        } else {
            return response()->json([
                'total_transactions' => 0,
                'by_store' => []
            ], 200);
        }
    }


    // Receita feita (de todas as lojas)
    public function getTotalTransactionStores(Request $request)
    {
        // $user = User::getUserAccount();
        $user = User::getUserDetails(auth()->user()->user_id);

        if ($user->profile === 'business') {
            // Obter as lojas do usuário de perfil 'business'
            $storeIds = Store::where('business_account_id', $user->account_id)->pluck('id');


            // return $storeIds;

            // Obter a lista de pagamentos feitos nas lojas filtrando pelos IDs
            // $payments = Payment::whereIn('store_id', $storeIds)->get();

            // Obter o total de `amount_credited` de todas as transações de hoje nas lojas
            $totalAmountCreditedToday = Payment::whereIn('store_id', $storeIds)
                ->whereDate('created_at', DB::raw('CURDATE()')) // Filtra pela data atual
                ->sum('amount_credited');

            // Contar o número total de transações feitas nas lojas
            // $totalTransactions = Payment::whereIn('store_id', $storeIds)
            //     ->whereDate('created_at', DB::raw('CURDATE()')) // Filtra pela data atual
            //     ->count();

            return response()->json(['data' => $totalAmountCreditedToday], 200);
        } else {
            // return response()->json(['data' => []], 200);
            return response()->json(['data' => 0], 200);
        }
    }

    // Lista de transações por loja
    // public function getTransactionByStores(Request $request, $id)
    // {
    //     $size = $request->get('per_page', 10);

    //     // $user = User::getUserAccount();
    //     $user = User::getUserDetails(auth()->user()->user_id);

    //     if ($user->profile === 'business') {
    //         // Obter os IDs das lojas do usuário com perfil 'business'
    //         $storeIds = Store::where('business_account_id', $user->account_id)->pluck('id');

    //         // Verificar se o `store_id` fornecido está na lista de lojas do usuário
    //         if ($storeIds->contains($id)) {
    //             // Obter a lista de pagamentos para a loja específica
    //             $payments = Payment::where('store_id', $id)
    //                 ->orderBy('created_at', 'desc')
    //                 ->paginate($size);
    //             // ->get();

    //             return response()->json(['data' => $payments], 200);
    //         } else {
    //             return response()->json(['error' => 'Loja não encontrada ou não pertence ao usuário'], 404);
    //         }
    //     } else {
    //         return response()->json(['data' => 0], 200);
    //     }
    // }

    public function getTransactionByStores(Request $request, $id)
    {
        $size = $request->get('per_page', 10);

        $user = User::getUserDetails(auth()->user()->user_id);

        if ($user->profile === 'business') {
            // Obter os IDs das lojas do usuário
            $storeIds = Store::where('business_account_id', $user->account_id)->pluck('id');

            if ($storeIds->contains($id)) {
                // Inicia a query
                $query = Payment::where('store_id', $id);

                // Filtrar por datas se os parâmetros existirem
                if ($request->has('start_date')) {
                    $query->whereDate('created_at', '>=', $request->start_date);
                }
                if ($request->has('end_date')) {
                    $query->whereDate('created_at', '<=', $request->end_date);
                }

                // Ordena e pagina
                $payments = $query->orderBy('created_at', 'desc')
                    ->paginate($size);

                return response()->json(['data' => $payments], 200);
            } else {
                return response()->json(['error' => 'Loja não encontrada ou não pertence ao usuário'], 404);
            }
        } else {
            return response()->json(['data' => 0], 200);
        }
    }


    // Total do dia X
    public function getTotalTransactionStoresDay(Request $request)
    {
        // $user = User::getUserAccount();
        $user = User::getUserDetails(auth()->user()->user_id);

        if ($user->profile === 'business') {
            // Obter as lojas do usuário de perfil 'business'
            $storeIds = Store::where('business_account_id', $user->account_id)->pluck('id');

            // Obter a data a partir dos parâmetros da requisição, ou usar a data atual como padrão
            $date = $request->input('date', now()->toDateString());

            // Obter o total de `amount_credited` de todas as transações de hoje nas lojas
            $totalAmountCreditedToday = Payment::whereIn('store_id', $storeIds)
                ->whereDate('created_at', $date) // Filtra pela data atual
                ->sum('amount_credited');

            return response()->json(['data' => $totalAmountCreditedToday], 200);
        } else {
            return response()->json(['data' => 0], 200);
        }
    }

    // Extrato de movimentos (transacções) entre duas datas. 
    public function getTransactionStoresDates($id, $startDate, $endDate)
    {
        // $user = User::getUserAccount();
        $user = User::getUserDetails(auth()->user()->user_id);

        if ($user->profile === 'business') {
            // Obter os IDs das lojas do usuário com perfil 'business'
            $storeIds = Store::where('business_account_id', $user->account_id)->pluck('id');

            // Verificar se o `store_id` fornecido está na lista de lojas do usuário
            if ($storeIds->contains($id)) {

                // Validação: verificar se as datas foram fornecidas
                if (!$startDate || !$endDate) {
                    return response()->json(['error' => 'Datas de início e fim são obrigatórias'], 400);
                }

                // Obter a lista de pagamentos para a loja específica no intervalo de datas
                $payments = Payment::where('store_id', $id)
                    ->whereDate('created_at', '>=', $startDate)
                    ->whereDate('created_at', '<=', $endDate)
                    ->get();

                return response()->json(['data' => $payments], 200);
            } else {
                return response()->json(['error' => 'Loja não encontrada ou não pertence ao usuário'], 404);
            }
        } else {
            return response()->json(['data' => 0], 200);
        }
    }


    // Get All Transactions Stores belongs to Business_Account
    public function getAllStoresTransactionsOLD(Request $request)
    {
        $size = (!request()->per_page) ? 10 : request()->per_page;

        $user = User::getUserAccount();

        if ($user->profile !== 'business') {
            return response()->json(['data' => 0], 200);
        }

        // Get Stores IDs belongs to business_account
        $storeIds = Store::where('business_account_id', $user->account_id)->pluck('id');

        $payments = Payment::whereIn('store_id', $storeIds)
            ->when($request->filled('transaction_id'), function ($query) use ($request) {
                $query->where('transaction_id', $request->transaction_id);
            })
            ->orderBy('created_at', 'desc')
            ->paginate($size);

        // return response()->json(['data' => $payments], 200);
        return response()->json($payments);
    }



    public function getAllStoresTransactionsOLLLLLL(Request $request)
    {
        $size = (!request()->per_page) ? 10 : request()->per_page;

        $user = User::getUserAccount();

        if ($user->profile !== 'business') {
            return response()->json(['data' => 0], 200);
        }

        // Get Stores IDs belongs to business_account
        $storeIds = Store::where('business_account_id', $user->account_id)->pluck('id');

        $payments = Payment::whereIn('store_id', $storeIds)
            ->when($request->filled('transaction_id'), function ($query) use ($request) {
                $query->where('transaction_id', $request->transaction_id);
            })
            // ->when($request->filled('start_date') && $request->filled('end_date'), function ($query) use ($request) {
            //     $query->whereBetween('created_at', [
            //         $request->start_date . ' 00:00:00',
            //         $request->end_date . ' 23:59:59'
            //     ]);
            // })
            ->whereDate('created_at', '>=', $request->start_date)
            ->whereDate('created_at', '<=', $request->end_date)
            ->orderBy('created_at', 'desc')
            ->paginate($size);

        return response()->json($payments);
    }

    public function getMonthClose(Request $request)
    {
        $size = (!request()->per_page) ? 10 : request()->per_page;

        $new_start_date  = null;
        $new_end_date  = null;

        if ($request->filled('start_date')) {
            $start_date = explode('-', $request->start_date);
            if (strlen($start_date[2]) >= 4)
                $new_start_date = $start_date[2] . '-' . $start_date[1] . '-' . $start_date[0];
        }

        if ($request->filled('end_date')) {
            $end_date = explode('-', $request->end_date);
            if (strlen($end_date[2]) >= 4)
                $new_end_date = $end_date[2] . '-' . $end_date[1] . '-' . $end_date[0];
        }

        $data =  \App\GeneralMonthClose::query()
            // new ------------------------
            ->when($request->filled('start_date'), function ($query) use ($request, $new_start_date) {
                $query->whereDate('general_month_closes.created_at', '>=', $new_start_date ?? $request->start_date);
            })
            ->when($request->filled('end_date'), function ($query) use ($request, $new_end_date) {
                $query->whereDate('general_month_closes.created_at', '<=', $new_end_date ?? $request->end_date);
            })
            // new ------------------------
            ->orderBy('created_at', 'desc')
            // ->get();
            ->paginate($size);

        // return $data;
        return response()->json($data, 200);
    }


    public function getAllStoresTransactionsAdmin(Request $request)
    {
        // $size = $request->get('per_page', 10);
        $size = (!request()->per_page) ? 10 : request()->per_page;

        $new_start_date  = null;
        $new_end_date  = null;

        if ($request->filled('start_date')) {
            $start_date = explode('-', $request->start_date);
            if (strlen($start_date[2]) >= 4)
                $new_start_date = $start_date[2] . '-' . $start_date[1] . '-' . $start_date[0];
        }

        if ($request->filled('end_date')) {
            $end_date = explode('-', $request->end_date);
            if (strlen($end_date[2]) >= 4)
                $new_end_date = $end_date[2] . '-' . $end_date[1] . '-' . $end_date[0];
        }

        $payments = Payment::query()
            ->join('stores', 'stores.id', 'payments.store_id')
            // ->whereIn('payments.store_id', $storeIds)
            ->when($request->filled('transactionId'), function ($query) use ($request) {
                $query->where('payments.transaction_id', $request->transactionId);
            })
            // ->when($request->filled('start_date'), function ($query) use ($request) {
            //     $query->whereDate('payments.created_at', '>=', $request->start_date);
            // })
            // ->when($request->filled('end_date'), function ($query) use ($request) {
            //     $query->whereDate('payments.created_at', '<=', $request->end_date);
            // })
            // new ------------------------
            ->when($request->filled('start_date'), function ($query) use ($request, $new_start_date) {
                $query->whereDate('payments.created_at', '>=', $new_start_date ?? $request->start_date);
            })
            ->when($request->filled('end_date'), function ($query) use ($request, $new_end_date) {
                $query->whereDate('payments.created_at', '<=', $new_end_date ?? $request->end_date);
            })
            // new ------------------------
            ->when($request->filled('store_name'), function ($query) use ($request) {
                $query->where('stores.name', 'like', '%' . $request->store_name . '%');
            })
            ->orderBy('payments.created_at', 'desc')
            ->select(
                'payments.*',
                'stores.name as store_name'
            )
            ->paginate($size);

        return response()->json($payments);
    }

    public function getAllStoresTransactionsBACK(Request $request)
    {
        $size = $request->get('per_page', 10);

        // $user = User::getUserAccount();
        $user = User::getUserDetails(auth()->user()->user_id);

        if ($user->profile !== 'business') {
            return response()->json(['data' => []], 200);
        }

        // Buscar os IDs das lojas do business_account
        $storeIds = Store::where('business_account_id', $user->account_id)->pluck('id');

        // return $storeIds;

        $payments = Payment::query()
            ->join('stores', 'stores.id', 'payments.store_id')
            ->whereIn('payments.store_id', $storeIds)
            ->when($request->filled('transactionId'), function ($query) use ($request) {
                $query->where('payments.transaction_id', $request->transactionId);
            })
            ->when($request->filled('start_date'), function ($query) use ($request) {
                $query->whereDate('payments.created_at', '>=', $request->start_date);
            })
            ->when($request->filled('end_date'), function ($query) use ($request) {
                $query->whereDate('payments.created_at', '<=', $request->end_date);
            })
            ->when($request->filled('store_name'), function ($query) use ($request) {
                $query->where('stores.name', 'like', '%' . $request->store_name . '%');
            })
            ->orderBy('payments.created_at', 'desc')
            ->select(
                'payments.*',
                'stores.name as store_name'
            )
            ->paginate($size);

        // $totalTransactions = (clone $payments)->count();
        // $totalAmount = (clone $payments)->sum('payments.amount');

        return response()->json($payments);
        // Retornar tudo no mesmo JSON
        // return response()->json([
        //     'data' => $payments,
        //     'total_transactions' => $totalTransactions,
        //     'total_amount' => $totalAmount,
        // ]);
    }


    // public function getAllStoresTransactions(Request $request)
    // {
    //     $size = $request->get('per_page', 10);

    //     // $user = User::getUserAccount();
    //     $user = User::getUserDetails(auth()->user()->user_id);

    //     if ($user->profile !== 'business') {
    //         return response()->json(['data' => []], 200);
    //     }

    //     // Buscar os IDs das lojas do business_account
    //     $storeIds = Store::where('business_account_id', $user->account_id)->pluck('id');

    //     // return $storeIds;

    //     $payments = Payment::query()
    //         ->join('stores', 'stores.id', 'payments.store_id')
    //         ->whereIn('payments.store_id', $storeIds)
    //         ->when($request->filled('transactionId'), function ($query) use ($request) {
    //             $query->where('payments.transaction_id', $request->transactionId);
    //         })
    //         ->when($request->filled('start_date'), function ($query) use ($request) {
    //             $query->whereDate('payments.created_at', '>=', $request->start_date);
    //         })
    //         ->when($request->filled('end_date'), function ($query) use ($request) {
    //             $query->whereDate('payments.created_at', '<=', $request->end_date);
    //         })
    //         ->when($request->filled('store_name'), function ($query) use ($request) {
    //             $query->where('stores.name', 'like', '%' . $request->store_name . '%');
    //         })
    //         ->orderBy('payments.created_at', 'desc')
    //         ->select(
    //             'payments.*',
    //             'stores.name as store_name'
    //         );

    //     // Clonar query para cálculo dos totais
    //     $totalsQuery = clone $payments;

    //     // Calcular totais
    //     $totalAmount = $totalsQuery->sum('payments.amount');
    //     $totalTransactions = $totalsQuery->count();
    //     // ->paginate($size);

    //     return response()->json([
    //         'filters' => [
    //             'start_date' => $request->start_date,
    //             'end_date' => $request->end_date,
    //         ],
    //         'total_amount' => $totalAmount,
    //         'total_transactions' => $totalTransactions,
    //         'data' => $payments,
    //     ]);

    //     // return response()->json($payments);
    // }
    // todo 22/10/2025
    public function getAllStoresTransactionsCORRECT(Request $request)
    {
        $size = $request->get('per_page', 10);

        $user = User::getUserDetails(auth()->user()->user_id);

        if ($user->profile !== 'business') {
            return response()->json(['data' => []], 200);
        }

        // Buscar IDs das lojas pertencentes ao business_account
        $storeIds = Store::where('business_account_id', $user->account_id)->pluck('id');

        // return $storeIds;

        // Definir as datas de filtro
        $startDate = $request->filled('start_date') ? $request->start_date : date('Y-m-d');
        $endDate = $request->filled('end_date') ? $request->end_date : date('Y-m-d');

        // Query principal | tabela de transferencias do iMali - tabela de Withdrawalls - Payments
        $query = Payment::query()
            ->join('stores', 'stores.id', '=', 'payments.store_id')
            ->whereIn('payments.store_id', $storeIds)
            ->whereDate('payments.created_at', '>=', $startDate)
            ->whereDate('payments.created_at', '<=', $endDate)
            ->when($request->filled('transactionId'), function ($query) use ($request) {
                $query->where('payments.transaction_id', $request->transactionId);
            })
            ->when($request->filled('store_name'), function ($query) use ($request) {
                $query->where('stores.name', 'like', '%' . $request->store_name . '%');
            });

        // Clonar query para cálculo dos totais
        $totalsQuery = clone $query;

        // Paginação
        $payments = $query
            ->orderBy('payments.created_at', 'desc')
            ->select('payments.*', 'stores.name as store_name')
            ->paginate($size);

        // Calcular totais
        $totalAmount = $totalsQuery->sum('payments.amount');
        $totalTransactions = $totalsQuery->count();

        return response()->json([
            // 'filters' => [
            //     'start_date' => $startDate,
            //     'end_date' => $endDate,
            // ],
            'total_amount' => $totalAmount,
            'total_transactions' => $totalTransactions,
            'data' => $payments,
        ]);
    }

    // todo 29/10/2025
    public function getAllStoresTransactionsPRIMEITATENTATIVA(Request $request)
    {
        // tabela de transferencias do iMali - tabela de Withdrawalls - Payments

        $user = User::getUserDetails(auth()->user()->user_id);

        if ($user->profile !== 'business') {
            return response()->json(['data' => []], 200);
        }

        // Buscar IDs das lojas pertencentes ao business_account
        $storeIds = Store::where('business_account_id', $user->account_id)->pluck('id');

        // return $storeIds . ' -- B.ID:' . $user;

        // 286,
        // 287
        // B.ID:1005 -- NAME: Pedro

        //? TRANSFER
        $transfer = Transfer::query()
            ->where('transfers.sender_id', $user->id)
            ->select(
                'id',
                'transaction_id',
                'amount',
                'created_at',
                'status',
                'sender_account',
                'sender_name',
                'reciever_account',
                'reciever_name',
                DB::raw("'TRANSFER' as type")
            )
            ->orderBy('created_at', 'desc')
            ->get();


        //? WITHDRAWALL
        $withdrall = WithdrawalsRequest::query()
            ->where('withdrawals_requests.user_id', $user->id)
            ->select(
                'id',
                'transaction_id',
                'total as amount',
                'created_at',
                'status',
                'imali_account as sender_account',
                'sender_name',
                'account_number as reciever_account',
                'reciever_name',
                DB::raw("'WITHDRAWALL' as type")
            )
            ->orderBy('created_at', 'desc')
            ->get();


        //? PAYMENTS
        $payment = Payment::query()
            ->join('stores', 'stores.id', '=', 'payments.store_id')
            ->join('users', 'users.id', '=', 'payments.sender_id')
            ->whereIn('payments.store_id', $storeIds)
            ->select(
                'payments.id',
                'transaction_id',
                'amount',
                'payments.created_at',
                'payments.status',
                'sender_account_number as sender_account',
                'users.name as sender_name',
                'account_number as reciever_account',
                'stores.name as reciever_name',
                DB::raw("'PAYMENT' as type")
            )
            ->orderBy('payments.created_at', 'desc')
            ->get();

        return response()->json([
            'withdrall' => $withdrall,
            'transfer' => $transfer,
            'payment' => $payment,
        ]);
    }

    public function getAllStoresTransactionsBOM(Request $request)
    {
        $user = User::getUserDetails(auth()->user()->user_id);

        if ($user->profile !== 'business') {
            return response()->json(['data' => []], 200);
        }

        // Buscar IDs das lojas pertencentes ao business_account
        $storeIds = Store::where('business_account_id', $user->account_id)->pluck('id');

        // TRANSFERS
        $transfers = Transfer::query()
            ->where('sender_id', $user->id)
            ->select(
                'id',
                'transaction_id',
                'amount',
                'created_at',
                'status',
                'sender_account',
                'sender_name',
                'reciever_account',
                'reciever_name',
                DB::raw("'TRANSFER' as type")
            )
            ->get();

        // WITHDRAWALS
        $withdrawals = WithdrawalsRequest::query()
            ->where('user_id', $user->id)
            ->select(
                'id',
                'transaction_id',
                'total as amount',
                'created_at',
                'status',
                'imali_account as sender_account',
                'sender_name',
                'account_number as reciever_account',
                'reciever_name',
                DB::raw("'WITHDRAWALL' as type")
            )
            ->get();

        // PAYMENTS
        $payments = Payment::query()
            ->join('stores', 'stores.id', '=', 'payments.store_id')
            ->join('users', 'users.id', '=', 'payments.sender_id')
            ->whereIn('payments.store_id', $storeIds)
            ->select(
                'payments.id',
                'transaction_id',
                'amount',
                'payments.created_at',
                'payments.status',
                'sender_account_number as sender_account',
                'users.name as sender_name',
                'account_number as reciever_account',
                'stores.name as reciever_name',
                DB::raw("'PAYMENT' as type")
            )
            ->get();

        // JUNTAR TUDO E ORDENAR
        $allTransactions = $transfers
            ->merge($withdrawals)
            ->merge($payments)
            ->sortByDesc('created_at')
            ->values(); // reindexa o array

        return response()->json([
            'data' => $allTransactions,
        ]);
    }

    // todo 29/10/2025
    public function getAllStoresTransactionsVALIDO(Request $request)
    {
        $size = $request->get('per_page', 10);
        $page = $request->get('page', 1);
        $transactionId = $request->get('transactionId');

        $user = User::getUserDetails(auth()->user()->user_id);

        if ($user->profile !== 'business') {
            return response()->json(['data' => []], 200);
        }

        $storeIds = Store::where('business_account_id', $user->account_id)->pluck('id');

        // TRANSFERS
        $transfers = Transfer::query()
            ->where('sender_id', $user->id)
            ->select(
                'id',
                'transaction_id',
                'amount',
                'created_at',
                'status',
                'sender_account',
                'sender_name',
                'reciever_account',
                'reciever_name',
                DB::raw("'TRANSFER' as type")
            )
            ->get();

        // WITHDRAWALS
        $withdrawals = WithdrawalsRequest::query()
            ->where('user_id', $user->id)
            ->select(
                'id',
                'transaction_id',
                'total as amount',
                'created_at',
                'status',
                'imali_account as sender_account',
                'sender_name',
                'account_number as reciever_account',
                'reciever_name',
                DB::raw("'WITHDRAWALL' as type")
            )
            ->get();

        // PAYMENTS
        $payments = Payment::query()
            ->join('stores', 'stores.id', '=', 'payments.store_id')
            ->join('users', 'users.id', '=', 'payments.sender_id')
            ->whereIn('payments.store_id', $storeIds)
            ->select(
                'payments.id',
                'transaction_id',
                'amount',
                'payments.created_at',
                'payments.status',
                'sender_account_number as sender_account',
                'users.name as sender_name',
                'account_number as reciever_account',
                'stores.name as reciever_name',
                DB::raw("'PAYMENT' as type")
            )
            ->get();

        // JUNTAR TUDO
        $allTransactions = $transfers->merge($withdrawals)->merge($payments);

        // FILTRAR POR TRANSACTION_ID (SE EXISTIR)
        if ($transactionId) {
            $transaction = $allTransactions->firstWhere('transaction_id', $transactionId);

            if ($transaction) {
                return response()->json([
                    'data' => $transaction
                ]);
            } else {
                return response()->json([
                    'data' => null,
                    'message' => 'Transação não encontrada.'
                ], 404);
            }
        }

        // FILTRAR POR INTERVALO DE DATAS
        $startDate = $request->get('start_date');
        $endDate = $request->get('end_date');

        if ($startDate && $endDate) {
            $allTransactions = $allTransactions->filter(function ($item) use ($startDate, $endDate) {
                $createdAt = date('Y-m-d', strtotime($item->created_at));
                return $createdAt >= $startDate && $createdAt <= $endDate;
            });
        }

        // ORDENAR POR DATA DESC
        $allTransactions = $allTransactions->sortByDesc('created_at')->values();

        // PAGINAÇÃO MANUAL
        $paginated = new LengthAwarePaginator(
            $allTransactions->forPage($page, $size),
            $allTransactions->count(),
            $size,
            $page,
            ['path' => $request->url(), 'query' => $request->query()]
        );

        return response()->json([
            'data' => $paginated,
        ]);
    }

    public function getAllStoresTransactions(Request $request)
    {
        $size = $request->get('per_page', 10);
        $page = $request->get('page', 1);
        $transactionId = $request->get('transactionId');

        $user = User::getUserDetails(auth()->user()->user_id);

        if ($user->profile !== 'business') {
            return response()->json([
                'total_amount' => 0,
                'total_transactions' => 0,
                'data' => []
            ], 200);
        }

        $storeIds = Store::where('business_account_id', $user->account_id)->pluck('id');

        // TRANSFERS
        $transfers = Transfer::query()
            ->where('sender_id', $user->id)
            ->select(
                'id',
                'transaction_id',
                'amount',
                'created_at',
                'status',
                DB::raw("'N/A' as store_name"), // <-- aqui
                'sender_account',
                'sender_name',
                'reciever_account',
                'reciever_name',
                'amount_debited as amount_credited',
                'commission as comissao',
                DB::raw("'TRANSFER' as type")
            )
            ->get();

        // WITHDRAWALS
        $withdrawals = WithdrawalsRequest::query()
            ->where('user_id', $user->id)
            ->select(
                'id',
                'transaction_id',
                'amount',
                'created_at',
                'status',
                DB::raw("'N/A' as store_name"), // <-- aqui
                'imali_account as sender_account',
                'sender_name',
                'account_number as reciever_account',
                'reciever_name',
                // 'total as -(amount_credited)',
                DB::raw('-total as amount_credited'),
                'commission as comissao',
                DB::raw("'WITHDRAWALL' as type")
            )
            ->get();

        // PAYMENTS
        $payments = Payment::query()
            ->join('stores', 'stores.id', '=', 'payments.store_id')
            ->join('users', 'users.id', '=', 'payments.sender_id')
            ->whereIn('payments.store_id', $storeIds)
            ->select(
                'payments.id',
                'transaction_id',
                'amount',
                'payments.created_at',
                'payments.status',
                'stores.name as store_name',
                'sender_account_number as sender_account',
                'users.name as sender_name',
                'account_number as reciever_account',
                'stores.name as reciever_name',
                'amount_credited',
                'comissao',
                DB::raw("'PAYMENT' as type")
            )
            ->get();

        // JUNTAR TUDO
        $allTransactions = $transfers->merge($withdrawals)->merge($payments);

        // FILTRAR POR TRANSACTION_ID (SE EXISTIR)
        if ($transactionId) {
            $transaction = $allTransactions->firstWhere('transaction_id', $transactionId);

            if ($transaction) {
                return response()->json([
                    'total_amount' => $transaction->amount,
                    'total_transactions' => 1,
                    'data' => $transaction
                ]);
            } else {
                return response()->json([
                    'total_amount' => 0,
                    'total_transactions' => 0,
                    'data' => null,
                    'message' => 'Transação não encontrada.'
                ], 404);
            }
        }

        // FILTRAR POR INTERVALO DE DATAS
        $startDate = $request->get('start_date');
        $endDate = $request->get('end_date');

        if ($startDate && $endDate) {
            $allTransactions = $allTransactions->filter(function ($item) use ($startDate, $endDate) {
                $createdAt = date('Y-m-d', strtotime($item->created_at));
                return $createdAt >= $startDate && $createdAt <= $endDate;
            });
        }

        // ORDENAR POR DATA DESC
        $allTransactions = $allTransactions->sortByDesc('created_at')->values();

        // CALCULAR TOTALS
        $totalAmount = $allTransactions->sum('amount');
        $totalTransactions = $allTransactions->count();

        // PAGINAÇÃO MANUAL
        $paginated = new LengthAwarePaginator(
            // $allTransactions->forPage($page, $size),
            $allTransactions->forPage($page, $size)->values(),  // <-- aqui
            $totalTransactions,
            $size,
            $page,
            ['path' => $request->url(), 'query' => $request->query()]
        );

        return response()->json([
            'total_amount' => $totalAmount,
            'total_transactions' => $totalTransactions,
            'data' => $paginated
        ]);
    }



    public function getAllStoresTransactionsC2B2C(Request $request)
    {
        $size = $request->get('per_page', 10);

        // $user = User::getUserAccount();
        $user = User::getUserDetails(auth()->user()->user_id);

        // Apenas usuários do tipo "business" podem ver
        if ($user->profile !== 'business') {
            return response()->json(['data' => []], 200);
        }

        // Buscar os account_numbers das lojas associadas ao business
        $storeAccountNumbers = Store::where('business_account_id', $user->account_id)
            ->pluck('account_number');

        if ($storeAccountNumbers->isEmpty()) {
            return response()->json(['data' => []], 200);
        }

        // Query 1: pagamentos C2B (clientes → loja)
        $c2b = DB::table('payment_requests')
            ->select(
                'id',
                'transaction_id',
                'amount',
                'created_at',
                'store_account_number as account_number',
                DB::raw("'C2B' as type")
            )
            ->whereIn('store_account_number', $storeAccountNumbers);

        // Query 2: transferências B2C (loja → clientes)
        $b2c = DB::table('transfers')
            ->select(
                'id',
                'transaction_id',
                'amount',
                'created_at',
                'sender_account as account_number',
                DB::raw("'B2C' as type")
            )
            ->whereIn('sender_account', $storeAccountNumbers);

        // Unir as duas queries com UNION
        $transactions = $c2b
            ->unionAll($b2c)
            ->orderByDesc('created_at');

        // Aplicar filtros adicionais via subquery
        $query = DB::query()->fromSub($transactions, 't')
            ->when($request->filled('transactionId'), function ($query) use ($request) {
                $query->where('transaction_id', $request->transactionId);
            })
            ->when($request->filled('start_date'), function ($query) use ($request) {
                $query->whereDate('created_at', '>=', $request->start_date);
            })
            ->when($request->filled('end_date'), function ($query) use ($request) {
                $query->whereDate('created_at', '<=', $request->end_date);
            })
            ->when($request->filled('store_name'), function ($query) use ($request) {
                $query->whereIn('account_number', function ($subquery) use ($request) {
                    $subquery->select('account_number')
                        ->from('stores')
                        ->where('name', 'like', '%' . $request->store_name . '%');
                });
            })
            ->orderByDesc('created_at');

        // Paginação dos dados
        $paginated = $query->paginate($size);

        // === Calcular os somatórios ===
        $totalC2B = DB::table('payment_requests')
            ->whereIn('store_account_number', $storeAccountNumbers)
            ->when($request->filled('start_date'), fn($q) => $q->whereDate('created_at', '>=', $request->start_date))
            ->when($request->filled('end_date'), fn($q) => $q->whereDate('created_at', '<=', $request->end_date))
            ->sum('amount');

        $totalB2C = DB::table('transfers')
            ->whereIn('sender_account', $storeAccountNumbers)
            ->when($request->filled('start_date'), fn($q) => $q->whereDate('created_at', '>=', $request->start_date))
            ->when($request->filled('end_date'), fn($q) => $q->whereDate('created_at', '<=', $request->end_date))
            ->sum('amount');

        // Retornar tudo no mesmo JSON
        return response()->json([
            'totals' => [
                '+ C2B' => $totalC2B,
                '- B2C' => $totalB2C,
                'final_value' => $totalC2B - $totalB2C,
            ],
            'data' => $paginated
        ]);
    }


    public function getAllStoresTransactionsNEWNEW(Request $request)
    {
        $size = $request->get('per_page', 10);
        $user = User::getUserAccount();

        if ($user->profile !== 'business') {
            return response()->json(['data' => []], 200);
        }

        // Buscar os IDs das lojas do business_account
        $storeIds = Store::where('business_account_id', $user->account_id)->pluck('id');

        $payments = Payment::query()
            ->join('stores', 'stores.id', '=', 'payments.store_id')
            ->whereIn('payments.store_id', $storeIds)
            ->when($request->filled('transactionId'), function ($query) use ($request) {
                $query->where('payments.transaction_id', $request->transactionId);
            })
            ->when($request->filled('start_date'), function ($query) use ($request) {
                $query->whereDate('payments.created_at', '>=', $request->start_date);
            })
            ->when($request->filled('end_date'), function ($query) use ($request) {
                $query->whereDate('payments.created_at', '<=', $request->end_date);
            })
            ->when($request->filled('store_name'), function ($query) use ($request) {
                $query->where('stores.name', 'like', '%' . $request->store_name . '%');
            })
            ->select(
                'payments.*',
                'stores.name as store_name',
                DB::raw('SUM(payments.amount) as total_amount'),
                DB::raw('COUNT(payments.id) as total_nr_transaction')
            )
            ->groupBy('payments.store_id', 'stores.name')
            ->orderBy('stores.name', 'asc')
            ->paginate($size);

        return response()->json($payments);
    }


    public function getStoresLink(Request $request)
    {

        $size = $request->get('per_page', 10);

        // $user = User::getUserAccount();
        $user = User::getUserDetails(auth()->user()->user_id);

        if ($user->profile !== 'business') {
            return response()->json(['data' => []], 200);
        }

        $new_start_date  = null;
        $new_end_date  = null;

        if ($request->filled('start_date')) {
            $start_date = explode('-', $request->start_date);
            if (strlen($start_date[2]) >= 4)
                $new_start_date = $start_date[2] . '-' . $start_date[1] . '-' . $start_date[0];
        }

        if ($request->filled('end_date')) {
            $end_date = explode('-', $request->end_date);
            if (strlen($end_date[2]) >= 4)
                $new_end_date = $end_date[2] . '-' . $end_date[1] . '-' . $end_date[0];
        }

        // Buscar os IDs das lojas do business_account
        $storeIds = Store::where('business_account_id', $user->account_id)->pluck('id');

        $links = Link::query()
            ->join('stores', 'stores.id', 'links.store_id')
            ->whereIn('links.store_id', $storeIds)
            ->when($request->filled('transactionId'), function ($query) use ($request) {
                $query->where('links.link_id', $request->transactionId);
            })
            // new ------------------------
            ->when($request->filled('start_date'), function ($query) use ($request, $new_start_date) {
                $query->whereDate('links.created_at', '>=', $new_start_date ?? $request->start_date);
            })
            ->when($request->filled('end_date'), function ($query) use ($request, $new_end_date) {
                $query->whereDate('links.created_at', '<=', $new_end_date ?? $request->end_date);
            })
            // new ------------------------
            ->when($request->filled('store_name'), function ($query) use ($request) {
                $query->where('stores.name', 'like', '%' . $request->store_name . '%');
            })
            ->orderBy('links.created_at', 'desc')
            ->select(
                'links.*',
                'stores.name as store_name'
            )
            ->paginate($size);

        return response()->json($links);
    }


    public function getLinkPayments(Request $request)
    {
        $size = $request->get('per_page', 10);

        // $user = User::getUserAccount();
        $user = User::getUserDetails(auth()->user()->user_id);

        if ($user->profile !== 'business') {
            return response()->json(['data' => []], 200);
        }

        $new_start_date  = null;
        $new_end_date  = null;

        if ($request->filled('start_date')) {
            $start_date = explode('-', $request->start_date);
            if (strlen($start_date[2]) >= 4)
                $new_start_date = $start_date[2] . '-' . $start_date[1] . '-' . $start_date[0];
        }

        if ($request->filled('end_date')) {
            $end_date = explode('-', $request->end_date);
            if (strlen($end_date[2]) >= 4)
                $new_end_date = $end_date[2] . '-' . $end_date[1] . '-' . $end_date[0];
        }

        // Buscar os IDs das lojas do business_account
        $storeIds = Store::where('business_account_id', $user->account_id)->pluck('id');

        $payments = Payment::query()
            ->join('stores', 'stores.id', 'payments.store_id')
            // ->where('payments.store_id', $storeIds)
            ->whereIn('payments.store_id', $storeIds)
            ->when($request->filled('transactionId'), function ($query) use ($request) {
                $query->where('payments.link_id_key', $request->transactionId);
            })
            // new ------------------------
            ->when($request->filled('start_date'), function ($query) use ($request, $new_start_date) {
                $query->whereDate('payments.created_at', '>=', $new_start_date ?? $request->start_date);
            })
            ->when($request->filled('end_date'), function ($query) use ($request, $new_end_date) {
                $query->whereDate('payments.created_at', '<=', $new_end_date ?? $request->end_date);
            })
            // new ------------------------
            ->when($request->filled('store_name'), function ($query) use ($request) {
                $query->where('stores.name', 'like', '%' . $request->store_name . '%');
            })
            ->orderBy('payments.created_at', 'desc')
            ->select(
                'payments.*',
                'stores.name as store_name'
            )
            ->paginate($size);

        return response()->json($payments);
    }




    public function card_export(Request $request)
    {
        // return "Card 🧨";
        // return $request->startDate . '---' . $request->subAccounNumber;

        $query = Payment::query()
            ->select(
                'transaction_id as TransactionID',
                'payments.status as Status',
                'payments.created_at as created_at',
                'sender_account_number as SubAccountNumber',
                'sender_card_number as Card'
            )
            ->where('sender_card_number', '=', $request->subAccounNumber)
            ->whereDate('created_at', '>=', $request->startDate)
            ->whereDate('created_at', '<=', $request->endDate)
            ->orderBy('payments.created_at', 'desc')
            ->get();

        return $query;
    }




    // todo 07-08-2024 Lojas Conta Empresa
    public function getEmpresaPaymentsStores(Request $request, $id)
    {
        $user = User::getUserAccount();
        // $user = User::getUserDetails(auth()->user()->user_id);

        if ($user->profile === 'business') {

            $size = $request->input('per_page', 4);

            $payments = Payment::query()
                ->where('payments.store_id', $id)
                ->where('payments.is_real_payment', 1)
                ->where('payments.transaction_type', 'debit');

            // Adiciona filtro por intervalo de datas sem usar Carbon
            if ($request->filled('start_date') && $request->filled('end_date')) {
                try {
                    $startDate = new \DateTime($request->input('start_date'));
                    $endDate = new \DateTime($request->input('end_date'));

                    // Adiciona um dia ao endDate para garantir que inclua a data final
                    $endDate->modify('+1 day');

                    $payments->whereBetween('payments.created_at', [
                        $startDate->format('Y-m-d H:i:s'),
                        $endDate->format('Y-m-d H:i:s')
                    ]);
                } catch (\Exception $e) {
                    return response()->json(['error' => 'Invalid date format.'], 400);
                }
            }

            $payments->orderBy('payments.created_at', 'desc')
                ->select(
                    'payments.*'
                );

            // Paginação
            $payments = $payments->paginate($size);

            if ($payments->isEmpty()) {

                return response()->json(['message' => 'Sem dados.'], 200);
            }

            return response()->json($payments, 200);
            // return response()->json($payments->paginate($size), 200);
        } else {
            return response()->json([], 200);
        }
    }

    public function getEmpresaPaymentsStoresORIGINAL(Request $request, $id)
    {
        // $size = (!request()->per_page) ? 4 : request()->per_page;

        $user = User::getUserAccount();
        if ($user->profile === 'business') {

            $perPage = !!$request->input('per_page') ? $request->input('per_page') : 4;
            $orderType = $request->input('order_type') === 'ASC' ? 'ASC' : 'DESC';
            $orderBy = !!$request->input('order_by') && $request->input('order_by') !== 'null' ? $request->input('order_by') : 'payments.id';

            $payments = Payment::query()
                ->select('payments.*')
                ->where('payments.store_id', $id)
                ->where('payments.is_real_payment', 1)
                ->where('payments.transaction_type', 'debit')
                ->orderBy('payments.id', 'DESC')
                ->orderBy($orderBy, $orderType)
                ->paginate($perPage);
            // ->paginate($size);
            // ->get();

            if ($payments->isEmpty()) {

                return response()->json(['message' => 'Sem dados.'], 200);
            }

            return response()->json($payments, 200);
        } else {
            return response()->json([], 200);
        }
    }


    public function getStoresData(Request $request, $id)
    {
        $perPage = !!$request->input('per_page') ? $request->input('per_page') : 10;
        $orderType = $request->input('order_type') === 'ASC' ? 'ASC' : 'DESC';
        $orderBy = !!$request->input('order_by') && $request->input('order_by') !== 'null' ? $request->input('order_by') : 'stores.id';

        $stores = Store::query()
            ->leftJoin('admins', 'admins.id', '=', 'stores.user_id')
            ->leftJoin('ramo_activities', 'ramo_activities.id', '=', 'stores.industry_activity')
            ->leftJoin('merchant_contracts', 'stores.merchant_contract_id', '=', 'merchant_contracts.id')
            ->leftJoin('merchant_accounts', 'stores.merchant_account_id', '=', 'merchant_accounts.id')
            ->select(
                'stores.*',
                'ramo_activities.nome as industry',
                'merchant_contracts.id as merchant_contract_id',
                'merchant_contracts.taxa',
                'merchant_contracts.max_balance',
                'merchant_contracts.nr_transaction',
                'merchant_contracts.min_amount',
                'merchant_contracts.max_amount',
                'merchant_contracts.use_point_limit',
                'merchant_accounts.name as merchant_name',
                'stores.name as store_name',
                'stores.id as id_store'
            )
            ->where('stores.id', $id)
            ->orderBy($orderBy, $orderType)
            ->paginate($perPage);


        //        return response()->json(['data' => $store]);
        return response()->json(['data' => $stores], 200);
    }

    // todo 07-08-2024 Lojas Conta Empresa
    public function getEmpresaPaymentsDetails(Request $request, $id)
    {
        $payments_details = Payment::query()
            ->join('stores', 'stores.id', '=', 'payments.store_id')
            ->select('payments.*', 'stores.logo', 'stores.name')
            ->where('payments.id', $id)
            ->first();

        return response()->json(['data' => $payments_details], 200);
    }


    public function getStoreMerchant($account_number)
    {
        $comerciante = Store::query()
            ->where('public_id', '=', $account_number)
            ->orWhere('stores.account_number', '=', $account_number)
            ->join('ramo_activities', 'ramo_activities.id', '=', 'stores.industry_activity')
            ->select(
                'stores.name',
                'stores.account_number',
                'stores.logo',
                'stores.address',
                'ramo_activities.nome as category',
                'ramo_activities.logo as logo_category'
            )
            ->first();

        return response()->json(['data' => $comerciante], 200);
    }


    private function checkExpiredPush($push)
    {
        if ($push) {
            $expirationDate = (int)strtotime($push->expiration_datetime);
            $todayDate = (int)strtotime(date('Y-m-d H:i:s'));
            $remaningtime =  $todayDate > $expirationDate;
            if ($remaningtime && ($push->status == 'PENDING')) {
                $push->status = 'EXPIRED';
                $push->update();
            }
        }
    }


    // New Generate Payment Push Method
    public function generatePushPaymentNew(Request $request)
    {

        try {
            $this->validate(
                $request,
                [
                    'store_account_number' => 'required',
                    'client_account_number' => 'required',
                    'amount' => 'required',
                    'expiration_datetime' => 'nullable|date_format:Y-m-d H:i:s',
                ],
                [
                    'store_account_number.required' => 'store_account_number is required',
                    'client_account_number.required' => 'client_account_number is required',
                    'amount.required' => 'amount is required',
                    'expiration_datetime.date_format' => 'expiration_datetime must be in the format Y-m-d H:i:s',
                ]
            );


            if ($request->has('expiration_datetime') && $request->expiration_datetime) {
                $expiration_date = (int)strtotime($request->expiration_datetime);
                $current_date = (int)strtotime(date('Y-m-d H:i:s'));

                if ($current_date > $expiration_date) return SendResponse::errorResp400('Data invalida', 'Invalid Date');
            }
        } catch (\Throwable $th) {
            //return response()->json(['error'=> $th->getMessage()], 400);
            return response()->json([
                'cody' => trans('error')[0]['cody'],
                'error' => trans('error')[0]['error'],
                'type' => trans('error')[0]['type'],
                'message' => $th->getMessage(),
            ], trans('error')[0]['http_code']);
        }

        $user_account = User::getAccount($request->client_account_number);
        if (!$user_account) return SendResponse::errorResp404notfound('Conta de Cliente inválida', 'Invalid Client Account');
        $user_payer = User::getUserDetails($user_account->user_id);

        $store = Store::getStoreContractsAndConfigs($request->store_account_number);
        if (!$store) return SendResponse::errorResp404notfound('Conta da Loja inválida', 'Invalid Store Account');


        $push_pr = PushPaymentRequest::query()
            ->where('amount', $request->amount)
            ->where('store_id', $store->id)
            ->where('client_account_number', $user_account->account_number)
            ->where('status', 'PENDING')
            ->first();

        $this->checkExpiredPush($push_pr);

        if ($push_pr && ($push_pr->status == 'PENDING')) return SendResponse::errorResp400('O cliente ja tem um  push com o mesmo montante por pagar na sua loja', 'The customer already has a push with the same amount to pay in your store');

        // Se o comerciante nao enviar o expiration_datetime a API por default adiciona + um dia de duracao do link
        if (!$request->has('expiration_datetime')) {

            $duration = new DateTime(); // Data atual
            $duration->modify('+5 minutes');
            $request->request->add(['expiration_datetime' => $duration->format('Y-m-d H:i:s')]);
        }


        $imali_fee = $request->amount * ($store->taxa / 100);
        $amount_to_credit = $request->amount - $imali_fee;

        $trasactionGeneration = new TransactionGeneration();
        $push_id = $trasactionGeneration->generateTransaction();

        PushPaymentRequest::create([
            'push_id' => $push_id,
            'amount' => $request->amount,
            'imali_fee' => $imali_fee,
            'amount_to_credit' => $amount_to_credit,
            'store_id' => $store->id,
            'store_account_number' => $store->account_number,
            'client_account_number' => $user_account->account_number,
            'business_account_id' => $store->business_account_id,
            'expiration_datetime' => $request->expiration_datetime,
        ]);

        $push = new PushNotification(
            'Pagamento iMali : ' . $push_id,
            $user_payer->name . ' gerou um pagamento de ' . $request->amount . ' MT',
            $user_payer->firebase_token,
            'com.imali.payapp.payment_PUSH_NOTIFICATION'
        );

        $data = array(
            'transaction' => $push_id,
            'amount' => $request->amount,
            'account_number' => (string)$store->account_number,
            'name' => $store->name,
            'address' => $store->address,
            'mobile_phone' => $store->mobile_phone,
            'logo' => $store->logo,
            'logo_category' => $store->logo_category,
            'category' => $store->category,
            'description' => 'Pagamento Push',
            'route' => 'PUSH_NOTIFICATION',
            'created_at' => now()
        );

        $is_push_sent =  $push->sendPush($data);

        if ($is_push_sent) return SendResponse::successResp200('Push enviado com sucesso', 'Push sent successfull');
        return SendResponse::errorResp400('Erro ao enviar Push', 'Error sending Push');
    }


    // Get All Payment Pushs exists in Database
    public function getMyGeneratedPushPayments()
    {
        $user = User::getUserAccount();
        // $user_account = User::getAccount($user->account_number);

        $push_pr = PushPaymentRequest::query()
            ->where('client_account_number', $user->account_number)
            ->get();

        // return response()->json($push_pr);
        return response()->json(['data' => $push_pr], 200);
    }



    // Get Push Status
    public function checkPushStatus($payment_method, $push_id)
    {
        // $push = PushPaymentRequest::query()->where('push_id', $push_id)->first();
        // if (!$push) return SendResponse::errorResp404notfound('Push invalido', 'Invalid Push');

        // $checkedPush = ['push_id' => $push->push_id, 'status' => $push->status];
        // return response()->json(['data' => $checkedPush]);


        // Buscar push na tabela PushPaymentRequest
        $push = PushPaymentRequest::query()->where('push_id', $push_id)->first();

        // Se não encontrar na tabela PushPaymentRequest, buscar na tabela Payment
        if (!$push) {
            $payment = Payment::query()->where('transaction_id', $push_id)->orWhere('partner_transaction_id', $push_id)->first();

            if (!$payment) {
                return SendResponse::errorResp404notfound('Push inválido', 'Invalid Push');
            }

            return response()->json([
                'data' => [
                    'push_id' => $payment->transaction_id,
                    'status' => $payment->status
                ]
            ]);
        }

        return response()->json([
            'data' => [
                'push_id' => $push->push_id,
                'status' => $push->status
            ]
        ]);
    }



    //? Gerar um pedido de pagamento 
    //? atraves da notificacao via PUSH

    public function generatePaymentPush(Request $request)
    {
        try {
            $this->validate(
                $request,
                [
                    'storeAccountNumber' => 'required',
                    'clientAccountNumber' => 'required',
                    'transactionID' => 'required',
                    'amount' => 'required',
                    'description' => 'required',
                    'terminalID' => 'required',
                    'terminalChannel' => 'required',
                    'terminalCompanyName' => 'required',
                ],
                [
                    'storeAccountNumber.required' => 'storeAccountNumber is required',

                    'clientAccountNumber.required' => 'clientAccountNumber is required',

                    'transactionID.required' => 'transactionID is required',
                    'amount.required' => 'amount is required',
                    'description.required' => 'description is required',
                    'terminalID.required' => 'terminalID is required',
                    'terminalChannel.required' => 'terminalChannel is required',
                    'terminalCompanyName.required' => 'terminalCompanyName is required'
                ]
            );
        } catch (\Throwable $th) {
            //return response()->json(['error'=> $th->getMessage()], 400);
            return response()->json([
                'cody' => trans('error')[0]['cody'],
                'error' => trans('error')[0]['error'],
                'type' => trans('error')[0]['type'],
                'message' => $th->getMessage(),
            ], trans('error')[0]['http_code']);
        }

        if (!is_numeric($request->storeAccountNumber))
            // return response()->json(['error'=> 'storeAccountNumber is numeric'], 400);
            return response()->json([
                'cody' => trans('error')[0]['cody'],
                'error' => trans('error')[0]['error'],
                'type' => trans('error')[0]['type'],
                'message' => 'storeAccountNumber is numeric',
            ], trans('error')[0]['http_code']);

        if (strlen($request->storeAccountNumber) != 9)
            //return response()->json(['error'=> 'storeAccountNumber min length is 9'], 400);
            return response()->json([
                'cody' => trans('error')[0]['cody'],
                'error' => trans('error')[0]['error'],
                'type' => trans('error')[0]['type'],
                'message' => 'storeAccountNumber min length is 9',
            ], trans('error')[0]['http_code']);

        if (!is_numeric($request->clientAccountNumber))
            //return response()->json(['error'=> 'clientAccountNumber is numeric'], 400);
            return response()->json([
                'cody' => trans('error')[0]['cody'],
                'error' => trans('error')[0]['error'],
                'type' => trans('error')[0]['type'],
                'message' => 'clientAccountNumber is numeric',
            ], trans('error')[0]['http_code']);

        if (strlen($request->clientAccountNumber) != 9)
            //return response()->json(['error'=> 'clientAccountNumber min length is 9'], 400);
            return response()->json([
                'cody' => trans('error')[0]['cody'],
                'error' => trans('error')[0]['error'],
                'type' => trans('error')[0]['type'],
                'message' => 'clientAccountNumber min length is 9',
            ], trans('error')[0]['http_code']);

        $this->request = $request;
        //? Validacoes
        $token = str_replace('Bearer ', '', $this->request->header('authorization'));
        $clientStore = UserClient::query()->where('client_key', $token)->first();
        if (!$clientStore)
            //return response()->json(['message'=> 'Invalid Client Key', 'error_code'=> 403], 403);
            return response()->json([
                'cody' => trans('error')[2]['cody'],
                'error' => trans('error')[2]['error'],
                'type' => trans('error')[2]['type'],
                'message' => 'Invalid Client Key',
            ], trans('error')[2]['http_code']);

        $store = Store::query()->where('account_number', '=', $this->request->storeAccountNumber)->first();
        if (!$store)
            //return response()->json(['message'=> 'Store not found', 'error_code'=> 404], 404);
            return response()->json([
                'cody' => trans('error')[3]['cody'],
                'error' => trans('error')[3]['error'],
                'type' => trans('error')[3]['type'],
                'message' => 'Store not found',
            ], trans('error')[3]['http_code']);

        //? fazer a verificacao aqui...
        // $imali = ImaliAccount::query()
        //     ->join('users', 'users.id', 'imali_accounts.user_id')
        //     ->where('imali_accounts.account_number', '=', $this->request->clientAccountNumber)
        //     ->orWhere('users.phone', '=', $this->request->clientAccountNumber)
        //     ->select('imali_accounts.*', 'users.phone as phone', 'users.firebase_token', 'users.name as nome', 'imali_accounts.user_id as user_id')
        //     ->first();

        $payer = User::getAccount($this->request->clientAccountNumber);
        $imali = User::getUserDetails($payer->id);

        if (!$imali)
            //return response()->json(['message'=> 'invalid iMali account or phone number', 'error_code'=> 404], 404);
            return response()->json([
                'cody' => trans('error')[3]['cody'],
                'error' => trans('error')[3]['error'],
                'type' => trans('error')[3]['type'],
                'message' => 'invalid iMali account or phone number',
            ], trans('error')[3]['http_code']);

        //? verificar auth
        if (!$imali->firebase_token)
            //return response()->json(['message'=> 'Unauthenticated, firebase token NULL', 'error_code'=> 403], 403);
            return response()->json([
                'cody' => trans('error')[2]['cody'],
                'error' => trans('error')[2]['error'],
                'type' => trans('error')[2]['type'],
                'message' => 'Unauthenticated, firebase token NULL',
            ], trans('error')[2]['http_code']);

        $trasactionGeneration = new TransactionGeneration();
        $kyc = new PartnerKyc();
        //        $checkKyc = $kyc->checkPaymentCliente($request);
        $checkKyc = $kyc->checkPaymentGeneration($request);

        $tokenGeral = '';

        if ($checkKyc) {
            return $checkKyc;
        } else {
            $this->request = $request;

            // DB::transaction(function () {

            // $client = User::query()->where('phone', $this->request->clientAccountNumber)->first();

            // $payerUser = DB::table('users')->where('id', $imali->user_id)->first();


            // $user = DB::table('users')->where('id', '=', $imali->user_id)->first();

            $merchant = MerchantAccount::find($store->merchant_account_id);


            // $trasactionGeneration = new GenerateToken();
            $tra = new TransactionGeneration();
            $transactionID = $tra->generateTransaction();

            DB::table('payments')->insert([
                // 'transaction_id' => $this->request->transactionID,
                'transaction_id' => $transactionID,
                'partner_transaction_id' => $this->request->transactionID,
                'amount' => $this->request->amount,
                'estado' => 'pending',
                'status' => 'pending',
                'description' => $this->request->description,
                'terminalID' => $this->request->terminalID,
                'terminalChannel' => $this->request->terminalChannel,
                'terminalCompanyName' => $this->request->terminalCompanyName,
                'store_id' => $store->id,
                'merchant_id' => $merchant->id,
                'client_id' => $clientStore->id,
                'business_account_id' => $imali->id, // todo actualizado 15-12-2023
                'imali_account_id' => $imali->id,
                'sender_id' => $imali->id,
                'created_at' => now(),
                'updated_at' => now(),
            ]);


            $paymentStore = Store::query()
                ->join('payments', 'payments.store_id', '=', 'stores.id')
                ->join('ramo_activities', 'ramo_activities.id', '=', 'stores.industry_activity')
                // ->where('payments.transaction_id', '=', $this->request->transactionID)
                ->where('payments.transaction_id', '=', $transactionID)
                ->select(
                    'stores.name',
                    'stores.logo',
                    'stores.mobile_phone',
                    'stores.account_number as storeAccountNumber',
                    'stores.address',
                    'payments.transaction_id as transaction',
                    'payments.amount',
                    'ramo_activities.nome as category',
                    'ramo_activities.logo as logo_category'
                )
                ->first();

            $push = new PushNotification(
                'Pagamento iMali : ' . $this->request->transactionID,
                $this->request->user()->name . ' gerou um pagamento de ' . $this->request->amount . ' MT',
                $imali->firebase_token,
                'com.imali.payapp.payment_PUSH_NOTIFICATION'
            );

            $data = array(
                'transaction' => $request->transactionID,
                'amount' => $request->amount,
                'account_number' => (string)$paymentStore->storeAccountNumber,
                'name' => $paymentStore->name,
                'address' => $paymentStore->address,
                'mobile_phone' => $paymentStore->mobile_phone,
                'logo' => $paymentStore->logo,
                'logo_category' => $paymentStore->logo_category,
                'category' => $paymentStore->category,
                'description' => $request->description,
                'route' => 'PUSH_NOTIFICATION',
                'created_at' => now()
            );

            // // return $payment;
            // $notification = array(
            //     //'icon' => 'ic_i_mali_cover',
            //     'icon' => 'ic_imali_logo_verde_01',
            //     'title' => 'Pagamento iMali : ' . $this->request->transactionID,
            //     'body' => $this->request->user()->name . ' gerou um pagamento de ' . $this->request->amount . ' MT',
            //     'click_action' => 'com.imali.payapp.payment_PUSH_NOTIFICATION',
            //     'color' => '#008577'
            // );

            // $data = array(
            //     'transaction' => $request->transactionID,
            //     'amount' => $request->amount,
            //     // 'account_number' => $request->clientAccountNumber,
            //     'account_number' => $paymentStore->storeAccountNumber,
            //     'name' => $paymentStore->name,
            //     'address' => $paymentStore->address,
            //     'mobile_phone' => $paymentStore->mobile_phone,
            //     //'store_account_number' => $paymentStore->storeAccountNumber,
            //     'logo' => $paymentStore->logo,
            //     'logo_category' => $paymentStore->logo_category,
            //     'category' => $paymentStore->category,
            //     'description' => $request->description,
            //     'route' => 'PUSH_NOTIFICATION',
            //     'created_at' => now()
            // );

            $is_push_sent =  $push->sendPush($data);


            // $pushNotifi =  $this->pushNotifification($imali->firebase_token, $notification, $data);


            // if ((int)$pushNotifi['success'] === 1) {
            if ($is_push_sent) {

                $payment = $this->checkTransactionStatus($request, $request->transactionID);

                $paymentStatus = $this->checkSuccessPayment($request, $payment);

                if ($paymentStatus === 'success') {

                    $payment = $this->checkTransactionStatus($request, $request->transactionID);
                    $formatedPayment = array(
                        "partnerTransactionID" => $payment->getData()->partner_transaction_id,
                        "storeAccountNumber" => $request->storeAccountNumber,
                        "customerAccountNumber" => $request->clientAccountNumber,
                        "amount" => $payment->getData()->amount,
                        "status" => $payment->getData()->status,
                        "description" => $payment->getData()->description,
                        "terminalID" => $payment->getData()->terminalID,
                        "terminalChannel" => $payment->getData()->terminalChannel,
                        "terminalCompanyName" => $payment->getData()->terminalCompanyName,
                        "createdAt" => $payment->getData()->created_at
                    );

                    $push = new PushNotification(
                        'Pagamento de ' . $request->amount . ' MZN',
                        'Pagamento de ' . $request->amount . ' MZN ' . ' feito para: ' . $paymentStore->name,
                        $imali->firebase_token
                    );
                    $push->sendPush($formatedPayment);

                    //return response()->json(['message' => trans('payment_done'), 'status'=> 200, 'data'=> $formatedPayment]);
                    return response()->json([
                        'cody' => trans('success')[0]['cody'],
                        'error' => trans('success')[0]['success'],
                        'type' => trans('success')[0]['type'],
                        'message' => 'Payment made successfully',
                        'data' => $formatedPayment
                    ], trans('success')[0]['http_code']);
                };

                if ($paymentStatus === 'rejected') {
                    $payment = $this->checkTransactionStatus($request, $request->transactionID);
                    $formatedPayment = array(
                        "partnerTransactionID" => $payment->getData()->partner_transaction_id,
                        "storeAccountNumber" => $request->storeAccountNumber,
                        "customerAccountNumber" => $request->clientAccountNumber,
                        "amount" => $payment->getData()->amount,
                        "status" => $payment->getData()->status,
                        "description" => $payment->getData()->description,
                        "terminalID" => $payment->getData()->terminalID,
                        "terminalChannel" => $payment->getData()->terminalChannel,
                        "terminalCompanyName" => $payment->getData()->terminalCompanyName,
                        "createdAt" => $payment->getData()->created_at
                    );

                    $push = new PushNotification(
                        'Pagamento rejeitado!',
                        'Pagamento de ' . $request->amount . ' MZN ' . ' para loja ' . $paymentStore->name . ' foi rejeitado.',
                        $imali->firebase_token
                    );
                    $push->sendPush($formatedPayment);

                    //return response()->json(['message' => trans('payment_rejected'), 'status'=> 401, 'data'=> $formatedPayment], 406);
                    return response()->json([
                        'cody' => trans('error')[7]['cody'],
                        'error' => trans('error')[7]['error'],
                        'type' => trans('error')[7]['type'],
                        'message' => 'Payment Rejected by User',
                        'data' => $formatedPayment
                    ], trans('error')[7]['http_code']);
                }

                if ($paymentStatus === 'expired') {

                    $payment = $this->checkTransactionStatus($request, $request->transactionID);
                    $formatedPayment = array(
                        "partnerTransactionID" => $payment->getData()->partner_transaction_id,
                        "storeAccountNumber" => $request->storeAccountNumber,
                        "customerAccountNumber" => $request->clientAccountNumber,
                        "amount" => $payment->getData()->amount,
                        "status" => $payment->getData()->status,
                        "description" => $payment->getData()->description,
                        "terminalID" => $payment->getData()->terminalID,
                        "terminalChannel" => $payment->getData()->terminalChannel,
                        "terminalCompanyName" => $payment->getData()->terminalCompanyName,
                        "createdAt" => $payment->getData()->created_at
                    );

                    $push = new PushNotification(
                        'Pagamento expirado!',
                        'Pagamento de ' . $request->amount . ' MZN ' . ' para loja ' . $paymentStore->name . ' expirou.',
                        $imali->firebase_token
                    );
                    $push->sendPush($formatedPayment);

                    // $clientUser = User::find($request->user()->id);

                    // $this->sendPush($clientUser->firebase_token, $data);

                    //return response()->json(['message' => trans('payment_expired'), 'status'=> 401, 'data'=> $formatedPayment], 408);
                    return response()->json([
                        'cody' => trans('error')[4]['cody'],
                        'error' => trans('error')[4]['error'],
                        'type' => trans('error')[4]['type'],
                        'message' => 'Expired Payment',
                        'data' => $formatedPayment
                    ], trans('error')[4]['http_code']);
                }
                // } else if ((int)$pushNotifi['failure'] === 1) {

                //     //return response()->json(['message' => 'Erro ao tentar enviar push de pagamento', 'status'=> 500]);
                //     return response()->json([
                //         'cody' => trans('warning')[0]['cody'],
                //         'error' => trans('warning')[0]['warning'],
                //         'type' => trans('warning')[0]['type'],
                //         'message' => 'Error when trying to send payment push',
                //     ], trans('warning')[0]['http_code']);

                //? Colocar aqui uma mensagem a informar que o utilizador deve se autenticar na APP
            } else {
                //return response()->json(['message' => 'Erro desconhecido', 'status'=> 500]);
                return response()->json([
                    'cody' => trans('warning')[0]['cody'],
                    'error' => trans('warning')[0]['warning'],
                    'type' => trans('warning')[0]['type'],
                    'message' => 'Unknown error',
                ], trans('warning')[0]['http_code']);
            }
        }
    }
}