Postbacks serveur-à-serveur
Les postbacks permettent aux affiliés de recevoir en temps réel les conversions confirmées et remboursées. Chaque postback est signé HMAC-SHA256 et inclut un mécanisme anti-replay.
Fonctionnement
Lorsqu'une conversion est confirmée (paiement réussi) ou remboursée, Flendra envoie un POST HTTP à l'URL de postback configurée par l'affilié. Le payload est signé avec HMAC-SHA256 pour garantir l'intégrité et l'authenticité.
Configurer un endpoint de postback
L'affilié configure son URL de réception et un secret partagé :
curl -X POST https://flendra.com/api/postback-endpoints \
-H "Content-Type: application/json" \
-b cookies.txt \
-d '{
"url": "https://votre-site.com/webhook/flendra",
"secret": "whsec_votre_secret_partage_aleatoire",
"scope": "full",
"events": ["confirmed", "refunded"],
"active": true
}'Réponse :
{
"success": true,
"data": {
"id": "pbe_abc123",
"affiliateUserId": "usr_aff456",
"url": "https://votre-site.com/webhook/flendra",
"scope": "full",
"events": ["confirmed", "refunded"],
"active": true,
"maxRetries": 3,
"retryDelay": 60,
"deadLetterAt": null,
"deadLetterReason": null,
"lastSentAt": null,
"lastStatus": null,
"createdAt": "2026-06-17T10:30:00.000Z",
"updatedAt": "2026-06-17T10:30:00.000Z"
}
}Sécurité : Le secret n'est jamais retourné par l'API après la création. Stockez-le de votre côté. Utilisez un secret d'au moins 32 caractères généré aléatoirement.
Format du payload
Chaque postback contient les champs suivants :
Scope minimal (défaut)
{
"version": "1",
"postbackId": "pbk_uuid_v4_unique",
"conversionId": "conv_def456",
"affiliateUserId": "usr_aff456",
"offerId": "off_abc123",
"commissionAmount": 1500,
"currency": "EUR",
"status": "confirmed",
"timestamp": "2026-06-17T10:35:00.000Z",
"nonce": "nonce_uuid_v4_anti_replay",
"signature": "hmac_sha256_hex_signature"
}Scope complet
Le scope full ajoute les champs supplémentaires :
{
"version": "1",
"postbackId": "pbk_uuid_v4_unique",
"conversionId": "conv_def456",
"affiliateUserId": "usr_aff456",
"offerId": "off_abc123",
"commissionAmount": 1500,
"currency": "EUR",
"status": "confirmed",
"timestamp": "2026-06-17T10:35:00.000Z",
"nonce": "nonce_uuid_v4_anti_replay",
"clickId": "clk_abc123",
"linkId": "link_xyz789",
"commissionRuleId": "rule_pct30",
"commissionType": "percentage",
"orderAmount": 4999,
"sub1": "newsletter",
"sub2": "janvier-2026",
"sub3": null,
"sub4": null,
"sub5": null,
"explanation": "Last-click attribution within 30-day window",
"windowStart": "2026-05-18T10:35:00.000Z",
"windowEnd": "2026-06-17T10:35:00.000Z",
"signature": "hmac_sha256_hex_signature"
}Vérification de la signature
La signature HMAC-SHA256 est calculée sur le payload sérialisé (sans le champ signature) en utilisant le secret partagé. Voici un exemple en Python :
import hmac
import hashlib
import json
def verify_postback(payload: dict, secret: str, signature: str) -> bool:
"""Verify HMAC-SHA256 signature of a Flendra postback."""
# Remove the signature from the payload before verification
payload_to_sign = {k: v for k, v in payload.items() if k != "signature"}
# Serialize with sorted keys for deterministic output
message = json.dumps(payload_to_sign, sort_keys=True, separators=(",", ":"))
expected = hmac.new(
secret.encode("utf-8"),
message.encode("utf-8"),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
# Usage
payload = request.get_json()
if verify_postback(payload, "whsec_votre_secret", payload["signature"]):
process_conversion(payload)
else:
return "Invalid signature", 401Et en Node.js :
const crypto = require('crypto');
function verifyPostback(payload, secret, signature) {
const { signature: _, ...rest } = payload;
const message = JSON.stringify(rest, Object.keys(rest).sort());
const expected = crypto
.createHmac('sha256', secret)
.update(message)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected, 'hex'),
Buffer.from(signature, 'hex')
);
}Protection anti-replay
Chaque postback inclut un nonce unique (UUID v4) et un timestamp. Pour vous protéger contre les rejeux :
- Vérifiez que le timestamp est dans une fenêtre acceptable (±5 minutes)
- Stockez les nonces déjà traités et rejetez les doublons
- Vérifiez la signature HMAC avant tout traitement
Politique de retry
En cas d'échec (timeout, erreur HTTP), Flendra retente avec backoff exponentiel :
- maxRetries : 3 (défaut)
- retryDelay : 60 secondes (défaut)
- Backoff : delay × 2^(attempt-1) + jitter(0-1000ms)
Après épuisement des retries, l'endpoint est marqué dead-letter et doit être réactivé manuellement via POST /postback-endpoints/:id/reactivate.
Types d'événements
| Événement | Description | Status |
|---|---|---|
confirmed | Conversion confirmée (paiement réussi) | confirmed |
refunded | Commande remboursée (total ou partiel) | refunded |
pending | Conversion en attente de confirmation | pending |
disputed | Transaction contestée (chargeback) | disputed |
Gestion des endpoints
| Méthode | Endpoint | Description |
|---|---|---|
POST | /postback-endpoints | Créer un endpoint |
GET | /postback-endpoints/me | Lister ses endpoints |
GET | /postback-endpoints/:id | Obtenir un endpoint |
PUT | /postback-endpoints/:id | Mettre à jour |
DELETE | /postback-endpoints/:id | Supprimer |
POST | /postback-endpoints/:id/reactivate | Réactiver (dead-letter) |
API publique
Consultez la référence complète des endpoints API.