add the api of credit operation for the wallet to bank and cash to bank transaction

This commit is contained in:
root 2026-02-19 12:26:45 +01:00
parent 752ca31428
commit aa18ffceeb
8 changed files with 217 additions and 104 deletions

View File

@ -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

View File

@ -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));

View File

@ -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;
/**

0
app/Mail/BankAccountLinked.php Normal file → Executable file
View File

View File

@ -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

0
resources/lang/en/emails.php Normal file → Executable file
View File

0
resources/lang/fr/emails.php Normal file → Executable file
View File

0
resources/views/emails/bank_account_linked.blade.php Normal file → Executable file
View File