Introduction
Learn how to listen to events whenever certain actions occur on your integration.
What are webhooks?
During a transaction lifecycle, Klump sends events that your application can listen to. A webhook is an accessible URL on your server to which we send payloads. For example, Klump sends two webhook events; when a transaction is initiated klump.payment.transaction.initiated
, klump.payment.transaction.abandoned
for when a transaction has been abandoned and when it's completed klump.payment.transaction.successful
.
By using webhooks, you can tightly integrate your backend application with Klump. The platform currently supports one kind of webhook: Transaction. Webhook follow a common set of rules:
- Webhook should be reachable from the public internet. During development and you are on localhost, tunnelling services like Ngrok are supported.
- Webhook should accept HTTP POST requests with JSON payload
- Webhook should respond with response codes 200 or 201
- Webhook should respond as fast as possible.
- Webhook should be ready to accept the same call multiple times: in case of network or remote server failure.
Klump will retry the request every hour for the next 72 hours. If after 72 hours Klump doesn't get a positive response(200 or 201) from the server, the request will be abandoned.
Abandoned Transaction
An abandoned transaction can come always become successful, this happens when a user goes back to pay for an abandoned transaction via the periodic reminder email that Klump sends. And the this happens, the webhook event changes accordingly.
All webhook requests contain these headers:
Name | Description | Example |
---|---|---|
X-Klump-Signature | HMAC signature of the request body. See Signature section | ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb |
X-Klump-Webhook-Id | Unique ID of the webhook call. This value in consistent between retries and could be used to deduplicate retry calls | 65843e9e-b12d-4120-8d1b-34abea95695e |
X-Klump-Webhook-Attempt | Number of webhook request attempt starting from 1 | 1 |
Security and Performance
We highly recommend following common security guidelines to make your webhook integration safe and fast:
- Use HTTPS with a certificate from a trusted authority (eg. Let's Encrypt, Cloudflare)
- Verify the "X-Klump-Signature" header
- Support HTTP Keep-Alive
- Be highly available
- Offload the processing of the message if possible to a background job.
Signature and webhook verification
All HTTP requests can be verified as coming from Klump (and not tampered with by a 3rd party) by analyzing the signature attached to the request. Every request includes an HTTP header called "X-Klump-Signature" containing a cryptographic signature of the message. Your webhook endpoint can validate that payload and signature match.
Sample code to validate and respond to a webhook
const crypto = require('crypto');
const secret = process.env.KLUMP_SECRET_KEY;
/**
* Grab the body of the event.
*/
const { body } = req;
const hash = crypto.createHmac('sha512', secret).update(JSON.stringify(body)).digest('hex');
if (hash === req.headers['x-klump-signature']) {
/**
* The request is verified and it's coming from Klump
* Go ahead and process it. While at it, return a 200 status code. when you are done.
*/
res.status(200).send('OK');
}
$klump_event_payload = json_decode(file_get_contents('php://input'), true);
$hash = hash_hmac('sha512', $klump_event_payload, $klump_secret_key);
if ($hash === $_SERVER['x-klump-signature']) {
/**
* The request is verified and it's coming from Klump
* Go ahead and process it. While at it, return a 200 status code. when you are done.
*/
header("HTTP/1.1 200 OK");
http_response_code(200);
}
Responding to an event
You should respond to a webhook event with a 200 OK. We consider this an acknowledgement of your application. If your application responds with any status outside of the 2xx range, we will consider it unacknowledged and thus, continue to send it every hour for 72 hours.
Supported event
Below are some of the supported webhook events on Klump today. Please note, when you get the klump.payment.transaction.initiated
you aren't meant to do anything. It's just an FYI.
{
"event":"klump.payment.transaction.initiated",
"data":{
"id":"d1c4e30f-bb2b-4d72-8701-f9ce3ef51b62",
"reference":"KLP-1643-891092-6351",
"amount":1195.48,
"currency":"NGN",
"meta_data":{},
"status":"new",
"is_live":false,
"shipping_fee":100,
"created_at":"2022-02-03T12:24:52.647Z",
"items":[
{
"id":"13b6be02-4a41-4227-b081-b58ebc6bccf9",
"transaction_id":"2dab6b4e-7db5-4dcc-bc03-470e3a417e97",
"name":"Timbeland",
"image_url":null,
"item_url":"http://omin.com/andndnd",
"unit_price":"300.00",
"quantity":1,
"created_at":"2022-02-05T17:59:22.537Z",
"updated_at":null
},
{
"id":"dd6f96fa-9f06-4930-a1fb-47d690a40478",
"transaction_id":"2dab6b4e-7db5-4dcc-bc03-470e3a417e97",
"name":"Orange Fruit",
"image_url":null,
"item_url":"http://omin.com/andndnd",
"unit_price":"300.00",
"quantity":1,
"created_at":"2022-02-05T17:59:22.537Z",
"updated_at":null
},
{
"id":"0234f2a1-2fb7-4b83-90ee-650327dbbda4",
"transaction_id":"2dab6b4e-7db5-4dcc-bc03-470e3a417e97",
"name":"Kiwi Fruit",
"image_url":null,
"item_url":"http://omin.com/andndnd",
"unit_price":"400.00",
"quantity":1,
"created_at":"2022-02-05T17:59:22.537Z",
"updated_at":null
}
],
"customer":{
"id":"6ea945c5-0575-473f-8ff9-0baed42cef94",
"firstname":"Garret",
"lastname":"Omin",
"email":"[email protected]",
"phone":"+2348122632296"
}
}
}
{
"event":"klump.payment.transaction.successful",
"data":{
"id":"d1c4e30f-bb2b-4d72-8701-f9ce3ef51b62",
"reference":"KLP-1643-891092-6351",
"amount":1195.48,
"currency":"NGN",
"meta_data":{},
"status":"successful",
"is_live":false,
"shipping_fee":100,
"created_at":"2022-02-03T12:24:52.647Z",
"items":[
{
"id":"13b6be02-4a41-4227-b081-b58ebc6bccf9",
"transaction_id":"2dab6b4e-7db5-4dcc-bc03-470e3a417e97",
"name":"Timbeland",
"image_url":null,
"item_url":"http://omin.com/andndnd",
"unit_price":"300.00",
"quantity":1,
"created_at":"2022-02-05T17:59:22.537Z",
"updated_at":null
},
{
"id":"dd6f96fa-9f06-4930-a1fb-47d690a40478",
"transaction_id":"2dab6b4e-7db5-4dcc-bc03-470e3a417e97",
"name":"Orange Fruit",
"image_url":null,
"item_url":"http://omin.com/andndnd",
"unit_price":"300.00",
"quantity":1,
"created_at":"2022-02-05T17:59:22.537Z",
"updated_at":null
},
{
"id":"0234f2a1-2fb7-4b83-90ee-650327dbbda4",
"transaction_id":"2dab6b4e-7db5-4dcc-bc03-470e3a417e97",
"name":"Kiwi Fruit",
"image_url":null,
"item_url":"http://omin.com/andndnd",
"unit_price":"400.00",
"quantity":1,
"created_at":"2022-02-05T17:59:22.537Z",
"updated_at":null
}
],
"customer":{
"id":"6ea945c5-0575-473f-8ff9-0baed42cef94",
"firstname":"Garret",
"lastname":"Omin",
"email":"[email protected]",
"phone":"+2348122632296"
}
}
}
{
"event": "klump.payment.transaction.abandoned",
"transaction_id": "f4400d5c-2dd0-4540-bc5c-7467800b3d8b",
"merchant_id": "45c0e2cd-c208-46db-8640-5bc01a21d9a6",
"webhook_url": "https://ogabassey.com/wc-api/klp_wc_payment_webhook/",
"data": {
"id": "f4400d5c-2dd0-4540-bc5c-7467800b3d8b",
"user_id": "f79e9fc0-a11d-496e-862f-2ccc96a82567",
"merchant_id": "45c0e2cd-c208-46db-8640-5bc01a21d9a6",
"amount": "205952.33",
"original_amount": "197000.00",
"commission": "3.00",
"interest": "3.00",
"merchant_payout_amount": "191090.00",
"shipping_fee": null,
"currency": "NGN",
"reference": "KLP-1660-945019-8651",
"merchant_reference": null,
"status": "abandoned",
"is_successful": false,
"is_live": true,
"meta_data": null,
"repayment_cycle": 3,
"loan_is_paid_off": false,
"is_picked_for_settlement": false,
"created_at": "2022-08-19T21:36:59.909Z",
"updated_at": "2022-08-20T22:55:03.479Z",
"redirect_url": null,
"processor_webhook_data": null,
"req_id": "eaHg0WolI7G",
"checkout_url": "https://checkout.paystack.com/r2jqcd442n1wk2t",
"source": null,
"merchant": {
"id": "45c0e2cd-c208-46db-8640-5bc01a21d9a6",
"country_id": "e798bb0c-bc65-45c2-87ad-6e7c2243451b",
"industry_id": "e3663a26-8e49-4d48-8229-8c334e40e9ce",
"active": true,
"logo": "https://s3.eu-west-1.amazonaws.com/cdn.useklump.com/logo/logo",
"address": "25 Montgomery Road Yaba",
"business_name": "Dapo Limited",
"firstname": "Mr",
"lastname": "Merchant",
"email": "[email protected]",
"phone": "+234814111111",
"role": "merchant",
"is_live": true,
"is_business_registered": true,
"is_email_verified": true,
"is_phone_verified": false,
"is_deleted": false,
"can_transact": true,
"player_id": null,
"device_brand": null,
"device_model": null,
"state": "Lagos",
"city": "Lagos Mainland",
"maximum_loan_amount": null,
"merchant_pays_commission": true,
"webhook": [
{
"id": "b0106134-097a-4ab4-a19b-8db765f91cbd",
"merchant_id": "45c0e2cd-c208-46db-8640-5bc01a21d9a6",
"url": "https://ogabassey.com/wc-api/klp_wc_payment_webhook/",
"environment": "live",
"created_at": "2022-04-28T16:26:42.401Z",
"updated_at": null
}
]
}
},
"environment": "live"
}
Idempotent Webhooks
We strongly suggest you make webhooks idempotent. That means, you should always check the webhook ID and make sure that you're not processing the same webhook more than once.
Updated over 2 years ago