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: [],