/** * @param {{otpHash?: string|null, otpTimestamp?: number|null}} stored the container's pending code * @param {string} providedHash sha256 of what the customer entered * @param {number} now * @returns {{ok: boolean, failure: null|'missing'|'expired'|'mismatch'}} */ export function checkOtp(stored: { otpHash?: string | null; otpTimestamp?: number | null; }, providedHash: string, now: number): { ok: boolean; failure: null | "missing" | "expired" | "mismatch"; }; /** A destination for a log line: the last 4 digits of a phone, or the domain of an email. */ export function maskDestination(to: any): string; /** * A flow's code check (validateOTP): hashes what the customer entered (SHA-256, the hash the code senders store in * container.otpHash) and checks it with checkOtp. Accepted: the pending code is cleared and container.otpVerified / * otpVerifiedAt are set. Expired: the code is cleared (it can never be accepted again). Every refusal leaves its reason * in container.otpFailure ('missing' | 'expired' | 'mismatch'). Uses the platform's Web Crypto, so it needs no host. */ export function validateOTP(otp: any, container: any): Promise; /** * Is this one-time code accepted — and if not, WHY? (validateOTP) * * validateOTP returns a boolean, which every flow condition depends on. Until 2026-10-05 the reason * a code was refused was lost: an EXPIRED code (which validateOTP also clears) and a wrong one * both read as "That code didn't match. Let's try again." — and the flow asked again for a code * that could no longer exist. A WhatsApp customer looped for 19 minutes on * exactly that (2026-10-05). The reason now rides on the container as `otpFailure`, so the * flow can send a fresh code for 'expired' / 'missing' and re-ask only on 'mismatch'. * * Pure, no imports. Tested by scripts/tests/otp-check.mjs in CI. */ export const OTP_EXPIRY_MS: number; //# sourceMappingURL=otp-check.d.ts.map