OTP WhatsApp au Maroc : intégrez l’authentification en 30 minutes
L’authentification par SMS, c’est fini. Au Maroc, les SMS OTP coûtent cher (0,50-1,20 MAD par message), arrivent avec des délais aléatoires (quand ils arrivent), et offrent une expérience utilisateur médiocre.
WhatsApp OTP offre 98% de délivrabilité, un coût 60-80% inférieur au SMS, et une expérience fluide pour vos utilisateurs. Et l’intégration prend 30 minutes.
Ce guide développeur vous donne le code prêt à l’emploi en Node.js, PHP et Python.
1. Pourquoi remplacer le SMS OTP par WhatsApp OTP ?
| Critère | SMS OTP | WhatsApp OTP |
|---|---|---|
| Coût par message (Maroc) | 0,50 - 1,20 MAD | ~0,20 MAD |
| Délivrabilité | 72-78% | 98% |
| Délai de réception | 5-30 secondes (parfois > 60s) | < 3 secondes |
| Expérience utilisateur | Code dans un SMS brut | Message formaté, branding entreprise |
| International | Roaming, filtrage opérateur | Fonctionne partout où WhatsApp est installé |
| Sécurité | Interception SIM possible | Chiffrement de bout en bout |
Pour 10 000 OTP/mois :
- SMS : 5 000 - 12 000 MAD/mois → 60 000 - 144 000 MAD/an
- WhatsApp : ~2 000 MAD/mois → ~24 000 MAD/an
- Économie annuelle : 36 000 à 120 000 MAD
2. Prérequis
Avant de coder, assurez-vous d’avoir :
- Un compte Wasel avec un numéro WhatsApp Business connecté à l’API
- Un template OTP approuvé par Meta — Wasel vous aide à le soumettre et le faire approuver (délai : 24-48h)
- Votre clé API Wasel — disponible dans votre dashboard
- Un endpoint sur votre serveur pour recevoir la vérification du code (webhook)
3. Intégration Node.js
const axios = require('axios');
const WASEL_API_KEY = 'votre_cle_api_wasel';const WASEL_API_URL = 'https://api.wasel.ma/v1';
async function sendWhatsAppOTP(phoneNumber, otpCode) { try { const response = await axios.post( `${WASEL_API_URL}/messages/authentication`, { to: phoneNumber, // Format: "212612345678" template_name: "otp_verification", template_language: "fr", components: [ { type: "body", parameters: [ { type: "text", text: otpCode } ] }, { type: "button", sub_type: "url", index: "0", parameters: [ { type: "text", text: otpCode } ] } ] }, { headers: { 'Authorization': `Bearer ${WASEL_API_KEY}`, 'Content-Type': 'application/json' } } );
console.log('OTP envoyé avec succès:', response.data.message_id); return response.data.message_id; } catch (error) { console.error('Erreur envoi OTP:', error.response?.data || error.message); throw error; }}
// Génération d'un code OTP à 6 chiffresfunction generateOTP() { return Math.floor(100000 + Math.random() * 900000).toString();}
// Exemple d'utilisationconst phone = "212612345678";const otp = generateOTP();
sendWhatsAppOTP(phone, otp) .then(messageId => { // Stocker l'OTP avec le messageId pour vérification ultérieure console.log(`OTP ${otp} envoyé au ${phone}, message_id: ${messageId}`); }) .catch(err => console.error("Échec de l'envoi:", err));Configuration du template OTP dans Meta
Votre template doit ressembler à :
Bonjour,
Votre code de vérification est : {{1}}
Ce code expire dans 5 minutes. Ne le partagez avec personne.
[Bouton: Copier le code] → {{1}}4. Intégration PHP
<?php
class WaselOTP { private $apiKey; private $apiUrl = 'https://api.wasel.ma/v1';
public function __construct($apiKey) { $this->apiKey = $apiKey; }
public function sendOTP($phoneNumber, $otpCode) { $payload = [ 'to' => $phoneNumber, 'template_name' => 'otp_verification', 'template_language' => 'fr', 'components' => [ [ 'type' => 'body', 'parameters' => [ ['type' => 'text', 'text' => $otpCode] ] ], [ 'type' => 'button', 'sub_type' => 'url', 'index' => '0', 'parameters' => [ ['type' => 'text', 'text' => $otpCode] ] ] ] ];
$ch = curl_init($this->apiUrl . '/messages/authentication'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer ' . $this->apiKey, 'Content-Type: application/json' ]);
$response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
if ($httpCode !== 200) { throw new Exception('Erreur API Wasel: ' . $response); }
$data = json_decode($response, true); return $data['message_id']; }
public function verifyOTP($phoneNumber, $otpCode, $messageId) { $payload = [ 'to' => $phoneNumber, 'code' => $otpCode, 'message_id' => $messageId ];
$ch = curl_init($this->apiUrl . '/messages/authentication/verify'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Authorization: Bearer ' . $this->apiKey, 'Content-Type: application/json' ]);
$response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
return $httpCode === 200; }}
// Utilisation$wasel = new WaselOTP('votre_cle_api_wasel');$otp = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT);
try { $messageId = $wasel->sendOTP('212612345678', $otp); $_SESSION['wasel_otp'] = $otp; $_SESSION['wasel_message_id'] = $messageId; echo json_encode(['success' => true, 'message' => 'OTP envoyé']);} catch (Exception $e) { echo json_encode(['success' => false, 'error' => $e->getMessage()]);}?>5. Intégration Python
import requestsimport randomimport string
class WaselOTP: def __init__(self, api_key: str): self.api_key = api_key self.api_url = "https://api.wasel.ma/v1" self.headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" }
def send_otp(self, phone_number: str, otp_code: str) -> str: payload = { "to": phone_number, "template_name": "otp_verification", "template_language": "fr", "components": [ { "type": "body", "parameters": [ {"type": "text", "text": otp_code} ] }, { "type": "button", "sub_type": "url", "index": "0", "parameters": [ {"type": "text", "text": otp_code} ] } ] }
response = requests.post( f"{self.api_url}/messages/authentication", json=payload, headers=self.headers, timeout=10 )
if response.status_code != 200: raise Exception(f"API Error: {response.text}")
data = response.json() return data["message_id"]
def verify_otp(self, phone_number: str, otp_code: str, message_id: str) -> bool: payload = { "to": phone_number, "code": otp_code, "message_id": message_id }
response = requests.post( f"{self.api_url}/messages/authentication/verify", json=payload, headers=self.headers, timeout=10 )
return response.status_code == 200
@staticmethod def generate_otp(length: int = 6) -> str: return ''.join(random.choices(string.digits, k=length))
# Utilisationwasel = WaselOTP("votre_cle_api_wasel")phone = "212612345678"otp = WaselOTP.generate_otp()
try: message_id = wasel.send_otp(phone, otp) print(f"OTP {otp} envoyé au {phone}, message_id: {message_id}")except Exception as e: print(f"Erreur: {e}")6. Gestion des erreurs courantes
| Code HTTP | Erreur | Cause probable | Solution |
|---|---|---|---|
| 400 | Bad Request | Format du numéro invalide | Utilisez le format international sans ”+” : “212612345678” |
| 401 | Unauthorized | Clé API invalide ou expirée | Vérifiez votre clé dans le dashboard Wasel |
| 403 | Forbidden | Template non approuvé | Vérifiez le statut du template dans Meta Business Manager |
| 429 | Rate Limit | Trop de requêtes | Respectez la limite de 80 requêtes/seconde |
| 500 | Server Error | Erreur côté Wasel/Meta | Réessayez avec un backoff exponentiel |
// Retry avec backoff exponentiel en Node.jsasync function sendOTPWithRetry(phone, otp, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { return await sendWhatsAppOTP(phone, otp); } catch (error) { if (i === maxRetries - 1) throw error; const delay = Math.pow(2, i) * 1000; await new Promise(resolve => setTimeout(resolve, delay)); } }}7. Vérification du code OTP — flux complet
1. Utilisateur saisit son numéro → POST /api/auth/request-otp2. Serveur génère un OTP 6 chiffres3. Serveur appelle Wasel API → OTP envoyé sur WhatsApp4. Utilisateur reçoit le code → le saisit dans votre app5. Votre app envoie le code + message_id au serveur → POST /api/auth/verify-otp6. Serveur vérifie via Wasel API → retourne succès/échec7. Si succès → génération JWT/session tokenPassez à l’OTP WhatsApp en 30 minutes
Documentation API complète, SDKs, exemples de code et support développeur. Intégrez l’authentification WhatsApp la plus fiable et la moins chère du marché.