# Webhook

## Lưu ý

Để thiết lập Webhook bất kỳ, bạn cần phải: ​

* Tạo một điểm cuối trên máy chủ để xử lý các yêu cầu HTTP.
* Cấu hình Webhooks trong menu tích hợp tại trang [my.doopage.com](https://my.doopage.com/)

## Tạo điểm cuối xử lý webhook

Điểm cuối của bạn phải có khả năng xử lý 2 loại yêu cầu HTTP: \[[Yêu cầu xác minh](#yeu-cau-xac-minh)] và \[[Thông báo sự kiện](#thong-bao-su-kien)].

### Yêu cầu xác minh

Mỗi khi bạn cấu hình Webhooks tại trang [Tích hợp](https://my.doopage.com/dashboard/integrate-api), chúng tôi sẽ gửi yêu cầu `GET` đến URL điểm cuối của bạn. Yêu cầu xác minh bao gồm các thông số chuỗi truy vấn sau đây, được thêm vào cuối URL điểm cuối của bạn. Các yêu cầu này sẽ có dạng như sau:

#### **\*\*Yêu cầu xác minh từ DooPage\*\***

```
GET https://your-domain.com/webhook-endpoint?
  validation_key=<your_input_key>&
  challenge_key=<random_string>
```

| Tham số         | Giá trị mẫu  | Mô tả                                                                                                                 |
| --------------- | ------------ | --------------------------------------------------------------------------------------------------------------------- |
| validation\_key | your\_key    | Đây là chuỗi mà bạn đã nhập khi cấu hình webhook tại trang [Tích hợp](https://my.doopage.com/dashboard/integrate-api) |
| challenge\_key  | doopage\_key | Một chuỗi ngẫu nhiên được tạo bởi DooPage.                                                                            |

#### **\*\*Xác thực yêu cầu xác minh\*\***

Mỗi khi nhận được yêu cầu xác minh, điểm cuối của bạn phải: ​

* Xác minh rằng giá trị `validation_key` khớp với chuỗi mà bạn đặt trong trường `Validation key` khi cấu hình Webhooks trong trang [Tích hợp](https://my.doopage.com/dashboard/integrate-api).
* Phản hồi với giá trị `challenge_key`.

### Thông báo sự kiện

Khi cấu hình Webhooks, bạn sẽ đăng ký các sự kiện cụ thể (Ví dụ: Tích vào `Customer created` để nhận được thông tin mỗi khi có khách hàng mới được tạo trên DooPage). Mỗi khi có thay đổi cho một trong các trường này, chúng tôi sẽ gửi yêu cầu POST tới điểm cuối của bạn kèm theo JSON payload ​ Ví dụ: `Customer created` webhook payload.

```javascript
{
  "company_id": 1,
  "event_type": "customer_created",
  "timestamp": 1618118040123,
  "data": {
    "customer": {
      "id": 1,
      "name": "DooPage"
    }
  }
}
```

| Trường      | Mô tả                                                                      |
| ----------- | -------------------------------------------------------------------------- |
| company\_id | Mã công ty                                                                 |
| event\_type | Loại sự kiện                                                               |
| timestamp   | Dấu thời gian sự kiện xảy ra (millisecond)                                 |
| data        | Nội dung sự kiện (Tùy loại sự kiện mà bạn sẽ nhận được nội dung khác nhau) |

## Cấu hình Webhooks&#x20;

Sau khi điểm cuối đã sẵn sàng, hãy truy cập trang [Tích hợp](https://my.doopage.com/dashboard/integrate-api) để cài đặt Webhooks. ​  ​&#x20;

![Step1](https://i.imgur.com/CBvu49L.png)

Sau khi nhấn `lưu` DooPage sẽ gửi tới điểm cuối của bạn một [Yêu cầu xác minh](#yeu-cau-xac-minh) mà bạn phải xác thực.

## Xác thực yêu cầu đến từ DooPage

1. Để xác thực bạn cần liên hệ DooPage để có được `secret_key`
2. Mọi webhook http request đến từ DooPage đều chứa 1 đoạn mã xác thực nằm tại headers của http request. `X-Doo-Signature`
3. Mã xác thực được tạo bằng hàm mã hóa sha1: `verify_key` = `sha1(<request_payload>.<secret_key>)`
4. Nếu `verify_key` khớp với header `X-Doo-Signature` tức request này hợp lệ
5. Code example:

**Lưu ý**: sử dụng raw request body data để thực hiện hashing tránh xử dụng `JSON.parse` hoặc các hàm parse JSON tương tự trong các ngôn ngữ lập trình khác do thứ tự các key có thể sẽ không được đảm bảo dẫn đến tạo `verify_key` sai

Php:

```php
$expected_key = $_SERVER['HTTP_DOO_X_SIGNATURE'];
$secret_key = "<contact_doopage_admin>";
$expected_validation_key = "<your_string_on_doopage_dashboard_integrate_setting>";

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  $request_body = file_get_contents('php://input');
  $actual_key = sha1($request_body.".".$secret_key);
  if ($expected_key == $actual_key) {
      echo "valid request";
  } else {
      exit("wrong key");
  }
} else {
  $queries = array();
  parse_str($_SERVER['QUERY_STRING'], $queries);
  $validation_key = $queries['validation_key'];
  $challenge_key = $queries['challenge_key'];
  if ($validation_key == $expected_validation_key) {
    exit("Wrong validation key")
  } else {
    echo $challenge_key;
  }
}
```

NodeJS(Express):

```js
const express = require("express");
const crypto = require("crypto");
const app = express();
const port = 3333;

const DOOPAGE_SECRET_KEY = "<contact_doopage_admin>";
const VALIDATION_KEY = "<your_string_on_doopage_dashboard_integrate_setting>";

const handleGetRequest = async (req, res) => {
  if (req.query.validation_key === VALIDATION_KEY) {
    res.send(req.query.challenge_key);
  } else {
    res.send("Invalid Request");
  }
};

const handlePostRequest = async (req, res) => {
  const expected_key = req.header("X-Doo-Signature");
  const raw_body = req.body.toString();
  const hash = crypto
    .createHash("sha1")
    .update(`${raw_body}.${DOOPAGE_SECRET_KEY}`);
  const actual_key = hash.digest("hex");
  if (expected_key === actual_key) {
    res.send("valid request");
  } else {
    res.send("Invalid request");
  }
};

app.post(
  "/",
  express.raw({
    type: "*/*",
  }),
  async (req, res) => {
    if (req.method === "POST") {
      await handlePostRequest(req, res);
    } else if (req.method === "GET") {
      await handleGetRequest(req, res);
    } else {
      res.send("Invalid request method");
    }
  }
);

app.listen(port);
```

Python(Flask):

```python
import hashlib
import hmac
from flask import Flask
from flask import request

app = Flask(__name__)

DOOPAGE_SECRET_KEY = "<contact_doopage_admin>"
VALIDATION_KEY = "<your_string_on_doopage_dashboard_integrate_setting>"


def handle_get_request():
    if request.args.get('validation_key') != VALIDATION_KEY:
        return "Invalid request"
    return request.args.get('challenge_key')


def handle_post_request():
    expected_key = request.headers.get('x-doo-signature')
    request_body = request.data.decode('utf8')
    hash_data = f"{request_body}.{DOOPAGE_SECRET_KEY}"
    actual_key = hashlib.sha1(hash_data.encode()).hexdigest()
    if not hmac.compare_digest(actual_key, expected_key):
        return "Invalid request"
    return "Valid request"


@app.route('/', methods=['POST', 'GET'])
def webhook_handler():
    if request.method == 'GET':
        return handle_get_request()
    elif request.method == 'POST':
        return handle_post_request()


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

## Webhook payload sample

### **Customer created**

```javascript
{
  "company_id": 1,
  "event_type": "customer_created",
  "timestamp": 1618118040123,
  "data": {
    "customer": {
      "id": 1,
      "name": "DooPage"
    }
  }
}
```

### ​**Customer updated**

```javascript
{
  "company_id": 1,
  "event_type": "customer_updated",
  "timestamp": 1618118040123,
  "data": {
    "customer": {
      "id": 1,
      "name": "DooPage"
    }
  }
}
```

### ​**Customer took**

```javascript
{
  "company_id": 1,
  "event_type": "customer_took",
  "timestamp": 1618118040123,
  "data": {
    "customer": {
      "id": 1,
      "name": "DooPage"
    },
    "staff": {
      "id": 1,
      "name": "Doopage staff"
    }
  }
}
```

### ​**Customer transferred**

```javascript
{
  "company_id": 1,
  "event_type": "customer_transferred",
  "timestamp": 1618118040123,
  "data": {
    "customer": {
      "id": 1,
      "name": "DooPage"
    },
    "staff": {
      "id": 1,
      "name": "Doopage staff"
    },
    "new_staff": {
      "id": 1,
      "name": "Doopage staff 2"
    }
  }
}
```

### ​**Customer closed**

```javascript
{
  "company_id": 1,
  "event_type": "customer_closed",
  "timestamp": 1618118040123,
  "data": {
    "customer": {
      "id": 1,
      "name": "DooPage"
    },
    "staff": {
      "id": 1,
      "name": "Doopage staff"
    }
  }
}
```

### ​**Order created**

```javascript
{
  "company_id": 1,
  "event_type": "order_created",
  "timestamp": 1618118040123,
  "data": {
    "order_id": 1,
    "created_at": 1618118040
  }
}
```

### ​**Order update**

```javascript
{
  "company_id": 1,
  "event_type": "order_updated",
  "timestamp": 1618118040123,
  "data": {
    "order_id": 1,
    "created_at": 1618118040,
    "modified_at": 1618118041,
    "picking_status": "pending",
    "payment_status": "paid"
  }
}
```

### ​**Customer Tag set**

```javascript
{
 "company_id": 1,
 "event_type": "customer_tags_set",
 "data": {
    "customer": {
     "id": 1,
     "name": "DooPage customer"
    },
    "staff": {
      "id": 1,
      "name": "DooPage staff"
    },
    "tags": [
      {
        "id": 1,
        "name": "tag_name"
      },
      {
        "id": 2,
        "name": "tag 2"
      }
    ]
 }
}
```

### ​**Customer Tag unset**

```javascript
{
 "company_id": 1,
 "event_type": "customer_tags_unset",
 "data": {
    "customer": {
      "id": 1,
      "name": "DooPage customer"
    },
    "staff": {
        "id": 1,
        "name": "DooPage staff"
    },
    "tags": [
      {
        "id": 1,
        "name": "tag_name"
      },
      {
        "id": 2,
        "name": "tag 2"
      }
    ]
 }
}
```

### ​**Note changed**

```javascript
{
 "company_id": 1,
 "event_type": "customer_note_changed",
 "data": {
    "customer": {
      "id": 1,
      "name": "DooPage customer"
    },
    "note": "Customer note"
  }
}
```

### ​**Customer Pipeline Updated**

```javascript
{
 "company_id": 1,
 "event_type": "pipeline_updated",
 "data": {
    "customer": {
      "id": 1,
      "name": "DooPage customer"
    },
    "staff": {
        "id": 1,
        "name": "DooPage staff"
    },
    "pipeline": {
      "id": 1,
      "name": "Re-target"
    }
  }
}
```

### **Customer Message Have Phone**

```javascript
{
 "company_id": 1,
 "event_type": "customer_message_have_phone",
 "data": {
    "customer": {
      "id": 1,
      "name": "DooPage customer"
    },
    "phones": ["0389111222"]
  }
}
```
