# Webhooks Webhooks are used to send real-time events to an HTTP endpoint, for instance when a payment is paid. You can create webhooks and setup an endpoint to receive these events. Examples of events: - Payment has been paid, `payment.paid` - Chargeback created, `chargeback.created` - Subscription details updated, `subscription.updated` ## Getting started To receive events from webhooks you have to do the following: 1. Create a webhook endpoint handler to receive event POST requests. 2. Create a Webhook with an endpoint pointing to your webhook endpoint handler. You can create Webhooks in our [Dashboard](https://app.paypro.nl/developers/webhooks) or through the [API](/reference/api/webhooks/createwebhook). ## Webhook endpoint handler Set up an HTTPS endpoint that can accept POST requests from PayPro. This endpoint must use HTTPS to ensure the connection is secure. The handler you build must do three things: 1. Read the raw POST body and parse the JSON consisting of an Event object. 2. Verify that the webhook is correct and not tampered with. 3. Return a success status code (200). You should do this in a timely manner before the request can timeout. ### Example endpoint This code snippet of a webhook endpoint handler reads the POST body, parses the JSON and handles Event object. If successful it will return a 200 response. PHP ```php header('PayPro-Signature'); $timestamp = $request->header('PayPro-Timestamp'); // The secret of the Webhook which is used to generate the signature. // You can find this in the PayPro dashboard and every webhook has its own secret. $secret = '...'; $event = null; // Try to create an Event object based on the request. Here we also verify that // the signature send is valid. try { $event = new PayPro\Webhook::createEvent( $request->json()->all(), $signature, $secret, $timestamp ); } catch(PayPro\Exception\ApiErrorException $e) { return response(null, 400); } // Check event type and call the correct method. switch ($event->event_type) { case 'payment.paid': // Handle payment.paid event $this->handlePaymentPaid($event->payload); break; case 'refund.refunded': // Handle refund.refunded event $this->handleRefundRefunded($event->payload); break; } return response(null, 200); } } ``` Ruby ```ruby # We are using the Rails framework class WebhookController < ApplicationController # Make sure we don't do CSRF protection for the webhook endpoint protect_from_forgery except: :index def index signature = request.headers['HTTP_PAYPRO_SIGNATURE'] timestamp = request.headers['HTTP_PAYPRO_TIMESTAMP'] # The secret of the Webhook which is used to generate the signature. # You can find this in the PayPro dashboard and every webhook has its own secret. secret = '...' event = nil # Try to create an Event object based on the request. Here we also verify that # the signature send is valid. begin json = JSON.parse(request.raw_post) event = PayPro::Webhook.create_event( json, signature, secret, timestamp ) rescue PayPro::Error head :bad_request end # Check event type and call the correct method. case event.event_type when 'payment.paid' # Handle payment.paid event handle_payment_paid(event.payload) when 'refund.refunded' # Handle refund.refunded event handle_refund_refunded(event.payload) end head :ok end end ``` ## Verify signature manually Although we recommend that you use our official libraries to verify webhook signatures, it is possible to do this manually when necessary. The webhook request will include two headers that are used to verify if webhook is valid. 1. `PayPro-Signature`, containing the signature of webhook request 2. `PayPro-Timestamp`, containing the timestamp of when the webhook was sent The signature is generated using [HMAC](https://en.wikipedia.org/wiki/HMAC) using the [SHA-256](https://en.wikipedia.org/wiki/SHA-2) hasing algorithm. Another important variable is the secret of the webhook. This secret is used as the key in the HMAC computation. To manually verify the webhook signature you have to follow the following steps: ### Step 1: Get the signature and timestamp headers from the request The headers `PayPro-Signature` and `PayPro-Timestamp` should be extracted from the webhook request. ### Step 2: Create the payload string To create the `payload` for the HMAC computation we need to concat the following strings: - The timestamp from the `PayPro-Timestamp` header - The character `.` - The JSON body of the request (unaltered) ### Step 3: Generate the signature to compare with Compute an HMAC with the SHA-256 hash function. You must use the webhook secret as the key, and use the `payload` string as the data. You can find your webhook secrets in the [Dashboard](https://app.paypro.nl/developers/webhooks) or by [retrieving the webhook](/reference/api/webhooks/retrievewebhook) from the API. ### Step 4: Compare the signatures You can now compact the signature you generated to the signature in the `PayPro-Signature` header. If these are equal the webhook is valid. The `PayPro-Timestamp` can be compared to the current timestamp to determine if the webhook was received in the tolerable time. Our libraries use 10 minutes. Warning To protect yourself from timing attacks you should make sure to use a constant-time string comparison function. ## Webhook behavior This section explains certain webhook behaviors which are important to take into mind when developing a webhook endpoint handler. ### Retry behavior PayPro will try multiple times for over 3 days with exponential back off to deliver the webhooks. PayPro only considers a successful webhook request when we get a HTTP status code in the `2xx` range back from your webhook endpoint handler. ### Event ordering PayPro doesn't guarantee the order of delivery events are in the order the events were generated. Your webhook endpoint handler shouldn't expect the order of events and handle cases where the events were missed to fetch events or resources from our API. ### Duplicates In some cases it can happen that an event is received twice by your endpoint handler. You should be aware of this and handle these cases. All events have a unique ID which you could use to deduplicate them.