> For the complete documentation index, see [llms.txt](https://docs-processing.crypto-chief.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs-processing.crypto-chief.com/webhooks/validation-check.md).

# Validation check

Your API keys are confidential, and no one except you and our service should know them. This ensures that when you verify the signature, you can be confident the Webhook was sent by us.

We create the Signature using the following algorithm:

1. Sort the JSON request body in alphabetical order
2. Encode the sorted JSON string in base64
3. Concatenate the base64 string with your API Key
4. Calculate the MD5 hash of the resulting string

After obtaining the hash from the request body, you must compare it with the `Signature` value passed in the request header.

For maximum security and privacy, after receiving the webhook, you may verify the order status by using the order information retrieval [method](/payments/payin-information.md).

#### Example of webhook signature validation

{% tabs %}
{% tab title="JavaScript" %}

```javascript
const express = require('express');
const crypto = require('crypto');

const app = express();
const API_KEY = '6b8dcc0cd0bf4e42986d6b4da7edbfd5';

const getSortedObject = (obj) => {
  if (obj === null || obj === undefined) return obj;
  if (Array.isArray(obj)) return obj.map(item => getSortedObject(item));
  if (typeof obj !== 'object') return obj;
  const sortedObject = {};
  Object.keys(obj).sort().forEach(key => {
    sortedObject[key] = getSortedObject(obj[key]);
  });
  return sortedObject;
};

app.post('/webhook', express.json(), (req, res) => {
  const signature = req.headers['signature'];
  if (!signature) {
    return res.status(401).json({ error: 'Missing Signature header' });
  }

  const sortedBody = getSortedObject(req.body);
  const json = JSON.stringify(sortedBody);
  const calculatedSignature = crypto
    .createHash('md5')
    .update(Buffer.from(json).toString('base64') + API_KEY)
    .digest('hex');

  if (calculatedSignature !== signature) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Signature is valid, process the webhook
  console.log('Webhook verified:', req.body.event, req.body.order_id);

  res.status(200).json({ success: true });
});

app.listen(3000);
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php

$API_KEY = '6b8dcc0cd0bf4e42986d6b4da7edbfd5';

function getSortedArray($arr) {
    if (!is_array($arr)) return $arr;

    if (array_is_list($arr)) {
        return array_map('getSortedArray', $arr);
    }

    ksort($arr);
    foreach ($arr as $key => $value) {
        $arr[$key] = getSortedArray($value);
    }
    return $arr;
}

$signature = $_SERVER['HTTP_SIGNATURE'] ?? '';
if (empty($signature)) {
    http_response_code(401);
    echo json_encode(['error' => 'Missing Signature header']);
    exit;
}

$rawBody = file_get_contents('php://input');
$parsed = json_decode($rawBody, true);
$sortedData = getSortedArray($parsed);
$json = json_encode($sortedData, JSON_UNESCAPED_SLASHES);

$calculatedSignature = md5(base64_encode($json) . $API_KEY);

if ($calculatedSignature !== $signature) {
    http_response_code(401);
    echo json_encode(['error' => 'Invalid signature']);
    exit;
}

// Signature is valid, process the webhook
http_response_code(200);
echo json_encode(['success' => true]);

?>
```

{% endtab %}

{% tab title="GO" %}

```go
package main

import (
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"io"
	"net/http"
	"sort"
)

const apiKey = "6b8dcc0cd0bf4e42986d6b4da7edbfd5"

func getSortedValue(value interface{}) interface{} {
	switch v := value.(type) {
	case map[string]interface{}:
		return getSortedMap(v)
	case []interface{}:
		sorted := make([]interface{}, len(v))
		for i, item := range v {
			sorted[i] = getSortedValue(item)
		}
		return sorted
	default:
		return value
	}
}

func getSortedMap(obj map[string]interface{}) map[string]interface{} {
	sortedObj := make(map[string]interface{})
	keys := make([]string, 0, len(obj))
	for key := range obj {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	for _, key := range keys {
		sortedObj[key] = getSortedValue(obj[key])
	}
	return sortedObj
}

func webhookHandler(w http.ResponseWriter, r *http.Request) {
	signature := r.Header.Get("Signature")
	if signature == "" {
		http.Error(w, `{"error":"Missing Signature header"}`, http.StatusUnauthorized)
		return
	}

	body, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, `{"error":"Failed to read body"}`, http.StatusBadRequest)
		return
	}
	defer r.Body.Close()

	var parsed map[string]interface{}
	if err := json.Unmarshal(body, &parsed); err != nil {
		http.Error(w, `{"error":"Invalid JSON"}`, http.StatusBadRequest)
		return
	}

	sortedData := getSortedMap(parsed)
	jsonData, _ := json.Marshal(sortedData)

	hash := md5.New()
	hash.Write([]byte(base64.StdEncoding.EncodeToString(jsonData) + apiKey))
	calculatedSignature := hex.EncodeToString(hash.Sum(nil))

	if calculatedSignature != signature {
		http.Error(w, `{"error":"Invalid signature"}`, http.StatusUnauthorized)
		return
	}

	// Signature is valid, process the webhook
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(`{"success":true}`))
}

func main() {
	http.HandleFunc("/webhook", webhookHandler)
	http.ListenAndServe(":8080", nil)
}
```

{% endtab %}

{% tab title="Python" %}

```python
import json
import hashlib
import base64
from flask import Flask, request, jsonify

app = Flask(__name__)
API_KEY = '6b8dcc0cd0bf4e42986d6b4da7edbfd5'

def get_sorted_dict(obj):
    if isinstance(obj, dict):
        return {key: get_sorted_dict(obj[key]) for key in sorted(obj.keys())}
    if isinstance(obj, list):
        return [get_sorted_dict(item) for item in obj]
    return obj

@app.route('/webhook', methods=['POST'])
def webhook():
    signature = request.headers.get('Signature', '')
    if not signature:
        return jsonify({'error': 'Missing Signature header'}), 401

    parsed = request.get_json()
    sorted_data = get_sorted_dict(parsed)
    json_data = json.dumps(sorted_data, separators=(',', ':'))

    hash_obj = hashlib.md5()
    hash_obj.update(base64.b64encode(json_data.encode()) + API_KEY.encode())
    calculated_signature = hash_obj.hexdigest()

    if calculated_signature != signature:
        return jsonify({'error': 'Invalid signature'}), 401

    # Signature is valid, process the webhook
    return jsonify({'success': True}), 200

if __name__ == '__main__':
    app.run(port=5000)
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs-processing.crypto-chief.com/webhooks/validation-check.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
