Add WhatsApp API integration for automated receipt delivery Enforce country codes for all phone number inputs (defaults to +60)
112 lines
3.1 KiB
TypeScript
112 lines
3.1 KiB
TypeScript
import type { H3Event } from 'h3'
|
|
|
|
import type { PublicBooking, WhatsAppDeliveryResult } from '~~/shared/booking'
|
|
|
|
import {
|
|
DINNER_EVENT_DATE_LABEL,
|
|
DINNER_EVENT_TIME_LABEL,
|
|
DINNER_EVENT_TITLE,
|
|
DINNER_EVENT_VENUE,
|
|
formatBookingCurrency,
|
|
getTicketCatalogItem
|
|
} from '~~/shared/booking'
|
|
import { normalizePhoneNumber } from '~~/shared/auth'
|
|
|
|
import { buildAppUrl } from './app-url'
|
|
|
|
type WhatsAppMessagesResponse = {
|
|
messages?: Array<{
|
|
id?: string
|
|
}>
|
|
}
|
|
|
|
export function toWhatsAppPhoneNumber(phoneNumber: string) {
|
|
return normalizePhoneNumber(phoneNumber).replace(/\D/g, '')
|
|
}
|
|
|
|
export function buildWhatsAppDeepLink(phoneNumber: string, message: string) {
|
|
return `https://wa.me/${toWhatsAppPhoneNumber(phoneNumber)}?text=${encodeURIComponent(message)}`
|
|
}
|
|
|
|
export function buildBookingTicketReceiptMessage(event: H3Event, booking: PublicBooking) {
|
|
const ticket = getTicketCatalogItem(booking.ticketType)
|
|
const ticketLabel = ticket?.label || booking.ticketType.toUpperCase()
|
|
const receiptUrl = buildAppUrl(event, `/receipt/${booking.receiptToken}`)
|
|
|
|
return [
|
|
DINNER_EVENT_TITLE,
|
|
'',
|
|
`Hi ${booking.customerName}, your ticket receipt has been confirmed.`,
|
|
'',
|
|
`Receipt: ${receiptUrl}`,
|
|
`Seats: ${booking.seatCount}`,
|
|
`Ticket Category: ${ticketLabel}`,
|
|
`Total Price: ${formatBookingCurrency(booking.totalPrice)}`,
|
|
`Date: ${DINNER_EVENT_DATE_LABEL}`,
|
|
`Time: ${DINNER_EVENT_TIME_LABEL}`,
|
|
`Venue: ${DINNER_EVENT_VENUE}`,
|
|
'',
|
|
'Please present the QR code from the receipt at the event.'
|
|
].join('\n')
|
|
}
|
|
|
|
export async function sendBookingTicketReceiptViaWhatsApp(
|
|
event: H3Event,
|
|
booking: PublicBooking
|
|
): Promise<WhatsAppDeliveryResult> {
|
|
const recipientPhone = normalizePhoneNumber(booking.customerPhone)
|
|
const to = toWhatsAppPhoneNumber(recipientPhone)
|
|
const config = useRuntimeConfig()
|
|
const accessToken = String(config.whatsappAccessToken || '')
|
|
const phoneNumberId = String(config.whatsappPhoneNumberId || '')
|
|
const apiVersion = String(config.whatsappApiVersion || 'v23.0')
|
|
|
|
if (!accessToken || !phoneNumberId) {
|
|
return {
|
|
sent: false,
|
|
skipped: true,
|
|
recipientPhone,
|
|
apiRecipientPhone: to,
|
|
error: 'WhatsApp API credentials are not configured.'
|
|
}
|
|
}
|
|
|
|
try {
|
|
const response = await $fetch<WhatsAppMessagesResponse>(
|
|
`https://graph.facebook.com/${apiVersion}/${phoneNumberId}/messages`,
|
|
{
|
|
method: 'POST',
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`
|
|
},
|
|
body: {
|
|
messaging_product: 'whatsapp',
|
|
recipient_type: 'individual',
|
|
to,
|
|
type: 'text',
|
|
text: {
|
|
preview_url: true,
|
|
body: buildBookingTicketReceiptMessage(event, booking)
|
|
}
|
|
}
|
|
}
|
|
)
|
|
|
|
return {
|
|
sent: true,
|
|
skipped: false,
|
|
recipientPhone,
|
|
apiRecipientPhone: to,
|
|
messageId: response.messages?.[0]?.id
|
|
}
|
|
} catch (error: any) {
|
|
return {
|
|
sent: false,
|
|
skipped: false,
|
|
recipientPhone,
|
|
apiRecipientPhone: to,
|
|
error: error?.data?.error?.message || error?.message || 'WhatsApp API request failed.'
|
|
}
|
|
}
|
|
}
|