From 51e4647af111b9ef23b271352beb99460cf810ad Mon Sep 17 00:00:00 2001 From: Pachedev Date: Mon, 31 Aug 2026 15:37:16 -0600 Subject: [PATCH] fix(weex): corrige falso negativo cuando el ServiceLayer falla MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Weex responde 200 con un sobre { obj, error: { code, message, retry } } y su propio portal trata cualquier code distinto de 0 como fallo del servicio, no como resultado vacío. El código leía obj.dnActiveByCurpRfc.length === 0 sin mirar error.code, así que una consulta que nunca llegó a ocurrir se le mostraba al usuario como "no tienes líneas registradas a tu nombre". Es el espejo del falso positivo que corrigió 6429f02 en Sorcel, sólo que en la dirección contraria. Además obj.dnActiveByCurpRfc se accedía sin guardas, y en esos mismos errores el sobre viene sin obj: lanza TypeError y tumba el provider entero. Ahora se comprueba error.code antes de interpretar el arreglo, se lee obj con optional chaining para que un sobre inesperado no lance, y 403/429 pasan a temporaryUnavailable, que es lo que ya usan otros providers para distinguir un bloqueo de un fallo real del operador. Verificado ejecutando el código anterior y el nuevo sobre las mismas respuestas. Con líneas y sin líneas se comportan igual, así que no hay regresión en los caminos normales. Con error de negocio y el arreglo vacío el anterior decía "sin registro" y el nuevo marca no disponible; con el sobre sin obj el anterior lanzaba TypeError. --- src/lib/providers/weex.ts | 53 ++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/src/lib/providers/weex.ts b/src/lib/providers/weex.ts index 7b6cdd0..d18b90d 100644 --- a/src/lib/providers/weex.ts +++ b/src/lib/providers/weex.ts @@ -2,12 +2,16 @@ import { PROVIDER_TIMEOUT_MS } from "@/lib/data/content"; import { stripCURPs } from "@/lib/sanitize"; import type { LineResult } from "@/types"; -export async function loookupCURPINWeeex(curp: string): Promise { - const validationBody = { - documentType: 1, - searchData: curp, - }; +// documentType, as used by weex.mx/consultalineas.html: 1 = CURP, 2 = passport, +// 3 = RFC. +const DOCUMENT_TYPE_CURP = 1; + +type WeexResponse = { + obj?: { dnActiveByCurpRfc?: Array<{ msisdn?: string; provider?: string }> }; + error?: { code?: number; message?: string; retry?: number }; +}; +export async function loookupCURPINWeeex(curp: string): Promise { const validationResponse = await fetch( "https://app.weex.mx/ServiceLayer/Legislacion?ex=getDnActiveLines", { @@ -16,7 +20,10 @@ export async function loookupCURPINWeeex(curp: string): Promise { headers: { "Content-Type": "application/json", }, - body: JSON.stringify(validationBody), + body: JSON.stringify({ + documentType: DOCUMENT_TYPE_CURP, + searchData: curp, + }), }, ); @@ -26,16 +33,44 @@ export async function loookupCURPINWeeex(curp: string): Promise { validationResponse.statusText, ); + // A WAF block or a rate limit is not a lookup that failed, it's a lookup + // that never ran — the user should be told to check the portal, not that + // Weex is broken. + const blocked = + validationResponse.status === 403 || validationResponse.status === 429; + return { company: "Weex", lines: [], - error: "Failed to validate CURP with Weex", + temporaryUnavailable: blocked, + error: blocked ? undefined : "Failed to validate CURP with Weex", }; } - const validationData = await validationResponse.json(); + const validationData = (await validationResponse + .json() + .catch(() => null)) as WeexResponse | null; + + // Weex answers 200 with an envelope: { obj, error: { code, message, retry } }. + // Their own portal treats any non-zero error.code as a service failure rather + // than an empty result, and the array comes back empty (or absent) in that + // case. Reading that empty array as "no lines" tells the user nothing is + // registered to their CURP when the lookup never actually happened. + if ( + validationData?.error?.code !== undefined && + validationData.error.code !== 0 + ) { + console.error( + `[weex] business error ${validationData.error.code}: ${validationData.error.message ?? ""}`, + ); + return { company: "Weex", lines: [], temporaryUnavailable: true }; + } + + // Optional chaining on purpose: an unexpected envelope must not throw and + // take the whole provider down with it. + const found = validationData?.obj?.dnActiveByCurpRfc ?? []; - if (validationData.obj.dnActiveByCurpRfc.length === 0) { + if (found.length === 0) { return { company: "Weex", lines: [],