Implement useLocale composable and shared translation dictionaries Translate public pages, booking flow, and receipt views Store booking locale to send localized WhatsApp notifications
123 lines
3.6 KiB
TypeScript
123 lines
3.6 KiB
TypeScript
import type { H3Event } from 'h3'
|
|
|
|
import type { PublicBooking, WhatsAppDeliveryResult } from '~~/shared/booking'
|
|
|
|
import {
|
|
formatBookingCurrency
|
|
} 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 receiptUrl = buildAppUrl(event, `/receipt/${booking.receiptToken}`)
|
|
|
|
if (booking.locale === 'zh') {
|
|
return [
|
|
booking.event.title,
|
|
'',
|
|
`${booking.customerName} 您好,您的票券收据已确认。`,
|
|
'',
|
|
`收据:${receiptUrl}`,
|
|
`座位:${booking.seatCount}`,
|
|
`票券类别:${booking.ticketLabel || booking.ticketType.toUpperCase()}`,
|
|
`总价:${formatBookingCurrency(booking.totalPrice, booking.locale)}`,
|
|
`日期:${booking.event.dateLabel}`,
|
|
`时间:${booking.event.timeLabel}`,
|
|
`地点:${booking.event.venue}`,
|
|
'',
|
|
'请在活动当天出示收据中的二维码。'
|
|
].join('\n')
|
|
}
|
|
|
|
return [
|
|
booking.event.title,
|
|
'',
|
|
`Hi ${booking.customerName}, your ticket receipt has been confirmed.`,
|
|
'',
|
|
`Receipt: ${receiptUrl}`,
|
|
`Seats: ${booking.seatCount}`,
|
|
`Ticket Category: ${booking.ticketLabel || booking.ticketType.toUpperCase()}`,
|
|
`Total Price: ${formatBookingCurrency(booking.totalPrice, booking.locale)}`,
|
|
`Date: ${booking.event.dateLabel}`,
|
|
`Time: ${booking.event.timeLabel}`,
|
|
`Venue: ${booking.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.'
|
|
}
|
|
}
|
|
}
|