486 lines
18 KiB
PHP
486 lines
18 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Http\Controllers\Api\TelegramController;
|
|
use App\Http\Controllers\Page\VuelosController;
|
|
use Artesaos\SEOTools\Facades\SEOTools;
|
|
use DB;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Illuminate\Support\Facades\Validator;
|
|
|
|
|
|
class PagesController extends Controller
|
|
{
|
|
public $objVueloController;
|
|
public $telegramObject;
|
|
|
|
|
|
public function __construct()
|
|
{
|
|
$this->objVueloController = new VuelosController();
|
|
$this->telegramObject = new TelegramController();
|
|
}
|
|
|
|
//pagina de inicio
|
|
public function index()
|
|
{
|
|
// SEO mejorado para vueloenglobo.mx
|
|
SEOTools::setTitle('Vuelos en globo - vueloenglobo.mx | Experiencias en Teotihuacán');
|
|
SEOTools::setDescription('Reserva vuelos en globo sobre Teotihuacán. Experiencias seguras, guías expertos y las mejores vistas al mejor precio. Reserva online en VueloEnGlobo.mx.');
|
|
SEOTools::metatags()->setKeywords(['vuelos en globo', 'vuelo en globo teotihuacan', 'vuelos baratos', 'experiencias en globo', 'reservar vuelo en globo']);
|
|
SEOTools::setCanonical('https://vueloenglobo.mx/');
|
|
SEOTools::opengraph()->addProperty('type', 'website');
|
|
|
|
//$vuelos = EsConsultasController::getVueloCompleto();
|
|
$response = $this->objVueloController->getVuelosCollectionHome();
|
|
$data = $response->getData(true);
|
|
$vuelos = $data['vuelos'];
|
|
//return $vuelos;
|
|
return view('template.home', compact('vuelos'));
|
|
}
|
|
|
|
/**
|
|
* Método que me carga y agrupa los vuelos disponibles por categoría
|
|
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
|
*/
|
|
public function vuelosEnGlobo()
|
|
{
|
|
$vuelos = $this->agruparVuelosListado($this->objVueloController->getVuelosCollection());
|
|
return view('template.vuelos-en-globo', compact('vuelos'));
|
|
}
|
|
|
|
/**
|
|
* Método que agrupar por categoría los vuelos en globo
|
|
* @param mixed $listadoVuelos
|
|
* @return array<array>
|
|
*/
|
|
private function agruparVuelosListado($listadoVuelos)
|
|
{
|
|
$data = $listadoVuelos->getData(true);
|
|
$agrupados = [];
|
|
|
|
foreach ($data['vuelos'] as $item) {
|
|
$categoria = $item['categoria']['nombre'] ?? 'Sin categoría';
|
|
|
|
if (!isset($agrupados[$categoria])) {
|
|
$agrupados[$categoria] = [];
|
|
}
|
|
|
|
$agrupados[$categoria][] = $item;
|
|
}
|
|
|
|
return $agrupados;
|
|
}
|
|
|
|
public function vuelosEnGloboCopy()
|
|
{
|
|
$vuelos = EsConsultasController::getVueloCompleto();
|
|
// return $vuelos;
|
|
return view('template.vuelos-en-globo', compact('vuelos'));
|
|
}
|
|
|
|
public function vueloSeleccionado(Request $request, $vuelo)
|
|
{
|
|
$objClase = new VuelosController();
|
|
$response = $objClase->showByPermanentLink($request, $vuelo);
|
|
$datosVuelo = $response->getData(true);
|
|
return view('template.vuelo-seleccionado', compact('datosVuelo'));
|
|
}
|
|
|
|
// public function vueloSeleccionado($vuelo)
|
|
// {
|
|
// $datosVuelo = EsConsultasController::getInfoVuelo($vuelo);
|
|
// return view('template.vuelo-seleccionado', compact('datosVuelo'));
|
|
// }
|
|
|
|
public function realizaPago($vuelo)
|
|
{
|
|
$datosVuelo = EsConsultasController::getInfoVuelo($vuelo);
|
|
$adicionales = EsConsultasController::obtenerServiciosAdicionales();
|
|
//return $adicionales;
|
|
return view('template.checkout', compact('datosVuelo', 'adicionales'));
|
|
}
|
|
|
|
//esta pendiente
|
|
public function vueloSeleccionado2($vuelo)
|
|
{
|
|
$datos_vuelo = EsConsultasController::getDatosVuelo($vuelo);
|
|
|
|
if (!$datos_vuelo) {
|
|
abort(404);
|
|
}
|
|
// return $datos_vuelo;
|
|
return view('template.vuelo-seleccionado', [
|
|
'datos_vuelo' => $datos_vuelo,
|
|
'incluye' => json_decode($datos_vuelo->json_incluye ?? '[]', true) ?: [],
|
|
'adicionales' => json_decode($datos_vuelo->precios_adicionales ?? '[]', true) ?: [],
|
|
]);
|
|
}
|
|
|
|
// pagina de contacto
|
|
public function contacto()
|
|
{
|
|
return view('template.contacto');
|
|
}
|
|
|
|
// pagina de servicios
|
|
public function servicios()
|
|
{
|
|
return view('template.servicios');
|
|
}
|
|
|
|
// pagina de nosotros
|
|
public function nosotros()
|
|
{
|
|
return view('template.nosotros');
|
|
}
|
|
|
|
// pagina de preguntas frecuentes
|
|
public function preguntasFrecuentes()
|
|
{
|
|
return view('template.preguntasFrecuentes');
|
|
}
|
|
|
|
// pagina de terminos y condiciones
|
|
public function terminosCondiciones()
|
|
{
|
|
return view('template.terminosCondiciones');
|
|
}
|
|
|
|
public function cotizarVuelo($slug, Request $request)
|
|
{
|
|
//obtener el vuelo
|
|
$vuelo = DB::table('vuelos')->where('id_vuelo', $request->id_vuelo)->first();
|
|
$partes = explode(' ', $request->nombre);
|
|
$nombres = implode(' ', array_slice($partes, 0, -2));
|
|
$apellidoPaterno = $partes[count($partes) - 2];
|
|
$apellidoMaterno = end($partes);
|
|
|
|
CotizacionController::crearCotizacionYPosibleCompraErp([
|
|
'nombre' => $nombres,
|
|
'apellidos' => $apellidoPaterno . ' ' . $apellidoMaterno,
|
|
'email' => $request->email,
|
|
'telefono' => $request->telefono,
|
|
'total_compra' => $vuelo->precio_publico * $request->pasajeros,
|
|
'total_a_pagar' => $vuelo->precio_publico * $request->pasajeros,
|
|
]);
|
|
|
|
PagesController::sendTelegramMessage('se cotizo un vuelo: ' . $request->nombre_vuelo . ' a nombre de: ' . $request->nombre . '. mas información consultarla en el ERP');
|
|
$response = VuelosController::cotizaVuelo($slug, $request);
|
|
return json_encode(['mesaaje' => 'Se cotizo el vuelo exitosamente']);
|
|
}
|
|
|
|
private static function sendTelegramMessage($message)
|
|
{
|
|
$botToken = '8617998449:AAFK4DNGPV3efRpmcjeMVZhdTUeErGj-ePc';
|
|
$chatId = '8446032916';
|
|
|
|
Http::post("https://api.telegram.org/bot{$botToken}/sendMessage", [
|
|
'chat_id' => $chatId,
|
|
'text' => $message,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Método que se muestra al cliente para completar el proceso de su
|
|
* @param mixed $slug
|
|
* @param Request $request
|
|
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
|
|
*/
|
|
public function reservarVueloAhora($slug = null, Request $request)
|
|
{
|
|
$objClase = new VuelosController();
|
|
$response = $objClase->showByPermanentLink($request, $slug);
|
|
$datosVuelo = $response->getData(true);
|
|
$adicionales = $this->getServiciosAdicionales();
|
|
|
|
SEOTools::setTitle($datosVuelo['vuelo']['nombre_vuelo'] . ' | www.vueloenglobo.mx');
|
|
SEOTools::setDescription($datosVuelo['vuelo']['descripcion_corta']);
|
|
SEOTools::metatags()->setKeywords(['vuelos', 'vuelos baratos', 'ofertas']);
|
|
|
|
return view('template.checkout', compact('datosVuelo', 'adicionales'));
|
|
}
|
|
|
|
/**
|
|
* Obtiene y transforma los servicios adicionales desde la API externa
|
|
*
|
|
* @return array Array de categorías con sus servicios
|
|
*/
|
|
private function getServiciosAdicionales(): array
|
|
{
|
|
try {
|
|
$response = Http::get('https://erp.skyballoons.mx/servicios-adicionales-webservice', [
|
|
'comercio' => 'skyballoons',
|
|
'key' => 'f093328776ae4cd210dc7b4ee4093e45',
|
|
]);
|
|
|
|
if (!$response->successful()) {
|
|
return [];
|
|
}
|
|
|
|
$datos = $response->json();
|
|
if (!is_array($datos) || empty($datos)) {
|
|
return [];
|
|
}
|
|
|
|
// Transformar al formato de categorías (una sola categoría por simplicidad)
|
|
$servicios = array_map(function ($item) {
|
|
return [
|
|
'id' => $item['id_servicio_adicional'] ?? null,
|
|
'descripcion' => $item['nombre'] ?? 'Servicio',
|
|
'precio' => (float) ($item['precio'] ?? 0),
|
|
'precio_dolares' => (float) ($item['precio_dolares'] ?? 0),
|
|
'imagenes' => $item['galeria'] ?? [],
|
|
'observaciones' => $item['descripcion'] ?? '',
|
|
];
|
|
}, $datos);
|
|
|
|
return [
|
|
[
|
|
'nombre_categoria' => 'Servicios adicionales',
|
|
'servicios' => array_filter($servicios, fn($s) => !is_null($s['id'])),
|
|
],
|
|
];
|
|
} catch (\Exception $e) {
|
|
\Log::error('Error al obtener servicios adicionales: ' . $e->getMessage());
|
|
return [];
|
|
}
|
|
}
|
|
|
|
public function getNombresDeVuelos()
|
|
{
|
|
$vuelos = $this->agruparVuelosListado($this->objVueloController->getVuelosCollection());
|
|
|
|
return collect($vuelos)->map(function ($items) {
|
|
return collect($items)
|
|
->map(function ($item) {
|
|
return [
|
|
'nombre_vuelo' => $item['vuelo']['nombre_vuelo'] ?? null,
|
|
'enlace_permanente' => $item['vuelo']['enlace_permanente'] ?? null,
|
|
];
|
|
})
|
|
->values();
|
|
});
|
|
}
|
|
|
|
public function procesarR(Request $request)
|
|
{
|
|
try {
|
|
Log::info('Procesando pago', $request->all());
|
|
|
|
$payload = [
|
|
'amount' => (float) $request->input('total'),
|
|
'currency' => 'MXN',
|
|
'description' => $request->input('payment_description', 'Vuelo en Globo'),
|
|
'payment_method' => [
|
|
'token' => $request->input('card_token'),
|
|
],
|
|
'customer' => [
|
|
'email' => $request->input('payment_email'),
|
|
'phone' => preg_replace('/[^0-9]/', '', $request->input('payment_phone')),
|
|
],
|
|
];
|
|
|
|
$response = Http::withHeaders([
|
|
'Authorization' => 'Bearer ' . config('clip.api_key'),
|
|
'Content-Type' => 'application/json',
|
|
])->post(config('clip.payments_url'), $payload);
|
|
|
|
\Log::info('Respuesta Clip', ['status' => $response->status(), 'body' => $response->body()]);
|
|
|
|
if ($response->successful()) {
|
|
$paymentData = $response->json();
|
|
$paymentStatus = $paymentData['status'] ?? 'pending';
|
|
$paymentMessage = $paymentData['message'] ?? 'Pago procesado exitosamente';
|
|
|
|
if ($paymentStatus == 'approved') {
|
|
PagesController::sendTelegramMessage('Se realizó compra de vuelo en globo. ID: ' . ($paymentData['id'] ?? 'N/A'));
|
|
}
|
|
|
|
return response()->json(
|
|
[
|
|
'success' => true,
|
|
'payment_status' => $paymentStatus,
|
|
'payment_message' => $paymentMessage,
|
|
'redirect' => '#', // route('gracias')
|
|
'data' => $paymentData,
|
|
],
|
|
200,
|
|
);
|
|
} else {
|
|
$errorBody = $response->json();
|
|
|
|
// Motivo de rechazo que viene de Clip (ajusta el campo según la documentación real)
|
|
$reason = $errorBody['reason'] ?? ($errorBody['error_code']['reason'] ?? ($errorBody['rejection_reason'] ?? ''));
|
|
|
|
// Mapa de motivos de rechazo a mensajes en español
|
|
$reasonMessages = [
|
|
'not_sufficient_funds' => 'Fondos insuficientes en la tarjeta.',
|
|
'Not sufficient funds' => 'Fondos insuficientes en la tarjeta.',
|
|
'do_not_honor' => 'El banco emisor rechazó la transacción (Do not honor).',
|
|
'Do not honor' => 'El banco emisor rechazó la transacción.',
|
|
'restricted_card' => 'La tarjeta está restringida y no puede usarse.',
|
|
'Restricted card' => 'Tarjeta restringida.',
|
|
'exceeds_withdrawal_amount_limit' => 'La transacción excede el límite de retiro de esta tarjeta.',
|
|
'Exceeds withdrawal amount limit' => 'Excede el límite de retiro permitido.',
|
|
];
|
|
|
|
// Si hay un motivo conocido, usamos el mensaje traducido; si no, el mensaje original
|
|
if ($reason && isset($reasonMessages[$reason])) {
|
|
$errorMessage = $reasonMessages[$reason];
|
|
} else {
|
|
$errorMessage = $errorBody['detail'] ?? ($errorBody['error_code']['message'] ?? ($errorBody['message'] ?? 'Error al procesar el pago'));
|
|
}
|
|
|
|
PagesController::sendTelegramMessage('Error en compra: ' . $errorMessage);
|
|
|
|
return response()->json(
|
|
[
|
|
'success' => false,
|
|
'message' => $errorMessage,
|
|
'payment_status' => 'rejected',
|
|
'code' => $errorBody['error_code'] ?? null,
|
|
'reason' => $reason ?: null,
|
|
],
|
|
$response->status(),
|
|
);
|
|
}
|
|
} catch (\Exception $e) {
|
|
\Log::error('Error en procesar pago: ' . $e->getMessage(), [
|
|
'trace' => $e->getTraceAsString(),
|
|
]);
|
|
|
|
PagesController::sendTelegramMessage('Error en compra: ');
|
|
|
|
return response()->json(
|
|
[
|
|
'success' => false,
|
|
'message' => 'Error interno del servidor. Por favor intenta de nuevo.',
|
|
'payment_status' => 'error',
|
|
],
|
|
500,
|
|
);
|
|
}
|
|
}
|
|
|
|
public function procesar(Request $request)
|
|
{
|
|
// 1. Validación del payload real que envías desde frontend
|
|
// $validated = $request->validate([
|
|
// 'payment_email' => 'required|email',
|
|
// 'payment_phone' => 'required|string',
|
|
// 'payment_description' => 'nullable|string|max:255',
|
|
// 'card_token' => 'required|string',
|
|
// 'total' => 'required|numeric',
|
|
|
|
// // datos del vuelo / reserva
|
|
// 'id_vuelo' => 'required|integer',
|
|
// 'adultos' => 'nullable|integer',
|
|
// 'ninios' => 'nullable|integer',
|
|
// 'fecha' => 'nullable|date',
|
|
// 'cupon' => 'nullable|string',
|
|
// ]);
|
|
$validated = $request->all(); // Elimina esta línea y descomenta la validación real cuando estés listo para validar los datos de entrada
|
|
// 2. API Key
|
|
$apiKey = config('clip.api_key');
|
|
|
|
if (empty($apiKey)) {
|
|
Log::error('Clip API Key no configurada');
|
|
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Error de configuración del servidor de pagos.',
|
|
], 500);
|
|
}
|
|
|
|
// 3. Payload oficial Clip
|
|
$payload = [
|
|
"amount" => (float) $validated['total'],
|
|
"currency" => "MXN",
|
|
"description" => $validated['payment_description'] ?? 'Vuelo en Globo Zero',
|
|
|
|
"payment_method" => [
|
|
"token" => $validated['card_token']
|
|
],
|
|
|
|
"customer" => [
|
|
"email" => $validated['payment_email'],
|
|
"phone" => $validated['payment_phone'],
|
|
],
|
|
|
|
"metadata" => [
|
|
"id_vuelo" => $validated['id_vuelo'],
|
|
"adultos" => $validated['adultos'] ?? 0,
|
|
"ninios" => $validated['ninios'] ?? 0,
|
|
"fecha" => $validated['fecha'],
|
|
]
|
|
];
|
|
|
|
try {
|
|
// 4. Petición a Clip
|
|
$response = Http::withHeaders([
|
|
"Content-Type" => "application/json",
|
|
"Authorization" => "Bearer " . $apiKey,
|
|
])->post("https://api.payclip.com/payments", $payload);
|
|
|
|
$body = $response->json();
|
|
|
|
Log::info('Respuesta Clip:', [
|
|
'status' => $response->status(),
|
|
'body' => $body
|
|
]);
|
|
|
|
// 5. Respuesta HTTP inválida
|
|
if (!$response->successful()) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => $body['message'] ?? 'Error al procesar pago con Clip',
|
|
'error' => $body
|
|
], $response->status());
|
|
}
|
|
|
|
// 6. Estado del pago
|
|
$status = strtolower($body['status'] ?? '');
|
|
|
|
if (in_array($status, ['approved', 'paid'])) {
|
|
|
|
// 7. AQUÍ YA PUEDES GUARDAR LA RESERVA
|
|
// Reserva::create([...]);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'payment_id' => $body['id'] ?? null,
|
|
'payment_status' => $status,
|
|
'message' => 'Pago aprobado correctamente',
|
|
]);
|
|
}
|
|
|
|
if (in_array($status, ['declined'])) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'payment_status' => $status,
|
|
'message' => 'Pago rechazado por el banco.',
|
|
], 422);
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => false,
|
|
'payment_status' => $status,
|
|
'message' => 'Estado de pago: ' . $status,
|
|
], 422);
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
Log::error('Error Clip: ' . $e->getMessage());
|
|
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Error de conexión con el proveedor de pagos.',
|
|
], 500);
|
|
}
|
|
}
|
|
}
|