diff --git a/.env.example b/.env.example index 765b231..453e5ca 100755 --- a/.env.example +++ b/.env.example @@ -50,12 +50,8 @@ SENTRY_LARAVEL_DSN=https://9d6f6b6a24514295910a3b0e5bdd1449@o1053292.ingest.sent SENTRY_TRACES_SAMPLE_RATE=1 # Configuration API GBS -BANK_API_BASE_URL=http://192.168.1.173:8084/api/v1 -BANK_API_USERNAME=sa -BANK_API_PASSWORD=Sa123456 +BANK_API_BASE_URL=http://192.168.1.173:2087/api/v1 BANK_API_BRANCH_CODE=99001 -# Configuration API Bank Link -BANK_LINK_API_BASE_URL=http://192.168.1.173:2087/api/v1 -BANK_LINK_API_LOGIN=admin -BANK_LINK_API_PASSWORD=admin +BANK_API_LOGIN=admin +BANK_API_PASSWORD=admin diff --git a/app/Http/Controllers/WalletController.php b/app/Http/Controllers/WalletController.php index 48e4ced..95d9719 100755 --- a/app/Http/Controllers/WalletController.php +++ b/app/Http/Controllers/WalletController.php @@ -736,6 +736,8 @@ INNER JOIN countries c ON oc.id_country = c.id INNER JOIN type_operators top ON ->first(); if ($differentTypeAccount) { + $user = User::find($request->id_user); + if (!$user) return $this->errorResponse(trans('errors.user_not_found')); $customer_account_type = CustomerAccountType::find($request->account_type); if (!$customer_account_type) return $this->errorResponse(trans('errors.account_type_not_found')); @@ -771,6 +773,11 @@ INNER JOIN countries c ON oc.id_country = c.id INNER JOIN type_operators top ON $newAccount->status = 'actived'; $newAccount->save(); + $user->update([ + 'id_bank_country' => $newAccount->id_operator_country, + 'iban' => $result['accountNumber'] ?? null + ]); + try { Mail::to($user->email)->send(new BankAccountActivated($newAccount, $customer_account_type->name)); } catch (\Exception $e) { @@ -898,6 +905,7 @@ INNER JOIN countries c ON oc.id_country = c.id INNER JOIN type_operators top ON 'id_bank_country' => $bank_account->id_operator_country, 'iban' => $result['accountNumber'] ?? null ]); + LOG::info('Numero du compte bancaire du User : ' . $user->id . ' : ' . ($user->iban ?? 'N/A')); try { Mail::to($user->email)->send(new BankAccountActivated($bank_account, $name_of_account_type)); diff --git a/app/Http/Controllers/iLinkTransactionController.php b/app/Http/Controllers/iLinkTransactionController.php index ec4ca7b..444c08a 100755 --- a/app/Http/Controllers/iLinkTransactionController.php +++ b/app/Http/Controllers/iLinkTransactionController.php @@ -18,6 +18,7 @@ use App\Models\Regulation; use App\Models\TypeIlinkTransaction; use App\Models\TypeOperator; use App\Models\User; +use App\Models\UserBankAccount; use App\Models\Wallet; use App\Models\WalletAgent; use App\Models\WalletIlinkTransaction; @@ -618,75 +619,114 @@ class iLinkTransactionController extends Controller * ) * ) */ - case 4: //User - Envoi de wallet à banque - + case 4: // User - Envoi de wallet à banque $this->validate($request, [ 'id_wallet_network' => 'required|integer|min:0|not_in:0', + 'montant' => 'required|numeric|min:0', ]); $user = $walletUser->user; $this->validatePassword($request->password, $user->encrypted_password, $user->salt); - $this->checkUserIdentification($user->id); - if (!(isset($user->iban) && isset($user->id_bank_country))) + $userBankAccount = UserBankAccount::where('id_user', $user->id) + ->where('id_operator_country', $user->id_bank_country) + ->where('status', 'actived') + ->first(); + + if (!$userBankAccount || !$userBankAccount->account_number) { throw new Exception(trans('errors.wallet_not_linked_to_bank_account'), 422); + } - - if ($request->montant > $walletUser->balance) + if ($request->montant > $walletUser->balance) { throw new Exception(trans('errors.insufficient_balance'), 422); + } + // Vérification de l'opérateur réseau + $network_bank = NetworksOperator::where('id_network', $request->id_wallet_network) + ->where('id_operator_country', $user->id_bank_country) + ->first(); - //Reverifier si l'operateur est toujours associé au reseau - $network_bank = NetworksOperator::where('id_network', $request->id_wallet_network)->where('id_operator_country', $user->id_bank_country)->first(); - if (!$network_bank) - throw new Exception(trans('errors.operator_not_associated_with_network') . '. ' . trans('errors.update_banking_information')); + if (!$network_bank || $network_bank->operators_country->operator->type != 'bank') { + throw new Exception(trans('errors.not_banking_operator')); + } - if ($network_bank->operators_country->operator->type != 'bank') - throw new Exception(trans('errors.not_banking_operator') . '. ' . trans('errors.update_banking_information')); - - //Reverifier le code IBAN correspond toujours - // $country_code = $network_bank->network->country->code_country; - // $bank_code = $network_bank->operators_country->code; - // switch ($this->checkIBAN($user->iban, $country_code, $bank_code)) { - // case 0: - // throw new Exception(trans('errors.invalid_iban') . '. ' . trans('errors.update_banking_information')); - // case 1: - // throw new Exception(trans('errors.country_not_match_iban') . '. ' . trans('errors.update_banking_information')); - // case 2: - // throw new Exception(trans('errors.bank_not_match_iban') . '. ' . trans('errors.update_banking_information')); - // } - - $transaction->frais = $frais = 0; - $transaction->taxe = $taxe = 0; - //$walletUser->balance -= $transaction->montant; - //Emettre une trame SSL pour recharger le compte bancaire du montant de la transaction - $transaction->commission_banque = 0; - - $transaction->commission_hyp = 0; - - $transaction->id_wallet_hyp = $walletHyperviseur->id; - $transaction->frais = $frais; - $transaction->bank = $user->bank->operators_country->operator->nom; - $transaction->country = $user->bank->network->country->name; - $transaction->id_bank = $user->id_bank_country; - $transaction->iban = $user->iban; - $transaction->date = $this->getCurrentTime($init_country); - //$walletUser->save(); + // Préparation des données de transaction $transaction->id_transaction = $this->getTransactionID(); - //$transaction->save(); + $transaction->montant = $request->montant; + $transaction->frais = 0; + $transaction->taxe = 0; + $transaction->nom_emetteur = $user->lastname; + $transaction->prenom_emetteur = $user->firstname; + $transaction->email_emetteur = $user->email; + $transaction->network_emetteur = $network_bank->id_network; + $transaction->bank = $network_bank->operators_country->operator->nom; + $transaction->country = $network_bank->operators_country->country->name; + $transaction->iban = $userBankAccount->account_number; + $transaction->date = $this->getCurrentTime($init_country); - Log::info('-------------------------- User - Envoi de wallet à banque ------------------------------------'); - Log::info(json_encode($transaction)); - Log::info('------------------------------------------------------------------------------------------------'); + // --- DÉBUT DE LA TRANSACTION DB --- + DB::beginTransaction(); + try { + // Débit provisoire du Wallet + $walletUser->balance -= $transaction->montant; + $walletUser->save(); - $message = trans('messages.successful_user_send_to_bank', - ['id_transaction' => $transaction->id_transaction, 'amount' => $this->toMoney($transaction->montant, $init_country), - 'net' => $this->toMoney($transaction->montant, $init_country), 'iban' => $request->iban ?? $user->iban, 'fees' => $this->toMoney($frais, $init_country), - 'sender_code' => $user->user_code, 'bank' => $transaction->bank, 'country' => $transaction->country]); - $this->sendMail($user->email, trans('messages.successful_transaction'), $message); - $response_message = ($message . trans('messages.sent_by_mail')); + $baseUrl = env('BANK_API_BASE_URL'); + $client = new Client(['connect_timeout' => 60]); + // Authentification (Bearer Token) + $authResponse = $client->post($baseUrl . '/auth/authenticate', [ + 'json' => ['login' => env('BANK_API_LOGIN'), 'password' => env('BANK_API_PASSWORD')] + ]); + $token = json_decode($authResponse->getBody(), true)['data']['token'] ?? null; + + if (!$token) throw new Exception(trans('errors.token_not_found')); + + // Disbursement Payload + $payload = [ + "clientMatricule" => $userBankAccount->customer_number, + "crAccountId" => $userBankAccount->account_number, + "description" => "Credit wallet sunEx vers compte bancaire", + "operationDate" => $transaction->date, + "operationTypeCode" => "MOMODE", + "traceNo" => $transaction->id_transaction, + "transactionAmt" => (float)$transaction->montant + ]; + + $apiResponse = $client->post($baseUrl . '/account/disbursement', [ + 'headers' => [ + 'Authorization' => 'Bearer ' . $token, + 'Accept' => 'application/json', + ], + 'json' => $payload + ]); + + $result = json_decode($apiResponse->getBody(), true); + + if ($apiResponse->getStatusCode() <= 301 && ($result['success'] ?? true) != false) { + $transaction->canceled = 0; + $transaction->save(); + DB::commit(); + + // Envoi Mail + $message = trans('messages.successful_user_send_to_bank', [ + 'id_transaction' => $transaction->id_transaction, + 'amount' => $this->toMoney($transaction->montant, $init_country), + 'account_Number' => $userBankAccount->account_number + ]); + $this->sendMail($user->email, trans('messages.successful_transaction'), $message); + + Log::info("SUCCESS: Wallet to Bank - Trx: " . $transaction->id_transaction); + } else { + throw new Exception("API Bank Refused: " . $apiResponse->getBody()); + } + + } catch (\Exception $e) { + DB::rollBack(); + Log::error("FAILED: Wallet to Bank - Error: " . $e->getMessage()); + throw new Exception(trans('errors.bank_api_exception'), 500); + } break; // case 5: //User - Envoi de carte à wallet // $frais =$montant * $config->taux_com_user_carte_wallet / 100; @@ -1543,6 +1583,7 @@ class iLinkTransactionController extends Controller case 18: // Agent - Envoi de cash vers banque $this->validate($request, [ 'iban' => 'required', + 'montant' => 'required|numeric|min:1', 'id_wallet_network' => 'required|integer|min:0|not_in:0', 'id_bank' => 'required|integer|min:0|not_in:0', 'nom_emetteur' => 'required', @@ -1553,38 +1594,29 @@ class iLinkTransactionController extends Controller 'nom_destinataire' => 'required', 'prenom_destinataire' => 'required', ]); + $agent = AgentPlus::findOrFail($network_agent->agent_id); $this->validatePassword($request->password, $agent->encrypted_password, $agent->salt); - if ($request->montant > $walletAgent->balance_princ) + // Vérifications de solde et réseau + if ($request->montant > $walletAgent->balance_princ) { throw new Exception(trans('errors.insufficient_balance'), 422); + } - //Verifier si la banque est associée au reseau - $network_bank = NetworksOperator::where('id_network', $request->id_wallet_network)->where('id_operator_country', $request->id_bank)->first(); - if (!$network_bank) + $network_bank = NetworksOperator::where('id_network', $request->id_wallet_network) + ->where('id_operator_country', $request->id_bank) + ->first(); + + if (!$network_bank || $network_bank->operators_country->operator->type != 'bank') { throw new Exception(trans('errors.bank_not_associated_with_network')); + } - if ($network_bank->operators_country->operator->type != 'bank') - throw new Exception(trans('errors.not_banking_operator')); - - //Verifier le code IBAN - // $country_code = $network_bank->network->country->code_country; - // $bank_code = $network_bank->operators_country->code; - // switch ($this->checkIBAN($request->iban, $country_code, $bank_code)) { - // case 0: - // throw new Exception(trans('errors.invalid_iban')); - // case 1: - // throw new Exception(trans('errors.country_not_match_iban')); - // case 2: - // throw new Exception(trans('errors.bank_not_match_iban')); - // } - - $transaction->frais = $frais = 0; - $transaction->taxe = $taxe = 0; - //$walletUser->balance -= $transaction->montant; - //Emettre une trame SSL pour recharger le compte bancaire du montant de la transaction + // Préparation des données de la transaction + $transaction->id_transaction = $this->getTransactionID(); + $transaction->montant = $request->montant; + $transaction->frais = 0; + $transaction->taxe = 0; $transaction->commission_banque = 0; - $transaction->commission_hyp = 0; $transaction->commission_sup = 0; $transaction->commission_ag = 0; @@ -1592,26 +1624,92 @@ class iLinkTransactionController extends Controller $transaction->id_wallet_hyp = $walletHyperviseur->id; $transaction->id_wallet_ag = $walletAgent->id; $transaction->id_wallet_sup = $walletSuperviseur->id; - $transaction->frais = $frais; - $transaction->date = $this->getCurrentTime($init_country); + + $transaction->nom_emetteur = $request->nom_emetteur; + $transaction->prenom_emetteur = $request->prenom_emetteur; + $transaction->email_emetteur = $request->email_emetteur; + $transaction->nom_destinataire = $request->nom_destinataire; + $transaction->prenom_destinataire = $request->prenom_destinataire; + $transaction->bank = $network_bank->operators_country->operator->nom; $transaction->country = $network_bank->network->country->name; $transaction->id_bank = $request->id_bank; $transaction->iban = $request->iban; - //$walletUser->save(); - $transaction->id_transaction = $this->getTransactionID(); - //$transaction->save(); + $transaction->date = $this->getCurrentTime($init_country); + $receiver_client_matricule = UserBankAccount::where('iban', $request->iban)->first()->customer_number ?? null; - Log::info('-------------------------- Agent - Envoi de cash vers banque ------------------------------------'); - Log::info(json_encode($transaction)); - Log::info('------------------------------------------------------------------------------------------------'); + // --- DÉBUT DE LA TRANSACTION DB --- + DB::beginTransaction(); + try { + // Débit du compte Agent (balance_princ car c'est une opération Cash) + $walletAgent->balance_princ -= $transaction->montant; + $walletAgent->save(); - $message = trans('messages.successful_agent_send_from_cash_to_bank', - ['id_transaction' => $transaction->id_transaction, 'amount' => $this->toMoney($transaction->montant, $init_country), 'receiver_name' => $request->prenom_destinataire . ' ' . $request->nom_destinataire, - 'net' => $this->toMoney($transaction->montant, $init_country), 'iban' => $request->iban, 'fees' => $this->toMoney($frais, $init_country), 'sender_name' => $request->prenom_emetteur . ' ' . $request->nom_emetteur, - 'sender_code' => $codeGenerer->code_membre, 'bank' => $transaction->bank, 'country' => $transaction->country]); - $this->sendMail($request->email_emetteur, trans('messages.successful_transaction'), $message); - $response_message = ($message . trans('messages.sent_by_mail')); + $baseUrl = env('BANK_API_BASE_URL'); + $client = new Client(['connect_timeout' => 60]); + + // Authentification Bearer + $authResponse = $client->post($baseUrl . '/auth/authenticate', [ + 'json' => ['login' => env('BANK_API_LOGIN'), 'password' => env('BANK_API_PASSWORD')] + ]); + $token = json_decode($authResponse->getBody(), true)['data']['token'] ?? null; + + if (!$token) throw new Exception(trans('errors.token_not_found')); + + // Payload de décaissement (Disbursement) + // Note: Ici le clientMatricule est souvent un matricule système ou celui de l'agent + // s'il possède un compte banque rattaché. + $payload = [ + "clientMatricule" => $receiver_client_matricule, // Matricule receveur + "crAccountId" => $request->iban, //Numero de compte bancaire receveur + "description" => "Transfert Cash vers Banque - Emetteur: " . $request->prenom_emetteur . ' ' . $request->nom_emetteur, + "operationDate" => $transaction->date, + "operationTypeCode" => "MOMODE", + "traceNo" => $transaction->id_transaction, + "transactionAmt" => (float)$transaction->montant + ]; + + $apiResponse = $client->post($baseUrl . '/account/disbursement', [ + 'headers' => [ + 'Authorization' => 'Bearer ' . $token, + 'Accept' => 'application/json', + ], + 'json' => $payload + ]); + + $result = json_decode($apiResponse->getBody(), true); + + if ($apiResponse->getStatusCode() <= 301 && ($result['success'] ?? true) != false) { + $transaction->canceled = 0; + $transaction->save(); + DB::commit(); + + // Logs et Envoi Mail + Log::info('SUCCESS: Agent Cash to Bank - Trx: ' . $transaction->id_transaction); + + $message = trans('messages.successful_agent_send_from_cash_to_bank', [ + 'id_transaction' => $transaction->id_transaction, + 'amount' => $this->toMoney($transaction->montant, $init_country), + 'receiver_name' => $request->prenom_destinataire . ' ' . $request->nom_destinataire, + 'iban' => $request->iban, + 'sender_name' => $request->prenom_emetteur . ' ' . $request->nom_emetteur, + 'bank' => $transaction->bank, + 'country' => $transaction->country + ]); + + $this->sendMail($request->email_emetteur, trans('messages.successful_transaction'), $message); + $response_message = ($message . trans('messages.sent_by_mail')); + + } else { + throw new Exception("API Bank Refused: " . $apiResponse->getBody()); + } + + } catch (\Exception $e) { + // D. Échec : Annulation du débit agent + DB::rollBack(); + Log::error("FAILED: Agent Cash to Bank - Error: " . $e->getMessage()); + throw new Exception(trans('errors.bank_api_exception'), 500); + } break; /** diff --git a/app/Mail/BankAccountLinked.php b/app/Mail/BankAccountLinked.php old mode 100644 new mode 100755 diff --git a/app/Traits/Helper.php b/app/Traits/Helper.php index c53cf34..8522cb2 100755 --- a/app/Traits/Helper.php +++ b/app/Traits/Helper.php @@ -341,15 +341,26 @@ trait Helper return $randomString; } - public function generateTransactionCode($length = 12) + // public function generateTransactionCode($length = 12) + // { + // $characters = '23456789ABCDEFGHJKLMNOPQRSTUVWXYZ'; + // $charactersLength = strlen($characters); + // $randomString = ''; + // for ($i = 0; $i < $length; $i++) { + // $randomString .= $characters[rand(0, $charactersLength - 1)]; + // } + // return $randomString; + // } + + public function generateTransactionCode() { - $characters = '23456789ABCDEFGHJKLMNOPQRSTUVWXYZ'; - $charactersLength = strlen($characters); - $randomString = ''; - for ($i = 0; $i < $length; $i++) { - $randomString .= $characters[rand(0, $charactersLength - 1)]; - } - return $randomString; + $data = random_bytes(16); + + // Manipulation binaire pour respecter la norme UUID v4 + $data = chr(ord($data) & 0x0f | 0x40); // Version 4 + $data = chr(ord($data) & 0x3f | 0x80); // Variante RFC 4122 + + return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)); } // Fonction de tri par date diff --git a/resources/lang/en/emails.php b/resources/lang/en/emails.php old mode 100644 new mode 100755 diff --git a/resources/lang/fr/emails.php b/resources/lang/fr/emails.php old mode 100644 new mode 100755 diff --git a/resources/views/emails/bank_account_linked.blade.php b/resources/views/emails/bank_account_linked.blade.php old mode 100644 new mode 100755