This guide covers automating transactional WhatsApp alerts, including new invoice dispatches, payment confirmations, and courier tracking updates.
1. Mobile Preview of the Invoice Message
How a transactional invoice update appears with a direct payment action button:
CODE
1┌──────────────────────────────────────────────┐
2│ 🛍️ PFN Store: Order Invoice │
3│ │
4│ Hello *Budi Setiawan*, │
5│ Your order *#INV-2026-0891* has been placed! │
6│ │
7│ • Total Amount: *$120.00* │
8│ • Due Date : August 24, 2026, 23:59 UTC │
9│ │
10│ Please complete payment using the link below:│
11│ ──────────────────────────────────────────── │
12│ [ 💳 Pay Now ] │
13│ 10:15 ✓✓ │
14└──────────────────────────────────────────────┘
2. Template Setup (1 Minute)
- Navigate to Console > WhatsApp > Templates (
/console/whatsapp/templates). - Create a new template:
-
Template Name:
order_invoice_update -
Category:
UTILITY -
Language: English (
en) -
Body Content:
CODE1Hello *{{1}}*,2Your order *#{{2}}* has been placed!34• Total Amount: *{{3}}*5• Due Date : {{4}}67Please complete payment using the link below: -
Buttons: URL Button
https://pfnapp.id/pay/{{1}}
-

3. Implementation Code
Send an HTTP POST request to /api/whatsapp/messages carrying dynamic parameter values:
Node.js / TypeScript (Next.js / Express)
TYPESCRIPT
1interface InvoiceNotificationPayload {
2 customerPhone: string
3 customerName: string
4 invoiceNumber: string
5 totalAmount: string
6 dueDate: string
7 invoiceSlug: string
8}
9
10export async function sendInvoiceNotification(
11 data: InvoiceNotificationPayload
12) {
13 const response = await fetch("https://pfnapp.id/api/whatsapp/messages", {
14 method: "POST",
15 headers: {
16 Authorization: `Bearer ${process.env.PFN_WHATSAPP_API_KEY}`,
17 "Content-Type": "application/json",
18 },
19 body: JSON.stringify({
20 to: data.customerPhone,
21 type: "template",
22 template: {
23 name: "order_invoice_update",
24 language: { code: "en" },
25 components: [
26 {
27 type: "body",
28 parameters: [
29 { type: "text", text: data.customerName }, // {{1}}
30 { type: "text", text: data.invoiceNumber }, // {{2}}
31 { type: "text", text: data.totalAmount }, // {{3}}
32 { type: "text", text: data.dueDate }, // {{4}}
33 ],
34 },
35 {
36 type: "button",
37 sub_type: "url",
38 index: "0",
39 parameters: [
40 { type: "text", text: data.invoiceSlug }, // Appended to CTA URL
41 ],
42 },
43 ],
44 },
45 }),
46 })
47
48 return await response.json()
49}
PHP / Laravel (Payment Event Listener)
PHP
1namespace App\Listeners;
2
3use App\Events\OrderCreated;
4use Illuminate\Support\Facades\Http;
5
6class SendWhatsAppInvoiceNotification
7{
8 public function handle(OrderCreated $event): void
9 {
10 $order = $event->order;
11
12 Http::withToken(config('services.pfn.whatsapp_key'))
13 ->post('https://pfnapp.id/api/whatsapp/messages', [
14 'to' => $order->customer_phone,
15 'type' => 'template',
16 'template' => [
17 'name' => 'order_invoice_update',
18 'language' => ['code' => 'en'],
19 'components' => [
20 [
21 'type' => 'body',
22 'parameters' => [
23 ['type' => 'text', 'text' => $order->customer_name],
24 ['type' => 'text', 'text' => $order->invoice_number],
25 ['type' => 'text', 'text' => '$' . number_format($order->total_amount, 2)],
26 ['type' => 'text', 'text' => $order->due_date_formatted],
27 ],
28 ],
29 [
30 'type' => 'button',
31 'sub_type' => 'url',
32 'index' => '0',
33 'parameters' => [
34 ['type' => 'text', 'text' => $order->id],
35 ],
36 ],
37 ],
38 ],
39 ]);
40 }
41}
Python (Celery / Background Worker)
PYTHON
1import os
2import requests
3
4def dispatch_invoice_whatsapp(phone: str, name: str, inv_no: str, total: str, due: str, inv_id: str):
5 url = "https://pfnapp.id/api/whatsapp/messages"
6 payload = {
7 "to": phone,
8 "type": "template",
9 "template": {
10 "name": "order_invoice_update",
11 "language": {"code": "en"},
12 "components": [
13 {
14 "type": "body",
15 "parameters": [
16 {"type": "text", "text": name},
17 {"type": "text", "text": inv_no},
18 {"type": "text", "text": total},
19 {"type": "text", "text": due},
20 ],
21 },
22 {
23 "type": "button",
24 "sub_type": "url",
25 "index": "0",
26 "parameters": [{"type": "text", "text": inv_id}],
27 },
28 ],
29 },
30 }
31
32 headers = {
33 "Authorization": f"Bearer {os.getenv('PFN_WHATSAPP_API_KEY')}",
34 "Content-Type": "application/json",
35 }
36
37 res = requests.post(url, json=payload, headers=headers)
38 return res.json()
4. Operational Tips
- Parameter Count Matching: If a template contains 4 placeholders
{{1}}to{{4}}, exactly 4 items must be present inparameters. - Special Characters: Avoid inserting unescaped tabs or multiple line breaks inside a single string parameter.