Language

Step 3: Validating webhooks

Assume that you own an e-commerce site and a customer has just made a purchase and the following webhook has been posted to your system.

The topic field of an event holds a description of the event, which is similar the subject of an e-mail message. The webhook itself contains _links to the resource impacted by the event that can be used to retrieve more information about the webhook you have received.

NOTE: The event must be retrieved with a client_credentials granted access_token.

JSON
{
  "topic": "buygoods_transaction_received",
  "id": "2133dbfb-24b9-40fc-ae57-2d7559785760",
  "created_at": "2020-10-22T10:43:20+03:00",
  "event": {
    "type": "Buygoods Transaction",
    "resource": {
      "id": "458712f-gr76y-24b9-40fc-ae57-2d35785760",
      "amount": "100.0",
      "status": "Received",
      "system": "Lipa Na M-PESA",
      "currency": "KES",
      "reference": "OJM6Q1W84K",
      "till_number": "000000",
      "sender_phone_number": "+254999999999",
      "origination_time": "2020-10-22T10:43:19+03:00",
      "sender_last_name": "Doe",
      "sender_first_name": "Jane",
      "sender_middle_name": null
    }
  },
  "_links": {
    "self": "https://sandbox.kopokopo.com/webhook_events/2133dbfb-24b9-40fc-ae57-2d7559785760",
    "resource": "https://sandbox.kopokopo.com/financial_transaction/458712f-gr76y-24b9-40fc-ae57-2d35785760"
  }
}

Step 3A. Authenticating the webhook request

Before we process any data from the webhook we’ll want to validate that the request really came from Kopo Kopo and not someone pretending to be Kopo Kopo. Kopo Kopo signs each webhook request with the api_key you got when creating an oauth application. The signature is contained in the X-KopoKopo-Signature header and is a SHA256 HMAC hash of the request body with the key being your client secret.

You can validate the webhook by generating the same SHA256 HMAC hash and comparing it to the signature sent with the payload.

webhook_test = K2Client.new(API_KEY)
webhook_test.parse_request(request)
K2Authenticator.authenticate(webhook_test.hash_body, webhook_test.api_secret_key, webhook_test.k2_signature)
not available
const Webhooks = K2.Webhooks

//Router or whatever server you are using
// This is the endpoint you used when subscribing
router.post('/buygoodsreceived', function(req, res, next){  
    // This will both validate and process the payload for you
    Webhooks
        .webhookHandler(req, res)
        .then( response => {
            console.log(response)
        })
        .catch( error => {
            console.log(error)
        })
})

k2connect.initialize(CLIENT_ID, CLIENT_SECRET, BASE_URL)
result_handler = k2connect.ResultHandler
processed_payload = result_handler.process(request)
<?
  $webhooks = $K2->Webhooks();

  $webhook_payload = file_get_contents('php://input');

  // This will both validate and process the payload for you
  $response = $webhooks->webhookHandler($webhook_payload, $_SERVER['HTTP_X_KOPOKOPO_SIGNATURE']);

  echo json_encode($response);

Step 3B. Check for duplicate events (eg. duplicate transactions)

It is important to consider that it is possible for your system to receive the same webhook more than once and you should be able to handle duplicates on your end.

To do this, keep a queue of events in a database and check to see if an Event has the same self resource location in _links as another event. If not, process the logic for that event. To illustrate, this is how a developer would implement this using Ruby and the ActiveRecord ORM.

Ruby/ActiveRecord
# Assuming you have an ActiveRecord model called KopoKopoTransaction

unless KopoKopoTransaction.where(resource_id: 'cdb5f11f-62df-e611-80ee-0aa34a9b2388').first
    # do something
end