fix(pago): diagnose and harden IMAP email validation
- PagoValidadorService: add detailed Log::info at every step (emails count, body preview, parser result, which filter fails) so failures are visible in laravel.log - CorreoImapService::cleanBody: detect and decode base64-encoded BODY[1] before QP decoding — critical fix when Bancolombia sends HTML part as base64 - BancolombiaParser: make esBancolombia() broader (includes 'valor','$','abono' etc.), add extraerPrimero() helper, add multiple regex fallbacks for remitente/fecha/hora/valor to handle different Bancolombia email HTML formats beyond the SMS text pattern Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
1fd9a92afa
commit
595c75837f
Regular → Executable
+76
-22
@@ -39,16 +39,43 @@ class BancolombiaParser
|
||||
}
|
||||
|
||||
$result = [
|
||||
'banco' => 'Bancolombia',
|
||||
'tipo' => self::detectarTipo($text),
|
||||
'destinatario' => self::extraer($text, '/Bancolombia[:\s]+([A-ZÁÉÍÓÚÑÜ][A-Za-záéíóúñü\s]+?),\s+recibiste/u'),
|
||||
'remitente' => self::extraer($text, '/transferencia\s+de\s+(.+?)\s+por\s+\$/iu'),
|
||||
'valor' => self::extraerValor($text),
|
||||
'cuenta' => self::extraer($text, '/cuenta\s+\*(\d+)/iu'),
|
||||
'llave' => self::extraer($text, '/llave\s+([@\w.\-]+)/iu'),
|
||||
'fecha' => self::extraer($text, '/el\s+(\d{1,2}\/\d{1,2}\/\d{2,4})/iu'),
|
||||
'hora' => self::extraer($text, '/a\s+las\s+(\d{1,2}:\d{2})/iu'),
|
||||
'texto_original' => $text,
|
||||
'banco' => 'Bancolombia',
|
||||
'tipo' => self::detectarTipo($text),
|
||||
'destinatario' => self::extraerPrimero($text, [
|
||||
'/Bancolombia[:\s]+([A-ZÁÉÍÓÚÑÜ][A-Za-záéíóúñü\s]+?),\s+recibiste/u',
|
||||
'/Hola[,\s]+([A-ZÁÉÍÓÚÑÜ][A-Za-záéíóúñü\s]+?)[,\.]/u',
|
||||
'/estimado[a]?\s+([A-ZÁÉÍÓÚÑÜ][A-Za-záéíóúñü\s]+?)[,\.]/iu',
|
||||
]),
|
||||
'remitente' => self::extraerPrimero($text, [
|
||||
'/transferencia\s+de\s+(.+?)\s+por\s+\$/iu',
|
||||
'/realiz[oó]\s+(?:un[a]?\s+)?(?:transferencia|pago)\s+de\s+\$[0-9,.]+\s+(?:a\s+tu\s+cuenta|para)\s+(.+?)[\.,]/iu',
|
||||
'/(?:pagador|remitente|enviado\s+por)[:\s]+([A-ZÁÉÍÓÚÑÜ][A-Za-záéíóúñü\s]+?)[\.,]/iu',
|
||||
'/de\s+([A-ZÁÉÍÓÚÑÜ][A-Za-záéíóúñü\s]{3,50}?)\s+(?:por\s+\$|a\s+tu)/iu',
|
||||
]),
|
||||
'valor' => self::extraerValor($text),
|
||||
'cuenta' => self::extraerPrimero($text, [
|
||||
'/cuenta\s+\*(\d+)/iu',
|
||||
'/cuenta\s+(?:de\s+ahorros|corriente)?\s*(?:No\.?\s*)?\*?(\d{4,})/iu',
|
||||
'/terminad[ao]\s+en\s+(\d{4})/iu',
|
||||
]),
|
||||
'llave' => self::extraerPrimero($text, [
|
||||
'/llave\s+([@\w.\-]+)/iu',
|
||||
'/llave\s+(?:de\s+pago\s+)?([@\w.\-]+)/iu',
|
||||
'/([@][a-z0-9._\-]{3,})/iu',
|
||||
]),
|
||||
'fecha' => self::extraerPrimero($text, [
|
||||
'/el\s+(\d{1,2}\/\d{1,2}\/\d{2,4})/iu',
|
||||
'/(\d{1,2}\/\d{1,2}\/\d{2,4})/u',
|
||||
'/(\d{1,2}\s+de\s+\w+\s+de\s+\d{4})/iu',
|
||||
'/fecha[:\s]+(\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4})/iu',
|
||||
]),
|
||||
'hora' => self::extraerPrimero($text, [
|
||||
'/a\s+las\s+(\d{1,2}:\d{2})/iu',
|
||||
'/hora[:\s]+(\d{1,2}:\d{2})/iu',
|
||||
'/(\d{1,2}:\d{2})\s*(?:a\.?m\.?|p\.?m\.?)/iu',
|
||||
'/(\d{2}:\d{2})(?::\d{2})?/u',
|
||||
]),
|
||||
'texto_original' => $text,
|
||||
];
|
||||
|
||||
return $result;
|
||||
@@ -60,13 +87,19 @@ class BancolombiaParser
|
||||
|
||||
private static function esBancolombia(string $text): bool
|
||||
{
|
||||
return stripos($text, 'bancolombia') !== false
|
||||
&& (
|
||||
stripos($text, 'transferencia') !== false
|
||||
|| stripos($text, 'pago') !== false
|
||||
|| stripos($text, 'recibiste') !== false
|
||||
|| stripos($text, 'enviaste') !== false
|
||||
);
|
||||
if (stripos($text, 'bancolombia') === false) {
|
||||
return false;
|
||||
}
|
||||
// Al menos uno de estos indica una notificación transaccional
|
||||
$indicadores = ['transferencia', 'pago', 'recibiste', 'enviaste', 'credito', 'crédito',
|
||||
'abono', 'consignacion', 'consignación', 'recibido', 'recibiste',
|
||||
'valor', 'monto', '$'];
|
||||
foreach ($indicadores as $ind) {
|
||||
if (stripos($text, $ind) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function detectarTipo(string $text): string
|
||||
@@ -94,16 +127,37 @@ class BancolombiaParser
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Prueba múltiples patrones y devuelve el primer match no-vacío.
|
||||
*/
|
||||
private static function extraerPrimero(string $text, array $patterns): string
|
||||
{
|
||||
foreach ($patterns as $pat) {
|
||||
$v = self::extraer($text, $pat);
|
||||
if ($v !== '') {
|
||||
return $v;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extrae el valor monetario como string limpio, ej: "1000.00"
|
||||
* y también como float.
|
||||
* Devuelve el string formateado con separadores originales.
|
||||
*/
|
||||
private static function extraerValor(string $text): string
|
||||
{
|
||||
// Captura números con puntos y comas: $1,000.00 | $1.000,00 | $500
|
||||
if (preg_match('/por\s+\$([0-9][0-9.,]*)/iu', $text, $m)) {
|
||||
return trim($m[1]);
|
||||
// Patrones en orden de especificidad
|
||||
$patterns = [
|
||||
'/por\s+\$\s*([0-9][0-9.,]*)/iu',
|
||||
'/valor[:\s]+\$?\s*([0-9][0-9.,]*)/iu',
|
||||
'/monto[:\s]+\$?\s*([0-9][0-9.,]*)/iu',
|
||||
'/recibiste[^$]*\$\s*([0-9][0-9.,]*)/iu',
|
||||
'/\$\s*([0-9][0-9.,]{2,})/u',
|
||||
];
|
||||
foreach ($patterns as $pat) {
|
||||
if (preg_match($pat, $text, $m)) {
|
||||
return trim($m[1]);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user