Spaces:
Running
Running
Commit ·
2e4124f
1
Parent(s): a22c439
fix(wati_service): Update OTP template parameter names to numeric placeholders
Browse files- Change OTP parameter name from "otp_code" to "1" to match WhatsApp auth template format
- Change expiry parameter name from "expiry_time" to "2" for numeric placeholder compliance
- Update comments to reference Facebook WhatsApp Business Management API documentation
- Add decode_wati_token.py utility script for JWT token inspection and debugging
- Align parameter naming with WATI API and WhatsApp authentication template requirements
- app/auth/services/wati_service.py +6 -5
- decode_wati_token.py +94 -0
app/auth/services/wati_service.py
CHANGED
|
@@ -84,19 +84,20 @@ class WatiService:
|
|
| 84 |
url = f"{self.api_endpoint}/api/v1/sendTemplateMessage"
|
| 85 |
params = {"whatsappNumber": whatsapp_number}
|
| 86 |
|
| 87 |
-
# Build request payload
|
| 88 |
-
#
|
| 89 |
-
#
|
|
|
|
| 90 |
payload = {
|
| 91 |
"template_name": template,
|
| 92 |
"broadcast_name": "OTP_Login",
|
| 93 |
"parameters": [
|
| 94 |
{
|
| 95 |
-
"name": "
|
| 96 |
"value": otp
|
| 97 |
},
|
| 98 |
{
|
| 99 |
-
"name": "
|
| 100 |
"value": str(expiry_minutes)
|
| 101 |
}
|
| 102 |
]
|
|
|
|
| 84 |
url = f"{self.api_endpoint}/api/v1/sendTemplateMessage"
|
| 85 |
params = {"whatsappNumber": whatsapp_number}
|
| 86 |
|
| 87 |
+
# Build request payload for WhatsApp Authentication Template
|
| 88 |
+
# WhatsApp auth templates use numeric placeholders: {{1}}, {{2}}, etc.
|
| 89 |
+
# Format based on WATI API and Facebook WhatsApp documentation
|
| 90 |
+
# Reference: https://developers.facebook.com/docs/whatsapp/business-management-api/authentication-templates
|
| 91 |
payload = {
|
| 92 |
"template_name": template,
|
| 93 |
"broadcast_name": "OTP_Login",
|
| 94 |
"parameters": [
|
| 95 |
{
|
| 96 |
+
"name": "1",
|
| 97 |
"value": otp
|
| 98 |
},
|
| 99 |
{
|
| 100 |
+
"name": "2",
|
| 101 |
"value": str(expiry_minutes)
|
| 102 |
}
|
| 103 |
]
|
decode_wati_token.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Decode WATI JWT token to see what's inside.
|
| 4 |
+
"""
|
| 5 |
+
import base64
|
| 6 |
+
import json
|
| 7 |
+
|
| 8 |
+
def decode_jwt(token):
|
| 9 |
+
"""Decode JWT token without verification."""
|
| 10 |
+
try:
|
| 11 |
+
# Split token into parts
|
| 12 |
+
parts = token.split('.')
|
| 13 |
+
if len(parts) != 3:
|
| 14 |
+
print("❌ Invalid JWT format")
|
| 15 |
+
return
|
| 16 |
+
|
| 17 |
+
header_b64, payload_b64, signature = parts
|
| 18 |
+
|
| 19 |
+
# Decode header
|
| 20 |
+
header_padding = '=' * (4 - len(header_b64) % 4)
|
| 21 |
+
header_json = base64.urlsafe_b64decode(header_b64 + header_padding)
|
| 22 |
+
header = json.loads(header_json)
|
| 23 |
+
|
| 24 |
+
# Decode payload
|
| 25 |
+
payload_padding = '=' * (4 - len(payload_b64) % 4)
|
| 26 |
+
payload_json = base64.urlsafe_b64decode(payload_b64 + payload_padding)
|
| 27 |
+
payload = json.loads(payload_json)
|
| 28 |
+
|
| 29 |
+
print("=" * 80)
|
| 30 |
+
print("WATI Token Decoded")
|
| 31 |
+
print("=" * 80)
|
| 32 |
+
|
| 33 |
+
print("\nHeader:")
|
| 34 |
+
print(json.dumps(header, indent=2))
|
| 35 |
+
|
| 36 |
+
print("\nPayload:")
|
| 37 |
+
print(json.dumps(payload, indent=2))
|
| 38 |
+
|
| 39 |
+
print("\n" + "=" * 80)
|
| 40 |
+
print("Key Information")
|
| 41 |
+
print("=" * 80)
|
| 42 |
+
|
| 43 |
+
print(f"\nEmail: {payload.get('email', 'N/A')}")
|
| 44 |
+
print(f"Tenant ID: {payload.get('tenant_id', 'N/A')}")
|
| 45 |
+
print(f"Database: {payload.get('db_name', 'N/A')}")
|
| 46 |
+
print(f"Role: {payload.get('http://schemas.microsoft.com/ws/2008/06/identity/claims/role', 'N/A')}")
|
| 47 |
+
print(f"Issuer: {payload.get('iss', 'N/A')}")
|
| 48 |
+
print(f"Audience: {payload.get('aud', 'N/A')}")
|
| 49 |
+
|
| 50 |
+
# Check expiration
|
| 51 |
+
exp = payload.get('exp', 0)
|
| 52 |
+
if exp:
|
| 53 |
+
from datetime import datetime
|
| 54 |
+
exp_date = datetime.fromtimestamp(exp)
|
| 55 |
+
print(f"Expires: {exp_date}")
|
| 56 |
+
|
| 57 |
+
now = datetime.now()
|
| 58 |
+
if exp_date > now:
|
| 59 |
+
print(f"✅ Token is valid (expires in {(exp_date - now).days} days)")
|
| 60 |
+
else:
|
| 61 |
+
print(f"❌ Token is EXPIRED!")
|
| 62 |
+
|
| 63 |
+
print("\n" + "=" * 80)
|
| 64 |
+
print("Tenant Information")
|
| 65 |
+
print("=" * 80)
|
| 66 |
+
|
| 67 |
+
tenant_id = payload.get('tenant_id', '')
|
| 68 |
+
print(f"\nTenant ID from token: {tenant_id}")
|
| 69 |
+
print(f"API Endpoint: https://live-mt-server.wati.io/104318")
|
| 70 |
+
|
| 71 |
+
if tenant_id == "104318":
|
| 72 |
+
print("✅ Tenant ID matches API endpoint")
|
| 73 |
+
elif tenant_id == "1043182":
|
| 74 |
+
print("⚠️ Tenant ID has extra '2' at the end")
|
| 75 |
+
print(f" Token tenant: {tenant_id}")
|
| 76 |
+
print(f" Endpoint tenant: 104318")
|
| 77 |
+
print("\n This might be the issue!")
|
| 78 |
+
print(" Try using endpoint: https://live-mt-server.wati.io/1043182")
|
| 79 |
+
else:
|
| 80 |
+
print(f"❌ Tenant ID mismatch!")
|
| 81 |
+
print(f" Token tenant: {tenant_id}")
|
| 82 |
+
print(f" Endpoint tenant: 104318")
|
| 83 |
+
|
| 84 |
+
print("\n" + "=" * 80)
|
| 85 |
+
|
| 86 |
+
except Exception as e:
|
| 87 |
+
print(f"❌ Error decoding token: {str(e)}")
|
| 88 |
+
import traceback
|
| 89 |
+
traceback.print_exc()
|
| 90 |
+
|
| 91 |
+
if __name__ == "__main__":
|
| 92 |
+
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImN0b0BjdWF0cm9sYWJzLmNvbSIsIm5hbWVpZCI6ImN0b0BjdWF0cm9sYWJzLmNvbSIsImVtYWlsIjoiY3RvQGN1YXRyb2xhYnMuY29tIiwiYXV0aF90aW1lIjoiMDIvMDYvMjAyNiAxMjoyMzoyNiIsInRlbmFudF9pZCI6IjEwNDMxODIiLCJkYl9uYW1lIjoibXQtcHJvZC1UZW5hbnRzIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiQURNSU5JU1RSQVRPUiIsImV4cCI6MjUzNDAyMzAwODAwLCJpc3MiOiJDbGFyZV9BSSIsImF1ZCI6IkNsYXJlX0FJIn0.z5lE4gK903PpsSVIZgdNlpsdeXKAeSsGe-Kdr5WT19c"
|
| 93 |
+
|
| 94 |
+
decode_jwt(token)
|