Links

Webhooks

Webhooks provide a convenient way to establish a notification system for receiving updates on specific requests made to the Lahza API.

Introduction

Typically, when making a request to an API endpoint, a prompt response is anticipated. However, certain requests may require a longer processing time, potentially resulting in timeout errors. To mitigate such errors, an interim response known as a pending response is returned. To obtain the final status of the request and update your records accordingly, you have two options:
  1. 1.
    Polling: You can periodically make requests to check for updates on the request by polling the API endpoint. or,
  2. 2.
    Webhooks: Alternatively, you can set up a webhook URL to listen for events. This way, you will receive automatic notifications whenever there is an update on the request, eliminating the need for continuous polling.
Choose webhooks over callbacks or polling for better control and reliability. Callbacks can fail due to network issues or device shutdown, while webhooks provide more consistent and efficient notifications.

Polling vs Webhooks

To obtain the final status of a request through polling, you need to regularly send GET requests at specific intervals. For instance, when a customer makes a payment for a transaction, you would continuously request the transaction status until it becomes successful.
On the other hand, webhooks enable the resource server (such as Lahza) to send updates to your server whenever there is a change in the request status. These status changes are referred to as events, which you can conveniently listen to on a designated POST endpoint known as your webhook URL.
The table below summarizes the disparities between polling and webhooks:
Text
Polling
Webhooks
Mechanism
Periodically sending GET requests for status updates
Receiving automatic updates from the resource server
Workflow
Continuously checking for updates
Listening for events through a webhook URL
Efficiency
Requires frequent requests, resulting in higher API usage
Reduces API usage as updates are pushed from the resource server
Real-time
Updates may not be real-time due to polling intervals
Real-time updates as they are pushed from the resource server
Reliability
Relies on successful API requests and network connectivity
More reliable as updates are directly sent by the resource server

Create a webhook URL

A webhook URL is essentially a POST endpoint where a resource server sends updates. This URL should be capable of parsing a JSON request and responding with a 200 OK status code.
Node.JS
PHP
Python
1
const express = require('express');
2
const app = express();
3
4
app.use(express.json());
5
6
app.post('/webhook', (req, res) => {
7
const data = req.body;
8
// Process the data or perform desired actions
9
// ...
10
res.sendStatus(200);
11
});
12
13
app.listen(3000, () => {
14
console.log('Webhook server is running on port 3000');
15
});
1
$data = json_decode(file_get_contents('php://input'), true);
2
// Process the data or perform desired actions
3
// ...
4
5
http_response_code(200);
1
from flask import Flask, request
2
3
app = Flask(__name__)
4
5
@app.route('/webhook', methods=['POST'])
6
def handle_webhook():
7
data = request.get_json()
8
# Process the data or perform desired actions
9
# ...
10
return '200 OK'
11
12
if __name__ == '__main__':
13
app.run()
Once your webhook URL receives an event, it must parse and acknowledge the event by returning a 200 OK status in the HTTP header. If the response header does not contain a 200 OK status, we will continue sending events for the next 72 hours.
  • In live mode, webhooks are initially sent every 3 minutes for the first 4 attempts. After that, the frequency switches to hourly for the next 72 hours.
  • In test mode, webhooks are sent hourly for the next 72 hours.
To ensure proper handling of long-running tasks within your webhook function, it is essential to acknowledge the event before executing those tasks. Failure to do so may result in a request timeout and an automatic error response from your server. Remember that a 200 OK response is crucial to avoid retries, as described in the previous paragraph.

Verify event origin

To maintain the security and integrity of your webhook URL, it is crucial to verify that the events originate from Lahza rather than from malicious actors. You can employ two methods to ensure the authenticity of events sent to your webhook URL:
  1. 1.
    Signature Validation
  2. 2.
    IP Whitelisting

Signature validation

Lahza sends events with the x-lahza-signature header, which contains a HMAC SHA256 signature of the event payload. Before processing the event, it is important to verify this header signature to ensure the integrity of the payload.
NodeJS
PHP
C#
Java
Python
const crypto = require('crypto');
const secret = process.env.SECRET_KEY;
app.post("/my/webhook/url", (req, res) => {
// Validate event
const hash = crypto.createHmac('sha256', secret).update(req.body).digest('hex');
if (hash === req.headers['x-lahza-signature']) {
// Retrieve the request's body
const event = req.body;
// Do something with the event
// ...
}
res.sendStatus(200);
});
1
$secretKey = 'YOUR_SECRET_KEY';
2
3
// Retrieve the payload and signature from the request
4
$payload = file_get_contents('php://input');
5
$signature = $_SERVER['HTTP_X_LAHZA_SIGNATURE'];
6
7
// Verify signature
8
$hash = hash_hmac('sha256', $payload, $secretKey);
9
if ($hash === $signature) {
10
// Signature is valid, process the event
11
$event = json_decode($payload, true);
12
// Do something with the event
13
// ...
14
}
15
16
http_response_code(200);
17
1
using System;
2
using System.Security.Cryptography;
3
using System.Text;
4
5
public class WebhookVerification
6
{
7
private const string SecretKey = "YOUR_SECRET_KEY";
8
9
public static void Main(string[] args)
10
{
11
string payload = // Retrieve the payload from the request
12
string signature = // Retrieve the signature from the request headers
13
14
// Verify signature
15
string hmacDigest = CalculateHmac(payload, SecretKey, "HMACSHA256");
16
if (hmacDigest.Equals(signature))
17
{
18
// Signature is valid, process the event
19
// Do something with the event
20
// ...
21
}
22
}
23
24
private static string CalculateHmac(string payload, string secretKey, string algorithm)
25
{
26
byte[] keyBytes = Encoding.UTF8.GetBytes(secretKey);
27
byte[] payloadBytes = Encoding.UTF8.GetBytes(payload);
28
29
using (HMACSHA512 hmac = new HMACSHA512(keyBytes))
30
{
31
byte[] hmacBytes = hmac.ComputeHash(payloadBytes);
32
return BitConverter.ToString(hmacBytes).Replace("-", string.Empty).ToLower();
33
}
34
}
35
}
36
1
import javax.crypto.Mac;
2
import javax.crypto.spec.SecretKeySpec;
3
import java.security.MessageDigest;
4
import java.util.Base64;
5
import java.nio.charset.StandardCharsets;
6
7
public class WebhookVerification {
8
private static final String SECRET_KEY = "YOUR_SECRET_KEY";
9
10
public static void main(String[] args) {
11
String payload = // Retrieve the payload from the request
12
String signature = // Retrieve the signature from the request headers
13
14
// Verify signature
15
String hmacDigest = calculateHmac(payload, SECRET_KEY, "HmacSHA256");
16
if (hmacDigest.equals(signature)) {
17
// Signature is valid, process the event
18
// Do something with the event
19
// ...
20
}
21
}
22
23
private static String calculateHmac(String payload, String secretKey, String algorithm) {
24
try {
25
Mac mac = Mac.getInstance(algorithm);
26
SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), algorithm);
27
mac.init(keySpec);
28
byte[] hmacBytes = mac.doFinal(payload.getBytes(StandardCharsets.UTF_8));
29
return Base64.getEncoder().encodeToString(hmacBytes);
30
} catch (Exception e) {
31
e.printStackTrace();
32
return null;
33
}
34
}
35
}
36
1
import hashlib
2
import hmac
3
from flask import Flask, request
4
5
app = Flask(__name__)
6
secret_key = 'YOUR_SECRET_KEY'
7
8
@app.route('/my/webhook/url', methods=['POST'])
9
def handle_webhook():
10
payload = request.get_data()
11
signature = request.headers.get('x-lahza-signature')
12
13
# Verify signature
14
hmac_digest = hmac.new(secret_key.encode(), payload, hashlib.sha256).hexdigest()
15
if hmac.compare_digest(hmac_digest, signature):
16
# Signature is valid, process the event
17
event = request.json
18
# Do something with the event
19
# ...
20
21
return 'OK'
22
23
if __name__ == '__main__':
24
app.run()

IP whitelisting

To restrict access to your webhook URL and only allow requests from specific IP addresses, it is recommended to whitelist the following IP addresses for Lahza:
  • 1
  • 2
  • 3
By whitelisting these IP addresses and blocking requests from other IP addresses, you can ensure that only legitimate requests from Lahza are accepted, while considering requests from other IP addresses as potentially unauthorized.
Whitelisting is Domain Independent:
It's important to note that the IP addresses mentioned above are applicable to both the test and live environments. You can whitelist these IP addresses in both your staging and production environments, ensuring consistent and secure webhook handling across different stages of your application.

Go live checklist

To ensure a smooth experience with your webhook URL, consider the following suggestions:
  1. 1.
    Add the webhook URL on your Lahza dashboard: Make sure to register and configure your webhook URL in your Lahza dashboard. This allows Lahza to send event updates to your specified URL.
  2. 2.
    Ensure your webhook URL is publicly available: It is important that your webhook URL is accessible from the internet. Localhost URLs cannot receive webhook events. Ensure that your webhook URL is publicly accessible for successful event delivery.
  3. 3.
    Trailing slash in .htaccess (if applicable): If you are using .htaccess to handle URL rewriting, remember to include a trailing slash (/) at the end of the URL to ensure proper routing.
  4. 4.
    Test your webhook: Before deploying your webhook in a production environment, test it to ensure that you receive the JSON body of the events and respond with a 200 OK HTTP status code. This confirms that your webhook is functioning correctly.
  5. 5.
    Handle long-running tasks: If your webhook function involves long-running tasks, it is recommended to acknowledge the webhook event by returning a 200 OK response before proceeding with those tasks. This prevents request timeouts and allows for efficient processing of events.
  6. 6.
    Failure handling: If Lahza does not receive a 200 OK HTTP response from your webhook, it will be considered a failed attempt. In live mode, failed attempts are retried every 3 minutes for the first 4 tries. After that, the retry interval switches to hourly for the next 72 hours. Similarly, in test mode, failed attempts are retried hourly for the next 72 hours.
By following these guidelines, you can ensure a seamless and reliable webhook integration with Lahza.

Supported events

First Tab
Second Tab

Types of events

Event
Description
charge.success
A successful charge was made
refund.failed
Refund cannot be processed. Your account will be credited with refund amount
refund.pending
Refund initiated, waiting for response from the processor.
refund.processed
Refund has successfully been processed by the processor.
refund.processing
Refund has been received by the processor.