This commit is contained in:
Juan Pablo Vial
2025-05-12 19:46:09 -04:00
parent 3006adb0f7
commit f14cdd2730
10 changed files with 268 additions and 21 deletions

View File

@ -38,7 +38,7 @@ class Job extends Ideal\Service
public function getPending(): array
{
try {
return array_merge([$this, 'process'],$this->jobRepository->fetchPending());
return array_map([$this, 'process'],$this->jobRepository->fetchPending());
} catch (EmptyResult $exception) {
throw new Read(__CLASS__, $exception);
}

View File

@ -6,6 +6,10 @@ use DateTimeImmutable;
use DateInterval;
use Exception;
use PDOException;
use Incoviba\Exception\ServiceAction\Create;
use Incoviba\Exception\ServiceAction\Delete;
use Incoviba\Exception\ServiceAction\Read;
use Incoviba\Exception\ServiceAction\Update;
use Incoviba\Common\Implement\Exception\EmptyResult;
use Incoviba\Repository;
use Incoviba\Model;
@ -40,6 +44,11 @@ class Login
} catch (PDOException|EmptyResult) {}
return false;
}
/**
* @return Model\User
* @throws Exception
*/
public function getUser(): Model\User
{
$login = $this->repository->fetchActiveBySelector($this->selector);
@ -57,20 +66,90 @@ class Login
return $this->cookie_separator;
}
/**
* @param array $data
* @return Model\User
* @throws Create
* @throws Read
*/
public function addUser(array $data): Model\User
{
try {
return $this->userRepository->fetchByName($data['name']);
} catch (EmptyResult) {
list($passphrase, $encrypted) = $this->splitPassword($data['password']);
$password = $this->cryptoJs_aes_decrypt($encrypted, $passphrase);
try {
$password = $this->cryptoJs_aes_decrypt($encrypted, $passphrase);
} catch (Exception $exception) {
throw new Read(__CLASS__, $exception);
}
$password = password_hash($password, PASSWORD_DEFAULT);
$user = $this->userRepository->create([
'name' => $data['name'],
'password' => $password,
'enabled' => $data['enabled'] ?? 1
]);
return $this->userRepository->save($user);
try {
return $this->userRepository->save($user);
} catch (PDOException $exception) {
throw new Create(__CLASS__, $exception);
}
}
}
/**
* @param int $user_id
* @param array $data
* @return Model\User
* @throws Read
* @throws Update
*/
public function editUser(int $user_id, array $data): Model\User
{
try {
$user = $this->userRepository->fetchById($user_id);
} catch (EmptyResult $exception) {
throw new Read(__CLASS__, $exception);
}
if (!isset($data['force']) and !$user->validate($data['old_password'])) {
throw new Read(__CLASS__);
}
list($passphrase, $encrypted) = $this->splitPassword($data['password']);
try {
$password = $this->cryptoJs_aes_decrypt($encrypted, $passphrase);
} catch (Exception $exception) {
throw new Read(__CLASS__, $exception);
}
$password = password_hash($password, PASSWORD_DEFAULT);
$userData = [
'password' => $password
];
try {
$user = $this->userRepository->edit($user, $userData);
} catch (PDOException | EmptyResult $exception) {
throw new Update(__CLASS__, $exception);
}
return $user;
}
/**
* @param int $user_id
* @return Model\User
* @throws Delete
* @throws Read
*/
public function deleteUser(int $user_id): Model\User
{
try {
$user = $this->userRepository->fetchById($user_id);
} catch (EmptyResult $exception) {
throw new Read(__CLASS__, $exception);
}
try {
$this->userRepository->remove($user);
return $user;
} catch (PDOException | EmptyResult $exception) {
throw new Delete(__CLASS__, $exception);
}
}
public function validateUser(Model\User $user, string $encryptedPassword): bool
@ -88,7 +167,8 @@ class Login
try {
$login = $this->repository->fetchActiveByUser($user->id);
$this->logout($login->user);
} catch (PDOException|EmptyResult) {
} catch (PDOException | EmptyResult $exception) {
error_log($exception, 3, '/logs/exception.log');
}
try {
@ -104,7 +184,8 @@ class Login
$this->repository->save($login);
$this->saveCookie($selector, $token, $login->dateTime->add(new DateInterval("PT{$this->max_login_time}H")));
return true;
} catch (PDOException|Exception) {
} catch (PDOException | Exception $exception) {
error_log($exception, 3, '/logs/exception.log');
return false;
}
}

View File

@ -44,7 +44,7 @@ class Queue extends Ideal\Service
return false;
}
$status = true;
$errors = [];
foreach ($jobs as $job) {
$type = 'default';
if (isset($job->configuration['type'])) {
@ -57,13 +57,15 @@ class Queue extends Ideal\Service
$worker = $this->workers[$type];
try {
$status &= $worker->run($job);
if (!$worker->execute($job)) {
$errors []= $job->id;
}
} catch (Exception $exception) {
$final = new Exception("Could not run job", 0, $exception);
$this->logger->warning($final);
$status &= false;
$errors []= $job->id;
}
}
return $status;
return count($errors) === 0;
}
}

View File

@ -131,12 +131,36 @@ class Toku extends Ideal\Service
return $invoices;
}
/**
* @param array $input
* @return bool
* @throws InvalidResult
*/
public function successEvent(array $input): bool
{
$validEvents = ['payment_intent.succeeded', 'payment.succeeded', 'transaction.success',
'transaction.bulk_success', 'payment_intent.succeeded_batch'];
if (!in_array($input['event_type'], $validEvents)) {
throw new InvalidResult("{$input['event_type']} is not a valid event", 422);
}
switch ($input['event_type']) {
case 'payment_intent.succeeded_batch':
case 'transaction.bulk_success':
return $this->successBulk($input);
case 'transaction.success':
return $this->successTransaction($input);
default:
$paymentData = $this->mapEventData($input);
return $this->updatePago($paymentData);
}
}
/**
* @param array $request
* @return bool
* @throws InvalidResult
*/
public function updatePago(array $request): bool
protected function updatePago(array $request): bool
{
# If $customer is not found, it will throw an exception and stop
$customer = $this->customer->getByExternalId($request['customer']);
@ -144,4 +168,104 @@ class Toku extends Ideal\Service
return $this->invoice->update($invoice['id'], $request);
}
protected function successTransaction(array $input): bool
{
$intents = $this->mapMultiplePaymentIntentsData($input);
$errors = [];
foreach ($intents as $intent) {
if (!$this->updatePago($intent)) {
$errors []= $intent;
}
}
if (array_key_exists('wallet_movements', $input)) {
foreach ($input['wallet_movements'] as $walletMovement) {
if (array_key_exists('type', $walletMovement) and $walletMovement['type'] === 'SURPLUS') {
$this->logger->alert('Revisar el envío de cuotas de la Venta ' . $walletMovement['product_id']);
}
}
}
return count($errors) === 0;
}
/**
* @param array $input
* @return bool
* @throws InvalidResult
*/
protected function successBulk(array $input): bool
{
return match($input['event_type']) {
'payment_intent.succeeded_batch' => $this->successBulkPaymentIntent($input),
'transaction.bulk_success' => $this->successBulkTransaction($input),
default => false
};
}
/**
* @param array $input
* @return bool
* @throws InvalidResult
*/
protected function successBulkTransaction(array $input): bool
{
$errors = [];
foreach($input['events'] as $event) {
$event['event_type'] = 'transaction.success';
if (!$this->successEvent($event)) {
$errors []= $event;
}
}
return count($errors) === 0;
}
/**
* @param array $input
* @return bool
* @throws InvalidResult
*/
protected function successBulkPaymentIntent(array $input): bool
{
$errors = [];
foreach($input['payment_intent'] as $intent) {
$intent['event_type'] = 'payment_intent.succeeded';
$intent['payment_intent'] = $input['payment_intents'];
unset($intent['payment_intents']);
if (!$this->successEvent($intent)) {
$errors []= $intent;
}
}
return count($errors) === 0;
}
protected function mapEventData(array $input): array
{
return match ($input['event_type']) {
'payment_intent.succeeded' => $this->mapPaymentIntentData($input),
'payment.succeeded' => $this->mapPaymentEventData($input),
default => [],
};
}
protected function mapMultiplePaymentIntentsData(array $input): array
{
$output = [];
foreach ($input['payment_intents'] as $intent) {
$intent['transaction_date'] = $input['transaction']['transaction_date'];
$intent['customer'] = $input['customer']['id'];
$intent['invoice'] = $intent['id_invoice'];
$intent['subscription'] = $intent['id_subscription'];
$intent['cuota_id'] = $intent['invoice_external_id'];
$o = $this->mapPaymentIntentData($intent);
$output []= $o;
}
return $output;
}
protected function mapPaymentEventData(array $input): array
{
$data = $input['payment'];
$data['status'] = 'AUTHORIZED';
$data['date'] = $data['payment_date'];
return $data;
}
protected function mapPaymentIntentData(array $input): array
{
$data = $input['payment_intent'];
$data['date'] = $data['transaction_date'];
return $data;
}
}

View File

@ -66,14 +66,15 @@ class Invoice extends AbstractEndPoint
if ($data['status'] !== 'AUTHORIZED') {
throw new InvalidResult("Pago no autorizado", 422);
}
$dateString = $data['date'];
try {
$date = new DateTimeImmutable($data['transaction_date']);
$date = new DateTimeImmutable($dateString);
} catch (DateMalformedStringException $exception) {
throw new InvalidResult("Fecha no válida: {$data['transaction_date']}", 422, $exception);
throw new InvalidResult("Fecha no válida: {$dateString}", 422, $exception);
}
$uf = $this->ufService->get($date);
if ($uf === 0.0) {
throw new InvalidResult("No hay UF para la fecha: {$data['transaction_date']}", 422);
throw new InvalidResult("No hay UF para la fecha: {$dateString}", 422);
}
$valor = $data['amount'] / $uf;
if (abs($valor - $invoice->cuota->pago->valor()) >= 0.0001) {

View File

@ -2,13 +2,14 @@
namespace Incoviba\Service\Worker;
use Incoviba\Common\Implement\Exception\EmptyResponse;
use Incoviba\Service\Worker;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Log\LoggerInterface;
use Incoviba\Common\Ideal;
use Incoviba\Model;
class Request extends Ideal\Service
class Request extends Ideal\Service implements Worker
{
public function __construct(LoggerInterface $logger, protected ClientInterface $client)
{