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:
- Create a webhook endpoint handler to receive event POST requests.
- Create a Webhook with an endpoint pointing to your webhook endpoint handler.
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:
- Read the raw POST body and parse the JSON consisting of an Event object.
- Verify that the webhook is correct and not tampered with.
- 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
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use PayPro\Webhook;
use PayPro\Exception\ApiErrorException;
// We are using the Laravel framework
class WebhookController extends Controller
{
public function index(Request $request)
{
$signature = $request->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);
}
}
# 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.
PayPro-Signature
, containing the signature of webhook requestPayPro-Timestamp
, containing the timestamp of when the webhook was sent
The signature is generated using HMAC using the SHA-256 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 contact 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
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.