Webhooks
Les webhooks sont des notifications automatisees envoyees de Worldline Direct a votre serveur, vous permettant de recevoir des messages sur les resultats de transaction et les changements de statut sans interrogation continue.
Avantages
- Traitement automatise - Automatisez les flux de travail des transactions
- Mises a jour en temps reel - Suivez les resultats d'autorisation instantanement
- Evenements hors ligne - Surveillez les captures et remboursements apres les transactions initiales
- Pas d'interrogation - Eliminez les appels API reguliers de verification de statut
Les webhooks sont asynchrones et ne conviennent pas aux decisions de paiement en temps reel. Utilisez GetHostedCheckoutStatus ou GetPaymentDetails pour les verifications de statut immediates.
Configuration
Etape 1 : Generer les identifiants webhook
- Connectez-vous au Portail Marchand
- Naviguez vers Developpeur > Webhooks
- Cliquez sur "Generer les cles webhooks"
- Stockez immediatement la cle secrete webhook
La cle secrete s'affiche pendant 60 secondes uniquement. Stockez-la en securite. La regeneration des cles revoque immediatement les cles existantes.
Etape 2 : Definir les points d'acces webhook
Option A : Portail Marchand (code en dur)
- Allez dans Developpeur > Webhooks
- Cliquez sur "Ajouter un point d'acces webhook"
- Ajoutez jusqu'a cinq URLs
Option B : Par requete (dynamique)
Incluez les points d'acces dans votre requete API :
{
"order": { ... },
"feedbacks": {
"webhookUrls": [
"https://yoursite.com/webhooks/primary",
"https://yoursite.com/webhooks/backup"
]
}
}
Les webhooks dynamiques sont recommandes pour les environnements multi-boutiques ou chaque boutique a besoin d'URLs de notification differentes.
Etape 3 : Construire le gestionnaire de webhook
Votre point d'acces HTTPS doit :
- Accepter les requetes POST avec un corps JSON
- Verifier les signatures des messages
- Repondre avec un code de statut HTTP 2xx
- C#
- Java
- PHP
[HttpPost("webhook")]
public async Task<IActionResult> HandleWebhook()
{
// Lire le corps brut de la requete
using var reader = new StreamReader(Request.Body);
var body = await reader.ReadToEndAsync();
// Obtenir les en-tetes de signature
var keyId = Request.Headers["X-GCS-KeyId"].FirstOrDefault();
var signature = Request.Headers["X-GCS-Signature"].FirstOrDefault();
// Verifier et analyser le webhook
var webhookHelper = new WebhooksHelper(new InMemorySecretKeyStore());
var events = webhookHelper.Unmarshal(body, new List<RequestHeader>
{
new RequestHeader("X-GCS-KeyId", keyId),
new RequestHeader("X-GCS-Signature", signature)
});
foreach (var webhookEvent in events)
{
// Traiter l'evenement
ProcessWebhookEvent(webhookEvent);
}
// Toujours retourner 2xx immediatement
return Ok();
}
private void ProcessWebhookEvent(WebhooksEvent webhookEvent)
{
var paymentId = webhookEvent.Payment?.Id;
var eventType = webhookEvent.Type;
var statusCode = webhookEvent.Payment?.StatusOutput?.StatusCode;
// Mettre a jour votre base de donnees de maniere asynchrone
// Ne bloquez pas la reponse du webhook
}
@PostMapping("/webhook")
public ResponseEntity<Void> handleWebhook(
@RequestBody String body,
@RequestHeader("X-GCS-KeyId") String keyId,
@RequestHeader("X-GCS-Signature") String signature) {
List<RequestHeader> headers = Arrays.asList(
new RequestHeader("X-GCS-KeyId", keyId),
new RequestHeader("X-GCS-Signature", signature)
);
WebhooksHelper helper = new WebhooksHelper(new InMemorySecretKeyStore());
List<WebhooksEvent> events = helper.unmarshal(body, headers);
for (WebhooksEvent event : events) {
processWebhookEvent(event);
}
// Toujours retourner 2xx immediatement
return ResponseEntity.ok().build();
}
private void processWebhookEvent(WebhooksEvent event) {
String paymentId = event.getPayment().getId();
String eventType = event.getType();
Integer statusCode = event.getPayment().getStatusOutput().getStatusCode();
// Traiter de maniere asynchrone
}
<?php
$body = file_get_contents('php://input');
$keyId = $_SERVER['HTTP_X_GCS_KEYID'];
$signature = $_SERVER['HTTP_X_GCS_SIGNATURE'];
$headers = [
new RequestHeader('X-GCS-KeyId', $keyId),
new RequestHeader('X-GCS-Signature', $signature)
];
$helper = new WebhooksHelper(new InMemorySecretKeyStore());
$events = $helper->unmarshal($body, $headers);
foreach ($events as $event) {
$paymentId = $event->getPayment()->getId();
$eventType = $event->getType();
$statusCode = $event->getPayment()->getStatusOutput()->getStatusCode();
// Traiter l'evenement
}
// Retourner 200 OK
http_response_code(200);
Decouplez votre logique metier de la gestion des webhooks. Retournez un code de statut 2xx immediatement, puis traitez l'evenement de maniere asynchrone pour eviter les faux echecs de livraison.
Evenements webhook
Evenements de paiement
| Evenement | Description | Code de statut |
|---|---|---|
payment.created | Transaction creee | 0 |
payment.redirected | Client redirige vers 3DS | 46 |
payment.authorization_requested | Requete d'autorisation envoyee | 51 |
payment.pending_approval | En attente d'approbation marchand | 56 |
payment.pending_capture | Autorisation terminee, en attente de capture | 5 |
payment.capture_requested | Capture en file d'attente | 91 |
payment.captured | Capture confirmee | 9 |
payment.rejected | Transaction refusee | 2 |
payment.rejected_capture | Capture rejetee | 93 |
payment.cancelled | Transaction annulee | 1, 6 |
payment.refunded | Remboursement traite | 8 |
Evenements de remboursement
| Evenement | Description |
|---|---|
refund.refund_requested | Remboursement en file d'attente |
Evenements de lien de paiement
| Evenement | Description |
|---|---|
paymentlink.created | Lien de paiement genere |
paymentlink.clicked | Lien de paiement accede |
paymentlink.paid | Paiement reussi via le lien |
paymentlink.cancelled | Lien de paiement annule |
paymentlink.expired | Lien expire |
Exemples de messages webhook
payment.created
{
"apiVersion": "v1",
"created": "2024-01-15T14:30:00.000+01:00",
"id": "34b8a607-1fce-4003-b3ae-a4d29e92b232",
"merchantId": "YOUR_MERCHANT_ID",
"payment": {
"paymentOutput": {
"amountOfMoney": {
"amount": 2980,
"currencyCode": "EUR"
},
"references": {
"merchantReference": "order-12345"
},
"cardPaymentMethodSpecificOutput": {
"paymentProductId": 1,
"card": {
"cardNumber": "************1111",
"expiryDate": "1225"
}
},
"paymentMethod": "card"
},
"status": "CREATED",
"statusOutput": {
"statusCode": 0,
"statusCategory": "CREATED"
},
"id": "000000123400000012340000100001"
},
"type": "payment.created"
}
payment.captured
{
"apiVersion": "v1",
"created": "2024-01-15T14:35:00.000+01:00",
"id": "7aeb0c3d-066e-4d31-bfe9-f9b5e48414df",
"merchantId": "YOUR_MERCHANT_ID",
"payment": {
"status": "CAPTURED",
"statusOutput": {
"statusCode": 9,
"statusCategory": "COMPLETED"
},
"id": "000000123400000012340000100002"
},
"type": "payment.captured"
}
Gestion des doublons
Les evenements webhook en double sont une caracteristique de la livraison fiable, pas une erreur. Concevez votre systeme pour les gerer :
public async Task ProcessWebhookEvent(WebhooksEvent webhookEvent)
{
var eventId = webhookEvent.Id;
var paymentId = webhookEvent.Payment?.Id;
var eventType = webhookEvent.Type;
// Verifier si deja traite
if (await IsEventProcessed(eventId))
{
return; // Ignorer le doublon
}
// Marquer comme traite
await MarkEventProcessed(eventId);
// Traiter l'evenement
await HandleEvent(paymentId, eventType);
}
Strategie de nouvelle tentative
Si la livraison echoue, la plateforme reessaie cinq fois :
| Tentative | Delai |
|---|---|
| Initiale | Immediate |
| Nouvelle tentative 1 | 10 minutes |
| Nouvelle tentative 2 | 1 heure |
| Nouvelle tentative 3 | 2 heures |
| Nouvelle tentative 4 | 8 heures |
| Nouvelle tentative 5 | 24 heures |
Chaque nouvelle tentative inclut un en-tete retry-count (0 pour la tentative initiale).
Tester les webhooks
Valider les identifiants
curl -X POST https://payment.preprod.direct.worldline-solutions.com/v2/YOUR_MERCHANT_ID/webhooks/validateCredentials \
-H "Content-Type: application/json" \
-d '{
"key": "your-webhook-key-id",
"secret": "base64-encoded-hmac-secret"
}'
Envoyer un message de test
curl -X POST https://payment.preprod.direct.worldline-solutions.com/v2/YOUR_MERCHANT_ID/webhooks/sendTestWebhook \
-H "Content-Type: application/json" \
-d '{
"url": "https://yoursite.com/webhooks"
}'
La plateforme envoie un webhook de test avec type: "payment.test".
Changements d'ID de paiement
Le payment.id change pendant les operations de maintenance :
| Operation | payment.id | statusCode |
|---|---|---|
| Autorisation | payment.id1 | 5 |
| Capture | payment.id2 | 9 |
| Remboursement | payment.id3 | 8 |
Utilisez GetPaymentDetails pour suivre operations.id tout au long du cycle de vie de la transaction.
Bonnes pratiques
- Retourner 2xx immediatement - Ne bloquez pas sur la logique metier
- Gerer les doublons - Utilisez l'ID d'evenement pour l'idempotence
- Traiter de maniere asynchrone - Mettez les evenements en file d'attente pour un traitement en arriere-plan
- Implementer des solutions de secours - Utilisez
GetPaymentDetailscomme sauvegarde - Verifier les signatures - Validez toujours l'authenticite du webhook
- Tout enregistrer - Gardez des journaux detailles pour le debogage
- Surveiller les echecs - Alertez sur les echecs de livraison repetes
Prochaines etapes
- Configurer Hosted Checkout avec les webhooks
- Implementer l'integration serveur a serveur
- Decouvrir 3-D Secure pour l'authentification