All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
346 lines
8.1 KiB
Markdown
346 lines
8.1 KiB
Markdown
# WhatsApp Cloud API Debugging - Complete Implementation
|
|
|
|
## Problem Summary
|
|
|
|
WhatsApp Cloud API returns HTTP 200 with a wamid, but recipients don't receive messages.
|
|
|
|
**Root Cause:** The initial API response (HTTP 200) only means Meta **accepted** the message, NOT that it was delivered. The actual delivery status comes from **webhook callbacks** which were not implemented.
|
|
|
|
---
|
|
|
|
## Solution Implemented
|
|
|
|
### 1. ✅ Message Status Tracking
|
|
|
|
**File:** `models.py`
|
|
|
|
Added `WhatsAppMessage` model to track:
|
|
- `wamid` - WhatsApp message ID from Meta
|
|
- `status` - sent, delivered, read, failed
|
|
- `event_id`, `guest_id` - Context for tracking
|
|
- `error_code`, `error_title`, `error_message` - Failure details
|
|
- Timestamps: `sent_at`, `delivered_at`, `read_at`, `failed_at`
|
|
|
|
### 2. ✅ Webhook Endpoint
|
|
|
|
**File:** `main.py`
|
|
|
|
Added two webhook endpoints:
|
|
|
|
```python
|
|
GET /whatsapp/webhook # Webhook verification
|
|
POST /whatsapp/webhook # Status updates from Meta
|
|
```
|
|
|
|
**What it does:**
|
|
- Receives delivery status updates from Meta
|
|
- Updates message status in database
|
|
- Logs all webhook payloads for debugging
|
|
- Handles: sent, delivered, read, failed statuses
|
|
- Captures error details when messages fail
|
|
|
|
### 3. ✅ Enhanced WhatsApp Service
|
|
|
|
**File:** `whatsapp.py`
|
|
|
|
**Changes:**
|
|
1. Added `event_id` and `guest_id` parameters to `send_by_template_key()`
|
|
2. Saves messages to database after sending
|
|
3. Supports dynamic image headers via `header_handle_key`
|
|
4. Enhanced logging with full payload dumps
|
|
|
|
### 4. ✅ Fixed hina_invitation Template
|
|
|
|
**File:** `whatsapp_templates.py`
|
|
|
|
**Configuration:**
|
|
```python
|
|
{
|
|
"meta_name": "hina_invitation",
|
|
"language_code": "he",
|
|
"header_type": "IMAGE",
|
|
"header_handle_key": "invitation_image_url", # Dynamic image from params
|
|
"body_params": ["contact_name"],
|
|
"button_type": "URL",
|
|
"button_url": "https://invy.dvirlabs.com/guest/{{1}}",
|
|
"button_param_key": "event_id",
|
|
}
|
|
```
|
|
|
|
### 5. ✅ Comprehensive Test Script
|
|
|
|
**File:** `test_whatsapp_debug.py`
|
|
|
|
**Features:**
|
|
- Tests phone normalization
|
|
- Validates template configuration
|
|
- Sends test message with full logging
|
|
- Shows webhook setup instructions
|
|
- Provides clear debugging output
|
|
|
|
### 6. ✅ Database Migration
|
|
|
|
**File:** `add_whatsapp_messages_table.py`
|
|
|
|
Creates the `whatsapp_messages` table.
|
|
|
|
---
|
|
|
|
## Setup Instructions
|
|
|
|
### Step 1: Run Database Migration
|
|
|
|
```bash
|
|
cd backend
|
|
python add_whatsapp_messages_table.py
|
|
```
|
|
|
|
### Step 2: Configure Webhook in Meta Business Manager
|
|
|
|
1. Go to **Meta Business Manager** → **WhatsApp** → **Configuration** → **Webhook**
|
|
|
|
2. **Callback URL:**
|
|
```
|
|
https://invy.dvirlabs.com/whatsapp/webhook
|
|
```
|
|
|
|
3. **Verify Token:**
|
|
- Set this in `.env` as `WHATSAPP_VERIFY_TOKEN=your_secret_token_here`
|
|
- Use the same value in Meta's webhook configuration
|
|
|
|
4. Click **Verify and Save**
|
|
|
|
5. **Subscribe to Fields:**
|
|
- ✅ messages
|
|
|
|
### Step 3: Update Environment Variables
|
|
|
|
Add to `.env`:
|
|
```bash
|
|
# Existing WhatsApp credentials
|
|
WHATSAPP_ACCESS_TOKEN=your_meta_access_token
|
|
WHATSAPP_PHONE_NUMBER_ID=your_phone_number_id
|
|
WHATSAPP_API_VERSION=v20.0
|
|
|
|
# NEW: Webhook verification token
|
|
WHATSAPP_VERIFY_TOKEN=your_webhook_secret_token_here
|
|
```
|
|
|
|
### Step 4: Test the Integration
|
|
|
|
```bash
|
|
cd backend
|
|
python test_whatsapp_debug.py
|
|
```
|
|
|
|
**What to check:**
|
|
1. Phone normalization: ✓ All pass
|
|
2. Template configuration: ✓ All correct
|
|
3. Message send: ✓ Returns wamid
|
|
4. Webhook configured: ✓ Verify token set
|
|
|
|
### Step 5: Send Test Message
|
|
|
|
The script will prompt you to send a test message to: `0504370045`
|
|
|
|
**Expected Result:**
|
|
```json
|
|
{
|
|
"message_id": "wamid.HBgLOTcyNTA0MzcwMDQ1FQIAERgSQzI4QkE1RkQ5MzFFNTY1RTEwAA==",
|
|
"status": "sent",
|
|
"to": "+972504370045",
|
|
"template": "hina_invitation"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## How to Debug Delivery Issues
|
|
|
|
### Check 1: Backend Logs
|
|
|
|
Watch for webhook callbacks:
|
|
```bash
|
|
tail -f backend.log | grep "WhatsApp Webhook"
|
|
```
|
|
|
|
**Expected logs:**
|
|
```
|
|
[WhatsApp Webhook] Status update: wamid=xxx, status=delivered
|
|
[WhatsApp Webhook] ✓ Updated message xxx to status: delivered
|
|
```
|
|
|
|
### Check 2: Database Status
|
|
|
|
```sql
|
|
SELECT
|
|
wamid,
|
|
to_phone,
|
|
status,
|
|
template_name,
|
|
sent_at,
|
|
delivered_at,
|
|
failed_at,
|
|
error_code,
|
|
error_message
|
|
FROM whatsapp_messages
|
|
ORDER BY sent_at DESC
|
|
LIMIT 10;
|
|
```
|
|
|
|
**Status meanings:**
|
|
- `sent` - Meta accepted the message
|
|
- `delivered` - Recipient's phone received it
|
|
- `read` - Recipient opened the message
|
|
- `failed` - Delivery failed (check error_code/error_message)
|
|
|
|
### Check 3: Common Failure Reasons
|
|
|
|
**If status stays "sent" and never reaches "delivered":**
|
|
|
|
1. **Phone number not on WhatsApp**
|
|
- Error code: 131026
|
|
- Solution: Verify the recipient has WhatsApp
|
|
|
|
2. **Template parameters mismatch**
|
|
- Error code: 132000
|
|
- Solution: Check template in Meta matches your params
|
|
|
|
3. **Image URL not accessible**
|
|
- Error code: 132015
|
|
- Solution: Ensure image URL is publicly accessible
|
|
|
|
4. **Phone number blocked your business**
|
|
- Error code: 131031
|
|
- Solution: Recipient must unblock your business number
|
|
|
|
5. **Rate limiting**
|
|
- Error code: 130429
|
|
- Solution: Slow down sending rate
|
|
|
|
### Check 4: Webhook Delivery Test
|
|
|
|
Test webhook is receiving callbacks:
|
|
```bash
|
|
curl -X GET "https://invy.dvirlabs.com/whatsapp/webhook?hub.mode=subscribe&hub.verify_token=YOUR_TOKEN&hub.challenge=test123"
|
|
```
|
|
|
|
Expected: `test123` (echo challenge)
|
|
|
|
---
|
|
|
|
## Example: Sending hina_invitation
|
|
|
|
```python
|
|
from whatsapp import get_whatsapp_service
|
|
|
|
service = get_whatsapp_service(db)
|
|
|
|
params = {
|
|
"contact_name": "איילה חורב",
|
|
"event_id": "f3122a7d-1d7c-4cc1-955d-1c6b7358bd25",
|
|
"invitation_image_url": "https://api-invy.dvirlabs.com/uploads/1d32b5fbab0f494cae443b4188a83ca3.jpg",
|
|
}
|
|
|
|
result = await service.send_by_template_key(
|
|
template_key="hina_invitation",
|
|
to_phone="0504370045",
|
|
params=params,
|
|
event_id="f3122a7d-1d7c-4cc1-955d-1c6b7358bd25",
|
|
guest_id="guest-uuid-here",
|
|
)
|
|
|
|
print(f"Message ID: {result['message_id']}")
|
|
```
|
|
|
|
**Generated payload:**
|
|
```json
|
|
{
|
|
"messaging_product": "whatsapp",
|
|
"to": "+972504370045",
|
|
"type": "template",
|
|
"template": {
|
|
"name": "hina_invitation",
|
|
"language": { "code": "he" },
|
|
"components": [
|
|
{
|
|
"type": "header",
|
|
"parameters": [{
|
|
"type": "image",
|
|
"image": {
|
|
"link": "https://api-invy.dvirlabs.com/uploads/1d32b5fbab0f494cae443b4188a83ca3.jpg"
|
|
}
|
|
}]
|
|
},
|
|
{
|
|
"type": "body",
|
|
"parameters": [
|
|
{ "type": "text", "text": "איילה חורב" }
|
|
]
|
|
},
|
|
{
|
|
"type": "button",
|
|
"sub_type": "url",
|
|
"index": "0",
|
|
"parameters": [
|
|
{ "type": "text", "text": "f3122a7d-1d7c-4cc1-955d-1c6b7358bd25" }
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Phone Normalization
|
|
|
|
**Israeli numbers:**
|
|
- `0504370045` → `+972504370045` ✓
|
|
- `050-437-0045` → `+972504370045` ✓
|
|
- `+972504370045` → `+972504370045` ✓
|
|
- `972504370045` → `+972504370045` ✓
|
|
|
|
---
|
|
|
|
## Files Modified
|
|
|
|
1. ✅ `models.py` - Added WhatsAppMessage model
|
|
2. ✅ `main.py` - Added webhook endpoints, updated imports
|
|
3. ✅ `whatsapp.py` - Added event/guest tracking, dynamic image support
|
|
4. ✅ `whatsapp_templates.py` - Fixed hina_invitation template
|
|
|
|
## Files Created
|
|
|
|
1. ✅ `test_whatsapp_debug.py` - Comprehensive debugging script
|
|
2. ✅ `add_whatsapp_messages_table.py` - Database migration
|
|
3. ✅ `WHATSAPP_DEBUGGING.md` - This documentation
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
1. **Run migration:** `python add_whatsapp_messages_table.py`
|
|
2. **Configure webhook in Meta Business Manager**
|
|
3. **Set WHATSAPP_VERIFY_TOKEN in .env**
|
|
4. **Run test script:** `python test_whatsapp_debug.py`
|
|
5. **Monitor webhook logs for delivery status**
|
|
6. **Check database for message status updates**
|
|
|
|
---
|
|
|
|
## Critical Understanding
|
|
|
|
**HTTP 200 ≠ Message Delivered**
|
|
|
|
When Meta returns HTTP 200, it means:
|
|
- ✓ Request was valid
|
|
- ✓ Message was queued for delivery
|
|
- ✗ **NOT** that the recipient received it
|
|
|
|
**Actual delivery confirmation comes from webhook callbacks.**
|
|
|
|
Without webhooks, you'll never know if messages were delivered, read, or failed.
|
|
|
|
**This is why webhook implementation is CRITICAL.**
|