> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hub2.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Integration

Here are the steps to follow for a proper integration of webhooks :

<Steps>
  <Step title="Create a callback HTTP endpoint to receive webhooks requests.">
    This endpoint must be able to process a POST request as below:

    * **URL**: `https://website.com/webhook_payment`
    * **METHOD**: `POST`
    * **HEADERS**:

    ```
    ContentType: application/json
    HUB2-Signature: s1=XXXXXX,s0=XXXXXX
    ```

    * **BODY**:

    <Accordion title="Click to show.">
      ```json theme={null}
      {
      	"type": "payment_intent.created",
      	"data": {
      		"id":"pi_9lEjld8yF2USvijl7mnId",
      		"merchantId":"10",
      		"createdAt":"2021-03-03T11:16:39.280Z",
      		"updatedAt":"2021-03-03T11:16:39.288Z",
      		"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjaGFudElkIjoiMTAiLCJtb2RlIjoic2FuZGJveCIsInBheW1lbnRJZCI6InBpXzlsRWpsZDh5RjJVU3Zpamw3bW5JZCIsImlhdCI6MTYxNDc3MDE5OX0.fkak8HAfHvcCeCxtzBEx1bE39oWl4vkGNgWdhnAmhXo",
      		"purchaseReference":"ref_2020_11_10_001",
      		"customerReference":"cust_01924059",
      		"status":"payment_required",
      		"amount":100,
      		"currency":"XOF",
      		"payments":[],
      		"mode":"sandbox"
      	},
      	"id": "evt_O80mmIF5CNrBMbZIyHJye",
      	"createdAt": "2021-03-03 11:16:39.685+00"
      }
      ```
    </Accordion>

    <Note>
      This is an example of webhook issued by a `payment_intent.created` event.
    </Note>
  </Step>

  <Step title="Call HUB2 API to register this endpoint and subscribe to a webhook event.">
    The HUB2 API does have a dedicated endpoint for this action. It must be called with the following body:

    <Accordion title="Click to show.">
      ```json theme={null}
      {
      	"url": "https://website.com/webhook_payment",
      	"events": [
      		"payment.created",
      		"payment_intent.created",
      	],
      	"description": "This is a webhook trigger upon payment & payment_intent creation",
      	"metadata": {}
      }
      ```
    </Accordion>

    Here are samples of code to perform this:

    <CodeGroup>
      ```bash Curl theme={null}
      curl -X POST \
      	-H "Content-Type: application/json" \
      	-H "ApiKey: <Your API KEY>" \
      	-H "MerchantId: <Your merchant Id>" \
      	-H "Environment: <Your API KEY environment 'live' or 'sandbox'>" \
      	-d '{ "url": "https://my.webhook.target", "events": ["payment.created", "payment_intent.created"], "description": "This is a webhook trigger upon payment & payment_intent creation", "metadata": {} }' \
      https://api.hub2.io/webhooks
      ```

      ```typescript Typescript theme={null}
      import fetch from 'node-fetch';

      async function createWebhook() {
      		const response = await fetch('https://api.hub2.io/webhooks', {
      				method: 'POST',
      				headers: {
      						'Content-Type': 'application/json',
      						'ApiKey': 'Your API KEY',
      						'MerchantId': 'Your merchant Id',
      						'Environment': 'Your API KEY environment \'live\' or \'sandbox\''
      				},
      				body: JSON.stringify({
      						"url": "https://my.webhook.target",
      						"events": ["payment.created", "payment_intent.created"],
      						"description": "This is a webhook trigger upon payment & payment_intent creation",
      						"metadata": {}
      				})
      		});

      		if (response.ok) {
      				const result = await response.json();
      				console.log(result);
      		} else {
      				console.log(`HTTP Status: ${response.status}`);
      		}
      }

      createWebhook();
      ```

      ```python Python theme={null}
      import requests
      import json

      url = 'https://api.hub2.io/webhooks'

      headers = {
      		'Content-Type': 'application/json',
      		'ApiKey': 'Your API KEY',
      		'MerchantId': 'Your merchant Id',
      		'Environment': 'Your API KEY environment \'live\' or \'sandbox\'',
      }

      data = {
      		"url": "https://my.webhook.target",
      		"events": ["payment.created", "payment_intent.created"],
      		"description": "This is a webhook trigger upon payment & payment_intent creation",
      		"metadata": {}
      }

      response = requests.post(url, headers=headers, data=json.dumps(data))

      if response.status_code == 200:
      		print(response.json())
      else:
      		print(f'HTTP Status: {response.status_code}')
      ```
    </CodeGroup>

    This is a sample of the HUB2 API response to this call:

    <Accordion title="Click to show.">
      ```json theme={null}
      {
      	"id": "wh_z1urYtVFgEebtcj8fxp4v",
      	"createdAt": "2020-10-15T12:09:49.355Z",
      	"updatedAt": "2020-10-15T12:09:49.355Z",
      	"mode": "live",
      	"description": "This is a webhook trigger upon payment & payment_intent creation",
      	"events": ["payment.created", "payment_intent.created"],
      	"metadata": { },
      	"secret": "5257a869e7ecebeda32affa62cd...",
      	"status": "enabled",
      	"url": "https://website.com/webhook_payment"
      }
      ```
    </Accordion>

    <Note>
      This response contains a secret associated with the created webhook: `"secret": "5257a869e7ecebeda32affa62cd..."`.
    </Note>

    The secret must be kept carefully. It will be mandatory to verify the integrity of the webhook.

    For more details on the webhooks API, check the following documentation: [Webhooks API Documentation](/api-reference/webhooks)
  </Step>

  <Step title="Webhook signature verification.">
    <Warning>
      **This step is mandatory to ensure that no one can forge fake webhooks.**

      Skipping this step may offer a vulnerability to harmful people, allowing them to automatically validate transactions themselves.
    </Warning>

    To ensure the webhook received comes from the HUB2 API, it is imperative to check the signature of the body (payload).

    ### a. Get the secret from the webhook registration.

    This secret was generated in step 2, `"secret": "5257a869e7ecebeda32affa62cd..."` in our example.

    ### b. Sign the payload.

    Extract the contents of the BODY from the POST request sent by HUB2 first (see [Json response](#1-cr%C3%A9er-a-callback-endpoint-for-webhooks-in-your-server-environment)). It should result in a characters string that must be signed via HMAC256 and the `secret`.

    Here are some examples of procedures depending on the programming language used :

    <CodeGroup>
      ```js Javascript theme={null}
      // Example with Javascript/NestJS
      import { createHmac, Hmac } from 'crypto';

      sign(json: string, secret: string): string {
      	const hmac: Hmac = createHmac('sha256', secret);
      	hmac.update(json);
      	return hmac.digest('hex');
      }
      ```

      ```python Python theme={null}
      # Example with Python
      import hashlib
      import hmac

      def sign(json, secret):
      	hmac_obj = hmac.new(secret.encode('utf-8'), json.encode('utf-8'), hashlib.sha256)
      	return hmac_obj.hexdigest()
      ```

      ```java Java theme={null}
      // Example with java
      import javax.crypto.Mac;
      import javax.crypto.spec.SecretKeySpec;
      import java.security.InvalidKeyException;
      import java.security.NoSuchAlgorithmException;

      public String sign(String json, String secret) {
      	try {
      		Mac mac = Mac.getInstance("HmacSHA256");
      		SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
      		mac.init(secretKey);
      		byte[] hmacBytes = mac.doFinal(json.getBytes());
      		return javax.xml.bind.DatatypeConverter.printHexBinary(hmacBytes).toLowerCase();
      	} catch (NoSuchAlgorithmException | InvalidKeyException e) {
      		// Handle exceptions
      	}
      }
      ```

      ```ruby Ruby theme={null}
      # Example with ruby
      require 'openssl'

      def sign(json, secret)
      	hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), secret, json)
      end
      ```

      ```c# C# theme={null}
      // Example with C#
      using System.Security.Cryptography;
      using System.Text;

      public string Sign(string json, string secret)
      {
      	using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret))
      	{
      		byte[] bytes = Encoding.UTF8.GetBytes(json);
      		byte[] hashBytes = hmac.ComputeHash(bytes);
      		return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
      	}
      }
      ```

      ```php PHP theme={null}
      // Example with PHP
      function sign($json, $secret) {
      	$hash = hash_hmac('sha256', $json, $secret);
      	return $hash;
      }
      ```
    </CodeGroup>

    The request body must be in JSON string. Either the framework used directly returns a JSON string or it must be transformed to JSON format with a call like `JSON.stringify()` on the body of the request.

    ### c. Check signature.

    Finally, the computed signature must be compared to the one provided by HUB2, located in the HEADERS of the POST request.

    ```
    HUB2-Signature: s1=ABCD,s0=XYZ
    ```

    * `HUB2-Signature`: Header name
    * `s1`: Signature computed using current `secret`.
    * `s0`: Signature computed using old secret `oldSecret`if present.

    <Tip>
      When updating a `secret`, the previous active `secret` becomes `oldSecret` for a period of 24 hours and is used to generate the `s0` signature.
    </Tip>
  </Step>
</Steps>
