Files
bakery-ia/docs/whatsapp/shared-account-guide.md

751 lines
23 KiB
Markdown
Raw Permalink Normal View History

2025-11-18 07:17:17 +01:00
# WhatsApp Shared Account Model - Implementation Guide
## Overview
This guide documents the **Shared WhatsApp Business Account** implementation for the bakery-ia pilot program. This model simplifies WhatsApp setup by using a single master WhatsApp Business Account with phone numbers assigned to each bakery tenant.
---
## Architecture
### Shared Account Model
```
┌─────────────────────────────────────────────┐
│ Master WhatsApp Business Account (WABA) │
│ - Centrally managed by platform admin │
│ - Single set of credentials │
│ - Multiple phone numbers (up to 120) │
└─────────────────────────────────────────────┘
┌─────────────┼─────────────┐
│ │ │
Phone #1 Phone #2 Phone #3
Bakery A Bakery B Bakery C
```
### Key Benefits
**Zero configuration for bakery users** - No Meta navigation required
**5-minute setup** - Admin assigns phone number via UI
**Lower support burden** - Centralized management
**Predictable costs** - One WABA subscription
**Perfect for pilot** - Quick deployment for 10 bakeries
---
## User Experience
### For Bakery Owners (Non-Technical Users)
**Before (Manual Setup):**
- Navigate Meta Business Suite ❌
- Create WhatsApp Business Account ❌
- Create message templates ❌
- Get credentials (3 different IDs) ❌
- Copy/paste into settings ❌
- **Time:** 1-2 hours, high error rate
**After (Shared Account):**
- Toggle WhatsApp ON ✓
- See assigned phone number ✓
- **Time:** 30 seconds, zero configuration
### For Platform Admin
**Admin Workflow:**
1. Access WhatsApp Admin page (`/app/admin/whatsapp`)
2. View list of tenants
3. Select tenant
4. Assign phone number from dropdown
5. Done!
---
## Technical Implementation
### Backend Changes
#### 1. Tenant Settings Model
**File:** `services/tenant/app/models/tenant_settings.py`
**Changed:**
```python
# OLD (Per-Tenant Credentials)
notification_settings = {
"whatsapp_enabled": False,
"whatsapp_phone_number_id": "",
"whatsapp_access_token": "", # REMOVED
"whatsapp_business_account_id": "", # REMOVED
"whatsapp_api_version": "v18.0", # REMOVED
"whatsapp_default_language": "es"
}
# NEW (Shared Account)
notification_settings = {
"whatsapp_enabled": False,
"whatsapp_phone_number_id": "", # Phone # from shared account
"whatsapp_display_phone_number": "", # Display format "+34 612 345 678"
"whatsapp_default_language": "es"
}
```
#### 2. WhatsApp Business Service
**File:** `services/notification/app/services/whatsapp_business_service.py`
**Changed `_get_whatsapp_credentials()` method:**
```python
async def _get_whatsapp_credentials(self, tenant_id: str) -> Dict[str, str]:
"""
Uses global master account credentials with tenant-specific phone number
"""
# Always use global master account
access_token = self.global_access_token
business_account_id = self.global_business_account_id
phone_number_id = self.global_phone_number_id # Default
# Fetch tenant's assigned phone number
if self.tenant_client:
notification_settings = await self.tenant_client.get_notification_settings(tenant_id)
if notification_settings and notification_settings.get('whatsapp_enabled'):
tenant_phone_id = notification_settings.get('whatsapp_phone_number_id', '')
if tenant_phone_id:
phone_number_id = tenant_phone_id # Use tenant's phone
return {
'access_token': access_token,
'phone_number_id': phone_number_id,
'business_account_id': business_account_id
}
```
**Key Change:** Always uses global credentials, but selects the phone number based on tenant assignment.
#### 3. Phone Number Management API
**New File:** `services/tenant/app/api/whatsapp_admin.py`
**Endpoints:**
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/v1/admin/whatsapp/phone-numbers` | List available phone numbers from master WABA |
| GET | `/api/v1/admin/whatsapp/tenants` | List all tenants with WhatsApp status |
| POST | `/api/v1/admin/whatsapp/tenants/{id}/assign-phone` | Assign phone to tenant |
| DELETE | `/api/v1/admin/whatsapp/tenants/{id}/unassign-phone` | Remove phone assignment |
**Example: Assign Phone Number**
```bash
curl -X POST http://localhost:8001/api/v1/admin/whatsapp/tenants/{tenant_id}/assign-phone \
-H "Content-Type: application/json" \
-d '{
"phone_number_id": "123456789012345",
"display_phone_number": "+34 612 345 678"
}'
```
**Response:**
```json
{
"success": true,
"message": "Phone number +34 612 345 678 assigned to tenant 'Panadería San Juan'",
"tenant_id": "uuid-here",
"phone_number_id": "123456789012345",
"display_phone_number": "+34 612 345 678"
}
```
### Frontend Changes
#### 1. Simplified Notification Settings Card
**File:** `frontend/src/pages/app/database/ajustes/cards/NotificationSettingsCard.tsx`
**Removed:**
- Access Token input field
- Business Account ID input field
- Phone Number ID input field
- API Version selector
- Setup wizard instructions
**Added:**
- Display-only phone number (green badge if configured)
- "Contact support" message if not configured
- Language selector only
**UI Before/After:**
```
BEFORE:
┌────────────────────────────────────────┐
│ WhatsApp Business API Configuration │
│ │
│ Phone Number ID: [____________] │
│ Access Token: [____________] │
│ Business Acct: [____________] │
│ API Version: [v18.0 ▼] │
│ Language: [Español ▼] │
│ │
Setup Instructions: │
│ 1. Create WhatsApp Business... │
│ 2. Create templates... │
│ 3. Get credentials... │
└────────────────────────────────────────┘
AFTER:
┌────────────────────────────────────────┐
│ WhatsApp Configuration │
│ │
│ ✅ WhatsApp Configured │
│ Phone: +34 612 345 678 │
│ │
│ Language: [Español ▼] │
│ │
WhatsApp Notifications Included │
│ WhatsApp messaging is included │
│ in your subscription. │
└────────────────────────────────────────┘
```
#### 2. Admin Interface
**New File:** `frontend/src/pages/app/admin/WhatsAppAdminPage.tsx`
**Features:**
- Lists all available phone numbers from master WABA
- Shows phone number quality rating (GREEN/YELLOW/RED)
- Lists all tenants with WhatsApp status
- Dropdown to assign phone numbers
- One-click unassign button
- Real-time status updates
**Screenshot Mockup:**
```
┌──────────────────────────────────────────────────────────────┐
│ WhatsApp Admin Management │
│ Assign WhatsApp phone numbers to bakery tenants │
├──────────────────────────────────────────────────────────────┤
│ 📞 Available Phone Numbers (3) │
├──────────────────────────────────────────────────────────────┤
│ +34 612 345 678 Bakery Platform [GREEN] │
│ +34 612 345 679 Bakery Support [GREEN] │
│ +34 612 345 680 Bakery Notifications [YELLOW] │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ 👥 Bakery Tenants (10) │
├──────────────────────────────────────────────────────────────┤
│ Panadería San Juan ✅ Active │
│ Phone: +34 612 345 678 [Unassign] │
├──────────────────────────────────────────────────────────────┤
│ Panadería Goiko ⚠️ Not Configured │
│ No phone number assigned [Assign phone number... ▼] │
└──────────────────────────────────────────────────────────────┘
```
---
## Setup Instructions
### Step 1: Create Master WhatsApp Business Account (One-Time)
**Prerequisites:**
- Meta/Facebook Business account
- Verified business
- Phone number(s) to register
**Instructions:**
1. **Create WhatsApp Business Account**
- Go to [Meta Business Suite](https://business.facebook.com)
- Add WhatsApp product
- Complete business verification (1-3 days)
2. **Add Phone Numbers**
- Add at least 10 phone numbers (one per pilot bakery)
- Verify each phone number
- Note: You can request up to 120 phone numbers per WABA
3. **Create Message Templates**
- Create `po_notification` template:
```
Category: UTILITY
Language: Spanish (es)
Message: "Hola {{1}}, has recibido una nueva orden de compra {{2}} por un total de {{3}}."
```
- Submit for approval (15 min - 24 hours)
4. **Get Master Credentials**
- Business Account ID: From WhatsApp Manager settings
- Access Token: Create System User or use temporary token
- Phone Number ID: Listed in phone numbers section
### Step 2: Configure Environment Variables
**File:** `services/notification/.env`
```bash
# Master WhatsApp Business Account Credentials
WHATSAPP_BUSINESS_ACCOUNT_ID=987654321098765
WHATSAPP_ACCESS_TOKEN=EAAxxxxxxxxxxxxxxxxxxxxxxxxxx
WHATSAPP_PHONE_NUMBER_ID=123456789012345 # Default/fallback phone
WHATSAPP_API_VERSION=v18.0
ENABLE_WHATSAPP_NOTIFICATIONS=true
WHATSAPP_WEBHOOK_VERIFY_TOKEN=random-secret-token-here
```
**Security Notes:**
- Store `WHATSAPP_ACCESS_TOKEN` securely (use secrets manager in production)
- Rotate token every 60 days
- Use System User token (not temporary token) for production
### Step 3: Assign Phone Numbers to Tenants
**Via Admin UI:**
1. Access admin page: `http://localhost:5173/app/admin/whatsapp`
2. See list of tenants
3. For each tenant:
- Select phone number from dropdown
- Click assign
- Verify green checkmark appears
**Via API:**
```bash
# Assign phone to tenant
curl -X POST http://localhost:8001/api/v1/admin/whatsapp/tenants/{tenant_id}/assign-phone \
-H "Content-Type: application/json" \
-d '{
"phone_number_id": "123456789012345",
"display_phone_number": "+34 612 345 678"
}'
```
### Step 4: Test Notifications
**Enable WhatsApp for a Tenant:**
1. Login as bakery owner
2. Go to Settings → Notifications
3. Toggle WhatsApp ON
4. Verify phone number is displayed
5. Save settings
**Trigger Test Notification:**
```bash
# Create a purchase order (will trigger WhatsApp notification)
curl -X POST http://localhost:8003/api/v1/orders/purchase-orders \
-H "Content-Type: application/json" \
-H "X-Tenant-ID: {tenant_id}" \
-d '{
"supplier_id": "uuid",
"items": [...]
}'
```
**Verify:**
- Check notification service logs: `docker logs -f notification-service`
- Supplier should receive WhatsApp message from assigned phone number
- Message status tracked in `whatsapp_messages` table
---
## Monitoring & Operations
### Check Phone Number Usage
```bash
# List all tenants with assigned phone numbers
curl http://localhost:8001/api/v1/admin/whatsapp/tenants | jq
```
### View WhatsApp Message Logs
```sql
-- In notification database
SELECT
tenant_id,
recipient_phone,
template_name,
status,
created_at,
error_message
FROM whatsapp_messages
WHERE created_at > NOW() - INTERVAL '24 hours'
ORDER BY created_at DESC;
```
### Monitor Meta Rate Limits
WhatsApp Cloud API has the following limits:
| Metric | Limit |
|--------|-------|
| Messages per second | 80 |
| Messages per day (verified) | 100,000 |
| Messages per day (unverified) | 1,000 |
| Conversations per 24h | Unlimited (pay per conversation) |
**Check Quality Rating:**
```bash
curl -X GET "https://graph.facebook.com/v18.0/{PHONE_NUMBER_ID}" \
-H "Authorization: Bearer {ACCESS_TOKEN}" \
| jq '.quality_rating'
```
**Quality Ratings:**
- **GREEN** - No issues, full limits
- **YELLOW** - Warning, limits may be reduced
- **RED** - Quality issues, severely restricted
---
## Migration from Per-Tenant to Shared Account
If you have existing tenants with their own credentials:
### Automatic Migration Script
```python
# services/tenant/scripts/migrate_to_shared_account.py
"""
Migrate existing tenant WhatsApp credentials to shared account model
"""
import asyncio
from sqlalchemy import select
from app.core.database import database_manager
from app.models.tenant_settings import TenantSettings
async def migrate():
async with database_manager.get_session() as session:
# Get all tenant settings
result = await session.execute(select(TenantSettings))
all_settings = result.scalars().all()
for settings in all_settings:
notification_settings = settings.notification_settings
# If tenant has old credentials, preserve phone number ID
if notification_settings.get('whatsapp_access_token'):
phone_id = notification_settings.get('whatsapp_phone_number_id', '')
# Update to new schema
notification_settings['whatsapp_phone_number_id'] = phone_id
notification_settings['whatsapp_display_phone_number'] = '' # Admin will set
# Remove old fields
notification_settings.pop('whatsapp_access_token', None)
notification_settings.pop('whatsapp_business_account_id', None)
notification_settings.pop('whatsapp_api_version', None)
settings.notification_settings = notification_settings
print(f"Migrated tenant: {settings.tenant_id}")
await session.commit()
print("Migration complete!")
if __name__ == "__main__":
asyncio.run(migrate())
```
---
## Troubleshooting
### Issue: Tenant doesn't receive WhatsApp messages
**Checklist:**
1. ✅ WhatsApp enabled in tenant settings?
2. ✅ Phone number assigned to tenant?
3. ✅ Master credentials configured in environment?
4. ✅ Template approved by Meta?
5. ✅ Recipient phone number in E.164 format (+34612345678)?
**Check Logs:**
```bash
# Notification service logs
docker logs -f notification-service | grep whatsapp
# Look for:
# - "Using tenant-assigned WhatsApp phone number"
# - "WhatsApp template message sent successfully"
# - Any error messages
```
### Issue: Phone number assignment fails
**Error:** "Phone number already assigned to another tenant"
**Solution:**
```bash
# Find which tenant has the phone number
curl http://localhost:8001/api/v1/admin/whatsapp/tenants | \
jq '.[] | select(.phone_number_id == "123456789012345")'
# Unassign from old tenant first
curl -X DELETE http://localhost:8001/api/v1/admin/whatsapp/tenants/{old_tenant_id}/unassign-phone
```
### Issue: "WhatsApp master account not configured"
**Solution:**
Ensure environment variables are set:
```bash
# Check if variables exist
docker exec notification-service env | grep WHATSAPP
# Should show:
# WHATSAPP_BUSINESS_ACCOUNT_ID=...
# WHATSAPP_ACCESS_TOKEN=...
# WHATSAPP_PHONE_NUMBER_ID=...
```
### Issue: Template not found
**Error:** "Template po_notification not found"
**Solution:**
1. Create template in Meta Business Manager
2. Wait for approval (check status):
```bash
curl -X GET "https://graph.facebook.com/v18.0/{WABA_ID}/message_templates" \
-H "Authorization: Bearer {TOKEN}" \
| jq '.data[] | select(.name == "po_notification")'
```
3. Ensure template language matches tenant's `whatsapp_default_language`
---
## Cost Analysis
### WhatsApp Business API Pricing (as of 2024)
**Meta Pricing:**
- **Business-initiated conversations:** €0.0319 - €0.0699 per conversation (Spain)
- **User-initiated conversations:** Free (24-hour window)
- **Conversation window:** 24 hours
**Monthly Cost Estimate (10 Bakeries):**
- Assume 5 PO notifications per bakery per day
- 5 × 10 bakeries × 30 days = 1,500 messages/month
- Cost: 1,500 × €0.05 = **€75/month**
**Shared Account vs. Individual Accounts:**
| Model | Setup Time | Monthly Cost | Support Burden |
|-------|------------|--------------|----------------|
| Individual Accounts | 1-2 hrs/bakery | €75 total | High |
| Shared Account | 5 min/bakery | €75 total | Low |
**Savings:** Time savings = 10 hrs × €50/hr = **€500 in setup cost**
---
## Future Enhancements
### Option 1: Template Management API
Automate template creation for new tenants:
```python
async def create_po_template(waba_id: str, access_token: str):
"""Programmatically create PO notification template"""
url = f"https://graph.facebook.com/v18.0/{waba_id}/message_templates"
payload = {
"name": "po_notification",
"language": "es",
"category": "UTILITY",
"components": [{
"type": "BODY",
"text": "Hola {{1}}, has recibido una nueva orden de compra {{2}} por un total de {{3}}."
}]
}
response = await httpx.post(url, headers={"Authorization": f"Bearer {access_token}"}, json=payload)
return response.json()
```
### Option 2: WhatsApp Embedded Signup
For scaling beyond pilot:
- Apply for Meta Business Solution Provider program
- Implement OAuth-style signup flow
- Users click "Connect WhatsApp" → auto-configured
- Estimated implementation: 2-4 weeks
### Option 3: Tiered Pricing
```
Basic Tier (Free):
- Email notifications only
Standard Tier (€29/month):
- Shared WhatsApp account
- Pre-approved templates
- Up to 500 messages/month
Enterprise Tier (€99/month):
- Own WhatsApp Business Account
- Custom templates
- Unlimited messages
- White-label phone number
```
---
## Security & Compliance
### Data Privacy
**GDPR Compliance:**
- WhatsApp messages contain supplier contact info (phone numbers)
- Ensure GDPR consent for sending notifications
- Provide opt-out mechanism
- Data retention: Messages stored for 90 days (configurable)
**Encryption:**
- WhatsApp messages: End-to-end encrypted by Meta
- Access tokens: Stored in environment variables (use secrets manager in production)
- Database: Encrypt `notification_settings` JSON column
### Access Control
**Admin Access:**
- Only platform admins can assign/unassign phone numbers
- Implement role-based access control (RBAC)
- Audit log for phone number assignments
```python
# Example: Add admin check
@router.post("/admin/whatsapp/tenants/{tenant_id}/assign-phone")
async def assign_phone(tenant_id: UUID, current_user = Depends(require_admin_role)):
# Only admins can access
pass
```
---
## Support & Contacts
**Meta Support:**
- WhatsApp Business API Support: https://business.whatsapp.com/support
- Developer Docs: https://developers.facebook.com/docs/whatsapp
**Platform Admin:**
- Email: admin@bakery-platform.com
- Phone number assignment requests
- Template approval assistance
**Bakery Owner Help:**
- Settings → Notifications → Toggle WhatsApp ON
- If phone number not showing: Contact support
- Language preferences can be changed anytime
---
## Appendix
### A. Database Schema Changes
**Migration Script:**
```sql
-- Add new field, remove old fields
-- services/tenant/migrations/versions/00002_shared_whatsapp_account.py
ALTER TABLE tenant_settings
-- The notification_settings JSONB column now has:
-- + whatsapp_display_phone_number (new)
-- - whatsapp_access_token (removed)
-- - whatsapp_business_account_id (removed)
-- - whatsapp_api_version (removed)
;
-- No ALTER TABLE needed (JSONB is schema-less)
-- Migration handled by application code
```
### B. API Reference
**Phone Number Info Schema:**
```typescript
interface WhatsAppPhoneNumberInfo {
id: string; // Meta Phone Number ID
display_phone_number: string; // E.164 format: +34612345678
verified_name: string; // Business name verified by Meta
quality_rating: string; // GREEN, YELLOW, RED
}
```
**Tenant WhatsApp Status Schema:**
```typescript
interface TenantWhatsAppStatus {
tenant_id: string;
tenant_name: string;
whatsapp_enabled: boolean;
phone_number_id: string | null;
display_phone_number: string | null;
}
```
### C. Environment Variables Reference
```bash
# Notification Service (services/notification/.env)
WHATSAPP_BUSINESS_ACCOUNT_ID= # Meta WABA ID
WHATSAPP_ACCESS_TOKEN= # Meta System User Token
WHATSAPP_PHONE_NUMBER_ID= # Default phone (fallback)
WHATSAPP_API_VERSION=v18.0 # Meta API version
ENABLE_WHATSAPP_NOTIFICATIONS=true
WHATSAPP_WEBHOOK_VERIFY_TOKEN= # Random secret for webhook verification
```
### D. Useful Commands
```bash
# View all available phone numbers
curl http://localhost:8001/api/v1/admin/whatsapp/phone-numbers | jq
# View tenant WhatsApp status
curl http://localhost:8001/api/v1/admin/whatsapp/tenants | jq
# Assign phone to tenant
curl -X POST http://localhost:8001/api/v1/admin/whatsapp/tenants/{id}/assign-phone \
-H "Content-Type: application/json" \
-d '{"phone_number_id": "XXX", "display_phone_number": "+34 612 345 678"}'
# Unassign phone from tenant
curl -X DELETE http://localhost:8001/api/v1/admin/whatsapp/tenants/{id}/unassign-phone
# Test WhatsApp connectivity
curl -X GET "https://graph.facebook.com/v18.0/{PHONE_ID}" \
-H "Authorization: Bearer {TOKEN}"
# Check message template status
curl "https://graph.facebook.com/v18.0/{WABA_ID}/message_templates?fields=name,status,language" \
-H "Authorization: Bearer {TOKEN}" | jq
```
---
**Document Version:** 1.0
**Last Updated:** 2025-01-17
**Author:** Platform Engineering Team
**Status:** Production Ready for Pilot