File: /var/www/vhost/disk-apps/qas.sports-crowd.com/app/Core/Ticket/Application/TicketService.php
<?php
declare(strict_types=1);
namespace App\Core\Ticket\Application;
use App\Core\Payment\Entities\Amount;
use App\Core\Payment\Entities\Payment;
use App\Core\Payment\Entities\PaymentRetrieveResponse;
use App\Core\Payment\PaymentMethodContext;
use App\Core\Payment\PaymentStatusEnum;
use App\Core\Ticket\QRTypesEnum;
use App\Core\Ticket\TicketOriginEnum;
use App\Core\User\Application\UserService;
use App\CorporateIdentity;
use App\Http\Controllers\Api\TicketApiController;
use App\Http\Controllers\TicketsController;
use App\Parameter;
use App\Services\TicketParametersService;
use App\Team;
use App\Ticket;
use App\TicketMain;
use Carbon\Carbon;
use Illuminate\Http\Request;
use SimpleSoftwareIO\QrCode\Facades\QrCode;
class TicketService
{
private $userService;
private $ticketParametersService;
const ENCRYPTION_KEY = '0123456789abcdef0123456789abcdef';
const ENCRYPTION_IV = 'abcdef9876543210abcdef9876543210';
public function __construct()
{
$this->userService = new UserService();
$this->ticketParametersService = new TicketParametersService();
}
public function find(int $id)
{
return TicketMain::findOrFail($id);
}
public function getById(int $id)
{
return TicketMain::where("id", $id)->first();
}
public function getByReference(string $reference)
{
return TicketMain::where("payment_reference", $reference)->first();
}
public function getByPin(string $pin)
{
return TicketMain::where("pin", $pin)->first();
}
public function getByPaymentGatewayTxId(string $txId)
{
return TicketMain::where('payment_transaction_id', $txId)->first();
}
public function setPaymentGatewayId($ticket, int $paymentGatewayId)
{
$ticket->gateway_payments_id = $paymentGatewayId;
$ticket->update();
}
public function setPaymentGatewayTxId($ticket, $paymentGatewayTxId)
{
$ticket->payment_transaction_id = $paymentGatewayTxId;
$ticket->update();
}
public function setTicketAsPending($ticket)
{
$ticket->pin ??= $this->generatePin();
$ticket->payment_reference = strtoupper(hash("md5", (string) $ticket->id)) . '_' . $ticket->pin;
$ticket->payment_state = PaymentStatusEnum::PENDING;
$ticket->update();
}
public function validatePayment($ticket): PaymentRetrieveResponse
{
$this->increasePaymentAttemps($ticket);
if (!$this->hasPaymentProcessor($ticket)) {
return PaymentRetrieveResponse::createPending();
}
$payment = $this->buildPayment($ticket);
if ($payment->paymentGatewayStatus() == PaymentStatusEnum::CONFIRMED) {
$controller = new TicketsController;
if (!$ticket->ticket_user_blocks->isEmpty()) {
$controller->generateTickets($ticket->ticket_user_blocks, $ticket->id);
} else if (!$ticket->ticket_user_block_backups->isEmpty()) {
$controller->generateTickets($ticket->ticket_user_block_backups, $ticket->id, true);
}
$retrieveResponse = new PaymentRetrieveResponse(
(string) $payment->id(),
$payment->paymentGatewayStatus(),
$ticket->payment_comment
);
$retrieveResponse->setRawData(json_decode($ticket->gateway_response ?? ''));
return $retrieveResponse;
}
$paymentContext = PaymentMethodContext::init($ticket->gateway_payments_id);
$paymentMethodResponse = $paymentContext->retrieveTicketPayment($ticket);
if ($paymentMethodResponse->status() == PaymentStatusEnum::CONFIRMED) {
$this->confirmPayment($ticket, $paymentMethodResponse);
} else {
$this->voidPayment($ticket, $paymentMethodResponse);
}
$ticket->gateway_response = json_encode($paymentMethodResponse->rawData());
$ticket->update();
return $paymentMethodResponse;
}
public function buildPayment($ticket): Payment
{
$customer = $this->userService->buildCustomerFromUserId($ticket->user_id_log);
$payment = new Payment(
$ticket->id,
$ticket->pin,
$ticket->payment_reference,
"Pago de boleta o abono",
new Amount($ticket->total),
$customer
);
$payment->setPaymentGatewayId($ticket->gateway_payments_id);
$payment->setPaymentGatewayTxId($ticket->payment_transaction_id);
$payment->setPaymentGatewayStatus($ticket->payment_state);
return $payment;
}
private function confirmPayment($ticket, PaymentRetrieveResponse $paymentMethodResponse)
{
$this->updatePayment(
$ticket->payment_reference,
$paymentMethodResponse->status(),
$paymentMethodResponse->message(),
$paymentMethodResponse->id()
);
}
private function voidPayment($ticket, PaymentRetrieveResponse $paymentMethodResponse)
{
$this->updatePayment(
$ticket->payment_reference,
$paymentMethodResponse->status(),
$paymentMethodResponse->message(),
$paymentMethodResponse->id()
);
}
// TODO: check for refactor
private function updatePayment(
$paymentReference,
$paymentState,
$paymentComment,
$paymentTransactionId
) {
$ticketApiController = new TicketApiController();
$ticketApiController->updateTicketMainByPaymentReference(
$paymentReference,
$paymentState,
$paymentComment,
$paymentTransactionId
);
}
private function increasePaymentAttemps($ticket)
{
$ticket->payment_attempts = $ticket->payment_attempts + 1;
$ticket->update();
}
private function hasPaymentProcessor($ticket)
{
return !is_null($ticket->gateway_payments_id);
}
private function generatePin($length = 10)
{
return substr(str_shuffle("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, $length);
}
public function ticketViewDashboard($tickets = null)
{
$ticketParameters = $this->ticketParametersService->getParameterObject();
$corporateIdentity = CorporateIdentity::first();
$parameters = Parameter::first();
$mainTeam = Team::where('is_main', true)->first();
return view("tickets.view.dashboard", compact('tickets', 'ticketParameters', 'corporateIdentity', 'parameters', 'mainTeam'));
}
public function generateDynamicQr(Request $request)
{
$code = base64_decode($request->get('code'));
if (!$code) {
return response()->json([
'qrCode' => null,
'error' => 'Invalid code'
]);
}
$ticketParameters = $this->ticketParametersService->getParameterObject();
if (!property_exists($ticketParameters, 'qrcode_type') || !$ticketParameters->qrcode_type) {
return response()->json([
'qrCode' => null,
'error' => 'Invalid QR type'
]);
}
switch ($ticketParameters->qrcode_type) {
case QRTypesEnum::STATIC:
case QRTypesEnum::WITHOUT_TIMESTAMP:
return $this->standardQR($code);
case QRTypesEnum::WITH_TIMESTAMP:
return $this->timestampQR($code);
default:
return response()->json([
'qrCode' => null
]);
}
}
public function encryptedQR($code)
{
// Llaves para la encriptación
$key = pack("H*", self::ENCRYPTION_KEY);
$iv = pack("H*", self::ENCRYPTION_IV);
$secret = json_encode([
'data' => $code,
'origin' => TicketOriginEnum::WEB
]);
$encrypted = openssl_encrypt($secret, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
$encryptedBase64 = base64_encode($encrypted);
return $encryptedBase64;
}
public function decryptedQR($code)
{
try {
$key = pack("H*", self::ENCRYPTION_KEY);
$iv = pack("H*", self::ENCRYPTION_IV);
$ticketCode = str_replace(['-', '_'], ['+', '/'], $code);
$encrypted = base64_decode($ticketCode);
$decryptedData = openssl_decrypt($encrypted, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
$payload = json_decode($decryptedData);
if (!$payload) {
throw new \Exception('El código QR no existe');
}
} catch (\Throwable $th) {
throw new \Exception('El código QR no tiene formato correcto');
}
return $payload;
}
private function standardQR($code)
{
$qrCodeSize = $this->ticketParametersService->qrCodeSize();
$qrCode = QrCode::format('png')
->size($qrCodeSize)
->errorCorrection('M')
->generate($code);
$base64 = 'data:image/png;base64,' . base64_encode($qrCode->toHtml());
return response()->json([
'qrCode' => $base64
]);
}
private function timestampQR($code)
{
$encryptedBase64 = $this->encryptedQr($code);
$qrCodeSize = $this->ticketParametersService->qrCodeSize();
$qrCode = QrCode::format('png')
->size($qrCodeSize)
->errorCorrection('M')
->generate($encryptedBase64);
$base64 = 'data:image/png;base64,' . base64_encode($qrCode->toHtml());
return response()->json([
'qrCode' => $base64
]);
}
public function getInvoiceLink($code_ticket)
{
$ticket = $this->getTicketByCodeTicket($code_ticket);
if (!$ticket) {
return false;
}
return $ticket->ticket_main->invoice_link;
}
public function getTicketByCodeTicket($code_ticket)
{
return Ticket::where('code_ticket', $code_ticket)->first();
}
public function getTicketByTicketId($ticketId)
{
return Ticket::find($ticketId);
}
public function getTicketsByTicketMainIdAndUserId($ticketMainId, $userId)
{
return Ticket::where('ticket_main_id', $ticketMainId)
->where('user_id', $userId)
->get();
}
public function isProcessingPayment($ticket)
{
return $ticket->payment_state == PaymentStatusEnum::PENDING && $ticket->payment_reference != PaymentStatusEnum::PENDING && Carbon::now()->lessThan(Carbon::parse($ticket->updated_at)->addMinutes(30));
}
}