<?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']); } } } }