Webhooks
Webhook notifications
Payabbhi can send a webhook notification to your configured endpoint URL when a specific event
occurs within Payabbhi related to your account. This allows your application to respond programmatically to an event
within Payabbhi e.g. when the event Order.paid
occurs, your application endpoint receives a webhook event and may respond programmatically with an appropriate action like updating your application database.
Events
Payabbhi creates an Event object whenever a business event
occurs within Payabbhi e.g. when a successful Payment
is made against an Invoice
, Payabbhi creates an Event object of type payment.captured
.
This Event object describes the event
in terms of the event type and the data associated with the event. The data typically contains the state of the entity at the point when the event occurs e.g. payment.captured
event contains a payment
object and order.paid
event contains an order
object.
Types of Events
Refer to Types of events for the full list of event types currently available.
You may access the list of Events at Portal > Developers > Events. Click on any event for the details including the Event object in JSON format.
Usage scenarios
The primary usage scenario of webhooks is when you want your application to receive notification for asynchronous changes. Most changes or events within Payabbhi would happen synchronously e.g. an API invocation would trigger an event with a synchronous response. However there are cases when changes take place asynchronously e.g. when a customer makes a successful payment against an Invoice. In these cases, Webhook events can notifiy your application.
Webhook configuration
You may configure webhook endpoints from Portal > Developers > Webhooks.
- Add endpoint
- Provide endpoint URL
- Choose the specific event/s for receiving webhook notification
- List configured Webhook endpoints
- Update or deactivate a Webhook endpoint
Events & webhook configurations are separate for live
and test
modes. In other words, you need to configure two separate Webhook endpoints even if you want to receive webhook notifications for the same event in both live
and test
modes.
Payabbhi can be configured to send the same webhook event to multiple endpoints.
Processing a Webhook
When an event occurs, Payabbhi will send the event payload as part of a HTTP POST request to the endpoint/s as per your account’s Webhook configuration. The event payload is sent as JSON in the POST request body. Your application can parse the JSON into an Event object for further processing.
Webhook Signature verification
It is recommended to verify Webhook signature before processing a Webhook event.
Your application is expected to return 2XX HTTP Status code to indicate successful receipt of the Webhook to Payabbhi within a time-window of 10 seconds. Any other information returned via request headers or in the request body is not considered for determining webhook delivery status.
In case there is a possibility of your application logic resulting in time-out, it is better to return HTTP Status code in the range of 2XX before the processing begins to ensure successful delivery status.
PHP
<?php
// This example uses slim framework (https://www.slimframework.com) to receive and process webhooks
require 'vendor/autoload.php';
use \Slim\App;
$app = new App();
$app->post('/my/webhook/url/', function ($request, $response, $args) {
// Retrieve the signature from request header
$signature = $request->getHeaderLine('Payabbhi-Signature');
$body = $request->getBody();
// Make sure to set the ACCESS_ID, SECRET_KEY and WEBHOOK_ENDPOINT_SECRET environment variables
$accessId = getenv('ACCESS_ID');
$secretKey = getenv('SECRET_KEY');
$webhookSecret = getenv('WEBHOOK_ENDPOINT_SECRET');
try {
$client = new \Payabbhi\Client($accessId, $secretKey);
$result = $client->utility->verifyWebhookSignature($body, $signature, $webhookSecret);
// Now proceed with processing the event
// ...
} catch (\Payabbhi\Error\SignatureVerification $e) {
$er = $response->withStatus(400);
return $er->write("FAILED");
}
// Send a 200 response
return $response->write("OK");
});
$app->run();
Python
# This example uses Flask framework (http://flask.pocoo.org/) to receive and process webhooks
from flask import Flask
from flask import request
import os
import payabbhi
app = Flask(__name__)
@app.route('/my/webhook/url', methods=['POST'])
def process_webhook():
# Make sure to set the ACCESS_ID, SECRET_KEY and WEBHOOK_ENDPOINT_SECRET environment variables
client = payabbhi.Client(access_id=os.environ['ACCESS_ID'], secret_key=os.environ['SECRET_KEY'])
# Retrieve the signature from request header
signature = request.headers.get('Payabbhi-Signature')
jsonEvent = request.get_data()
secret = os.environ['WEBHOOK_ENDPOINT_SECRET']
try:
result = client.utility.verify_webhook_signature(jsonEvent,signature,secret)
# Now proceed with processing the event
# ...
except payabbhi.error.SignatureVerificationError as e:
abort(400)
# Send a 200 response back
return 'OK'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=3000, debug=False)
Java
// This example uses spark framework (http://sparkjava.com/) to receive and process webhooks
import static spark.Spark.*;
import spark.Request;
import com.payabbhi.Payabbhi;
import com.payabbhi.exception.PayabbhiException;
public class Example {
public static void main(String[] args) {
// Make sure to set the ACCESS_ID, SECRET_KEY and WEBHOOK_ENDPOINT_SECRET environment variables
Payabbhi.accessId = System.getenv("ACCESS_ID");
Payabbhi.secretKey = System.getenv("ASECRET_KEY");
port(3000);
post("/my/webhook/url", (request, response) -> {
// Retrieve the signature from request header
String signature = request.headers("Payabbhi-Signature");
String jsonEvent = request.body();
String secret = System.getenv("WEBHOOK_ENDPOINT_SECRET");
try {
boolean result = Payabbhi.verifyWebhookSignature(jsonEvent,signature,secret);
// Now proceed with processing the event
// ...
} catch (PayabbhiException e) {
response.status(400);
return "";
}
// Send a 200 response
response.status(200);
return "OK";
});
}
}
.Net
// This example uses .NET Core (https://dotnet.microsoft.com/download) to receive and process webhooks
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Payabbhi;
namespace Example
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.Run(async (context) =>
{
if (context.Request.Method == "POST")
{
// Retrieve the signature from request header
var signature = context.Request.Headers["Payabbhi-Signature"];
var jsonEvent = new StreamReader(context.Request.Body).ReadToEnd();
// Make sure to set the ACCESS_ID, SECRET_KEY and WEBHOOK_ENDPOINT_SECRET environment variables
var accessID = Environment.GetEnvironmentVariable("ACCESS_ID");
var secretKey = Environment.GetEnvironmentVariable("SECRET_KEY");
var webhookSecret = Environment.GetEnvironmentVariable("WEBHOOK_ENDPOINT_SECRET");
try
{
Client client = new Client(accessID, secretKey);
Boolean result = client.Utility.VerifyWebhookSignature(jsonEvent,
signature, webhookSecret);
// Now proceed with processing the event
// ...
}
catch (Payabbhi.Error.SignatureVerificationError e)
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync("Failed");
}
// Send a 200 response back
await context.Response.WriteAsync("OK");
}
else
{
context.Response.StatusCode = 404;
await context.Response.WriteAsync("Not Supported");
}
});
}
}
}
Node.js
// This example uses express framework (https://expressjs.com/) to receive and process webhooks
const express = require('express')
const app = express()
const bodyParser = require('body-parser');
// Make sure to set the ACCESS_ID, SECRET_KEY and WEBHOOK_ENDPOINT_SECRET environment variables
const payabbhi = require('payabbhi')(process.env.ACCESS_ID, process.env.SECRET_KEY);
app.use(bodyParser.raw({type: 'application/json'}));
app.post('/my/webhook/url', (req, res) => {
// Retrieve the signature from request header
var signature = req.get('Payabbhi-Signature');
try {
var result = payabbhi.verifyWebhookSignature(
req.body, // Contains the event JSON
signature,
process.env.WEBHOOK_ENDPOINT_SECRET);
// Now proceed with processing the event
// ...
}catch(e) {
res.status(400).end()
}
// Send a 200 response
res.json({received: true});
})
app.listen(3000, () => console.log('Running on port 3000'));
Idempotent Processing
It is recommended that your Webhook event processing is idempotent. In other words, your processing logic should consider the possibility of receiving the same event more than once and be capable of ignoring events that have already been processed once.
Webhook Delivery status
Payabbhi attempts to deliver webhooks for up to 24 hours with exponential backoff. Failure to deliver a webhook results in an email notification to the Profile > Contact Details > Email ID
.
You may check for Webhook delivery status via Portal > Developers > Events. Click on any event for a list of all attempted Webhook/s and the corresponding status based on the HTTP Status codes received.
Webhook signature verification
In order to help you validate that a webhook event is indeed sent from Payabbhi, we provide a signature in the Payabbhi-Signature
header of the HTTP post request. The Webhook signature needs to be verified at the Merchant’s end using server-side code. This verification is typically done using a utility method provided in any one of our Client Libraries.
Webhook Endpoint secret
Each Webhook endpoint has a unique secret
. This secret
is needed for signature verification.
Navigate to Portal > Developers > Webhooks.
For any given webhook endpoint, click on the icon in the Secret
field to reveal the value.
In case of multiple endpoints, each endpoint will have its own secret
and therefore these steps need to be repeated for each endpoint.
Verification using a Client Library
It is recommended to use the utility method provided in any one of our Client Libraries for Webhook signature verification. The utility method needs to be provided with the event payload, the Payabbhi-Signature
header, and the endpoint’s secret. If verification fails, an error is returned. You may examine the exception/error details for debugging purposes, typically during development.
Default Tolerance
Default value of replayInterval in Client libraries is 300 seconds.
The verification method in respective libraries typically accepts an optional parameter ‘replayInterval’, if you wish to override the default value.
Refer to Webhook event processing for full working examples including webhook signature verification.
PHP
$signature = $request->getHeaderLine('Payabbhi-Signature');
$body = $request->getBody();
try {
$client = new \Payabbhi\Client(<accessId>, <secretKey>);
$result = $client->utility->verifyWebhookSignature($body, $signature, <WEBHOOK_ENDPOINT_SECRET>);
// verification passed
} catch (\Payabbhi\Error\SignatureVerification $e) {
// verification failed
}
Python
# Retrieve the signature from request header
signature = request.headers.get('Payabbhi-Signature')
jsonEvent = request.get_data()
try:
result = client.utility.verify_webhook_signature(jsonEvent,signature,<WEBHOOK_ENDPOINT_SECRET>)
# verification passed
except payabbhi.error.SignatureVerificationError as e:
# verification failed
Java
// Retrieve the signature from request header
String signature = request.headers("Payabbhi-Signature");
String jsonEvent = request.body();
try {
boolean result = Payabbhi.verifyWebhookSignature(jsonEvent,signature,<WEBHOOK_ENDPOINT_SECRET>);
// verification passed
} catch (PayabbhiException e) {
// verification failed
}
.Net
// Retrieve the signature from request header
var signature = context.Request.Headers["Payabbhi-Signature"];
var jsonEvent = new StreamReader(context.Request.Body).ReadToEnd();
try
{
Client client = new Client(<accessID>, <secretKey>);
Boolean result = client.Utility.VerifyWebhookSignature(jsonEvent,
signature,
WEBHOOK_ENDPOINT_SECRET);
// Verification passed
}
catch (Payabbhi.Error.SignatureVerificationError e)
{
// Verification failed
}
Node.js
// Retrieve the signature from request header
var signature = req.get('Payabbhi-Signature');
try {
var result = payabbhi.verifyWebhookSignature(
req.body, // Contains the event JSON
signature,
<WEBHOOK_ENDPOINT_SECRET>);
// Verification passed
}catch(e) {
// Verification failed
}
Preventing replay attacks
A replay attack is when a valid payload is intercepted and then re-transmitted with a malicious intent. As a counter-measure against replay attacks, Payabbhi includes a timestamp in the Payabbhi-Signature
header. Any change in the timestamp therefore invalidates the signature since the timestamp is part of the signed payload. The timestamp also allows you to optionally add logic to reject a Payabbhi webhook payload even when the signature is valid.
The default value used by our Client libraries for acceptable time interval between the timestamp and the current time is 5 minutes
. if you wish to override the default value with your own interval, the utility method for Webhook signature verification accepts an additional parameter replay_interval
.
Payabbhi generates the timestamp and signature each time we send an event to your endpoint. This means that every retry attempt for a Webhook event would have a new signature and timestamp.
Manual verification of Webhook signature
The verification is typically done using a utility method provided in any one of our Client Libraries. However, if you wish to create your own implementation, the following section provides the necessary information.
Step 1: Extract timestamp and signature from the header.
The value for the prefix t corresponds to the timestamp, and v1 corresponds to the signature.
Sample header
Payabbhi-Signature: t=1543720056, v1=123a456b789c123a456b789c123a456b789c123a456b789c123a456b789c123a456b789c
Step 2: Concatenate the values to get the event payload.
String event_payload = CONCATENATE(json_payload, "&" , timestamp)
Step 3: Compute the HMAC hex digest using SHA256 algorithm with the concatenated string as message and endpoint secret as key.
generated_signature_at_merchant_end = hmac_sha256(event_payload, endpoint_secret)
Step 4: Compare signatures & determine if time difference is within permissible limits
If the generated signature at the Merchant’s end matches the signature in the payload, the verification check is a pass. You can now compute the difference between the current timestamp and the payload timestamp and use your own logic to determine if the difference is within permissible time limits.
Boolean matches = (generated_signature_at_merchant_end == payabbhi_signature)
Boolean time_difference_ok = (current_timestamp - timestamp) > your_replay_interval