WhatsApp dejaba de usar su bot propio (árbol de menús sin login ni compras) y pasa al motor compartido, así los tres canales quedan iguales. - TelegramBotService recibe el canal por constructor; flujos, menús, Gemini, OCR, validación de pagos y datos bancarios salen de ChatConfig sin duplicar - WhatsApp no tiene teclados inline: las opciones se envían numeradas y la respuesta numérica se traduce al callback equivalente - estado en caché con prefijo por canal (Telegram conserva el suyo para no cerrar las sesiones abiertas) - descarga de comprobantes y audios por la Graph API de Meta - respuesta manual del asesor y aviso de vencimiento salen también por WhatsApp - la pantalla de WhatsApp queda sólo con credenciales; el resto se administra en Chat / Bot Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
91 lines
3.2 KiB
PHP
91 lines
3.2 KiB
PHP
<?php
|
|
|
|
namespace App\Jobs;
|
|
|
|
use App\Models\SolicitudRecarga;
|
|
use App\Services\PagoValidadorService;
|
|
use App\Services\TelegramBotService;
|
|
use Illuminate\Bus\Queueable;
|
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
use Illuminate\Foundation\Bus\Dispatchable;
|
|
use Illuminate\Queue\InteractsWithQueue;
|
|
use Illuminate\Queue\SerializesModels;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class ValidarPagoTelegramJob implements ShouldQueue
|
|
{
|
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
|
|
|
public int $tries = 1;
|
|
public int $timeout = 60;
|
|
|
|
private const MAX_INTENTOS = 9;
|
|
private const STATE_TTL = 315360000;
|
|
|
|
public function __construct(
|
|
private string $chatId,
|
|
private array $datosPago,
|
|
private int $intento = 1,
|
|
private string $canal = 'telegram'
|
|
) {}
|
|
|
|
public function handle(): void
|
|
{
|
|
// Telegram conserva su prefijo histórico de caché; los demás canales usan el suyo.
|
|
$cacheKey = $this->canal === 'telegram'
|
|
? "tgbot_state_{$this->chatId}"
|
|
: "{$this->canal}bot_state_{$this->chatId}";
|
|
$state = Cache::get($cacheKey, []);
|
|
$usuarioId = $state['user_id'] ?? null;
|
|
$bot = new TelegramBotService($this->canal);
|
|
|
|
if (! $usuarioId) {
|
|
return;
|
|
}
|
|
|
|
$solicitud = SolicitudRecarga::pendiente($usuarioId);
|
|
if (! $solicitud) {
|
|
Log::info("[ValidarPagoTelegramJob] chatId={$this->chatId}: solicitud ya no está pendiente, abortando.");
|
|
return;
|
|
}
|
|
|
|
$nombreUsuario = $state['data']['nombre_remitente'] ?? null;
|
|
|
|
$resultado = app(PagoValidadorService::class)->validar(
|
|
$this->datosPago, $usuarioId, $nombreUsuario, $this->canal
|
|
);
|
|
|
|
if ($resultado['estado'] === 'confirmado') {
|
|
$bot->autoAplicarRecarga($this->chatId, $state, $solicitud, $solicitud->monto);
|
|
return;
|
|
}
|
|
|
|
if ($resultado['estado'] === 'ya_usado') {
|
|
$bot->sendWithKeyboard($this->chatId,
|
|
"⛔ *Comprobante ya utilizado.*\nEste pago ya fue registrado anteriormente.",
|
|
[[['text' => '🔙 Menú principal', 'callback_data' => 'menu']]]
|
|
);
|
|
return;
|
|
}
|
|
|
|
// No encontrado — reintentar o agotar
|
|
if ($this->intento < self::MAX_INTENTOS) {
|
|
Log::info("[ValidarPagoTelegramJob] chatId={$this->chatId}: intento {$this->intento}/" . self::MAX_INTENTOS . " — reintentando en 20s");
|
|
self::dispatch($this->chatId, $this->datosPago, $this->intento + 1, $this->canal)
|
|
->delay(now()->addSeconds(20));
|
|
} else {
|
|
Log::info("[ValidarPagoTelegramJob] chatId={$this->chatId}: intentos agotados, mostrando botón manual");
|
|
$bot->sendWithKeyboard(
|
|
$this->chatId,
|
|
"⚠️ No pudimos confirmar tu pago automáticamente.\nSi ya realizaste la transferencia, presiona Reintentar.",
|
|
[
|
|
[['text' => '🔄 Reintentar', 'callback_data' => 'pay_retry']],
|
|
[['text' => '🔙 Menú principal', 'callback_data' => 'menu']],
|
|
]
|
|
);
|
|
}
|
|
}
|
|
|
|
}
|