Webhook
CuanTuan menolak setiap penghantaran borang ke URL anda, ditandatangani supaya anda boleh membuktikan ia benar-benar daripada kami.
Memasangnya
Panel admin → Formulir Data → pilih borang → Integrasi. Masukkan URL penerima anda dan simpan; rahsia tandatangan dijana pada masa itu dan dipaparkan sekali.
Bentuk permintaan
Setiap penghantaran borang dihantar sebagai POST JSON dengan pengepala ini:
| Pengepala | Isi |
|---|---|
| X-CuanTuan-Signature | t=<unix>,v1=<hmac-sha256 hex> |
| X-CuanTuan-Delivery-Id | Id event. Sama pada setiap cubaan semula — gunakannya untuk nyahduplikasi. |
| X-CuanTuan-Submission-Id | Id penghantaran borang. |
| X-CuanTuan-Form-Id | Id borang. |
Mengesahkan tandatangan
Tandatangan dikira ke atas rentetan <t>.<badan mentah> menggunakan HMAC-SHA256 dengan rahsia integrasi anda.
import crypto from "node:crypto";
export function sahkan(badanMentah, pengepala, rahsia) {
const bahagian = Object.fromEntries(
pengepala.split(",").map((p) => p.split("=").map((x) => x.trim()))
);
const t = Number(bahagian.t);
if (!Number.isFinite(t)) return false;
// 1. Tolak permintaan lama. Tanpa ini, seseorang yang merakam satu
// permintaan sah boleh memainkannya semula bertahun kemudian.
if (Math.abs(Math.floor(Date.now() / 1000) - t) > 300) return false;
// 2. Kira semula ke atas BADAN MENTAH — bukan objek yang sudah dihurai.
// JSON.stringify(JSON.parse(x)) tidak semestinya sama dengan x.
const jangkaan = crypto
.createHmac("sha256", rahsia)
.update(`${t}.${badanMentah}`)
.digest("hex");
// 3. Bandingkan dalam masa TETAP. Perbandingan === bocor melalui masa.
const a = Buffer.from(jangkaan, "hex");
const b = Buffer.from(String(bahagian.v1 || ""), "hex");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}=== membocorkan tandatangan sebenar melalui masa tindak balas.Cubaan semula dan nyahduplikasi
Penghantaran yang gagal dicuba semula. Setiap cubaan membawa X-CuanTuan-Delivery-Id yang sama, jadi simpan id itu dan abaikan yang sudah dilihat — kalau tidak, satu penghantaran borang menjadi dua baris dalam helaian kerja anda.
Yang TIDAK dihantar
- Alamat IP penghantar.
- Mana-mana jawapan yang tidak ditanda secara eksplisit dalam pemetaan medan.
Ini keputusan privasi, bukan kelalaian: destinasi seperti helaian kerja berkongsi selalunya dilihat lebih ramai orang daripada panel admin.

