/** * Meta WhatsApp Business Cloud API Provider * * Direct integration with Meta's Graph API for WhatsApp Business. * Docs: https://developers.facebook.com/docs/whatsapp/cloud-api */ import type { WhatsAppProvider, WhatsAppResult } from './index' const GRAPH_API_VERSION = 'v18.0' const BASE_URL = `https://graph.facebook.com/${GRAPH_API_VERSION}` export class MetaWhatsAppProvider implements WhatsAppProvider { constructor( private phoneNumberId: string, private accessToken: string ) {} /** * Send a text message */ async sendText(to: string, body: string): Promise { try { const response = await fetch( `${BASE_URL}/${this.phoneNumberId}/messages`, { method: 'POST', headers: { Authorization: `Bearer ${this.accessToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ messaging_product: 'whatsapp', recipient_type: 'individual', to: formatPhoneNumber(to), type: 'text', text: { body }, }), } ) const data = await response.json() if (!response.ok) { return { success: false, error: data.error?.message || `API error: ${response.status}`, } } return { success: true, messageId: data.messages?.[0]?.id, } } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error', } } } /** * Send a template message * * Templates must be pre-approved by Meta. * Params are passed as components to the template. */ async sendTemplate( to: string, template: string, params: Record ): Promise { try { // Build template components from params const components = buildTemplateComponents(params) const response = await fetch( `${BASE_URL}/${this.phoneNumberId}/messages`, { method: 'POST', headers: { Authorization: `Bearer ${this.accessToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ messaging_product: 'whatsapp', recipient_type: 'individual', to: formatPhoneNumber(to), type: 'template', template: { name: template, language: { code: 'en' }, components, }, }), } ) const data = await response.json() if (!response.ok) { return { success: false, error: data.error?.message || `API error: ${response.status}`, } } return { success: true, messageId: data.messages?.[0]?.id, } } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error', } } } /** * Test the connection to Meta API */ async testConnection(): Promise<{ success: boolean; error?: string }> { try { // Try to get phone number info to verify credentials const response = await fetch( `${BASE_URL}/${this.phoneNumberId}`, { headers: { Authorization: `Bearer ${this.accessToken}`, }, } ) if (!response.ok) { const data = await response.json() return { success: false, error: data.error?.message || `API error: ${response.status}`, } } return { success: true } } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Connection failed', } } } } /** * Format phone number for WhatsApp API * Removes + prefix and any non-digit characters */ function formatPhoneNumber(phone: string): string { return phone.replace(/[^\d]/g, '') } /** * Build template components from params * Converts { param1: "value1", param2: "value2" } to WhatsApp component format */ function buildTemplateComponents( params: Record ): Array<{ type: 'body' parameters: Array<{ type: 'text'; text: string }> }> { const paramValues = Object.values(params) if (paramValues.length === 0) { return [] } return [ { type: 'body', parameters: paramValues.map((value) => ({ type: 'text', text: value, })), }, ] }