Webhooks

Các API về webhook cho phép xem payload mẫu và cách xác thực nguồn gốc webhook.

QUAN TRỌNG

Vui lòng liên hệ SuperAI qua SuperAI Support APIsopen in new window để được hỗ trợ cấu hình Callback URL.

Cập Nhật Mẫu

Mỗi khi có sự thay đổi Trạng Thái Đơn Hàng ở Hệ Thống SuperAI, chúng tôi sẽ gửi một cập nhật sang hệ thống của Đối Tác thông qua Callback URL mà Đối Tác đã thiếp lập trước đó.

Request

Các Tham Số

Thuộc_TínhKiểu_DLMô_Tả_Chi_Tiết
typeStringLoại Cập Nhật. Ví dụ: update_status.
Các giá trị có thể:
- Trạng Thái: update_status.
- Khối Lượng: update_weight.
codeStringMã Đơn Hàng của SuperAI. Ví dụ: SGNS336484LM.883568271.
shortcodeStringMã Đơn Ngắn của SuperAI. Ví dụ: 883568271.
socStringMã Đơn Hàng của Người Gửi. Ví dụ: JLN-1805-1456.
phoneStringSố Điện Thoại của Người Nhận. Ví dụ: 01629091355.
addressStringĐịa Chỉ của Người Nhận. Ví dụ: 47 Huỳnh Văn Bánh, Phường 5, Quận Phú Nhuận, Thành phố Hồ Chí Minh.
amountIntegerSố Tiền cần Thu Hộ. Ví dụ: 450000.
weightIntegerKhối Lượng của Đơn Hàng. Ví dụ: 200.
fshipmentIntegerCước Phí Giao Hàng. Ví dụ: 25000.
statusStringMã Trạng Thái của Đơn Hàng. Ví dụ: 12.
status_nameStringTên Trạng Thái của Đơn Hàng. Ví dụ: Đã Giao Hàng Toàn Bộ.
partialStringCó Phải Đơn Đã Giao Hàng Một Phần? Ví dụ: 1.
barterStringCó Phải Đơn Có Hàng Đổi Trả? Ví dụ: 1.
reason_codeStringMã Lý Do. Ví dụ: 304.
reason_textStringChi Tiết Lý Do. Ví dụ: Không liên lạc được với Người Nhận.
created_atString- Thời Gian Tạo của Đơn Hàng. Ví dụ: 2018-07-03T17:18:29+07:00.
- Định dạng ISO 8601
updated_atString- Thời Gian Cập Nhật của Đơn Hàng. Ví dụ: 2018-07-03T17:18:29+07:00.
- Định dạng ISO 8601
pushed_atString- Thời Gian Đẩy Webhook. Ví dụ: 2018-07-03T17:18:29+07:00.
- Định dạng ISO 8601

Ví Dụ

curl --request POST \
  --url https://example.com/listen/superai \
  --header 'Content-Type: application/json' \
  --data '{
    "type": "update_status",
    "code": "SGNS336484LM.883568271",
    "shortcode": "883568271",
    "soc": "EG27533038-24",
    "name": "Anh Nam",
    "phone": "078882888",
    "address": "Đường Hữu Trí - Thị Trấn Tân Túc -Bình Chánh",
    "payer": "1",
    "amount": 230000,
    "weight": 800,
    "fshipment": 40000,
    "finsurance": 0,
    "status": "14",
    "status_name": "Hoãn Giao Hàng",
    "partial": "0",
    "barter": "0",
    "reason_code": "304",
    "reason_text": "Không liên lạc được với Người Nhận",
    "created_at": "2022-05-10T10:27:26+07:00",
    "updated_at": "2022-05-11T22:26:11+07:00",
    "pushed_at": "2022-05-11T22:26:11+07:00"
}'

Response

SuperAI dựa vào HTTP Response Status Code để xác định xem lần gửi cập nhật Đơn Hàng sang Hệ Thống Đối Tác. Nếu kết quả là 200 thì SuperAI xác định đó là cập nhật thành công.

Xác Thực Webhook

Cách thức xác thực webhook thường là thông qua việc sử dụng Chữ Ký Số hoặc Mã Bí Mật (Mã Đối Tác) được chia sẻ giữa hai bên: Người Gửi (SuperAI) và Người Nhận (Đối Tác).

Tạo Chữ Ký (Signature)

Trước tiên, Người Nhận (Đối Tác) sẽ tạo một chữ ký dựa trên dữ liệu webhook sử dụng một thuật toán băm (hashing algorithm) như SHA-256 và một Mã Bí Mật đã được chia sẻ trước đó.

So Sánh Chữ Ký

Sau đó, người nhận sẽ lấy giá trị từ trường Signature trong Header của yêu cầu webhook đến và so sánh với giá trị chữ ký đã tính toán phía trên. Nếu hai giá trị này trùng khớp, điều đó có nghĩa là webhook được gửi từ người gửi được xác thực và không bị giả mạo.

Ví Dụ

PHP

$partnerCode = 'YOUR_PARTNER_CODE';

function get_header_value($name) {
    $headers = getallheaders();
    return isset($headers[$name]) ? $headers[$name] : null;
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $requestContent = file_get_contents('php://input');
    $receivedSignature = get_header_value('Signature');

    if ($receivedSignature === null) {
        echo "Signature header not found";
        exit;
    }

    $computedSignature = hash_hmac('sha256', $requestContent, $partnerCode);

    if (hash_equals($computedSignature, $receivedSignature)) {
        echo "Signature is valid";
    } else {
        echo "Invalid signature";
    }
} else {
    echo "Only POST requests are accepted.";
}

Node.js

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

const partnerCode = 'YOUR_PARTNER_CODE';

app.use(express.raw({ type: '*/*' }));

app.post('/', (req, res) => {
    const requestContent = req.body;
    const receivedSignature = req.headers['signature'];

    if (!receivedSignature) {
        return res.status(400).send('Signature header not found');
    }

    const computedSignature = crypto.createHmac('sha256', partnerCode)
                                    .update(requestContent)
                                    .digest('hex');

    if (crypto.timingSafeEqual(Buffer.from(computedSignature), Buffer.from(receivedSignature))) {
        res.send('Signature is valid');
    } else {
        res.status(400).send('Invalid signature');
    }
});

app.listen(3000, () => {
    console.log('Server is listening on port 3000');
});

Python

from flask import Flask, request
import hmac
import hashlib

app = Flask(__name__)
partner_code = 'YOUR_PARTNER_CODE'

@app.route('/', methods=['POST'])
def compute_signature():
    request_content = request.get_data()
    received_signature = request.headers.get('Signature')

    if not received_signature:
        return 'Signature header not found', 400

    computed_signature = hmac.new(partner_code.encode(), request_content, hashlib.sha256).hexdigest()

    if hmac.compare_digest(computed_signature, received_signature):
        return 'Signature is valid'
    else:
        return 'Invalid signature', 400

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

Golang

package main

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

const partnerCode = "YOUR_PARTNER_CODE"

func computeSignature(w http.ResponseWriter, r *http.Request) {
    requestContent, err := ioutil.ReadAll(r.Body)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    receivedSignature := r.Header.Get("Signature")
    if receivedSignature == "" {
        http.Error(w, "Signature header not found", http.StatusBadRequest)
        return
    }

    h := hmac.New(sha256.New, []byte(partnerCode))
    h.Write(requestContent)
    computedSignature := hex.EncodeToString(h.Sum(nil))

    if hmac.Equal([]byte(computedSignature), []byte(receivedSignature)) {
        fmt.Fprintln(w, "Signature is valid")
    } else {
        http.Error(w, "Invalid signature", http.StatusBadRequest)
    }
}

func main() {
    http.HandleFunc("/", computeSignature)
    fmt.Println("Server is listening on port 3000")
    http.ListenAndServe(":3000", nil)
}
Cập Nhật Lần Cuối: