Webhook for Notifications

You can create webhooks to receive events associated with instant payments so that your integration can react automatically.

Objectives

By using webhooks, you can program applications to receive events as they happen and have your backend perform actions based on the type of event.

In version 3.0 of the instant payments API, there is also a new version (3.0) of the notifications API, but its purpose remains the same: to send real-time data to the webhook when events occur in the collection with the configured endpoint. Khipu uses HTTPS to send events to your application with a JSON object representing the event.

Important

In the notifications API (1.3) associated with the 2.0 payments API, at the time of reconciliation, a request is sent to the configured webhook for the payment, with the notification_token parameter, which can then be used to query the payment details in a subsequent API call. In this new version of the notifications API, that model no longer exists, and a simplified flow is used, where the webhook request contains payment data.

Usage

To activate automatic notifications to your webhook, when creating the collection, you must include the following parameters in the request:

Copy
Copied
{
  ...
  "notify_url": "<Webhook HTTPS URL>",
  "notify_api_version": "3.0",
  ...
}

Events

Khipu is capable of sending data generated in the activities of a specific collection. When a new event occurs, Khipu generates the corresponding object with the necessary data to represent the state of the payment. For example, it's possible to receive the event of payment creation, successful (or failed) payment, and reconciliation result. You can choose to subscribe to one or more events.

Each event is sent in a POST request to the webhook registered when using the collection creation API. You can program your backend to execute actions upon receiving an event.

Object Structure

The data contained in the JSON object is associated with the type of events. Currently, our API is capable of informing the following events:

  • Reconciliation successfully performed
  • Collection successfully generated (upcoming)
  • Payment successfully made (upcoming)
  • Payment ended in error (upcoming)

The body of each event is defined as a subset of the data from the payment object. To understand its structure and details, review the documentation of the endpoint GET /payments/{id} in the "Responses - 200" section.

Example

The following example shows an event of a collection successfully reconciled, as it would be sent to a webhook. Later, this same message will be used to perform signature header verification:

Copy
Copied
{
  "payment_id": "zfxnocsow6mz",
  "receiver_id": 990939,
  "subject": "TEST_COBRO",
  "amount": "1000.0000",
  "discount": "0.0000",
  "currency": "CLP",
  "receipt_url": "https://s3.amazonaws.com/staging.notifications.khipu.com/CPKH-1804240956-zfxnocsow6mz.pdf",
  "bank": "DemoBank",
  "bank_id": "Bawdf",
  "payer_name": "Developer User #990.939",
  "payer_email": "test@khipu.com",
  "personal_identifier": "44.444.444-4",
  "bank_account_number": "000000000000444444444",
  "out_of_date_conciliation": false,
  "transaction_id": "15f836bd-e8a7-4d12-b2f1-56403012b555",
  "responsible_user_email": "test@khipu.com",
  "payment_method": "simplified_transfer",
  "conciliation_date": "2024-04-18T13:56:54.859Z"
}

Notification API Version

For collections generated using the 3.0 API, the notification will be exclusively performed using the 3.0 version of the event notification API.

Signature Verification

It is recommended that upon receiving an event in your webhook, you perform signature verification to ensure that the content has not been modified and corresponds exactly to the data generated by Khipu. The validation can only be performed using your merchant secret.

Warning

Make sure not to manipulate the content of the message body before performing the verification. Avoid ordering, indenting, or transforming the JSON object.

Each message sent to a webhook is accompanied by a x-khipu-signature header containing a timestamp and the signature value you should verify. The timestamp value prefix is t= and the signature value prefix is s=. The attributes are separated by a comma ,.

Example header:

Copy
Copied
x-khipu-signature: t=1711965600393,s=GYzpjnXlTKQ+BJY7pZJmrM6DZgWMSJdtOr/dleBKTdg=

t corresponds to the value of the UNIX time in UTC, in milliseconds.

s is generated using a Hash-based Message Authentication Code (HMAC) with the SHA-256 hash function applied to the JSON object containing the event.

The steps to verify a message signature are:

  1. Extract timestamp and signature from the header
    The header value should be split by comma , to obtain the elements, and then each element should be split by the equal sign = to obtain the prefix/value pair.
  2. Prepare the string for signature verification
    Create a text string by concatenating:
    • The value of timestamp (converted to text)
    • The dot .
    • The JSON object received in the message body.
  3. Calculate the expected signature
    Use the SHA256 function to calculate the HMAC, using your merchant secret as the key to apply the function over the text created in the previous step.
  4. Compare signatures
    Compare the calculated signature from the previous step with the signature received in the header, both values should match. Additionally, you can calculate the difference between the current timestamp and the received timestamp to determine if the difference is within an acceptable range. This could be useful for discarding outdated messages or preventing replay attacks.

Example

The following Python code performs signature and message verification received by the webhook. Note that these are simulated values. In a real scenario, the values should be obtained from the request received by the service. The secret to use is the one corresponding to the account used to generate the collection.

Copy
Copied
import hmac
import hashlibNotification API Version

import base64

# merchant secret:
my_secret = '1a4cbbbeb8bdb7e1d73572b9cc43ce4ce18f79d9'

# extract timestamp and signature values from the received header:
x_khipu_signature = 't=1711965600393,s=GYzpjnXlTKQ+BJY7pZJmrM6DZgWMSJdtOr/dleBKTdg='
signature_values = x_khipu_signature.split(',')
t_value = None
s_value = None

for value in signature_values:
    key, val = value.split('=', 1)
    if key == 't':
        t_value = val
    elif key == 's':
        s_value = val

print('Received hash:  ' + s_value)

# received body payload:
json_payload = """{"payment_id":"zfxnocsow6mz","receiver_id":990939,"subject":"TEST_COBRO","amount":"1000.0000","discount":"0.0000","currency":"CLP","receipt_url":"https:\/\/s3.amazonaws.com\/staging.notifications.khipu.com\/CPKH-1804240956-zfxnocsow6mz.pdf","bank":"DemoBank","bank_id":"Bawdf","payer_name":"Cobrador de desarrollo #990.939","payer_email":"test@khipu.com","personal_identifier":"44.444.444-4","bank_account_number":"000000000000444444444","out_of_date_conciliation":false,"transaction_id":"15f836bd-e8a7-4d12-b2f1-56403012b555","responsible_user_email":"test@khipu.com","payment_method":"simplified_transfer","conciliation_date":"2024-04-18T13:56:54.859Z"}"""

# calculate hash of the received payload:
to_hash = '{}.{}'.format(str(t_value), json_payload)
secret_bytes = bytes(my_secret, 'utf-8')
hash_bytes = bytes(to_hash, 'utf-8')
hmac_signature = hmac.new(secret_bytes, hash_bytes, hashlib.sha256).digest()
hmac_base64 = base64.b64encode(hmac_signature).decode('utf-8')
print('Generated hash: ' + hmac_base64)

# verify:
if (hmac_base64 == s_value):
    print('HMAC is correct')
else:
    print('Message was tampered')

Result:

Copy
Copied
Received hash:  GYzpjnXlTKQ+BJY7pZJmrM6DZgWMSJdtOr/dleBKTdg=
Generated hash: GYzpjnXlTKQ+BJY7pZJmrM6DZgWMSJdtOr/dleBKTdg=
HMAC is correct