'required', 'celular_comprarPromo' => 'nullable', 'email_comprarPromo' => 'required', 'cantidad_comprarPromo' => 'required|numeric|min:1', ]; public function render() { $date = Carbon::now()->subDay(30); $this->fecha = $date->format('Y-m-d'); $this->fechaActual = Carbon::now(); $this->fechaActual = Carbon::parse($this->fechaActual); $this->fechaHoy = Carbon::now(); $this->msj = Configuracione::orderby('id', 'desc')->select('msj_compra', 'msj_wp', 'clave_1', 'clave_2', 'clave_3', 'clave_4', 'clave_5', 'clave_6', 'iptv')->first(); //$this->fechaHoy = $this->fechaHoy->format('d/m/Y'); if (empty($this->nombre_comprarPromo) && empty($this->email_comprarPromo)) { $usuario_actual = Auth::user(); $this->nombre_comprarPromo = $usuario_actual->name; $this->email_comprarPromo = $usuario_actual->email; $this->celular_comprarPromo = $usuario_actual->celular; //dd($this-> nombre_tarjetaPlan); } return view('livewire.show-promociones', [ 'categorias' => Categoria::get(), 'tarifas' => Tarifas::with('servicio')->get(), ]); } public function verPromocion($id) { $consulta_verpromocion = Promociones::where('id', $id) ->with([ 'tarifas', 'usuario_promos' => function ($query) { $query->where('usuario_id', Auth::id()); }, 'tarifaPromo' => function ($query) { $query->where('rol_id', Auth::user()->rol_id); } ]) ->first(); // dd($consulta_verpromocion, Auth::user()->rol->id, Auth::user()->id); //dd($consulta_verpromocion); //$cuentas_creadas = Cuentas::whereDate('inicio','>=',$this->fecha)->where('servicio_id',$tarifas->servicio->id)->where('estado','!=','inactivo')->where('estado','!=','delete')->get(); //Traer la tarifa personalizada del usuario para esta tarifa. $usuario_tarifa = Usuario_promo::where('usuario_id', auth()->user()->id)->where('promocion_id', $id)->first(); // dd($usuario_tarifa); $this->consulta = $consulta_verpromocion; //Si tiene tarifa personalizada toma el precio, si no tiene da el valor de la tarifa. $this->precioAhora = ($usuario_tarifa) ? $usuario_tarifa->precio : $consulta_verpromocion->tarifaPromo->first()->precio; //Si tiene tarifa personalizada toma la utilidad, si no tiene toma la utilidad de la tarifa. $this->utilidad = ($usuario_tarifa) ? $usuario_tarifa->utilidad : $consulta_verpromocion->tarifaPromo->first()->utilidad; $this->imagen = $consulta_verpromocion->img_publicidad; $this->fechaLimite = $consulta_verpromocion->fecha_limite; $this->nombrePromo = $consulta_verpromocion->nombre; $this->dias = $consulta_verpromocion->dias; $this->descripcionPromo = $consulta_verpromocion->descripcion; $this->fechaFinal = $this->fechaActual->addDay($this->dias); $this->mensaje_servicio = $consulta_verpromocion->mensaje; $this->verPromo = true; } public function comprarPromo($id) { $consulta_verpromocion = Promociones::with('tarifas')->where('id', $id)->first(); //Traer la tarifa personalizada del usuario para esta tarifa. $usuario_tarifa = Usuario_promo::where('usuario_id', auth()->user()->id)->where('promocion_id', $id)->first(); $this->consulta = $consulta_verpromocion; //Si tiene tarifa personalizada toma el precio, si no tiene da el valor de la tarifa. $this->precioAhora = ($usuario_tarifa) ? $usuario_tarifa->precio : $consulta_verpromocion->precio; //Si tiene tarifa personalizada toma la utilidad, si no tiene toma la utilidad de la tarifa. $this->utilidad = ($usuario_tarifa) ? $usuario_tarifa->utilidad : $consulta_verpromocion->utilidad; $this->imagen = $consulta_verpromocion->img_publicidad; $this->fechaLimite = $consulta_verpromocion->fecha_limite; $this->nombrePromo = $consulta_verpromocion->nombre; $this->dias = $consulta_verpromocion->dias; $this->descripcionPromo = $consulta_verpromocion->descripcion; $this->fechaFinal = $this->fechaActual->addDay($this->dias); $this->mensaje_servicio = $consulta_verpromocion->mensaje; $this->modalComprarPromo = true; } /* ---------------- Btn Comprar ---------------- */ public function getListeners() { return [ 'confirmed', 'confirmarCompra', 'denegarCompra' ]; } public $historial; public $n = []; public $f = []; public $r = 0; public function pagar() { $maxIntentos = 3; // cantidad de veces que se intentará $intento = 0; while ($intento < $maxIntentos) { try { $intento++; DB::transaction(function () { $this->validate(); // === Validaciones iniciales === if ($this->cantidad_comprarPromo == 0) { throw new \Exception('La cantidad debe ser mayor a 0', 400); } $costoTotal = $this->precioAhora * $this->cantidad_comprarPromo; $saldoDisponible = auth()->user()->saldo->valor; if ($saldoDisponible < $costoTotal) { throw new \Exception('Saldo insuficiente', 400); } // === Crear o buscar usuario === $usuario = User::firstWhere('email', $this->email_comprarPromo); if (!$usuario) { $usuario = User::create([ 'name' => $this->nombre_comprarPromo, 'email' => $this->email_comprarPromo, 'password' => bcrypt(Str::random(12)), // 🔒 contraseña aleatoria 'celular' => $this->celular_comprarPromo, ]); } // === Historial === $historial = Historiale::create([ 'fecha_inicio' => $this->fechaHoy, 'fecha_final' => $this->fechaFinal, 'valor' => $costoTotal, 'tipo_pago' => 'manual', 'estado' => 'pendiente', 'vendedor_id' => Auth()->user()->id, 'promocion_id' => $this->consulta->id, 'cliente_id' => $usuario->id, 'cantidad' => $this->cantidad_comprarPromo, 'nombre_cliente' => $this->nombre_comprarPromo, 'utilidad' => $this->utilidad * $this->cantidad_comprarPromo, ]); $utilidad_total = (int) $this->utilidad * (int) $this->cantidad_comprarPromo; $this->historial = $historial; // === Asignar cuentas === $this->n = []; $this->f = []; for ($p = 0; $p < (int) $this->cantidad_comprarPromo; $p++) { foreach ($this->consulta->tarifas as $tarifa) { $pantallas_tarifa = $tarifa->pantallas; $servicio_id = $tarifa->servicio_id; $pantalla_servicio = $tarifa->servicio->pantallas; $pantalla_servicio_completa = $tarifa->servicio->completa; $faltante = $pantalla_servicio - $pantallas_tarifa; if ($pantalla_servicio_completa == $pantallas_tarifa) { $fecha_final_1 = Carbon::now()->addDays($tarifa->dias - 20); $fecha_final_2 = Carbon::now()->addDays($tarifa->dias + 20); $baseQuery = Cuentas::where('servicio_id', $servicio_id) ->whereDate('inicio', '>=', $this->fecha) ->whereDate('vencimiento', '>=', $fecha_final_1) ->whereDate('vencimiento', '<=', $fecha_final_2) ->where('estado', 'pendiente') ->withCount('historiales') ->orderBy('inicio', 'ASC'); $cuenta = Cuentas::fromSub($baseQuery, 'alias') ->where('historiales_count', '=', 0) ->lockForUpdate() ->first(); } else { $baseQuery = Cuentas::where('servicio_id', $servicio_id) ->whereDate('inicio', '>=', $this->fecha) ->where('estado', 'activo') ->withCount('historiales') ->orderBy('inicio', 'ASC'); $cuenta = Cuentas::fromSub($baseQuery, 'alias') ->where('historiales_count', '<=', (int) $faltante) ->lockForUpdate() ->first(); } if ($cuenta) { $perfil = Historial_cuenta::where('cuenta_id', $cuenta->id)->max('perfil') ?? 0; for ($y = 0; $y < (int) $pantallas_tarifa; $y++) { $historial_cuenta = Historial_cuenta::create([ 'historial_id' => $historial->id, 'cuenta_id' => $cuenta->id, 'perfil' => (int) $perfil + (int) $y + 1, ]); if ($historial_cuenta->id) { $this->n[] = $historial_cuenta->id; } } } else { $this->f[] = $servicio_id; throw new \Exception('No hay cuentas disponibles de ' . $tarifa->servicio->nombre, 409); } } } // === Cobro de saldo === $descuento = Saldo::where('usuario_id', Auth::user()->id)->first(); $saldo_anterior = $descuento->valor; $nuevo_saldo = $saldo_anterior - $costoTotal; if (!$descuento->update(['valor' => $nuevo_saldo])) { Historial_cuenta::whereIn('id', $this->n)->delete(); throw new \Exception('Error en el cobro, comuníquese con soporte!', 500); } // Activar historial $historial->update(['estado' => 'activo']); // Registrar logs Log_general::create([ 'user_id' => Auth::user()->id, 'detalle' => 'El usuario ' . Auth::user()->name . ' - ' . Auth::user()->email . ' ha comprado la promoción ' . $this->consulta->nombre . ' - ' . $this->consulta->descripcion . ' cantidad de ' . $this->historial->cantidad . ' por un valor de $' . $this->historial->valor . ', Saldo anterior: $' . $saldo_anterior . ', Nuevo saldo disponible: $' . $nuevo_saldo, 'tipo' => 'compra', 'historial_id' => $historial->id, ]); // Utilidad para el padre if (Auth::user()->subvendedor_id && $utilidad_total > 0) { $subvendedorPadre = User::with('saldo')->find(Auth::user()->subvendedor_id); if ($subvendedorPadre && $subvendedorPadre->saldo) { $nuevoSaldo = $subvendedorPadre->saldo->valor + $utilidad_total; $subvendedorPadre->saldo->update(['valor' => $nuevoSaldo]); Log_general::create([ 'user_id' => Auth::user()->id, 'detalle' => 'Compra generó utilidad de $' . $utilidad_total . ' al usuario padre ' . $subvendedorPadre->name . ' - ' . $subvendedorPadre->email, 'historial_id' => $historial->id, 'tipo' => 'compra', ]); } } $this->modalComprarPromo = false; $this->credenciales($historial); }, 3); // Laravel volverá a intentar hasta 3 veces si hay deadlocks // ✅ si llega aquí es que todo salió bien break; } catch (QueryException $e) { if (in_array($e->getCode(), ['23505', '1062'])) { // PostgreSQL o MySQL if ($intento >= $maxIntentos) { $this->alert('warning', 'Otra persona tomó la cuenta al mismo tiempo. Intenta nuevamente.', ['position' => 'top']); return; } usleep(200000); // esperar 200ms antes de reintentar continue; } $this->alert('error', 'Error en la compra: ' . $e->getMessage(), ['position' => 'top']); return; } catch (\Throwable $e) { $this->alert('error', 'Error inesperado: ' . $e->getMessage(), ['position' => 'top']); return; } } } public function confirmarCompra() { $descuento = Saldo::where('usuario_id', Auth::user()->id)->first(); $saldo_anterior = $descuento->valor; $nuevo_saldo = $saldo_anterior - ($this->precioAhora * $this->cantidad_comprarPromo); $cobro = $descuento->update(['valor' => $nuevo_saldo]); if ($cobro) { $this->historial->update(['estado' => 'activo']); $this->modalComprarPromo = false; foreach ($this->f as $serviceId) { for ($y = 0; $y < (int) $this->cantidad_comprarPromo; $y++) { $pendiente = new Pendientes(); $pendiente->historial_id = $this->historial->id; $pendiente->cantidad = '1'; $pendiente->estado = 'pendiente'; $pendiente->servicio_id = $serviceId; $pendiente->save(); } } $logsGeneral = new Log_general(); $logsGeneral->user_id = Auth::user()->id; $logsGeneral->detalle = 'El usuario ' . Auth::user()->name . ' - ' . Auth::user()->email . ' ha comprado la promocion ' . $this->consulta->nombre . ' - ' . $this->consulta->descripcion . ' cantidad de ' . $this->historial->cantidad . ' por un valor de $' . $this->historial->valor ?? '' . ' y algunos han quedado pendientes' . ', Saldo anterior: $' . $saldo_anterior . ', Nuevo saldo disponible: $' . $nuevo_saldo; $logsGeneral->tipo = 'compra'; $logsGeneral->historial_id = $this->historial->id; $logsGeneral->save(); $this->credenciales($this->historial); } else { Historial_cuenta::whereIn('id', $this->n)->delete(); $logsGeneral = new Log_general(); $logsGeneral->user_id = Auth::user()->id; $logsGeneral->detalle = 'El usuario ' . Auth::user()->name . ' - ' . Auth::user()->email . ' Tuvo error en el cobro, Comprando la promoción ' . $this->consulta->nombre . ' cantidad de ' . $this->historial->cantidad . ' por un valor de $' . $this->historial->valor . ' No se desconto el saldo, ni se asignaron cuentas.'; $logsGeneral->tipo = 'compra_error'; $logsGeneral->historial_id = $this->historial->id; $logsGeneral->save(); $this->alert('warning', 'Error en el cobro, comuniquese con soporte!', [ 'position' => 'top' ]); } $this->alert('success', 'Compra exitosa!', [ 'position' => 'top' ]); $this->f = null; $this->n = null; $this->r = 0; $this->modalComprarPromo = false; } public function denegarCompra() { Historial_cuenta::whereIn('id', $this->n)->delete(); $logsGeneral = new Log_general(); $logsGeneral->user_id = Auth::user()->id; $logsGeneral->detalle = 'El usuario ' . Auth::user()->name . ' - ' . Auth::user()->email . ' Cancelo la compra del plan ' . $this->consulta->nombre . ' cantidad de ' . $this->historial->cantidad . ' por un valor de $' . $this->historial->valor . ' No se desconto el saldo, ni se asignaron cuentas.'; $logsGeneral->tipo = 'compra_cancelada'; $logsGeneral->historial_id = $this->historial->id; $logsGeneral->save(); $this->alert('success', 'Compra cancelada!', [ 'position' => 'top' ]); $this->f = null; $this->n = null; $this->r = 0; $this->modalComprarPromo = false; } public function credenciales($historial) { $this->cuentas = $historial; $this->credenciales = true; $encryptado = Crypt::encryptString($historial->id); $enlace = '/credenciales?id=' . $encryptado; $user = $historial->cliente; $notificacion = [ 'enlace' => $enlace, 'fecha_final' => $historial->fecha_final, 'cliente' => $historial->cliente->name, 'servicio' => $historial->promocion->nombre, 'tipo' => 'compra', 'vendedor' => auth()->user()->username, ]; Notification::send($user, new compra($notificacion)); $this->alert('success', 'Credenciales enviadas a su email!', [ 'position' => 'top' ]); $shortURLObject = ShortURL::destinationUrl('https://app.sirpremium.com.co' . $enlace)->make(); $shortURL = $shortURLObject->default_short_url; $message = $historial->promocion->nombre . '. Ver credenciales: ' . $shortURL; $recipients = '+57' . $this->celular_comprarPromo; $this->sendMessage($message, $recipients); $this->cantidad_comprarPromo = 1; } public function sendMessage($message, $recipients) { $auth_basic = base64_encode('admin@jaguarws.ga:9u3xxXhHt8maeiOexg07pFKI0rpYazI0'); $curl = curl_init(); curl_setopt_array( $curl, array( CURLOPT_URL => "https://api.labsmobile.com/json/send", CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => "", CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 30, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => "POST", CURLOPT_POSTFIELDS => '{"message":"' . $message . '", "tpoa":"Sender","recipient":[{"msisdn":"' . $recipients . '"}]}', CURLOPT_HTTPHEADER => array( "Authorization: Basic " . $auth_basic, "Cache-Control: no-cache", "Content-Type: application/json" ), ) ); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) { $this->alert('warning', 'Error enviando SMS!' . $err, [ 'position' => 'top' ]); } else { if (!empty(json_decode($response)->subid)) { $this->alert('success', 'Credenciales enviadas por Email y SMS! ' . json_decode($response)->subid, [ 'position' => 'top' ]); } else { $this->alert('success', 'Credenciales enviadas por Email! ' . json_decode($response)->code, [ 'position' => 'top' ]); } } } public function create_newCategoria() { if (!empty($this->categoria_photo)) { $url_foto = $this->categoria_photo->store('photos'); } else { $url_foto = ''; } $categoria = new Categoria; $categoria->nombre = $this->nombre_categoria; $categoria->descripcion = $this->descripcion_categoria; $categoria->imagen = $url_foto; $categoria->save(); $this->newCategoria = false; } public function add_tarifa() { $consulta = Tarifas::find($this->tarifa_id_promocion); $promocion_tarifa = new Promocion_tarifa; $promocion_tarifa->tarifa_id = $this->tarifa_id_promocion; $promocion_tarifa->nombre = $consulta->servicio->nombre; $promocion_tarifa->cantidad = $consulta->pantallas; $promocion_tarifa->dias = $consulta->dias; if (is_null($this->list)) { $this->list = collect(); } $this->list->add($promocion_tarifa); } public function del_item($id) { $this->list->pull($id); } public function guardar() { $this->validate([ 'nombre_promocion' => 'required', 'descripcion_promocion' => 'required', 'utilidad' => 'required', 'valor_promocion' => 'required', 'estado_promocion' => 'required', 'fecha_limite_promocion' => 'required', 'categoria_id_promocion' => 'required', 'photo' => 'required', 'list' => 'required', ]); if (!empty($this->photo)) { $nombre = Str::random(10); $img = img::make($this->photo)->orientate()->resize(1000, null, function ($constraint) { $constraint->aspectRatio(); $constraint->upsize(); })->encode('webp'); $location = "photos/" . $nombre . ".webp"; Storage::disk('local')->put($location, $img); $url_foto = $location; } else { $url_foto = null; } $nueva_promocion = new Promociones; $nueva_promocion->nombre = $this->nombre_promocion; $nueva_promocion->descripcion = $this->descripcion_promocion; $nueva_promocion->precio = $this->valor_promocion; $nueva_promocion->utilidad = $this->utilidad; $nueva_promocion->visible = $this->estado_promocion; $nueva_promocion->fecha_limite = $this->fecha_limite_promocion; $nueva_promocion->categoria_id = $this->categoria_id_promocion; $nueva_promocion->img_publicidad = $url_foto; $nueva_promocion->dias = 30; $nueva_promocion->mensaje = $this->mensaje_servicio; $nueva_promocion->save(); foreach ($this->list as $item) { $guardar_promotarifa = new Promocion_tarifa; $guardar_promotarifa->promocion_id = $nueva_promocion->id; $guardar_promotarifa->tarifa_id = $item['tarifa_id']; //falta $guardar_promotarifa->save(); } $this->newPromocion = false; $this->alert('success', 'Promocion creada correctamente!', [ 'position' => 'top' ]); $this->limpiarInputPromocion(); } public function eliminar($id) { $this->eliminar_id = $id; $this->alert('warning', '¿Eliminar promocion?', [ 'position' => 'center', 'timer' => '10000', 'toast' => false, 'text' => 'Esta seguro', 'showConfirmButton' => true, 'onConfirmed' => 'confirmed', 'showDenyButton' => false, 'onDenied' => '', 'showCancelButton' => true, 'onDismissed' => '', 'cancelButtonText' => 'Salir', 'confirmButtonText' => 'Si', 'customClass' => [ 'confirmButton' => 'bg-green-500', 'denyButton' => 'bg-red-500', 'cancelButton' => 'bg-gray-500', ] ]); } public function confirmed() { $consulta_promociones = Promociones::findOrFail($this->eliminar_id); $nombre = $consulta_promociones->nombre; Promociones::where('id', $this->eliminar_id)->update([ 'nombre' => $nombre . '-Delete', 'visible' => 'false', ]); } /* ---------------- Editar Promocion ---------------- */ public function editarPromo($id) { $promocion = Promociones::where('id', $id)->first(); // $this->imagen_editarPromo= $promocion->img_publicidad; $this->editar_promo_id = $promocion->id; $this->valor_editarPromo = $promocion->precio; $this->fecha_editarPromo = $promocion->fecha_limite; $this->nombre_editarPromo = $promocion->nombre; $this->descripcion_editarPromo = $promocion->descripcion; $this->estado_editarPromo = $promocion->visible; $this->categoria_id_editarPromo = $promocion->categoria_id; $this->img_ver = $promocion->img_publicidad; $this->mensaje_servicio = $promocion->mensaje; $consultas = Promocion_tarifa::selectRaw('promocion_tarifas.id as id, servicios.nombre as nombre, tarifas.pantallas as pantallas ') ->join('tarifas', 'tarifas.id', '=', 'promocion_tarifas.tarifa_id') ->join('servicios', 'servicios.id', '=', 'tarifas.servicio_id') ->where('promocion_tarifas.promocion_id', $this->editar_promo_id) ->get(); $this->list2 = collect(); foreach ($consultas as $consulta) { $promocionTarifa = new Promocion_tarifa; $promocionTarifa->tarifa_id = $this->tarifa_id_promocion; $promocionTarifa->nombre = $consulta->nombre; $promocionTarifa->cantidad = $consulta->pantallas; $promocionTarifa->identificador = $consulta->id; $promocionTarifa->dias = $consulta->dias; $this->list2->add($promocionTarifa); } $this->editarPromo = true; } public function editarPromoUpdate() { if (!empty($this->photo)) { $nombre = Str::random(10); $img = img::make($this->photo)->orientate()->resize(1000, null, function ($constraint) { $constraint->aspectRatio(); $constraint->upsize(); })->encode('webp'); $location = "photos/" . $nombre . ".webp"; Storage::disk('local')->put($location, $img); $url_foto = $location; } else { $url_foto = $this->img_ver; } Promociones::where('id', $this->editar_promo_id)->update([ 'precio' => $this->valor_editarPromo, 'fecha_limite' => $this->fecha_editarPromo, 'visible' => $this->estado_editarPromo, 'categoria_id' => $this->categoria_id_editarPromo, 'nombre' => $this->nombre_editarPromo, 'descripcion' => $this->descripcion_editarPromo, 'img_publicidad' => $url_foto, 'mensaje' => $this->mensaje_servicio, ]); $this->editarPromo = false; $this->list2 = null; $this->limpiarInputPromocion(); } public function add_tarifa_list2() { $add = new Promocion_tarifa; $add->promocion_id = $this->editar_promo_id; $add->tarifa_id = $this->tarifa_id_promocion; $add->save(); $consultas = Promocion_tarifa::selectRaw('promocion_tarifas.id as id, servicios.nombre as nombre, tarifas.pantallas as pantallas ') ->join('tarifas', 'tarifas.id', '=', 'promocion_tarifas.tarifa_id') ->join('servicios', 'servicios.id', '=', 'tarifas.servicio_id') ->where('promocion_tarifas.promocion_id', $this->editar_promo_id) ->get(); $this->list2 = collect(); foreach ($consultas as $consulta) { $promocionTarifa = new Promocion_tarifa; $promocionTarifa->tarifa_id = $this->tarifa_id_promocion; $promocionTarifa->nombre = $consulta->nombre; $promocionTarifa->cantidad = $consulta->pantallas; $promocionTarifa->identificador = $consulta->id; $this->list2->add($promocionTarifa); } } public function del_item_list2($id, $n) { Promocion_tarifa::where('id', $id)->delete(); $this->list2->pull($n); $this->alert('success', 'Tarifa de usuario eliminada correctamente!', [ 'position' => 'top' ]); } public function limpiarInputPromocion() { $this->nombre_promocion = ''; $this->descripcion_promocion = ''; $this->valor_promocion = ''; $this->utilidad = ''; $this->estado_promocion = null; $this->fecha_limite_promocion = null; $this->categoria_id_promocion = null; $this->newCategoria = false; $this->newPromocion = false; $this->photo = null; $this->mensaje_servicio = null; $this->list = null; } }