Spécifications Webhooks
Notifications temps réel pour une intégration fluide
VUE D'ENSEMBLE
1.1 Objectif du Document
Ce document spécifie l'ensemble des webhooks fournis par la plateforme REWAPP. Les webhooks permettent aux systèmes partenaires de recevoir des notifications en temps réel lors d'événements spécifiques au sein de l'écosystème REWAPP.
WEBHOOKS
Les webhooks constituent un mécanisme push permettant d'éviter le polling constant de l'API et d'assurer une réactivité optimale dans le traitement des événements métier.
1.2 Principes des Webhooks REWAPP
-
Notifications temps réel Envoi immédiat après chaque événement (latence < 500ms)
-
Sécurité par signature Chaque webhook est signé via HMAC-SHA256
-
Garantie de livraison Politique de retry avec backoff exponentiel
-
Idempotence Chaque événement possède un identifiant unique pour le dédoublonnage
-
Format standardisé Structure JSON cohérente pour tous les événements
1.3 Architecture Générale
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────────┐
│ Événement │────▶│ Queue de │────▶│ Endpoint │
│ REWAPP │ │ Messages (Bull) │ │ Partenaire │
│ (Transaction, │ │ │ │ (HTTPS POST) │
│ Points, QR...) │ │ Redis │ │ │
└─────────────────┘ └──────────────────┘ └─────────────────────┘
│
▼
┌──────────────────┐
│ Retry Manager │
│ (Backoff Exp.) │
└──────────────────┘
Flux de traitement
-
1
Événement métier
Un événement se produit dans REWAPP (transaction, crédit points, scan QR...)
-
2
Publication dans la queue
L'événement est publié dans la queue Bull (Redis)
-
3
Construction du payload
Le worker webhook récupère l'événement et construit le payload
-
4
Signature HMAC
Le payload est signé avec le secret HMAC du partenaire
-
5
Envoi HTTPS
La requête POST HTTPS est envoyée à l'endpoint configuré
-
6
Retry si échec
En cas d'échec, le Retry Manager planifie les réessais
CONFIGURATION DES WEBHOOKS
2.1 Enregistrement d'un Endpoint
Les partenaires peuvent configurer leurs webhooks via le Dashboard Partenaire ou via l'API de configuration.
Via Dashboard Partenaire
- 1
Accéder à « Paramètres » → « Webhooks »
- 2
Cliquer sur « Ajouter un endpoint »
- 3
Renseigner l'URL HTTPS de réception
- 4
Sélectionner les événements à recevoir
- 5
Copier le secret généré automatiquement
- 6
Valider la configuration
Via API
/api/v1/webhooks/endpoints
Enregistre un nouvel endpoint webhook pour le partenaire.
Requête
{
"url": "https://example.com/webhooks/rewapp",
"events": [
"transaction.created",
"transaction.validated",
"points.credited",
"qr_code.scanned"
],
"description": "Endpoint production",
"active": true
}
Réponse 201 Created
{
"id": "wh_endpoint_a1b2c3d4e5f6",
"url": "https://example.com/webhooks/rewapp",
"events": ["transaction.created", "transaction.validated", "points.credited", "qr_code.scanned"],
"secret": "whsec_aBcDeFgHiJkLmNoPqRsTuVwXyZ123456",
"active": true,
"created_at": "2025-11-24T10:30:00Z"
}
IMPORTANT
Le secret n'est affiché qu'une seule fois lors de la création. Il doit être stocké de manière sécurisée.
2.2 Paramètres de Configuration
Paramètres
| Paramètre | Type | Obligatoire | Description |
|---|---|---|---|
url |
string | Oui | URL HTTPS de l'endpoint (TLS 1.2+ requis) |
events |
array | Oui | Liste des événements à recevoir |
description |
string | Non | Description libre de l'endpoint |
active |
boolean | Non | Statut actif/inactif (défaut: true) |
metadata |
object | Non | Métadonnées personnalisées |
Contraintes techniques
- URL obligatoirement en HTTPS (HTTP non autorisé)
- TLS version 1.2 minimum
- Port standard (443) ou ports autorisés (8443, 8080)
- Timeout de connexion : 5 secondes
- Timeout de réponse : 10 secondes
- Taille maximale payload : 64 Ko
2.3 Gestion des Secrets
Chaque endpoint webhook possède un secret unique utilisé pour signer les requêtes.
/api/v1/webhooks/endpoints/{endpoint_id}/rotate-secret
Régénère le secret HMAC de l'endpoint.
Réponse 200 OK
{
"id": "wh_endpoint_a1b2c3d4e5f6",
"new_secret": "whsec_NewSecretKey987654321AbCdEfGh",
"old_secret_valid_until": "2025-11-25T10:30:00Z"
}
PÉRIODE DE GRÂCE
L'ancien secret reste valide pendant 24 heures pour permettre une transition sans interruption.
ÉVÉNEMENTS DISPONIBLES
3.1 Événements Transactions
Transactions
| Événement | Description | Déclencheur |
|---|---|---|
transaction.created | Nouvelle transaction détectée | Détection d'un achat via carte liée |
transaction.validated | Transaction validée | Validation après période de contestation |
transaction.cancelled | Transaction annulée | Annulation par le commerçant ou remboursement |
transaction.disputed | Transaction contestée | Litige ouvert par l'utilisateur |
3.2 Événements Points
Points
| Événement | Description | Déclencheur |
|---|---|---|
points.credited | Points crédités au compte | Validation transaction ou bonus |
points.debited | Points débités du compte | Utilisation QR code ou virement |
points.expired | Points expirés | Expiration après 12 mois d'inactivité |
points.transferred | Points transférés | Transfert entre comptes (si activé) |
3.3 Événements QR Code
QR Codes
| Événement | Description | Déclencheur |
|---|---|---|
qr_code.generated | QR code généré | Génération par l'utilisateur mobile |
qr_code.scanned | QR code scanné avec succès | Scan validé par le partenaire |
qr_code.expired | QR code expiré | Expiration après 60 secondes |
qr_code.rejected | QR code rejeté | Scan échoué (solde insuffisant, déjà utilisé...) |
3.4 Événements Utilisateur
Utilisateurs
| Événement | Description | Déclencheur |
|---|---|---|
user.registered | Nouvel utilisateur inscrit | Finalisation inscription |
user.card_linked | Carte bancaire liée | Liaison réussie via OpenBanking |
user.card_unlinked | Carte bancaire supprimée | Suppression par l'utilisateur |
user.tier_upgraded | Palier fidélité augmenté | Passage au palier supérieur |
user.tier_downgraded | Palier fidélité réduit | Rétrogradation de palier |
3.5 Événements Partenaire
Partenaires
| Événement | Description | Déclencheur |
|---|---|---|
partner.validated | Partenaire validé | Validation par l'admin REWAPP |
partner.suspended | Partenaire suspendu | Suspension du compte partenaire |
partner.settings_updated | Paramètres modifiés | Modification taux cashback ou config |
3.6 Événements Campagne
Campagnes
| Événement | Description | Déclencheur |
|---|---|---|
campaign.created | Nouvelle campagne créée | Création campagne promotionnelle |
campaign.started | Campagne démarrée | Début de la période active |
campaign.ended | Campagne terminée | Fin de la période ou objectif atteint |
campaign.cancelled | Campagne annulée | Annulation manuelle |
FORMAT DES PAYLOADS
4.1 Structure Générale
Tous les webhooks REWAPP suivent une structure JSON standardisée :
{
"id": "evt_a1b2c3d4e5f6g7h8i9j0",
"type": "transaction.validated",
"api_version": "2025-11-01",
"created_at": "2025-11-24T14:35:22.123Z",
"livemode": true,
"data": {
"object": { ... }
},
"metadata": {
"source": "api",
"correlation_id": "req_xyz789"
}
}
Champs du Payload
| Champ | Type | Description |
|---|---|---|
id | string | Identifiant unique de l'événement (format: evt_*) |
type | string | Type d'événement (ex: transaction.validated) |
api_version | string | Version de l'API utilisée |
created_at | string | Timestamp ISO 8601 de création |
livemode | boolean | true = production, false = test |
data.object | object | Données spécifiques à l'événement |
metadata | object | Métadonnées contextuelles |
4.2 En-têtes HTTP
Chaque requête webhook inclut les en-têtes suivants :
En-têtes
| En-tête | Valeur | Description |
|---|---|---|
Content-Type | application/json | Type de contenu |
X-Rewapp-Signature | hmac-sha256=... | Signature HMAC de la requête |
X-Rewapp-Event | transaction.validated | Type d'événement |
X-Rewapp-Delivery | dlv_a1b2c3d4 | Identifiant unique de la livraison |
X-Rewapp-Timestamp | 1732456522 | Timestamp Unix de l'envoi |
User-Agent | REWAPP-Webhook/1.0 | Agent utilisateur |
4.3 Corps de la Requête
Le corps est toujours au format JSON UTF-8. Les dates sont au format ISO 8601. Les montants sont en centimes (integer) pour éviter les erreurs de floating point.
CONVENTIONS
- Montants : en centimes (10000 = 100,00€)
- Points : en unités entières (100 points = 10,00€)
- Dates : ISO 8601 avec timezone UTC
- Identifiants : préfixés par type (usr_, txn_, pts_, qr_...)
4.4 Exemples de Payloads
transaction.validated
{
"id": "evt_txn_validated_20251124143522",
"type": "transaction.validated",
"api_version": "2025-11-01",
"created_at": "2025-11-24T14:35:22.123Z",
"livemode": true,
"data": {
"object": {
"id": "txn_a1b2c3d4e5f6",
"user_id": "usr_x1y2z3",
"partner_id": "ptn_m1n2o3",
"amount": 5000,
"currency": "EUR",
"cashback_rate": 4.0,
"points_earned": 20,
"status": "validated",
"card_last_four": "4242",
"merchant_name": "Boulangerie Martin",
"transaction_date": "2025-11-24T10:15:00Z",
"validated_at": "2025-11-24T14:35:22Z"
}
},
"metadata": { "source": "card_linking", "correlation_id": "req_abc123" }
}
points.credited
{
"id": "evt_pts_credited_20251124143530",
"type": "points.credited",
"api_version": "2025-11-01",
"created_at": "2025-11-24T14:35:30.456Z",
"livemode": true,
"data": {
"object": {
"id": "pts_credit_a1b2c3",
"user_id": "usr_x1y2z3",
"amount": 20,
"balance_after": 520,
"source_type": "transaction",
"source_id": "txn_a1b2c3d4e5f6",
"description": "Cashback Boulangerie Martin",
"expires_at": "2026-11-24T14:35:30Z"
}
},
"metadata": { "tier": "silver", "bonus_applied": 5 }
}
qr_code.scanned
{
"id": "evt_qr_scanned_20251124150000",
"type": "qr_code.scanned",
"api_version": "2025-11-01",
"created_at": "2025-11-24T15:00:00.789Z",
"livemode": true,
"data": {
"object": {
"id": "qr_scan_x1y2z3",
"qr_code_id": "qr_a1b2c3d4",
"user_id": "usr_x1y2z3",
"partner_id": "ptn_m1n2o3",
"points_used": 100,
"euro_value": 1050,
"scanned_at": "2025-11-24T15:00:00Z",
"scanner_device": "iPad Pro",
"location": { "latitude": 48.8566, "longitude": 2.3522 }
}
},
"metadata": { "app_version": "2.1.0" }
}
SÉCURITÉ DES WEBHOOKS
5.1 Signature HMAC-SHA256
Chaque webhook est signé avec le secret HMAC du partenaire pour garantir l'authenticité et l'intégrité des données.
Mécanisme de signature
- 1
Concaténation du timestamp et du body JSON
- 2
Calcul du hash HMAC-SHA256 avec le secret
- 3
Envoi de la signature dans l'en-tête X-Rewapp-Signature
timestamp = current_unix_timestamp()
payload = JSON.stringify(webhook_body)
signed_payload = timestamp + "." + payload
signature = HMAC_SHA256(secret, signed_payload)
header = "t=" + timestamp + ",v1=" + signature
Format de l'en-tête X-Rewapp-Signature :
X-Rewapp-Signature: t=1732456522,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
5.2 Validation de la Signature
Le récepteur DOIT valider la signature avant de traiter l'événement.
Étapes de validation
- 1
Extraire le timestamp (t) et la signature (v1) de l'en-tête
- 2
Vérifier que le timestamp n'est pas trop ancien (< 5 minutes)
- 3
Reconstruire le signed_payload : timestamp + "." + raw_body
- 4
Calculer le HMAC-SHA256 avec le secret stocké
- 5
Comparer la signature (comparaison timing-safe)
Exemple Node.js
const crypto = require('crypto');
function verifyWebhookSignature(rawBody, signature, secret) {
const elements = signature.split(',');
const timestamp = elements.find(e => e.startsWith('t=')).slice(2);
const receivedSig = elements.find(e => e.startsWith('v1=')).slice(3);
// Vérifier la fraîcheur du timestamp (5 min max)
const currentTime = Math.floor(Date.now() / 1000);
if (currentTime - parseInt(timestamp) > 300) {
throw new Error('Timestamp too old');
}
// Calculer la signature attendue
const signedPayload = timestamp + '.' + rawBody;
const expectedSig = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
// Comparaison timing-safe
if (!crypto.timingSafeEqual(
Buffer.from(receivedSig),
Buffer.from(expectedSig)
)) {
throw new Error('Invalid signature');
}
return JSON.parse(rawBody);
}
Exemple Python
import hmac
import hashlib
import time
import json
def verify_webhook_signature(raw_body, signature, secret):
elements = dict(e.split('=') for e in signature.split(','))
timestamp = elements['t']
received_sig = elements['v1']
# Vérifier la fraîcheur (5 min max)
if int(time.time()) - int(timestamp) > 300:
raise ValueError('Timestamp too old')
# Calculer la signature attendue
signed_payload = f"{timestamp}.{raw_body}"
expected_sig = hmac.new(
secret.encode(),
signed_payload.encode(),
hashlib.sha256
).hexdigest()
# Comparaison timing-safe
if not hmac.compare_digest(received_sig, expected_sig):
raise ValueError('Invalid signature')
return json.loads(raw_body)
5.3 Bonnes Pratiques de Sécurité
À FAIRE
- Toujours valider la signature AVANT tout traitement
- Utiliser une comparaison timing-safe
- Rejeter les webhooks avec timestamp > 5 minutes
- Stocker le secret de manière sécurisée
- Utiliser HTTPS avec TLS 1.2+
- Logger les échecs de validation
À ÉVITER
- Ne jamais exposer le secret dans le code source
- Ne pas désactiver la validation de signature
- Ne pas utiliser HTTP (toujours HTTPS)
- Ne pas faire confiance aux données sans validation
POLITIQUE DE RETRY
6.1 Mécanisme de Réessai
En cas d'échec de livraison d'un webhook, REWAPP applique automatiquement une politique de retry.
CRITÈRES D'ÉCHEC
- Timeout de connexion (> 5 secondes)
- Timeout de réponse (> 10 secondes)
- Code HTTP 5xx (erreur serveur)
- Code HTTP 429 (rate limiting)
- Erreur réseau (DNS, connexion refusée...)
CRITÈRES DE SUCCÈS
- Code HTTP 200 OK
- Code HTTP 201 Created
- Code HTTP 202 Accepted
- Code HTTP 204 No Content
ATTENTION
Les codes 4xx (sauf 429) sont considérés comme des échecs définitifs et ne déclenchent PAS de retry.
6.2 Backoff Exponentiel
REWAPP utilise un algorithme de backoff exponentiel avec jitter pour espacer les tentatives.
Délais de Retry
| Tentative | Délai après échec | Délai cumulé |
|---|---|---|
| 1 | Immédiat | 0 |
| 2 | 1 minute | 1 min |
| 3 | 5 minutes | 6 min |
| 4 | 30 minutes | 36 min |
| 5 | 2 heures | 2h36 |
| 6 | 6 heures | 8h36 |
| 7 | 12 heures | 20h36 |
| 8 | 24 heures (abandon) | 44h36 |
delay = min(base_delay * (2 ^ attempt) + random_jitter, max_delay)
Où :
- base_delay = 60 secondes
- max_delay = 86400 secondes (24h)
- random_jitter = 0 à 30 secondes
6.3 Gestion des Échecs
Après 8 tentatives échouées :
- L'événement est marqué comme « failed »
- Une alerte est envoyée au partenaire (email + notification dashboard)
- L'événement reste consultable dans l'historique pendant 30 jours
- Le partenaire peut déclencher un renvoi manuel via le dashboard
/api/v1/webhooks/events/{event_id}/retry
Déclenche manuellement un renvoi de l'événement.
/api/v1/webhooks/events?status=failed&from=2025-11-01&to=2025-11-24
Récupère la liste des événements en échec.
IMPLÉMENTATION CÔTÉ RÉCEPTEUR
7.1 Exigences Techniques
Exigences
| Exigence | Valeur | Obligatoire |
|---|---|---|
| Protocole | HTTPS (TLS 1.2+) | Oui |
| Méthode HTTP | POST | Oui |
| Content-Type accepté | application/json | Oui |
| Temps de réponse | < 10 secondes | Oui |
| Code de succès | 2xx | Oui |
| Idempotence | Gestion des doublons | Recommandé |
7.2 Traitement des Événements
Workflow recommandé
- 1
Recevoir la requête POST
- 2
Valider la signature HMAC (rejeter si invalide)
- 3
Parser le JSON
- 4
Vérifier l'idempotence (event_id déjà traité ?)
- 5
Répondre 200 OK immédiatement
- 6
Traiter l'événement de manière asynchrone
IMPORTANT
Répondre rapidement (< 10s) même si le traitement est long. Utiliser une queue interne pour le traitement asynchrone.
Exemple d'endpoint (Express.js)
const express = require('express');
const app = express();
app.post('/webhooks/rewapp',
express.raw({type: 'application/json'}),
async (req, res) => {
const signature = req.headers['x-rewapp-signature'];
const rawBody = req.body.toString();
try {
// 1. Valider la signature
const event = verifyWebhookSignature(rawBody, signature, WEBHOOK_SECRET);
// 2. Vérifier l'idempotence
if (await isEventAlreadyProcessed(event.id)) {
return res.status(200).send('Already processed');
}
// 3. Répondre immédiatement
res.status(200).send('Received');
// 4. Traiter de manière asynchrone
await queueEventForProcessing(event);
} catch (error) {
console.error('Webhook error:', error);
return res.status(400).send('Invalid webhook');
}
});
7.3 Gestion de l'Idempotence
Chaque événement possède un identifiant unique (champ id). Le récepteur DOIT implémenter une logique d'idempotence pour éviter de traiter plusieurs fois le même événement.
Stratégies recommandées
- Table de déduplication : Stocker les event_id traités avec TTL de 7 jours
- Clé unique en base : Utiliser event_id comme clé unique
- Cache distribué : Redis avec SETNX pour vérification atomique
async function isEventAlreadyProcessed(eventId) {
const key = 'webhook:processed:' + eventId;
const result = await redis.set(key, '1', 'NX', 'EX', 604800); // 7 jours
return result === null; // null = clé existait déjà
}
MONITORING ET DEBUGGING
8.1 Logs des Webhooks
Tous les webhooks envoyés sont loggés et consultables via le Dashboard Partenaire ou l'API.
Informations disponibles
- Identifiant de l'événement
- Type d'événement
- Timestamp d'envoi
- Statut de livraison (pending, delivered, failed)
- Nombre de tentatives
- Code de réponse HTTP
- Temps de réponse
- Payload envoyé
8.2 Tableau de Bord des Livraisons
Accessible via : Dashboard Partenaire → Webhooks → Historique
Fonctionnalités
8.3 Outils de Test
REWAPP fournit un endpoint pour tester la configuration webhook sans générer de vrais événements.
/api/v1/webhooks/endpoints/{endpoint_id}/test
Envoie un webhook de test à l'endpoint configuré.
Requête
{
"event_type": "transaction.validated"
}
Réponse 200 OK
{
"success": true,
"delivery_id": "dlv_test_a1b2c3",
"response_code": 200,
"response_time_ms": 145,
"message": "Test webhook delivered successfully"
}
MODE SANDBOX
En environnement sandbox, les webhooks sont envoyés avec livemode: false. Cela permet de tester l'intégration sans affecter les données de production.
CODES D'ERREUR
9.1 Codes de Réponse HTTP
Codes que le récepteur DOIT retourner :
Codes HTTP
| Code | Signification | Action REWAPP |
|---|---|---|
| 200 | OK | Livraison confirmée |
| 201 | Created | Livraison confirmée |
| 202 | Accepted | Livraison confirmée |
| 204 | No Content | Livraison confirmée |
| 400 | Bad Request | Échec définitif (pas de retry) |
| 401 | Unauthorized | Échec définitif (pas de retry) |
| 403 | Forbidden | Échec définitif (pas de retry) |
| 404 | Not Found | Échec définitif (pas de retry) |
| 429 | Too Many Requests | Retry avec backoff |
| 500 | Internal Server Error | Retry avec backoff |
| 502 | Bad Gateway | Retry avec backoff |
| 503 | Service Unavailable | Retry avec backoff |
| 504 | Gateway Timeout | Retry avec backoff |
9.2 Codes d'Erreur Métier
Erreurs retournées par l'API de configuration webhooks :
Erreurs Métier
| Code | Message | Description |
|---|---|---|
WEBHOOK_URL_INVALID | URL invalide ou non HTTPS | L'URL doit être HTTPS valide |
WEBHOOK_URL_UNREACHABLE | Endpoint injoignable | Impossible de contacter l'URL |
WEBHOOK_EVENT_UNKNOWN | Type d'événement inconnu | Event type non reconnu |
WEBHOOK_LIMIT_REACHED | Limite d'endpoints atteinte | Maximum 10 endpoints par partenaire |
WEBHOOK_SECRET_ROTATION_FAILED | Échec rotation secret | Erreur lors de la régénération |
WEBHOOK_ENDPOINT_NOT_FOUND | Endpoint non trouvé | ID endpoint invalide |
WEBHOOK_DELIVERY_FAILED | Échec de livraison | Toutes les tentatives ont échoué |
RÉCAPITULATIF
10.1 Points Clés
- Architecture robuste : Queue Bull + Redis pour garantie de livraison
- Sécurité maximale : Signature HMAC-SHA256 sur chaque webhook
- Retry intelligent : Backoff exponentiel jusqu'à 8 tentatives sur 44 heures
- 14 types d'événements : Transactions, points, QR codes, utilisateurs, partenaires, campagnes
- Monitoring complet : Dashboard, logs, outils de test
10.2 Checklist d'Intégration
- Configurer l'endpoint HTTPS (TLS 1.2+)
- Stocker le secret de manière sécurisée
- Implémenter la validation de signature HMAC-SHA256
- Implémenter la gestion d'idempotence
- Répondre en moins de 10 secondes
- Traiter les événements de manière asynchrone
- Tester avec l'endpoint de test REWAPP
- Monitorer les livraisons via le dashboard
10.3 Support
« Les webhooks REWAPP : la notification en temps réel pour une intégration fluide »