From 1245d255bba92e3c3d798d49340882efa047ca40 Mon Sep 17 00:00:00 2001 From: Lizandro Date: Wed, 29 Jul 2026 12:04:33 +0000 Subject: [PATCH] =?UTF-8?q?4=20mejoras=20en=20canales=20de=20comunicaci?= =?UTF-8?q?=C3=B3n=20con=20el=20cliente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Reintentos automáticos (Telegram Job): usa autoAplicarRecarga para ejecutar la compra pendiente automáticamente al confirmar el pago, sin botón extra. 2. Pagar servicio directo con Bre-B sin recargar saldo previamente (ambos canales): botón 'Pagar con Bre-B directo' en el flujo de compra con saldo insuficiente. 3. Mensaje de gracias + bloque de credenciales fácil de copiar al entregar servicio: Telegram usa bloque de código (tiene botón Copy nativo), Chat Web muestra el texto completo de gracias con copia por campo y botón 'Copiar todo'. 4. Botón copiar llave Bre-B: ya existía en Chat Web, confirmado sin cambio. Co-Authored-By: Claude Sonnet 4.6 --- app/Http/Livewire/Chat/PublicChat.php | 66 ++++++++- app/Jobs/ValidarPagoTelegramJob.php | 63 +------- app/Services/TelegramBotService.php | 138 ++++++++++++------ .../views/livewire/chat/public-chat.blade.php | 78 ++++++++-- 4 files changed, 224 insertions(+), 121 deletions(-) diff --git a/app/Http/Livewire/Chat/PublicChat.php b/app/Http/Livewire/Chat/PublicChat.php index 839e632..3298b4c 100755 --- a/app/Http/Livewire/Chat/PublicChat.php +++ b/app/Http/Livewire/Chat/PublicChat.php @@ -500,6 +500,7 @@ class PublicChat extends Component $action === 'historial.ver' => $this->verHistorial(), $action === 'perfil.ver' => $this->verPerfil(), $action === 'asesor.solicitar' => $this->solicitarAsesor(), + $action === 'pago.directo.breb' => $this->pagarDirectoBreb($data), $action === 'pago.aplicar' => $this->aplicarRecargaValidada($data), $action === 'pago.reintentar' => $this->reintentarValidacionPago(), $action === 'pago.nombre_previo' => $this->usarNombrePrevio($data['nombre'] ?? ''), @@ -776,7 +777,6 @@ class PublicChat extends Component } else { $falta = $precio - (float) $this->saldoUsuario; - // Guardar compra pendiente para ejecutarla automáticamente tras recargar $this->flujoData = array_merge($this->flujoData, [ 'pending_purchase' => [ 'type' => 'tarifa', @@ -792,9 +792,10 @@ class PublicChat extends Component 'conversation_id' => $this->convId, 'tipo' => 'bot', 'tipo_ui' => 'buttons', - 'contenido' => "{$tarifa->servicio->nombre} - {$detalle}\nPrecio: $" . number_format($precio) . "\n\nSaldo actual: $" . number_format($this->saldoUsuario) . " — te faltan $" . number_format($falta) . "\n\nRecarga para comprar:", + 'contenido' => "{$tarifa->servicio->nombre} - {$detalle}\nPrecio: $" . number_format($precio) . "\n\nSaldo actual: $" . number_format($this->saldoUsuario) . " — te faltan $" . number_format($falta) . "\n\n¿Cómo quieres pagar?", 'payload' => ['botones' => [ - ['label' => "💰 Recargar $" . number_format($falta) . " para comprar {$tarifa->servicio->nombre}", 'action' => 'recarga.iniciar', 'data' => []], + ['label' => "💳 Pagar con Bre-B directo ($" . number_format($precio) . ")", 'action' => 'pago.directo.breb', 'data' => ['tipo' => 'tarifa', 'tarifa_id' => $tarifaId, 'valor' => $precio]], + ['label' => "💰 Recargar saldo ($" . number_format($falta) . " faltantes)", 'action' => 'recarga.iniciar', 'data' => []], ['label' => 'Volver', 'action' => 'servicios.listar', 'data' => []], ]], 'leido' => true, @@ -860,7 +861,7 @@ class PublicChat extends Component 'conversation_id' => $this->convId, 'tipo' => 'bot', 'tipo_ui' => 'credenciales', - 'contenido' => 'Compra exitosa! Tus credenciales:', + 'contenido' => '¡Muchas gracias por tu compra! 🎉 Disfruta tu servicio. Si tienes algún inconveniente no dudes en escribirnos, estamos aquí para ayudarte.', 'payload' => [ 'servicio' => $tarifa->servicio->nombre, 'email' => $cuenta->correo, @@ -960,7 +961,6 @@ class PublicChat extends Component } else { $falta = $precio - (float) $this->saldoUsuario; - // Guardar compra pendiente para ejecutarla automáticamente tras recargar $this->flujoData = array_merge($this->flujoData, [ 'pending_purchase' => [ 'type' => 'promo', @@ -974,9 +974,10 @@ class PublicChat extends Component 'conversation_id' => $this->convId, 'tipo' => 'bot', 'tipo_ui' => 'buttons', - 'contenido' => ($promo->nombre ?? 'Promocion') . "\n$" . number_format($precio) . "\n\nSaldo actual: $" . number_format($this->saldoUsuario) . " — te faltan $" . number_format($falta) . "\n\nRecarga para comprar:", + 'contenido' => ($promo->nombre ?? 'Promocion') . "\n$" . number_format($precio) . "\n\nSaldo actual: $" . number_format($this->saldoUsuario) . " — te faltan $" . number_format($falta) . "\n\n¿Cómo quieres pagar?", 'payload' => ['botones' => [ - ['label' => "💰 Recargar $" . number_format($falta) . " para comprar " . ($promo->nombre ?? 'Promo'), 'action' => 'recarga.iniciar', 'data' => []], + ['label' => "💳 Pagar con Bre-B directo ($" . number_format($precio) . ")", 'action' => 'pago.directo.breb', 'data' => ['tipo' => 'promo', 'promo_id' => $id, 'valor' => $precio]], + ['label' => "💰 Recargar saldo ($" . number_format($falta) . " faltantes)", 'action' => 'recarga.iniciar', 'data' => []], ['label' => 'Volver', 'action' => 'promo.listar', 'data' => []], ]], 'leido' => true, @@ -1045,7 +1046,7 @@ class PublicChat extends Component 'conversation_id' => $this->convId, 'tipo' => 'bot', 'tipo_ui' => 'credenciales', - 'contenido' => 'Promo activada! Tus credenciales:', + 'contenido' => '¡Muchas gracias por tu compra! 🎉 Disfruta tu servicio. Si tienes algún inconveniente no dudes en escribirnos, estamos aquí para ayudarte.', 'payload' => [ 'servicio' => $promo->nombre ?? 'Promocion', 'email' => $cuenta->correo, @@ -1059,6 +1060,55 @@ class PublicChat extends Component } + // ───────────────────────────────────────────────────────── + // Pago directo con Bre-B sin necesidad de saldo previo + // ───────────────────────────────────────────────────────── + + private function pagarDirectoBreb(array $data): void + { + $tipo = $data['tipo'] ?? 'tarifa'; + $valor = (int) ($data['valor'] ?? 0); + + if ($valor <= 0) { + $this->guardarMensajeBot($this->convId, "No se pudo iniciar el pago. Intenta de nuevo."); + return; + } + + if ($tipo === 'tarifa') { + $tarifa = Tarifas::with('servicio')->find($data['tarifa_id'] ?? 0); + if (! $tarifa) { + $this->guardarMensajeBot($this->convId, "Plan no encontrado."); + return; + } + $this->flujoData = array_merge($this->flujoData, [ + 'pending_purchase' => [ + 'type' => 'tarifa', + 'tarifa_id' => $data['tarifa_id'], + 'valor' => $valor, + 'servicio' => $tarifa->servicio->nombre, + 'dias' => $tarifa->dias, + 'pantallas' => $tarifa->pantallas, + ], + ]); + } elseif ($tipo === 'promo') { + $promo = Promociones::find($data['promo_id'] ?? 0); + if (! $promo) { + $this->guardarMensajeBot($this->convId, "Promoción no encontrada."); + return; + } + $this->flujoData = array_merge($this->flujoData, [ + 'pending_purchase' => [ + 'type' => 'promo', + 'promo_id' => $data['promo_id'], + 'valor' => $valor, + 'nombre' => $promo->nombre ?? 'Promoción', + ], + ]); + } + + $this->mostrarTransferencia($valor); + } + // ───────────────────────────────────────────────────────── // Recarga de saldo // ───────────────────────────────────────────────────────── diff --git a/app/Jobs/ValidarPagoTelegramJob.php b/app/Jobs/ValidarPagoTelegramJob.php index 0f23c4d..1308efb 100644 --- a/app/Jobs/ValidarPagoTelegramJob.php +++ b/app/Jobs/ValidarPagoTelegramJob.php @@ -2,8 +2,6 @@ namespace App\Jobs; -use App\Models\recarga; -use App\Models\Saldo; use App\Models\SolicitudRecarga; use App\Models\User; use App\Services\PagoValidadorService; @@ -56,7 +54,7 @@ class ValidarPagoTelegramJob implements ShouldQueue ); if ($resultado['estado'] === 'confirmado') { - $this->aplicarYNotificar($bot, $state, $cacheKey, $solicitud, $usuarioId); + $bot->autoAplicarRecarga($this->chatId, $state, $solicitud, $solicitud->monto); return; } @@ -86,63 +84,4 @@ class ValidarPagoTelegramJob implements ShouldQueue } } - private function aplicarYNotificar( - TelegramBotService $bot, - array $state, - string $cacheKey, - SolicitudRecarga $solicitud, - int $usuarioId - ): void { - $user = User::with('saldo')->find($usuarioId); - $saldo = $user->saldo ?? Saldo::create(['usuario_id' => $usuarioId, 'valor' => 0]); - $monto = $solicitud->monto; - $nuevoSaldo = $saldo->valor + $monto; - - $saldo->update(['valor' => $nuevoSaldo]); - - recarga::create([ - 'usuario_id' => $usuarioId, - 'saldo_id' => $saldo->id, - 'monto' => $monto, - 'valor_recarga' => $monto, - 'status' => 'Confirmado', - 'reference' => 'breb-bot-' . $solicitud->id, - ]); - - $solicitud->confirmar(); - - $pp = $state['data']['pending_purchase'] ?? null; - unset($state['data']['pago_pendiente'], $state['data']['solicitud_recarga_id'], $state['data']['pending_purchase']); - Cache::put($cacheKey, $state, now()->addSeconds(self::STATE_TTL)); - - $bot->send($this->chatId, - "✅ *¡Tu recarga de \$" . number_format($monto) . " fue aplicada exitosamente!*\n" - . "Tu nuevo saldo es *\$" . number_format($nuevoSaldo) . "*" - ); - - if ($pp && ($pp['type'] ?? '') === 'tarifa') { - $bot->sendWithKeyboard( - $this->chatId, - "🛒 Tienes una compra pendiente: *{$pp['servicio']}* — \$" . number_format($pp['valor']) . "\nPresiona para completarla:", - [[['text' => "Comprar {$pp['servicio']}", 'callback_data' => 'buy_start|' . $pp['tarifa_id']]]] - ); - } elseif ($pp && ($pp['type'] ?? '') === 'promo') { - $bot->sendWithKeyboard( - $this->chatId, - "🛒 Tienes una compra pendiente: *{$pp['nombre']}* — \$" . number_format($pp['valor']) . "\nPresiona para completarla:", - [[['text' => "Comprar {$pp['nombre']}", 'callback_data' => 'promo_saldo|' . $pp['promo_id']]]] - ); - } else { - $bot->sendWithKeyboard($this->chatId, "¿En qué más te puedo ayudar?", [ - [ - ['text' => '📺 Ver Servicios', 'callback_data' => 'svc_list'], - ['text' => '🎁 Promociones', 'callback_data' => 'promo_list'], - ], - [ - ['text' => '🔑 Mis Credenciales', 'callback_data' => 'creds'], - ['text' => '📋 Historial', 'callback_data' => 'history'], - ], - ]); - } - } } diff --git a/app/Services/TelegramBotService.php b/app/Services/TelegramBotService.php index 05a8e24..1ae55eb 100755 --- a/app/Services/TelegramBotService.php +++ b/app/Services/TelegramBotService.php @@ -133,24 +133,26 @@ class TelegramBotService 'menu' => $this->showMainMenu($chatId), 'logout_ask' => $this->showLogoutConfirmation($chatId), 'logout_confirm' => $this->confirmLogout($chatId), - 'svc_list' => $this->listServices($chatId), - 'svc_view' => $this->viewService($chatId, (int) ($parts[1] ?? 0)), - 'buy_start' => $this->startPurchase($chatId, (int) ($parts[1] ?? 0)), - 'buy_saldo' => $this->payWithSaldo($chatId), - 'promo_list' => $this->listPromos($chatId), - 'promo_view' => $this->viewPromo($chatId, (int) ($parts[1] ?? 0)), - 'promo_saldo' => $this->buyPromoSaldo($chatId, (int) ($parts[1] ?? 0)), - 'rec_init' => $this->showRechargeAmounts($chatId), - 'rec_custom' => $this->askCustomAmount($chatId), - 'rec_amount' => $this->chooseRechargeMethod($chatId, (int) ($parts[1] ?? 0)), - 'rec_breb' => $this->askRemitenteNombre($chatId, (int) ($parts[1] ?? 0)), - 'breb_nom' => $this->seleccionarNombreRemitente($chatId, (string) ($parts[1] ?? 'nuevo')), - 'creds' => $this->showCredentials($chatId), - 'history' => $this->showHistory($chatId), - 'profile' => $this->showProfile($chatId), - 'agent' => $this->showMainMenu($chatId), - 'pay_retry' => $this->retryPayment($chatId), - default => $this->showMainMenu($chatId), + 'svc_list' => $this->listServices($chatId), + 'svc_view' => $this->viewService($chatId, (int) ($parts[1] ?? 0)), + 'buy_start' => $this->startPurchase($chatId, (int) ($parts[1] ?? 0)), + 'buy_saldo' => $this->payWithSaldo($chatId), + 'breb_tarifa' => $this->startDirectBrebPayment($chatId, (int) ($parts[1] ?? 0), (int) ($parts[2] ?? 0)), + 'promo_list' => $this->listPromos($chatId), + 'promo_view' => $this->viewPromo($chatId, (int) ($parts[1] ?? 0)), + 'promo_saldo' => $this->buyPromoSaldo($chatId, (int) ($parts[1] ?? 0)), + 'breb_promo' => $this->startDirectBrebPromo($chatId, (int) ($parts[1] ?? 0), (int) ($parts[2] ?? 0)), + 'rec_init' => $this->showRechargeAmounts($chatId), + 'rec_custom' => $this->askCustomAmount($chatId), + 'rec_amount' => $this->chooseRechargeMethod($chatId, (int) ($parts[1] ?? 0)), + 'rec_breb' => $this->askRemitenteNombre($chatId, (int) ($parts[1] ?? 0)), + 'breb_nom' => $this->seleccionarNombreRemitente($chatId, (string) ($parts[1] ?? 'nuevo')), + 'creds' => $this->showCredentials($chatId), + 'history' => $this->showHistory($chatId), + 'profile' => $this->showProfile($chatId), + 'agent' => $this->showMainMenu($chatId), + 'pay_retry' => $this->retryPayment($chatId), + default => $this->showMainMenu($chatId), }; } @@ -823,7 +825,6 @@ class TelegramBotService } else { $falta = $valor - $saldo; - // Guardar compra pendiente para ejecutarla automáticamente tras recargar $state['data']['pending_purchase'] = [ 'type' => 'tarifa', 'tarifa_id' => $tarifaId, @@ -833,12 +834,13 @@ class TelegramBotService ]; $this->setState($chatId, $state); - $buttons[] = [['text' => "💰 Recargar \$" . number_format($falta) . " para comprar {$tarifa->servicio->nombre}", 'callback_data' => 'rec_init']]; + $buttons[] = [['text' => "💳 Pagar con Bre-B directo (\$" . number_format($valor) . ")", 'callback_data' => 'breb_tarifa|' . $tarifaId . '|' . $valor]]; + $buttons[] = [['text' => "💰 Recargar saldo (\$" . number_format($falta) . " faltantes)", 'callback_data' => 'rec_init']]; $buttons[] = [['text' => '🔙 Ver planes', 'callback_data' => 'svc_view|' . $tarifa->servicio_id]]; $this->sendWithKeyboard( $chatId, - "*{$tarifa->servicio->nombre}* — {$detalle}\nPrecio: *\$" . number_format($valor) . "*\n\nSaldo actual: \$" . number_format($saldo) . " — te faltan *\$" . number_format($falta) . "*\n\nRecarga para continuar:", + "*{$tarifa->servicio->nombre}* — {$detalle}\nPrecio: *\$" . number_format($valor) . "*\n\nSaldo actual: \$" . number_format($saldo) . " — te faltan *\$" . number_format($falta) . "*\n\n¿Cómo quieres pagar?", $buttons ); } @@ -896,18 +898,22 @@ class TelegramBotService $saldo->update(['valor' => $nuevoSaldo]); }); - $text = "✅ *¡Compra exitosa!*\n\n"; - $text .= "🎬 *{$tarifa->servicio->nombre}*\n"; - $text .= "📧 Email: `{$cuenta->correo}`\n"; - $text .= "🔑 Password: `{$cuenta->password}`\n"; - if ($tarifa->pantallas) { - $text .= "📺 Perfil: {$tarifa->pantallas}\n"; - } - $text .= "📅 Vence: " . now()->addDays($tarifa->dias)->format('d/m/Y') . "\n"; - $text .= "💰 Pagado: *\$" . number_format($precio) . "*\n"; - $text .= "\nSaldo restante: *\$" . number_format($nuevoSaldo) . "*"; + $this->send($chatId, + "🎉 *¡Muchas gracias por tu compra!*\n\n" . + "Disfruta tu servicio. Si tienes algún inconveniente no dudes en escribirnos, estamos aquí para ayudarte. 😊" + ); - $this->send($chatId, $text); + $code = "```\n"; + $code .= "Servicio: {$tarifa->servicio->nombre}\n"; + $code .= "Email: {$cuenta->correo}\n"; + $code .= "Password: {$cuenta->password}\n"; + if ($tarifa->pantallas) { + $code .= "Perfil: {$tarifa->pantallas}\n"; + } + $code .= "Vence: " . now()->addDays($tarifa->dias)->format('d/m/Y') . "\n"; + $code .= "Pagado: \$" . number_format($precio) . "\n"; + $code .= "```"; + $this->send($chatId, $code); $state['data'] = []; $this->setState($chatId, $state); @@ -915,6 +921,49 @@ class TelegramBotService } + // ─── Direct Bre-B purchase (sin saldo previo) ──────────── + + private function startDirectBrebPayment(string $chatId, int $tarifaId, int $valor): void + { + $state = $this->getState($chatId); + $tarifa = Tarifas::with('servicio')->find($tarifaId); + + if (! $tarifa) { + $this->send($chatId, "Plan no encontrado."); + return; + } + + $state['data']['pending_purchase'] = [ + 'type' => 'tarifa', + 'tarifa_id' => $tarifaId, + 'valor' => $valor, + 'servicio' => $tarifa->servicio->nombre, + 'servicio_id' => $tarifa->servicio_id, + ]; + $this->setState($chatId, $state); + $this->askRemitenteNombre($chatId, $valor); + } + + private function startDirectBrebPromo(string $chatId, int $promoId, int $precio): void + { + $state = $this->getState($chatId); + $promo = \App\Models\Promociones::find($promoId); + + if (! $promo) { + $this->send($chatId, "Promoción no encontrada."); + return; + } + + $state['data']['pending_purchase'] = [ + 'type' => 'promo', + 'promo_id' => $promoId, + 'valor' => $precio, + 'nombre' => $promo->nombre ?? 'Promoción', + ]; + $this->setState($chatId, $state); + $this->askRemitenteNombre($chatId, $precio); + } + // ─── Promos ─────────────────────────────────────────────── private function listPromos(string $chatId): void @@ -997,7 +1046,6 @@ class TelegramBotService } else { $falta = $precio - $saldo; - // Guardar compra pendiente para ejecutarla automáticamente tras recargar $state = $this->getState($chatId); $state['data']['pending_purchase'] = [ 'type' => 'promo', @@ -1007,12 +1055,13 @@ class TelegramBotService ]; $this->setState($chatId, $state); - $buttons[] = [['text' => "💰 Recargar \$" . number_format($falta) . " para comprar " . ($promo->nombre ?? 'Promo'), 'callback_data' => 'rec_init']]; + $buttons[] = [['text' => "💳 Pagar con Bre-B directo (\$" . number_format($precio) . ")", 'callback_data' => 'breb_promo|' . $promoId . '|' . $precio]]; + $buttons[] = [['text' => "💰 Recargar saldo (\$" . number_format($falta) . " faltantes)", 'callback_data' => 'rec_init']]; $buttons[] = [['text' => '🔙 Promociones', 'callback_data' => 'promo_list']]; $this->sendWithKeyboard( $chatId, - "*{$promo->nombre}*\n\$" . number_format($precio) . "\n\nSaldo actual: \$" . number_format($saldo) . " — te faltan *\$" . number_format($falta) . "*\n\nRecarga para continuar:", + "*{$promo->nombre}*\n\$" . number_format($precio) . "\n\nSaldo actual: \$" . number_format($saldo) . " — te faltan *\$" . number_format($falta) . "*\n\n¿Cómo quieres pagar?", $buttons ); } @@ -1074,14 +1123,19 @@ class TelegramBotService $saldo->update(['valor' => $nuevoSaldo]); }); - $text = "✅ *¡Promo activada!*\n\n"; - $text .= "🎬 *{$promo->nombre}*\n"; - $text .= "📧 Email: `{$cuenta->correo}`\n"; - $text .= "🔑 Password: `{$cuenta->password}`\n"; - $text .= "📅 Vence: " . now()->addDays($dias)->format('d/m/Y') . "\n"; - $text .= "\nSaldo restante: *\$" . number_format($nuevoSaldo) . "*"; + $this->send($chatId, + "🎉 *¡Muchas gracias por tu compra!*\n\n" . + "Disfruta tu servicio. Si tienes algún inconveniente no dudes en escribirnos, estamos aquí para ayudarte. 😊" + ); - $this->send($chatId, $text); + $code = "```\n"; + $code .= "Promoción: {$promo->nombre}\n"; + $code .= "Email: {$cuenta->correo}\n"; + $code .= "Password: {$cuenta->password}\n"; + $code .= "Vence: " . now()->addDays($dias)->format('d/m/Y') . "\n"; + $code .= "Pagado: \$" . number_format($precio) . "\n"; + $code .= "```"; + $this->send($chatId, $code); $this->showMainMenu($chatId); } diff --git a/resources/views/livewire/chat/public-chat.blade.php b/resources/views/livewire/chat/public-chat.blade.php index f39dc34..756b2f2 100755 --- a/resources/views/livewire/chat/public-chat.blade.php +++ b/resources/views/livewire/chat/public-chat.blade.php @@ -440,21 +440,62 @@ {{-- CREDENCIALES: tarjeta con usuario/contraseña --}} @elseif ($tipoUi === 'credenciales') -
-
+
+ {{-- Encabezado servicio --}} +

{{ $payload['servicio'] ?? 'Credenciales' }}

+ ✅ Compra exitosa
+ {{-- Mensaje de gracias --}} + @if (!empty($msg['contenido'])) +
+

{{ $msg['contenido'] }}

+
+ @endif + {{-- Campos con copiar individual --}}
@if (!empty($payload['email'])) -
-

Usuario / Email

-

{{ $payload['email'] }}

+
+
+

Usuario / Email

+ + + + +
+

{{ $payload['email'] }}

@endif @if (!empty($payload['password'])) -
-

Contraseña

-

{{ $payload['password'] }}

+
+
+

Contraseña

+ + + + +
+

{{ $payload['password'] }}

@endif @if (!empty($payload['perfil'])) @@ -464,9 +505,28 @@
@endif @if (!empty($payload['vence'])) -

Vence: {{ $payload['vence'] }}

+

📅 Vence: {{ $payload['vence'] }}

@endif
+ {{-- Botón Copiar todo --}} +
+ +
{{ $msg['created_at'] }}