Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | 9x 9x 1x 1x 8x 8x 9x 9x 9x 9x 8x 2x 2x 6x 9x | import { WhatsAppAdapter } from "./adapters/whatsapp.adapter";
import type { CommunicationQueueMessage, WhatsAppContent } from "./types";
import { normalizeToE164 } from "@interioring/utils/validation/phone";
/**
* Send OTP via WhatsApp, routed through WhatsAppAdapter for safety guards.
*
* In non-prod environments, the adapter redirects non-allowlisted numbers
* to the configured override number (see env-config.ts).
* In local/dev + redirected, the adapter performs a dry run (no API call).
*
* Falls back to console logging if WHATSAPP_ACCESS_TOKEN is not configured.
*
* Returns delivery metadata so callers can log actualRecipient and provider.
*/
export async function sendWhatsAppOtp(
env: CloudflareBindings,
phoneNumber: string,
code: string,
): Promise<{ actualRecipient: string; provider: string }> {
const accessToken = env.WHATSAPP_ACCESS_TOKEN;
if (!accessToken) {
// DEV ONLY: Console fallback logs OTP code for local testing.
// In production, WHATSAPP_ACCESS_TOKEN must be set as a secret.
console.log(
`[WhatsApp OTP] Phone: ${phoneNumber} | Code: ${code} | Provider: console (no WhatsApp credentials)`,
);
return { actualRecipient: phoneNumber, provider: "console" };
}
// WhatsApp Cloud API expects digits-only with country code (e.g., "919876543210").
// Normalize first so any input format (E.164, formatted, raw) ends up consistent.
const e164 = normalizeToE164(phoneNumber);
const strippedPhone = e164 ? e164.slice(1) : phoneNumber.replace(/\D/g, "");
const content: WhatsAppContent = {
templateName: "interioring_auth_otp",
languageCode: "en",
components: [
{
type: "body",
parameters: [{ type: "text", text: code }],
},
{
type: "button",
sub_type: "url",
index: 0,
parameters: [{ type: "text", text: code }],
},
],
};
const message: CommunicationQueueMessage = {
logId: 0, // Not queued — no log entry yet
channel: "whatsapp",
recipient: strippedPhone,
eventType: "otp_verification",
content,
transactional: true,
};
const adapter = new WhatsAppAdapter();
const result = await adapter.send(message, env);
if (result.status === "failed") {
console.error(
`[WhatsApp OTP] Failed: ${result.errorMessage}`,
);
throw new Error(
`WhatsApp OTP delivery failed: ${result.errorMessage}`,
);
}
console.log(
`[WhatsApp OTP] Sent to ${result.actualRecipient} (provider: ${result.provider}${result.externalId ? `, message_id: ${result.externalId}` : ""})`,
);
return {
actualRecipient: result.actualRecipient,
provider: result.provider,
};
}
|