sitio_web = config('sitioweb.sitio_web_erp_id', 0); $this->status = 1; // Asumiendo que 1 es el estatus para vuelos activos } public function getVuelosNombresPreciosPublico() { $vuelos = DB::table('vuelos') ->where('id_sitio_web', $this->sitio_web) ->where('id_estatus', $this->status ?? 0) ->get(); $imagenes = DB::table('vuelos_imagenes_galeria') ->where('id_estatus', 1) ->orderBy('orden', 'asc') ->get() ->groupBy('id_vuelo'); $unidades = DB::table('unidades') ->where('id_estatus', 1) ->get() ->keyBy('id_unidad'); $vuelos->transform(function ($vuelo) use ($imagenes, $unidades) { $vuelo->json_incluye = json_decode($vuelo->json_incluye, true); $vuelo->precios_adicionales = collect(json_decode($vuelo->precios_adicionales, true)) ->map(function ($item) use ($unidades) { $idUnidad = (int) $item['id_unidad']; return [ 'id_unidad' => $idUnidad, 'precio' => (float) $item['precio'], 'unidad' => $unidades->has($idUnidad) ? [ 'nombre_unidad' => $unidades[$idUnidad]->nombre_unidad, 'nombre_corto' => $unidades[$idUnidad]->nombre_corto, 'numero_personas_piezas' => $unidades[$idUnidad]->numero_personas_piezas, ] : null ]; }) ->values(); $vuelo->imagenes_galeria = $imagenes->get($vuelo->id_vuelo, collect()) ->map(function ($img) { return [ 'id_imagen_vuelo' => $img->id_imagen_vuelo, 'ruta' => $img->ruta_imagen_vuelo, 'descripcion' => $img->descripcion_imagen_vuelo, 'orden' => $img->orden, 'imagen_destacada' => (bool) $img->imagen_destacada, ]; }) ->values(); return $vuelo; }); return response()->json( $vuelos, 200, [], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE ); } public function getServiciosAdicionales() { $response = Http::get('https://erp.skyballoons.mx/servicios-adicionales-webservice', [ 'comercio' => 'skyballoons', 'key' => 'f093328776ae4cd210dc7b4ee4093e45', ]); if ($response->successful()) { $data = $response->json(); return response()->json( $data, 200, [], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE ); } else { return response()->json(['error' => 'No se pudieron obtener los servicios adicionales'], 500); } } /*public function reservarVuelo(Request $request) { try { //consultar el vuelo en globo $vuelo = DB::table('vuelos')->where('id_vuelo', $request->id_vuelo)->first(); $data = [ // Datos del cliente 'nombre_reserva' => $request->responsable['nombres'], 'apellido_reserva' => $request->responsable['apellidos'], 'email_reserva' => $request->responsable['email'], 'tel_reserva' => preg_replace('/\D/', '', $request->responsable['telefono']), 'Lada' => 52, 'fecha_vuelo' => $request->fecha, // Datos del vuelo 'id_vuelo' => $request->id_vuelo, 'id_unidad' => $vuelo->id_unidad, 'pasajeros' => ($request->adultos ?? 0) + ($request->ninios ?? 0), 'precio_publico' => $request->total, // Totales 'total_compra' => $request->total, // Pasajeros adicionales 'num_pax_adicionales' => 0, 'id_unidad_adicionales' => null, 'precio_adicional' => 0, // Servicios adicionales 'servicios_adicionales' => collect($request->servicios ?? []) ->map(function ($servicio) { return [ 'id_servicio' => $servicio['id_servicio'], 'cantidad_solicitada' => $servicio['cantidad'] ?? 1, 'precio' => $servicio['precio'] ?? 0, ]; }) ->toArray(), ]; return $data; $resultado = $this->crearCotizacionCompleta($data); return response()->json($resultado); } catch (\Exception $e) { return response()->json(['error' => 'Error al procesar la reserva: ' . $e->getMessage()], 500); } }*/ public function reservarVuelo(Request $request) { try { // 1. BLINDAJE: Detectar si 'servicios' llegó como string JSON y parsearlo $serviciosRaw = $request->servicios; if (is_string($serviciosRaw)) { $serviciosRaw = json_decode($serviciosRaw, true); } // Si no es un array válido tras el decode o viene vacío, asignamos un array limpio $serviciosArray = is_array($serviciosRaw) ? $serviciosRaw : []; // Consultar el vuelo en globo $vuelo = DB::table('vuelos')->where('id_vuelo', $request->id_vuelo)->first(); if (!$vuelo) { return response()->json(['error' => 'El vuelo solicitado no existe.'], 404); } $data = [ // Datos del cliente 'nombre_reserva' => $request->responsable['nombres'] ?? '', 'apellido_reserva' => $request->responsable['apellidos'] ?? '', 'email_reserva' => $request->responsable['email'] ?? '', 'tel_reserva' => preg_replace('/\D/', '', $request->responsable['telefono'] ?? ''), 'Lada' => 52, 'fecha_vuelo' => $request->fecha, // Datos del vuelo 'id_vuelo' => $vuelo->id_vuelo, 'id_unidad' => $vuelo->id_unidad, 'pasajeros' => ($request->adultos ?? 0) + ($request->ninios ?? 0), 'precio_publico' => $request->total, // Totales 'total_compra' => $request->total, // Pasajeros adicionales 'num_pax_adicionales' => 0, 'id_unidad_adicionales' => null, 'precio_adicional' => 0, // Servicios adicionales (procesados de forma segura) 'servicios_adicionales' => collect($serviciosArray) ->map(function ($servicio) { return [ 'id_servicio' => $servicio['id_servicio'] ?? null, 'cantidad_solicitada' => $servicio['cantidad'] ?? 1, 'precio' => $servicio['precio'] ?? 0, ]; }) ->filter(fn($s) => !is_null($s['id_servicio'])) // Quita elementos mal formados ->toArray(), ]; // ⚠️ NOTA: Eliminamos el 'return $data;' para que el flujo continúe hacia la cotización real. $id_cotizacion = $this->crearCotizacionCompleta($data); $route = route('checkout.detalle.cotizacion.chatbot', ['cotizacion' => $id_cotizacion]); return response()->json(['id_cotizacion' => $id_cotizacion, 'route' => $route]); } catch (\Exception $e) { return response()->json(['error' => 'Error al procesar la reserva: ' . $e->getMessage()], 500); } } public function crearCotizacionCompleta(array $data) { try { DB::beginTransaction(); // 1. Generar folio $ultimoId = (DB::table('cotizaciones')->max('id_cotizacion') ?? 0) + 1; $folio = "COT" . str_pad($ultimoId, 7, "0", STR_PAD_LEFT); $vuelo = DB::table('vuelos')->where('id_vuelo', $data['id_vuelo'])->where('id_estatus', 1)->where('id_sitio_web', $this->sitio_web)->first() ?? null; // 2. Insertar cotización principal $id_cotizacion = DB::table('cotizaciones')->insertGetId([ 'folio' => $folio, 'nombre' => $data['nombre_reserva'], 'apellidos' => $data['apellido_reserva'], 'mail' => $data['email_reserva'], 'telefono' => $data['tel_reserva'], 'iso_country_id_primary_phone' => $data['Lada'], 'subtotal' => $data['total_compra'], 'total' => $data['total_compra'], 'total_con_iva' => $data['total_compra'], 'total_neto' => $data['total_compra'], 'id_sitio_web' => $this->sitio_web, 'observaciones' => 'Cotizacion desde www.vueloenglobo.mx', 'id_user_alta' => 98, 'fecha_alta' => now(), 'fecha_vuelo' => $data['fecha_vuelo'] ?? null, ]); // 3. Insertar vuelo principal DB::table('vuelos_cotizaciones')->insert([ 'id_cotizacion' => $id_cotizacion, 'id_vuelo' => $vuelo->id_vuelo, 'id_unidad' => $data['id_unidad'], 'cantidades' => $data['pasajeros'], 'precio_vendido_p_pax_vuelo' => $vuelo->precio_publico, 'precio_especial' => $vuelo->precio_publico, 'id_estatus' => 1, 'id_user_alta' => 98, 'fecha_alta' => now(), ]); // 4. Pasajeros adicionales /* if (!empty($data['num_pax_adicionales']) && $data['num_pax_adicionales'] > 0) { DB::table('vuelos_cotizaciones')->insert([ 'id_cotizacion' => $id_cotizacion, 'id_vuelo' => $data['id_vuelo'], 'id_unidad' => $data['id_unidad_adicionales'], 'cantidades' => $data['num_pax_adicionales'], 'precio_vendido_p_pax_vuelo' => $data['precio_adicional'], 'pasajero_adicional' => 1, 'id_estatus' => 1, 'id_user_alta' => 98, 'fecha_alta' => now(), ]); }*/ // 5. Servicios adicionales if (!empty($data['servicios_adicionales'])) { $servicios = is_string($data['servicios_adicionales']) ? json_decode($data['servicios_adicionales'], true) : $data['servicios_adicionales']; foreach ($servicios as $servicio) { DB::table('servicios_adicionales_cotizaciones')->insert([ 'id_cotizacion' => $id_cotizacion, 'id_servicio_adicional' => $servicio['id_servicio'], 'cantidades' => $servicio['cantidad_solicitada'], 'precio_vendido' => $servicio['precio'], 'id_estatus' => 1, 'id_user_alta' => 98, 'fecha_alta' => now(), ]); } } DB::commit(); return $id_cotizacion; return [ 'success' => true, 'id_cotizacion' => $id_cotizacion, 'folio' => $folio ]; } catch (\Exception $e) { DB::rollBack(); return [ 'success' => false, 'error' => $e->getMessage() ]; } } /* public function efectuarPago(Request $request) { try { $cotizacion_id = $request->id_cotizacion ?? 0; $cotizacion = DB::table('cotizaciones')->where('id_cotizacion', $cotizacion_id)->first(); //aqui va lo del pago con clip, si se efectua entonces se generan el pase de abordar y el pedido DB::table('pases_de_abordar')->insert([ 'id_cotizacion' => $cotizacion_id, 'id_lugar_cita' => 12, 'hora_cita' => '06:00', 'id_recibo' => 0, 'id_estatus' => 1, 'id_user_alta' => 98, 'fecha_alta' => now(), ]); DB::table('pedidos')->insert([ 'id_cotizacion' => $cotizacion_id, 'foto_comprobante' => null, 'cantidad_pagada' => $cotizacion->total_neto, 'pago_confirmado' => 1, 'fecha_pago' => date('Y-m-d'), 'observaciones' => 'Transacción realizada vía Clip PAGO CON TARJETA desde vueloenglobo.mx | ' . $cotizacion->nombre . ' ' . $cotizacion->apellidos . ' - ID Cotizacion:' . $cotizacion_id, 'observaciones_logistica' => 'Transacción realizada vía Clip PAGO CON TARJETA desde vueloenglobo.mx | ' . $cotizacion->nombre . ' ' . $cotizacion->apellidos . ' - ID Cotizacion:' . $cotizacion_id, 'ultimos_digitos' => null, 'id_cuenta_bancaria' => 1, 'id_metodo_pago_cuenta_bancaria' => 9, 'id_banco' => 0, 'id_estatus' => 1, 'id_user_alta' => 98, 'fecha_alta' => now(), ]); $url = url('https://erp.skyballoons.mx/enviar-pase/2/' . $cotizacion_id); Http::get($url); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'Error al procesar el pago: ' . $e->getMessage() ], 500); } }*/ public function efectuarPago(Request $request) { try { $validated = $request->validate([ 'token' => 'required|string', 'id_cotizacion' => 'required|integer' ]); $cotizacion = DB::table('cotizaciones') ->where('id_cotizacion', $validated['id_cotizacion']) ->first(); if (!$cotizacion) { return response()->json([ 'success' => false, 'message' => 'Cotización no encontrada' ], 404); } // ========================== // COBRO CON CLIP // ========================== $payload = [ 'amount' => (float) $cotizacion->total_neto, 'currency' => 'MXN', 'description' => 'Pago vuelo en globo - ' . $cotizacion->folio, 'payment_method' => [ 'token' => $validated['token'] ], 'customer' => [ 'email' => $cotizacion->mail, 'phone' => $cotizacion->telefono ] ]; $response = Http::withHeaders([ 'Authorization' => 'Bearer ' . config('clip.api_key'), 'Content-Type' => 'application/json', ])->post(config('clip.payments_url'), $payload); // 🔴 FALLO TOTAL: cortar flujo if (!$response->successful()) { return response()->json([ 'success' => false, 'message' => $response->json('detail') ?? 'Pago rechazado', 'clip_response' => $response->json() ], 400); } $clipPayment = $response->json(); // 🔴 VALIDAR ESTATUS REAL DEL PAGO (MUY IMPORTANTE) if (($clipPayment['status'] ?? null) !== 'approved') { return response()->json([ 'success' => false, 'message' => 'El pago no fue aprobado', 'clip_response' => $clipPayment ], 400); } // ========================== // SOLO SI FUE APROBADO // ========================== DB::beginTransaction(); DB::table('pases_de_abordar')->insert([ 'id_cotizacion' => $cotizacion->id_cotizacion, 'id_lugar_cita' => 12, 'hora_cita' => '06:00', 'id_recibo' => 0, 'id_estatus' => 1, 'id_user_alta' => 98, 'fecha_alta' => now(), ]); DB::table('pedidos')->insert([ 'id_cotizacion' => $cotizacion->id_cotizacion, 'foto_comprobante' => null, 'cantidad_pagada' => $cotizacion->total_neto, 'pago_confirmado' => 1, 'fecha_pago' => now(), 'observaciones' => 'Clip payment ID: ' . ($clipPayment['id'] ?? ''), 'observaciones_logistica' => 'Clip payment ID: ' . ($clipPayment['id'] ?? ''), 'ultimos_digitos' => $clipPayment['payment_method']['card']['last4'] ?? null, 'id_cuenta_bancaria' => 1, 'id_metodo_pago_cuenta_bancaria' => 9, 'id_banco' => 0, 'id_estatus' => 1, 'id_user_alta' => 98, 'fecha_alta' => now(), ]); DB::commit(); Http::timeout(30)->get( 'https://erp.skyballoons.mx/enviar-pase/2/' . $cotizacion->id_cotizacion ); return response()->json([ 'success' => true, 'message' => 'Pago realizado correctamente', 'payment_id' => $clipPayment['id'] ?? null ]); } catch (\Exception $e) { DB::rollBack(); return response()->json([ 'success' => false, 'message' => $e->getMessage() ], 500); } } }