Langkah
Baca badan permintaan mentah sebelum di-parse
Simpan byte badan persis seperti diterima. Framework yang mem-parse JSON otomatis perlu diatur agar badan mentahnya tetap tersedia.
Hitung HMAC-SHA256 atas
X-Gocap-Timestamp, satu titik, lalu badan mentahKuncinya adalah Webhook Secret dari Pengaturan akun persis seperti ditampilkan: 64 karakter heksa itu sendiri adalah kuncinya.
Bandingkan
sha256=diikuti hasilnya dengan headerX-Gocap-Signaturesecara waktu-konstanPakai
hash_equals,crypto.timingSafeEqual, atauhmac.compare_digest, bukan==.Tolak bila
X-Gocap-Timestampbukan angka atau selisihnya dengan jam server Anda lebih dari 300 detikBalas 2xx tanpa badan, lalu proses isinya
gocap menunggu balasan paling lama 10 detik. Badan JSON berisi
messageatauurlmenjadi balasan webhook: gocap mengirimnya sebagai pesan WhatsApp ke pengirim (ke grup bila pesan berasal dari grup) dan memakai kuota. Balas 401 untuk permintaan yang ditolak.
Contoh penerima
Setiap contoh berisi fungsi verifikasi yang lulus semua vektor uji dan server kecil yang memakainya.
PHP
<?php
// Webhook Secret dari Pengaturan akun, disalin apa adanya.
$secret = getenv('WAVA_WEBHOOK_SECRET');
if ($secret === false || $secret === '') {
error_log('WAVA_WEBHOOK_SECRET kosong');
http_response_code(500);
exit;
}
// true bila permintaan sah dari gocap. $raw: badan mentah.
// $timestamp, $signature: isi header X-Gocap-Timestamp dan
// X-Gocap-Signature. $secret: Webhook Secret apa adanya.
// $now: detik unix saat ini.
function verifikasi_webhook(
string $raw,
string $timestamp,
string $signature,
string $secret,
int $now
): bool {
if (!ctype_digit($timestamp)) {
return false;
}
if (abs($now - (int) $timestamp) > 300) {
return false;
}
$expected = 'sha256=' . hash_hmac(
'sha256',
$timestamp . '.' . $raw,
$secret
);
return hash_equals($expected, $signature);
}
$raw = file_get_contents('php://input');
$sah = verifikasi_webhook(
$raw,
$_SERVER['HTTP_X_GOCAP_TIMESTAMP'] ?? '',
$_SERVER['HTTP_X_GOCAP_SIGNATURE'] ?? '',
$secret,
time()
);
if (!$sah) {
http_response_code(401);
exit;
}
// Balas 2xx tanpa badan dalam 10 detik, lalu proses.
http_response_code(200);
if (function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
}
$event = json_decode($raw, true);
error_log('pesan masuk ' . ($event['inboxid'] ?? ''));Node.js
const crypto = require("node:crypto");
const http = require("node:http");
// Webhook Secret dari Pengaturan akun, disalin apa adanya.
const secret = process.env.WAVA_WEBHOOK_SECRET;
if (!secret) throw new Error("WAVA_WEBHOOK_SECRET kosong");
// true bila permintaan sah dari gocap. raw: Buffer badan
// mentah. timestamp, signature: isi header
// X-Gocap-Timestamp dan X-Gocap-Signature. secret: Webhook
// Secret apa adanya. now: detik unix saat ini.
function verifikasiWebhook(
raw,
timestamp,
signature,
secret,
now,
) {
if (!/^[0-9]+$/.test(timestamp)) return false;
if (Math.abs(now - Number(timestamp)) > 300) {
return false;
}
const hmac = crypto.createHmac("sha256", secret);
hmac.update(timestamp + ".");
hmac.update(raw);
const expected = Buffer.from(
"sha256=" + hmac.digest("hex"),
);
const received = Buffer.from(signature);
// Panjang dibandingkan dalam byte, bukan karakter:
// header non-ASCII tidak membuat timingSafeEqual crash.
return (
received.length === expected.length &&
crypto.timingSafeEqual(received, expected)
);
}
http
.createServer((req, res) => {
const chunks = [];
req.on("data", (chunk) => chunks.push(chunk));
req.on("end", () => {
const raw = Buffer.concat(chunks);
const sah = verifikasiWebhook(
raw,
String(req.headers["x-gocap-timestamp"] ?? ""),
String(req.headers["x-gocap-signature"] ?? ""),
secret,
Math.floor(Date.now() / 1000),
);
if (!sah) {
res.writeHead(401).end();
return;
}
// Balas 2xx tanpa badan dalam 10 detik, lalu proses.
res.writeHead(200).end();
try {
const event = JSON.parse(raw);
console.log("pesan masuk", event.inboxid);
} catch (err) {
console.error("payload tidak terbaca", err);
}
});
})
.listen(3000);Python
import hashlib
import hmac
import json
import os
import time
from http.server import BaseHTTPRequestHandler, HTTPServer
# Webhook Secret dari Pengaturan akun, disalin apa adanya.
SECRET = os.environ["WAVA_WEBHOOK_SECRET"]
def verifikasi_webhook(
raw: bytes,
timestamp: str,
signature: str,
secret: str,
now: int,
) -> bool:
"""True bila permintaan sah dari gocap.
raw: badan mentah. timestamp, signature: isi header
X-Gocap-Timestamp dan X-Gocap-Signature. secret: Webhook
Secret apa adanya. now: detik unix saat ini.
"""
if not (timestamp.isascii() and timestamp.isdigit()):
return False
if abs(now - int(timestamp)) > 300:
return False
mac = hmac.new(
secret.encode(),
timestamp.encode() + b"." + raw,
hashlib.sha256,
)
expected = "sha256=" + mac.hexdigest()
return hmac.compare_digest(
signature.encode(), expected.encode()
)
class Penerima(BaseHTTPRequestHandler):
def do_POST(self):
panjang = int(self.headers.get("Content-Length", 0))
raw = self.rfile.read(panjang)
sah = verifikasi_webhook(
raw,
self.headers.get("X-Gocap-Timestamp", ""),
self.headers.get("X-Gocap-Signature", ""),
SECRET,
int(time.time()),
)
# Balas 2xx tanpa badan dalam 10 detik, lalu proses.
self.send_response(200 if sah else 401)
self.end_headers()
if sah:
event = json.loads(raw)
print("pesan masuk", event.get("inboxid"))
HTTPServer(("", 3000), Penerima).serve_forever()Simpan Webhook Secret dari Pengaturan akun, disalin apa adanya, di variabel lingkungan WAVA_WEBHOOK_SECRET sebelum menjalankan penerima; jangan menuliskannya di kode.
Kesalahan umum
- Badan di-parse lalu diserialisasi ulang. JSON yang dibentuk ulang tidak lagi sama byte demi byte dengan yang ditandatangani (spasi, urutan kunci, escape). Hitung HMAC atas badan mentah, lalu parse sesudahnya.
- Secret didekode dari heksa. Webhook Secret tampil sebagai 64 karakter heksa, dan teks itulah kuncinya. Kunci 32 byte hasil dekode menghasilkan tanda tangan lain, sehingga semua permintaan tertolak.
- Jam server melenceng. Selisih waktu dihitung dari jam server Anda; jam yang melenceng lebih dari 300 detik menolak permintaan yang sah. Sinkronkan jam dengan NTP.
- Membalas 401 lalu heran ada percobaan ulang. Setiap balasan selain 2xx dihitung gagal dan dicoba lagi dengan jeda 1, 5, 15, lalu 40 menit antar-percobaan (total 5 percobaan dalam ±61 menit). Itu disengaja: setelah secret di penerima diperbaiki, percobaan berikutnya ditandatangani dengan Webhook Secret yang berlaku saat itu dan diterima.
- Membandingkan panjang string sebelum timingSafeEqual. Di Node.js,
timingSafeEqualmelempar galat bila keduaBufferberbeda panjang byte. Header berkarakter non-ASCII dapat sama panjang dengan tanda tangan asli bila dihitung dalam karakter, tetapi lebih panjang dalam byte; pemeriksaan panjang string lalu lolos dantimingSafeEqualmelempar galat. Bandingkan panjangBuffer, seperti pada contoh.
Vektor uji
Uji fungsi verifikasi Anda tanpa menunggu webhook sungguhan. Semua vektor memakai Webhook Secret uji di bawah, bukan secret akun Anda, dan kolom now sebagai jam sekarang. Vektor dibangkitkan tes Go dari kode penanda tangan gocap, jadi selalu sesuai dengan tanda tangan yang dikirim.
- Webhook Secret uji
527da983912e24551b118a0a4a76707e66f63e1326f371529615e77ed162bf3a- Toleransi selisih waktu
- 300 detik
| Vektor | Hasil | Alasan | X-Gocap-Timestamp | now | Badan | X-Gocap-Signature |
|---|---|---|---|---|---|---|
| sah-ascii | sah | Tanda tangan HMAC-SHA256 atas timestamp, titik, dan badan persis, dengan Webhook Secret apa adanya sebagai kunci; selisih waktu 10 detik. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0 |
| sah-utf8-multibita | sah | Badan memuat karakter UTF-8 multibita (emoji, tanda pisah, huruf beraksen); HMAC dihitung atas byte badan persis. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628zzzzzzzzz","name":"Siti Aisyah","message":"Terima kasih 🙏 pesanan sudah sampai ✅ — salam dari Café Nusa","timestamp":1757578376,"inboxid":"91027734"} | sha256=3dd61109caf6431b458f766f8674af31ac6fa2b265b451ef77ceea79f8ec0b75 |
| sah-spasi-dan-baris-baru | sah | Badan berindentasi dengan spasi dan baris baru ditandatangani apa adanya; jangan merapikan badan sebelum verifikasi. | 1757578380 | 1757578390 | {
"device": "628xxxxxxxxx",
"sender": "628yyyyyyyyy",
"name": "Budi",
"message": "Halo, apakah stok masih ada?",
"timestamp": 1757578375,
"inboxid": "91027733"
} | sha256=2073caa730c1460981a088d153960cd6f4714ea20a11ff344b1b86e916e9d2f4 |
| sah-karakter-html | sah | Karakter <, >, dan & dikirim literal, tidak di-escape; verifikasi atas badan mentah tetap sah. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Harga <promo> & diskon 10% hari ini","timestamp":1757578377,"inboxid":"91027735"} | sha256=b4cc1abd2918f9fb9c17d036b0e638c9101b03fd81d0a79a0a6c229389f4d669 |
| sah-badan-kosong | sah | Badan kosong: masukan HMAC hanya timestamp dan titik. | 1757578380 | 1757578390 | (kosong) | sha256=8c2a99712ad320cfb1cc6e51cb409a67db6d7c273f730834dc14f5f1814208e0 |
| sah-pesan-grup | sah | Payload pesan grup: sender berisi ID grup dan member berisi pengirim. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","sender":"120363000000000000@g.us","member":"628zzzzzzzzz","name":"Siti Aisyah","message":"Rapat jam 10 ya","timestamp":1757578378,"inboxid":"91027736"} | sha256=822ceb0dbd0f7ff3da868218cbfd4f96a43a1dd628fd7dad1d600e5478403956 |
| sah-waktu-sama | sah | Timestamp sama persis dengan jam penerima. | 1757578380 | 1757578380 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0 |
| sah-selisih-300-detik-lalu | sah | Selisih tepat 300 detik ke masa lalu masih di dalam toleransi. | 1757578380 | 1757578680 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0 |
| sah-selisih-300-detik-depan | sah | Selisih tepat 300 detik ke masa depan (jam penerima tertinggal) masih di dalam toleransi. | 1757578380 | 1757578080 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0 |
| tolak-badan-diubah-satu-byte | tolak | Satu huruf badan diubah setelah ditandatangani, jadi HMAC tidak cocok. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stoK masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0 |
| tolak-badan-diserialisasi-ulang | tolak | Badan di-parse lalu diserialisasi ulang sehingga spasi dan baris baru hilang; tanda tangan dihitung atas byte asli, jadi tidak cocok. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=2073caa730c1460981a088d153960cd6f4714ea20a11ff344b1b86e916e9d2f4 |
| tolak-badan-escape-html | tolak | Badan diserialisasi ulang dengan <, >, dan & di-escape menjadi \u003c, \u003e, dan \u0026; byte berubah sehingga HMAC tidak cocok. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Harga \u003cpromo\u003e \u0026 diskon 10% hari ini","timestamp":1757578377,"inboxid":"91027735"} | sha256=b4cc1abd2918f9fb9c17d036b0e638c9101b03fd81d0a79a0a6c229389f4d669 |
| tolak-badan-escape-unicode | tolak | Badan diserialisasi ulang menjadi ASCII saja (karakter non-ASCII menjadi \uXXXX, bawaan sebagian pustaka JSON); byte berubah sehingga HMAC tidak cocok. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628zzzzzzzzz","name":"Siti Aisyah","message":"Terima kasih \ud83d\ude4f pesanan sudah sampai \u2705 \u2014 salam dari Caf\u00e9 Nusa","timestamp":1757578376,"inboxid":"91027734"} | sha256=3dd61109caf6431b458f766f8674af31ac6fa2b265b451ef77ceea79f8ec0b75 |
| tolak-badan-urutan-kunci | tolak | Kunci JSON diurutkan ulang; isinya sama tetapi byte berbeda, jadi HMAC tidak cocok. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","inboxid":"91027733","message":"Halo, apakah stok masih ada?","name":"Budi","sender":"628yyyyyyyyy","timestamp":1757578375} | sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0 |
| tolak-badan-crlf | tolak | Baris baru badan diubah menjadi CRLF; HMAC tidak cocok. | 1757578380 | 1757578390 | {
"device": "628xxxxxxxxx",
"sender": "628yyyyyyyyy",
"name": "Budi",
"message": "Halo, apakah stok masih ada?",
"timestamp": 1757578375,
"inboxid": "91027733"
} | sha256=2073caa730c1460981a088d153960cd6f4714ea20a11ff344b1b86e916e9d2f4 |
| tolak-badan-baris-baru-di-akhir | tolak | Badan ditambah baris baru di akhir; HMAC tidak cocok. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"}
| sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0 |
| tolak-badan-unicode-dinormalisasi | tolak | Huruf beraksen dinormalisasi ke bentuk terurai (NFD); tampak sama tetapi byte berbeda, jadi HMAC tidak cocok. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628zzzzzzzzz","name":"Siti Aisyah","message":"Terima kasih 🙏 pesanan sudah sampai ✅ — salam dari Café Nusa","timestamp":1757578376,"inboxid":"91027734"} | sha256=3dd61109caf6431b458f766f8674af31ac6fa2b265b451ef77ceea79f8ec0b75 |
| tolak-secret-didekode-heksa | tolak | Tanda tangan dibuat dengan kunci 32 byte mentah di balik teks heksa, bukan teks secret; penerima yang benar memakai teks 64 heksa apa adanya sebagai kunci, jadi HMAC tidak cocok. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=ec5ca0ee7c6ccc0df44074d6e16074fdbd290b59ba53c42998a853e132e9d3c3 |
| tolak-secret-huruf-besar | tolak | Tanda tangan dibuat dengan secret berhuruf besar; secret yang ditampilkan dashboard berhuruf kecil dan dipakai persis. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=1c8679d44f18e30d1eb6d61155b64544adcd6b576a32f5270ed0f0c6ae8489e4 |
| tolak-secret-baris-baru-di-akhir | tolak | Tanda tangan dibuat dengan secret berakhiran baris baru, misalnya tersalin dari berkas; kuncinya harus persis 64 karakter. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=f880ec91b8fc8db3db832c0665016a2f5c61dd43d4c033b0e5813b3162ce7d11 |
| tolak-secret-lain | tolak | Tanda tangan dari Webhook Secret lain, misalnya secret lama sebelum dibuat ulang. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=caba3938b27aceb7edf2d59a1e391791c701494d92c028c4a2a7f410c969d485 |
| tolak-tanpa-awalan-sha256 | tolak | Header tanpa awalan sha256=; nilai header dibandingkan utuh. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | 1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0 |
| tolak-awalan-huruf-besar | tolak | Awalan berhuruf besar (SHA256=); awalan yang sah persis sha256=. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | SHA256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0 |
| tolak-tanda-tangan-terpotong | tolak | Heksa tanda tangan kurang satu karakter. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc |
| tolak-tanda-tangan-kosong | tolak | Header X-Gocap-Signature kosong atau tidak ada. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | (kosong) |
| tolak-tanda-tangan-non-ascii | tolak | Header 71 karakter dengan satu karakter non-ASCII: jumlah karakternya sama dengan tanda tangan sah, tetapi panjang byte-nya berbeda. Bandingkan panjang dalam byte agar pembanding waktu-konstan tidak galat. | 1757578380 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bcé |
| tolak-timestamp-10-menit-lalu | tolak | Tanda tangan sah untuk timestamp 600 detik sebelum jam penerima; di luar toleransi 300 detik, jadi ditolak untuk mencegah pengulangan. | 1757577790 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=db4e6d98b519d36a2d0d26813b16e1489eeeb2cbdbef60e62bbd38c9b1ae482d |
| tolak-selisih-301-detik-lalu | tolak | Selisih 301 detik ke masa lalu, satu detik di luar toleransi. | 1757578380 | 1757578681 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0 |
| tolak-selisih-301-detik-depan | tolak | Selisih 301 detik ke masa depan, satu detik di luar toleransi. | 1757578380 | 1757578079 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0 |
| tolak-timestamp-10-menit-depan | tolak | Tanda tangan sah untuk timestamp 600 detik sesudah jam penerima; di luar toleransi 300 detik. | 1757578990 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=9d5ee5e1de3a8eecd97bd347c084349139a9d646f78ee4b121f1e0212c73de74 |
| tolak-timestamp-diganti | tolak | Header timestamp diganti setelah ditandatangani; timestamp ikut ditandatangani, jadi HMAC tidak cocok. | 1757578381 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0 |
| tolak-timestamp-milidetik | tolak | Timestamp dalam milidetik; tanda tangannya sah, tetapi selisihnya dengan detik unix jauh di luar toleransi. | 1757578380000 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=e37a9132f59fb5ee6846872e3cb0f0a6b47ade43b0cf601725181dab4f58318c |
| tolak-timestamp-desimal | tolak | Timestamp berisi titik desimal; HMAC-nya cocok dan nilainya dalam toleransi, tetapi header yang sah hanya deret digit. | 1757578380.0 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=0ac961527783627f3621820d6bceaea55a98732fad410a2991969bf718a7f628 |
| tolak-timestamp-eksponen | tolak | Timestamp dalam notasi eksponen bernilai sama dengan detik unix yang sah dan HMAC-nya cocok, tetapi bukan deret digit. | 1.75757838e9 | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=a94e1b027d07b49038dea83de6f928f7be19bbe421eedd251341eb4d6d6638a7 |
| tolak-timestamp-titik-di-akhir | tolak | Timestamp berakhiran titik; HMAC-nya cocok, tetapi header yang sah hanya deret digit. | 1757578380. | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=af5a85d19d1f6ae3c2dc50a4b0b677a8b096431a9ea5a38819ceff4f66b44f83 |
| tolak-timestamp-kosong | tolak | Header X-Gocap-Timestamp kosong atau tidak ada. | (kosong) | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0 |
| tolak-tanpa-header | tolak | Permintaan tanpa kedua header tanda tangan. | (kosong) | 1757578390 | {"device":"628xxxxxxxxx","sender":"628yyyyyyyyy","name":"Budi","message":"Halo, apakah stok masih ada?","timestamp":1757578375,"inboxid":"91027733"} | (kosong) |
Semua vektor sebagai JSON (webhook-vectors.json)
{
"generated_by": "apps/api/internal/webhook/vectors_test.go",
"secret": "527da983912e24551b118a0a4a76707e66f63e1326f371529615e77ed162bf3a",
"tolerance_seconds": 300,
"vectors": [
{
"name": "sah-ascii",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0",
"valid": true,
"why": "Tanda tangan HMAC-SHA256 atas timestamp, titik, dan badan persis, dengan Webhook Secret apa adanya sebagai kunci; selisih waktu 10 detik."
},
{
"name": "sah-utf8-multibita",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628zzzzzzzzz\",\"name\":\"Siti Aisyah\",\"message\":\"Terima kasih 🙏 pesanan sudah sampai ✅ — salam dari Café Nusa\",\"timestamp\":1757578376,\"inboxid\":\"91027734\"}",
"signature": "sha256=3dd61109caf6431b458f766f8674af31ac6fa2b265b451ef77ceea79f8ec0b75",
"valid": true,
"why": "Badan memuat karakter UTF-8 multibita (emoji, tanda pisah, huruf beraksen); HMAC dihitung atas byte badan persis."
},
{
"name": "sah-spasi-dan-baris-baru",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\n \"device\": \"628xxxxxxxxx\",\n \"sender\": \"628yyyyyyyyy\",\n \"name\": \"Budi\",\n \"message\": \"Halo, apakah stok masih ada?\",\n \"timestamp\": 1757578375,\n \"inboxid\": \"91027733\"\n}",
"signature": "sha256=2073caa730c1460981a088d153960cd6f4714ea20a11ff344b1b86e916e9d2f4",
"valid": true,
"why": "Badan berindentasi dengan spasi dan baris baru ditandatangani apa adanya; jangan merapikan badan sebelum verifikasi."
},
{
"name": "sah-karakter-html",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Harga <promo> & diskon 10% hari ini\",\"timestamp\":1757578377,\"inboxid\":\"91027735\"}",
"signature": "sha256=b4cc1abd2918f9fb9c17d036b0e638c9101b03fd81d0a79a0a6c229389f4d669",
"valid": true,
"why": "Karakter <, >, dan & dikirim literal, tidak di-escape; verifikasi atas badan mentah tetap sah."
},
{
"name": "sah-badan-kosong",
"timestamp": "1757578380",
"now": 1757578390,
"body": "",
"signature": "sha256=8c2a99712ad320cfb1cc6e51cb409a67db6d7c273f730834dc14f5f1814208e0",
"valid": true,
"why": "Badan kosong: masukan HMAC hanya timestamp dan titik."
},
{
"name": "sah-pesan-grup",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"120363000000000000@g.us\",\"member\":\"628zzzzzzzzz\",\"name\":\"Siti Aisyah\",\"message\":\"Rapat jam 10 ya\",\"timestamp\":1757578378,\"inboxid\":\"91027736\"}",
"signature": "sha256=822ceb0dbd0f7ff3da868218cbfd4f96a43a1dd628fd7dad1d600e5478403956",
"valid": true,
"why": "Payload pesan grup: sender berisi ID grup dan member berisi pengirim."
},
{
"name": "sah-waktu-sama",
"timestamp": "1757578380",
"now": 1757578380,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0",
"valid": true,
"why": "Timestamp sama persis dengan jam penerima."
},
{
"name": "sah-selisih-300-detik-lalu",
"timestamp": "1757578380",
"now": 1757578680,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0",
"valid": true,
"why": "Selisih tepat 300 detik ke masa lalu masih di dalam toleransi."
},
{
"name": "sah-selisih-300-detik-depan",
"timestamp": "1757578380",
"now": 1757578080,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0",
"valid": true,
"why": "Selisih tepat 300 detik ke masa depan (jam penerima tertinggal) masih di dalam toleransi."
},
{
"name": "tolak-badan-diubah-satu-byte",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stoK masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0",
"valid": false,
"why": "Satu huruf badan diubah setelah ditandatangani, jadi HMAC tidak cocok."
},
{
"name": "tolak-badan-diserialisasi-ulang",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=2073caa730c1460981a088d153960cd6f4714ea20a11ff344b1b86e916e9d2f4",
"valid": false,
"why": "Badan di-parse lalu diserialisasi ulang sehingga spasi dan baris baru hilang; tanda tangan dihitung atas byte asli, jadi tidak cocok."
},
{
"name": "tolak-badan-escape-html",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Harga \\u003cpromo\\u003e \\u0026 diskon 10% hari ini\",\"timestamp\":1757578377,\"inboxid\":\"91027735\"}",
"signature": "sha256=b4cc1abd2918f9fb9c17d036b0e638c9101b03fd81d0a79a0a6c229389f4d669",
"valid": false,
"why": "Badan diserialisasi ulang dengan <, >, dan & di-escape menjadi \\u003c, \\u003e, dan \\u0026; byte berubah sehingga HMAC tidak cocok."
},
{
"name": "tolak-badan-escape-unicode",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628zzzzzzzzz\",\"name\":\"Siti Aisyah\",\"message\":\"Terima kasih \\ud83d\\ude4f pesanan sudah sampai \\u2705 \\u2014 salam dari Caf\\u00e9 Nusa\",\"timestamp\":1757578376,\"inboxid\":\"91027734\"}",
"signature": "sha256=3dd61109caf6431b458f766f8674af31ac6fa2b265b451ef77ceea79f8ec0b75",
"valid": false,
"why": "Badan diserialisasi ulang menjadi ASCII saja (karakter non-ASCII menjadi \\uXXXX, bawaan sebagian pustaka JSON); byte berubah sehingga HMAC tidak cocok."
},
{
"name": "tolak-badan-urutan-kunci",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"inboxid\":\"91027733\",\"message\":\"Halo, apakah stok masih ada?\",\"name\":\"Budi\",\"sender\":\"628yyyyyyyyy\",\"timestamp\":1757578375}",
"signature": "sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0",
"valid": false,
"why": "Kunci JSON diurutkan ulang; isinya sama tetapi byte berbeda, jadi HMAC tidak cocok."
},
{
"name": "tolak-badan-crlf",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\r\n \"device\": \"628xxxxxxxxx\",\r\n \"sender\": \"628yyyyyyyyy\",\r\n \"name\": \"Budi\",\r\n \"message\": \"Halo, apakah stok masih ada?\",\r\n \"timestamp\": 1757578375,\r\n \"inboxid\": \"91027733\"\r\n}",
"signature": "sha256=2073caa730c1460981a088d153960cd6f4714ea20a11ff344b1b86e916e9d2f4",
"valid": false,
"why": "Baris baru badan diubah menjadi CRLF; HMAC tidak cocok."
},
{
"name": "tolak-badan-baris-baru-di-akhir",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}\n",
"signature": "sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0",
"valid": false,
"why": "Badan ditambah baris baru di akhir; HMAC tidak cocok."
},
{
"name": "tolak-badan-unicode-dinormalisasi",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628zzzzzzzzz\",\"name\":\"Siti Aisyah\",\"message\":\"Terima kasih 🙏 pesanan sudah sampai ✅ — salam dari Café Nusa\",\"timestamp\":1757578376,\"inboxid\":\"91027734\"}",
"signature": "sha256=3dd61109caf6431b458f766f8674af31ac6fa2b265b451ef77ceea79f8ec0b75",
"valid": false,
"why": "Huruf beraksen dinormalisasi ke bentuk terurai (NFD); tampak sama tetapi byte berbeda, jadi HMAC tidak cocok."
},
{
"name": "tolak-secret-didekode-heksa",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=ec5ca0ee7c6ccc0df44074d6e16074fdbd290b59ba53c42998a853e132e9d3c3",
"valid": false,
"why": "Tanda tangan dibuat dengan kunci 32 byte mentah di balik teks heksa, bukan teks secret; penerima yang benar memakai teks 64 heksa apa adanya sebagai kunci, jadi HMAC tidak cocok."
},
{
"name": "tolak-secret-huruf-besar",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=1c8679d44f18e30d1eb6d61155b64544adcd6b576a32f5270ed0f0c6ae8489e4",
"valid": false,
"why": "Tanda tangan dibuat dengan secret berhuruf besar; secret yang ditampilkan dashboard berhuruf kecil dan dipakai persis."
},
{
"name": "tolak-secret-baris-baru-di-akhir",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=f880ec91b8fc8db3db832c0665016a2f5c61dd43d4c033b0e5813b3162ce7d11",
"valid": false,
"why": "Tanda tangan dibuat dengan secret berakhiran baris baru, misalnya tersalin dari berkas; kuncinya harus persis 64 karakter."
},
{
"name": "tolak-secret-lain",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=caba3938b27aceb7edf2d59a1e391791c701494d92c028c4a2a7f410c969d485",
"valid": false,
"why": "Tanda tangan dari Webhook Secret lain, misalnya secret lama sebelum dibuat ulang."
},
{
"name": "tolak-tanpa-awalan-sha256",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0",
"valid": false,
"why": "Header tanpa awalan sha256=; nilai header dibandingkan utuh."
},
{
"name": "tolak-awalan-huruf-besar",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "SHA256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0",
"valid": false,
"why": "Awalan berhuruf besar (SHA256=); awalan yang sah persis sha256=."
},
{
"name": "tolak-tanda-tangan-terpotong",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc",
"valid": false,
"why": "Heksa tanda tangan kurang satu karakter."
},
{
"name": "tolak-tanda-tangan-kosong",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "",
"valid": false,
"why": "Header X-Gocap-Signature kosong atau tidak ada."
},
{
"name": "tolak-tanda-tangan-non-ascii",
"timestamp": "1757578380",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bcé",
"valid": false,
"why": "Header 71 karakter dengan satu karakter non-ASCII: jumlah karakternya sama dengan tanda tangan sah, tetapi panjang byte-nya berbeda. Bandingkan panjang dalam byte agar pembanding waktu-konstan tidak galat."
},
{
"name": "tolak-timestamp-10-menit-lalu",
"timestamp": "1757577790",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=db4e6d98b519d36a2d0d26813b16e1489eeeb2cbdbef60e62bbd38c9b1ae482d",
"valid": false,
"why": "Tanda tangan sah untuk timestamp 600 detik sebelum jam penerima; di luar toleransi 300 detik, jadi ditolak untuk mencegah pengulangan."
},
{
"name": "tolak-selisih-301-detik-lalu",
"timestamp": "1757578380",
"now": 1757578681,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0",
"valid": false,
"why": "Selisih 301 detik ke masa lalu, satu detik di luar toleransi."
},
{
"name": "tolak-selisih-301-detik-depan",
"timestamp": "1757578380",
"now": 1757578079,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0",
"valid": false,
"why": "Selisih 301 detik ke masa depan, satu detik di luar toleransi."
},
{
"name": "tolak-timestamp-10-menit-depan",
"timestamp": "1757578990",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=9d5ee5e1de3a8eecd97bd347c084349139a9d646f78ee4b121f1e0212c73de74",
"valid": false,
"why": "Tanda tangan sah untuk timestamp 600 detik sesudah jam penerima; di luar toleransi 300 detik."
},
{
"name": "tolak-timestamp-diganti",
"timestamp": "1757578381",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0",
"valid": false,
"why": "Header timestamp diganti setelah ditandatangani; timestamp ikut ditandatangani, jadi HMAC tidak cocok."
},
{
"name": "tolak-timestamp-milidetik",
"timestamp": "1757578380000",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=e37a9132f59fb5ee6846872e3cb0f0a6b47ade43b0cf601725181dab4f58318c",
"valid": false,
"why": "Timestamp dalam milidetik; tanda tangannya sah, tetapi selisihnya dengan detik unix jauh di luar toleransi."
},
{
"name": "tolak-timestamp-desimal",
"timestamp": "1757578380.0",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=0ac961527783627f3621820d6bceaea55a98732fad410a2991969bf718a7f628",
"valid": false,
"why": "Timestamp berisi titik desimal; HMAC-nya cocok dan nilainya dalam toleransi, tetapi header yang sah hanya deret digit."
},
{
"name": "tolak-timestamp-eksponen",
"timestamp": "1.75757838e9",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=a94e1b027d07b49038dea83de6f928f7be19bbe421eedd251341eb4d6d6638a7",
"valid": false,
"why": "Timestamp dalam notasi eksponen bernilai sama dengan detik unix yang sah dan HMAC-nya cocok, tetapi bukan deret digit."
},
{
"name": "tolak-timestamp-titik-di-akhir",
"timestamp": "1757578380.",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=af5a85d19d1f6ae3c2dc50a4b0b677a8b096431a9ea5a38819ceff4f66b44f83",
"valid": false,
"why": "Timestamp berakhiran titik; HMAC-nya cocok, tetapi header yang sah hanya deret digit."
},
{
"name": "tolak-timestamp-kosong",
"timestamp": "",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "sha256=1a2140f43d279c5cb01b1060b6f62b7e5a4da98ee41b9b96e5897a10255c0bc0",
"valid": false,
"why": "Header X-Gocap-Timestamp kosong atau tidak ada."
},
{
"name": "tolak-tanpa-header",
"timestamp": "",
"now": 1757578390,
"body": "{\"device\":\"628xxxxxxxxx\",\"sender\":\"628yyyyyyyyy\",\"name\":\"Budi\",\"message\":\"Halo, apakah stok masih ada?\",\"timestamp\":1757578375,\"inboxid\":\"91027733\"}",
"signature": "",
"valid": false,
"why": "Permintaan tanpa kedua header tanda tangan."
}
]
}Google Apps Script
Google Apps Script tidak bisa menerima webhook gocap. Objek event doPost(e) tidak memuat header, sehingga X-Gocap-Signature tidak terbaca, dan balasan web app dialihkan ke URL lain, sedangkan gocap menghitung pengalihan 3xx sebagai gagal lalu mencoba ulang. Pakai penerima PHP, Node.js, atau Python di atas. Apps Script tetap dapat mengirim pesan (contoh kirim pesan dengan Apps Script).
Uji dari awal sampai akhir
Jalankan penerima dan buat URL
httpspublik ke sanaUntuk pengembangan di komputer sendiri, pakai tunnel HTTPS ke port penerima.
Simpan URL itu sebagai URL webhook pesan masuk
Buka pengaturan device dari daftar device, lalu isi URL webhook pesan masuk.
Tekan "Uji webhook"
gocap mengirim satu payload contoh bertanda tangan ke URL yang tersimpan, lalu menampilkan kode HTTP dan durasinya. Penerima yang benar menjawab 2xx. Payload uji memakai
inboxidbernilai"0", jadi mudah dibedakan dari pesan sungguhan. Tombol ini mengabaikan badan jawaban penerima, jadi tidak dapat dipakai untuk menguji balasan webhook.
Ringkasan pengiriman webhook ada di Terima pesan masuk.