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