Webhooks
L'API MyTV peut envoyer des notifications webhook vers des systemes externes lorsque certains evenements se produisent. Les webhooks permettent une integration en temps reel avec des services tiers.
Configuration des Webhooks
Enregistrer un Webhook
Les webhooks sont configures dans le panneau d'administration ou via l'API Admin :
POST /api/v2/admin/webhooks
Authorization: Bearer <admin_token>
Content-Type: application/json
{
"url": "https://your-server.com/webhooks/mytv",
"events": ["subscription.created", "subscription.canceled", "payment.failed"],
"secret": "your-webhook-secret",
"isActive": true
}
Lister les Webhooks
GET /api/v2/admin/webhooks
Authorization: Bearer <admin_token>
Supprimer un Webhook
DELETE /api/v2/admin/webhooks/:id
Authorization: Bearer <admin_token>
Format du Payload
Tous les webhooks suivent un format de payload standard :
{
"id": "evt_abc123",
"type": "subscription.created",
"timestamp": "2025-01-15T10:30:00Z",
"apiVersion": "2025-01-01",
"data": {
// Donnees specifiques a l'evenement
}
}
Verification des Webhooks
Les webhooks sont signes avec HMAC-SHA256. Verifiez la signature avant de traiter :
import crypto from 'crypto';
function verifyWebhookSignature(
payload: string,
signature: string,
secret: string,
): boolean {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature),
);
}
// Middleware Express
app.post(
'/webhooks/mytv',
express.raw({ type: 'application/json' }),
(req, res) => {
const signature = req.headers['x-mytv-signature'];
if (
!verifyWebhookSignature(req.body.toString(), signature, WEBHOOK_SECRET)
) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
// Traiter l'evenement...
res.status(200).send('OK');
},
);
Evenements Disponibles
Evenements d'Abonnement
subscription.created
Emis lorsqu'un nouvel abonnement est cree.
{
"type": "subscription.created",
"data": {
"subscriptionId": "sub-uuid",
"accountId": "account-uuid",
"planId": "plan-premium",
"planName": "Premium",
"status": "ACTIVE",
"billingCycle": "MONTHLY",
"amount": 9.99,
"currency": "EUR",
"currentPeriodStart": "2025-01-15T00:00:00Z",
"currentPeriodEnd": "2025-02-15T00:00:00Z"
}
}
subscription.updated
Emis lorsqu'un plan d'abonnement est modifie.
{
"type": "subscription.updated",
"data": {
"subscriptionId": "sub-uuid",
"accountId": "account-uuid",
"previousPlanId": "plan-basic",
"newPlanId": "plan-premium",
"changeType": "UPGRADE",
"effectiveDate": "2025-01-15T00:00:00Z"
}
}
subscription.canceled
Emis lorsqu'un abonnement est annule.
{
"type": "subscription.canceled",
"data": {
"subscriptionId": "sub-uuid",
"accountId": "account-uuid",
"planId": "plan-premium",
"cancelReason": "too_expensive",
"canceledAt": "2025-01-15T10:30:00Z",
"accessEndsAt": "2025-02-15T00:00:00Z"
}
}
subscription.renewed
Emis lorsqu'un abonnement est renouvele avec succes.
{
"type": "subscription.renewed",
"data": {
"subscriptionId": "sub-uuid",
"accountId": "account-uuid",
"planId": "plan-premium",
"amount": 9.99,
"currency": "EUR",
"newPeriodEnd": "2025-03-15T00:00:00Z"
}
}
Evenements de Paiement
payment.succeeded
Emis lorsqu'un paiement reussit.
{
"type": "payment.succeeded",
"data": {
"transactionId": "txn-uuid",
"accountId": "account-uuid",
"subscriptionId": "sub-uuid",
"amount": 9.99,
"currency": "EUR",
"paymentMethod": "CARD",
"last4": "4242",
"invoiceId": "inv-uuid",
"invoiceUrl": "https://..."
}
}
payment.failed
Emis lorsqu'un paiement echoue.
{
"type": "payment.failed",
"data": {
"transactionId": "txn-uuid",
"accountId": "account-uuid",
"subscriptionId": "sub-uuid",
"amount": 9.99,
"currency": "EUR",
"failureReason": "card_declined",
"failureCode": "insufficient_funds",
"nextRetryAt": "2025-01-18T00:00:00Z",
"retryCount": 1,
"maxRetries": 3
}
}
Evenements de Compte
account.created
Emis lorsqu'un nouveau compte est cree.
{
"type": "account.created",
"data": {
"accountId": "account-uuid",
"email": "[email protected]",
"tenantId": "tenant-uuid",
"createdAt": "2025-01-15T10:30:00Z"
}
}
account.deleted
Emis lorsqu'un compte est supprime.
{
"type": "account.deleted",
"data": {
"accountId": "account-uuid",
"email": "u***@example.com",
"deletionType": "USER_REQUEST",
"deletedAt": "2025-01-15T10:30:00Z"
}
}
Evenements de Securite
security.suspicious_activity
Emis lorsqu'une activite suspecte est detectee.
{
"type": "security.suspicious_activity",
"data": {
"accountId": "account-uuid",
"eventType": "IMPOSSIBLE_TRAVEL",
"severity": "HIGH",
"riskScore": 75,
"details": {
"previousLocation": "Paris, France",
"newLocation": "London, UK",
"timeDelta": 1800
},
"actionTaken": "CHALLENGE"
}
}
security.account_locked
Emis lorsqu'un compte est verrouille.
{
"type": "security.account_locked",
"data": {
"accountId": "account-uuid",
"reason": "BRUTE_FORCE",
"failedAttempts": 5,
"lockDuration": 900,
"lockedAt": "2025-01-15T10:30:00Z"
}
}
Evenements de Contenu
content.published
Emis lorsqu'un nouveau contenu est publie.
{
"type": "content.published",
"data": {
"contentId": "movie-uuid",
"contentType": "MOVIE",
"title": "The New Movie",
"accessType": "PREMIUM",
"publishedAt": "2025-01-15T00:00:00Z"
}
}
Politique de Retry
Les livraisons de webhooks echouees sont retentees avec un backoff exponentiel :
| Tentative | Delai |
|---|---|
| 1 | Immediat |
| 2 | 5 minutes |
| 3 | 30 minutes |
| 4 | 2 heures |
| 5 | 24 heures |
Apres 5 tentatives echouees, le webhook est desactive et une notification admin est envoyee.
Exigences de Reponse
Votre endpoint webhook doit :
- Repondre dans les 30 secondes
- Retourner un code de statut 2xx en cas de succes
- Retourner 4xx pour les erreurs client (pas de retry)
- Retourner 5xx pour les erreurs serveur (retry automatique)
Headers
Tous les webhooks incluent ces headers :
| Header | Description |
|---|---|
X-MyTV-Signature | Signature HMAC-SHA256 |
X-MyTV-Event | Type d'evenement |
X-MyTV-Timestamp | Timestamp Unix |
X-MyTV-Delivery-ID | ID unique de livraison |
Content-Type | application/json |
Idempotence
Les webhooks peuvent etre livres plus d'une fois. Utilisez le champ id pour assurer un traitement idempotent :
async function processWebhook(event: WebhookEvent) {
// Verifier si deja traite
const exists = await db.webhookEvents.findUnique({
where: { eventId: event.id },
});
if (exists) {
console.log(`Event ${event.id} already processed, skipping`);
return;
}
// Traiter l'evenement
await handleEvent(event);
// Marquer comme traite
await db.webhookEvents.create({
data: { eventId: event.id, processedAt: new Date() },
});
}
Test des Webhooks
Endpoint de Test
Envoyez un webhook de test pour verifier votre endpoint :
POST /api/v2/admin/webhooks/:id/test
Authorization: Bearer <admin_token>
Content-Type: application/json
{
"eventType": "subscription.created"
}
Developpement Local
Utilisez ngrok ou des outils similaires pour recevoir les webhooks localement :
ngrok http 3000
# Utilisez l'URL ngrok comme endpoint webhook
Logs des Webhooks
Consultez les logs de livraison des webhooks :
GET /api/v2/admin/webhooks/:id/logs
Authorization: Bearer <admin_token>
{
"data": [
{
"id": "log-uuid",
"eventId": "evt-uuid",
"eventType": "subscription.created",
"status": "DELIVERED",
"statusCode": 200,
"responseTime": 245,
"createdAt": "2025-01-15T10:30:00Z"
},
{
"id": "log-uuid-2",
"eventId": "evt-uuid-2",
"eventType": "payment.failed",
"status": "FAILED",
"statusCode": 500,
"error": "Connection timeout",
"retryAt": "2025-01-15T10:35:00Z",
"createdAt": "2025-01-15T10:30:00Z"
}
]
}
Bonnes Pratiques
- Verifiez les signatures - Toujours verifier la signature du webhook
- Repondez rapidement - Retournez 200 immediatement, traitez de maniere asynchrone
- Gerez les doublons - Implementez un traitement idempotent
- Loguez tout - Loguez tous les evenements webhook pour le debug
- Surveillez les echecs - Alertez sur les echecs repetes
- Utilisez HTTPS - Toujours utiliser des endpoints HTTPS