Webhooks

Webhooks provide a way to receive notifications for your transactions in real time. While your transaction is being processed, its status progresses until it is completed. This makes it very important to get the final status for that transaction, and that's where webhooks are very beneficial.

Put simply, a webhook URL is an endpoint on your server that can receive API requests from Korapay’s server. Note that the request is going to be an HTTP POST request.

Setting your Webhook URL

You can specify your webhook URL in the API Configuration tab of the Settings page of your dashboard. Make sure that the webhook URL is unauthenticated and publicly available.

The request to the webhook URL comes with a payload, and this payload contains the details of the transaction for which you are being notified.

Webhook Request Payload

FieldData TypeDescription
eventStringtransfer.success, transfer.failed, charge.success or charge.failed
dataObjectThe object containing transaction details: amount, fee, currency, status, reference
data.amountNumberTransaction amount
data.feeNumberTransaction fee
data.currencyStringTransaction currency
data.statusStringTransaction status. This can be success or failed.
data.referenceStringTransaction reference. This reference can be used to query the transaction

Verifying Webhooks Request

It is important to verify that requests are coming from Korapay to avoid delivering value based on a counterfeit request. To verify our requests, you need to validate the signature assigned to the request.
Valid requests are sent with a header x-korapay-signature which is essentially an HMAC SHA256 signature of ONLY the data object in response payload signed using your secret key.

const crypto = require(“crypto”);
const secretKey = sk_live_******

router.post(‘/your_webhook_url’, (req, res, next) => {
  const hash = crypto.createHmac('sha256', secretKey).update(JSON.stringify(req.body.data)).digest('hex');

   If (hash === req.headers[‘x-korapay-signature’]) {
     // Continue with the request functionality
   } else {
     // Don’t do anything, the request is not from us.
   }
});
 
import javax.crypto.Mac; 

public class WebhookVerification {
 private static readonly char[] HEX_ARRAY = "0123456789abcdef".ToCharArray();

 private static string bytesToHex(byte[] bytes) {
   char[] hexChars = new char[bytes.Length * 2];
   for (int j = 0; j < bytes.Length; j++) {
     int v = bytes[j] & 0xFF;
     hexChars[j * 2] = HEX_ARRAY[v >> 4];
     hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
   }

   return new string (hexChars);
 }

 public static String generateWebhookSignature (String wehbookData, String korapaySecretKey) throws Exception {
   Mac sha256HMAC = Mac.getInstance("HmacSHA256");
   SecretKeySpec secretKey = new SecretKeySpec(korapaySecretKey.getBytes("UTF-8"), "HmacSHA256");
   sha256HMAC.init(secretKey);
    
   return bytesToHex(sha256HMAC.doFinal(wehbookData.getBytes("UTF-8")));
 }
}
<?php
  ini_set('serialize_precision','-1');// ensures that the precision for the amount field is maintained for json_encode
  if ((strtoupper($_SERVER['REQUEST_METHOD']) != 'POST' ) || !array_key_exists('HTTP_X_KORAPAY_SIGNATURE', $_SERVER)) {
    // invalid request, ignore
    return http_response_code(200);
  }

  $requestBody = json_decode(@file_get_contents("php://input"), JSON_UNESCAPED_SLASHES);
  $webhookSignature = $_SERVER['HTTP_X_KORAPAY_SIGNATURE'];
  $korapaySecretKey = 'sk_live_******';

  If ($webhookSignature !== hash_hmac('sha256', json_encode($requestBody['data']), $korapaySecretKey)) {
    // do nothing, request is invalid
    return http_response_code(200);
  }

  // continue with the request functionality
  
  return http_response_code(200);
?>

Responding to Webhooks Request

It is important to respond to the requests with a 200 status code to acknowledge that you have received the requests. Korapay does not pay attention to any request parameters apart from the request status code.

Please note that if any other response code is received, or there’s a timeout while sending the request, we retry the request periodically within 72 hours after which retries stop.


Best Practices

It is recommended to do the following when receiving webhook notifications from us:

  • Keep track of all notifications received: It’s important to keep track of all notifications you’ve received. When a new notification is received proceed to check that this has not been processed before giving value. A retry of already processed notifications can happen if we do not get a 200 HTTP Status code from your notification URL, or if there was a request time out.
  • Acknowledge receipt of notifications with a 200 HTTP status code: It’s recommended you immediately acknowledge receipt of the notification by returning a 200 HTTP Status code before proceeding to perform other logics, failure to do so might result in a timeout which would trigger a retry of such notification.