> ## Knowledge Base Index
> Fetch the complete knowledge base index at: https://help.bentoweb.com/sitemap.xml
> Use this file to discover available pages before exploring further.
> Pure-Markdown content can be obtained by appending a '.md' suffix to the content URLs listed in the sitemap (without the trailing slash).

# การตรวจสอบ Signature ของ BentoWeb Webhook ด้วย HMAC-SHA256

# 🔐 การตรวจสอบ Signature ของ BentoWeb Webhook ด้วย HMAC-SHA256

เมื่อคุณรับ Webhook จาก BentoWeb ระบบจะส่งข้อมูลมาพร้อมกับ Signature เพื่อให้คุณสามารถตรวจสอบความถูกต้องของข้อมูล และป้องกันการปลอมแปลงได้

---

## 📬 ข้อมูลที่คุณจะได้รับจาก Webhook

* **Body**: JSON Payload
* **Header**:
* `Authorization`: Signature สำหรับตรวจสอบ

---

## 🔑 หลักการสร้าง Signature

Signature ถูกสร้างจาก: 

```
HMAC_SHA256(client_id + json_payload, client_secret)
```

โดย
* `client_id` คือรหัสประจำระบบของคุณ
* `json_payload` คือข้อมูลใน body (ต้อง normalize ก่อน hash)
* `client_secret` คือรหัสลับที่คุณได้รับจาก BentoWeb

---

## ⚠️ จุดสำคัญที่ทำให้ Signature ไม่ตรง

1. JSON ที่รับมามีการ escape unicode (`\u0e1e`) ซึ่งต้องแปลงกลับก่อน
2. ช่องว่างหรือลำดับ key เปลี่ยนไปตอนทำ `json_encode()` ใหม่
3. ไม่ได้ใช้ UTF-8 encoding ก่อน hash
4. ใช้ hash ผิดประเภท (ต้องใช้ **HMAC-SHA256** เท่านั้น)
5. `Authorization` ไม่มีการส่งค่า โปรดติดต่อเจ้าหน้าที่ เพื่อระบุ ID ร้านลงในตัว Client ID ที่คุณใช้งานอยู่

---

## ✅ ขั้นตอนการตรวจสอบ Signature
1. รับ `Authorization` จาก header
2. รับ raw body JSON
3. `json_decode()` เป็น object/array
4. `json_encode()` กลับแบบไม่ escape:
    * ต้องใช้ `JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES` (หรือเทียบเท่า)
5. รวม `client_id + json`
6. สร้าง HMAC-SHA256 ด้วย `client_secret`
7. เปรียบเทียบกับ Signature ที่ส่งมา

---

## 🔧 ตัวอย่างโค้ดในหลายภาษา

### 🐘 PHP

```php
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);

$json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$baseString = 'your_client_id' . $json;

$expected = hash_hmac('sha256', $baseString, 'your_client_secret');
$provided = $_SERVER['HTTP_AUTHORIZATION'];

if (!hash_equals($expected, $provided)) {
    http_response_code(403);
    exit('Signature mismatch');
}
```

### 🐍 Python

```python
import json, hmac, hashlib
from flask import request

client_id = "your_client_id"
client_secret = b"your_client_secret"

raw = request.get_data(as_text=True)
data = json.loads(raw)
json_string = json.dumps(data, ensure_ascii=False, separators=(',', ':'))

base_string = client_id + json_string
expected_signature = hmac.new(client_secret, base_string.encode('utf-8'), hashlib.sha256).hexdigest()

if request.headers.get('Authorization') != expected_signature:
    return "Signature mismatch", 403
```


### 🌐 Node.js

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

app.use(express.json({ verify: (req, res, buf) => req.rawBody = buf }));

app.post('/webhook', (req, res) => {
  const clientId = "your_client_id";
  const clientSecret = "your_client_secret";
  const json = JSON.stringify(req.body);
  const baseString = clientId + json;

  const expectedSignature = crypto.createHmac('sha256', clientSecret)
    .update(baseString, 'utf8')
    .digest('hex');

  const providedSignature = req.get('Authorization');

  if (providedSignature !== expectedSignature) {
    return res.status(403).send('Signature mismatch');
  }

  res.send('OK');
});
```

### 🦫 Go

```
import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "io/ioutil"
    "net/http"
)

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    clientId := "your_client_id"
    clientSecret := "your_client_secret"

    body, _ := ioutil.ReadAll(r.Body)
    var payload map[string]interface{}
    json.Unmarshal(body, &payload)

    jsonBytes, _ := json.Marshal(payload)
    baseString := clientId + string(jsonBytes)

    mac := hmac.New(sha256.New, []byte(clientSecret))
    mac.Write([]byte(baseString))
    expectedSignature := hex.EncodeToString(mac.Sum(nil))

    providedSignature := r.Header.Get("Authorization")

    if providedSignature != expectedSignature {
        http.Error(w, "Signature mismatch", http.StatusForbidden)
        return
    }

    w.Write([]byte("OK"))
}
```


## 🧪 ทดสอบลายเซ็นไม่ตรง
หากคุณทดสอบแล้วลายเซ็นไม่ตรง ให้ตรวจสอบว่า:
* JSON ถูก encode ซ้ำหรือ escape ไม่ตรง (ไม่ escape unicode, ไม่มีช่องว่าง)
* ตรวจว่าใช้ `HMAC-SHA256`
* ตรวจว่าใช้ `UTF-8` encoding
* client_id หรือ client_secret ไม่ถูกต้อง
* มีการแก้ไข body หลังจาก verify แล้ว
* มี request จากภายนอกที่ไม่ใช่ BentoWeb










