Skip to main content

Webhooks

Webhooks are automated notifications sent from Worldline Direct to your server, enabling you to receive messages about transaction results and status changes without continuous polling.

Benefits

  • Automated Processing - Automate transaction workflows
  • Real-time Updates - Track authorization results instantly
  • Offline Events - Monitor captures, refunds after initial transactions
  • No Polling - Eliminate regular status-checking API calls
Important

Webhooks are asynchronous and not suitable for real-time checkout decisions. Use GetHostedCheckoutStatus or GetPaymentDetails for immediate status checks.

Configuration

Step 1: Generate Webhook Credentials

  1. Log into the Merchant Portal
  2. Navigate to Developer > Webhooks
  3. Click "Generate webhooks keys"
  4. Store the Secret Webhook Key immediately
Security Warning

The secret key displays for 60 seconds only. Store it securely. Regenerating keys revokes existing ones immediately.

Step 2: Define Webhook Endpoints

Option A: Merchant Portal (Hard-coded)

  1. Go to Developer > Webhooks
  2. Click "Add webhook endpoint"
  3. Add up to five URLs

Option B: Per-Request (Dynamic)

Include endpoints in your API request:

{
"order": { ... },
"feedbacks": {
"webhookUrls": [
"https://yoursite.com/webhooks/primary",
"https://yoursite.com/webhooks/backup"
]
}
}
Multi-webshop

Dynamic webhooks are recommended for multi-webshop environments where each shop needs different notification URLs.

Step 3: Build Webhook Handler

Your HTTPS endpoint must:

  • Accept POST requests with JSON body
  • Verify message signatures
  • Respond with 2xx HTTP status code
[HttpPost("webhook")]
public async Task<IActionResult> HandleWebhook()
{
// Read the raw request body
using var reader = new StreamReader(Request.Body);
var body = await reader.ReadToEndAsync();

// Get signature headers
var keyId = Request.Headers["X-GCS-KeyId"].FirstOrDefault();
var signature = Request.Headers["X-GCS-Signature"].FirstOrDefault();

// Verify and parse the 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)
{
// Process the event
ProcessWebhookEvent(webhookEvent);
}

// Always return 2xx immediately
return Ok();
}

private void ProcessWebhookEvent(WebhooksEvent webhookEvent)
{
var paymentId = webhookEvent.Payment?.Id;
var eventType = webhookEvent.Type;
var statusCode = webhookEvent.Payment?.StatusOutput?.StatusCode;

// Update your database asynchronously
// Don't block the webhook response
}
Best Practice

Decouple your business logic from webhook handling. Return a 2xx status code immediately, then process the event asynchronously to prevent false delivery failures.

Webhook Events

Payment Events

EventDescriptionStatus Code
payment.createdTransaction created0
payment.redirectedCustomer redirected to 3DS46
payment.authorization_requestedAuth request sent51
payment.pending_approvalAwaiting merchant approval56
payment.pending_captureAuthorization complete, awaiting capture5
payment.capture_requestedCapture queued91
payment.capturedCapture confirmed9
payment.rejectedTransaction declined2
payment.rejected_captureCapture rejected93
payment.cancelledTransaction cancelled1, 6
payment.refundedRefund processed8

Refund Events

EventDescription
refund.refund_requestedRefund queued
EventDescription
paymentlink.createdPayment link generated
paymentlink.clickedPayment link accessed
paymentlink.paidSuccessful payment via link
paymentlink.cancelledPayment link cancelled
paymentlink.expiredLink expired

Sample Webhook Messages

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"
}

Handling Duplicates

Duplicate webhook events are a feature of reliable delivery, not an error. Design your system to handle them:

public async Task ProcessWebhookEvent(WebhooksEvent webhookEvent)
{
var eventId = webhookEvent.Id;
var paymentId = webhookEvent.Payment?.Id;
var eventType = webhookEvent.Type;

// Check if already processed
if (await IsEventProcessed(eventId))
{
return; // Skip duplicate
}

// Mark as processed
await MarkEventProcessed(eventId);

// Process the event
await HandleEvent(paymentId, eventType);
}

Retry Strategy

If delivery fails, the platform retries five times:

AttemptDelay
InitialImmediate
Retry 110 minutes
Retry 21 hour
Retry 32 hours
Retry 48 hours
Retry 524 hours

Each retry includes a retry-count header (0 for initial attempt).

Testing Webhooks

Validate Credentials

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"
}'

Send Test Message

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"
}'

The platform sends a test webhook with type: "payment.test".

Payment ID Changes

The payment.id changes during maintenance operations:

Operationpayment.idstatusCode
Authorizationpayment.id15
Capturepayment.id29
Refundpayment.id38

Use GetPaymentDetails to track operations.id across the transaction lifecycle.

Best Practices

  1. Return 2xx Immediately - Don't block on business logic
  2. Handle Duplicates - Use event ID for idempotency
  3. Process Asynchronously - Queue events for background processing
  4. Implement Fallbacks - Use GetPaymentDetails as backup
  5. Verify Signatures - Always validate webhook authenticity
  6. Log Everything - Keep detailed logs for debugging
  7. Monitor Failures - Alert on repeated delivery failures

Next Steps