Files
bakery-ia/docs/whatsapp/shared-account-guide.md
2025-12-05 20:07:01 +01:00

23 KiB
Raw Blame History

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:

# 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:

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

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:

{
  "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

  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

# 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:

# 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:

# 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

# List all tenants with assigned phone numbers
curl http://localhost:8001/api/v1/admin/whatsapp/tenants | jq

View WhatsApp Message Logs

-- 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:

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

# 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:

# 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:

# 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:

# 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):
    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:

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
# 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:

Platform Admin:

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:

-- 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:

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:

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

# 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

# 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