Authentication
Setiap request ke /v1 wajib membawa tiga header:
| Header | Isi |
|---|---|
X-Payroute-Key |
key_id client, mis. pk_… |
X-Payroute-Timestamp |
waktu saat menandatangani, Unix detik |
X-Payroute-Signature |
HMAC-SHA256 (hex huruf kecil) dengan secret |
String yang ditandatangani
timestamp + "." + METHOD + "." + path_and_query + "." + raw_body
METHODhuruf besar (GET,POST).path_and_querypersis seperti dikirim, termasuk query string:/v1/charges?invoice_id=INV-1.raw_bodyadalah byte body persis yang dikirim (kosong untuk GET). Tanda tangani setelah JSON di-serialize, dan kirim byte yang sama.
Test vector — pastikan implementasi Anda menghasilkan nilai ini:
secret = sk_test
timestamp = 1700000000
method = POST
path = /v1/charges
body = {"a":1}
signature = 9820b41afa1a23569cd152af00c31b5467859f336c1b16f7e2fdde2dda89bb50
Contoh
Go (lengkap di docs/examples/sign.go):
func Sign(secret, ts, method, pathAndQuery string, body []byte) string {
m := hmac.New(sha256.New, []byte(secret))
m.Write([]byte(ts + "." + strings.ToUpper(method) + "." + pathAndQuery + "."))
m.Write(body)
return hex.EncodeToString(m.Sum(nil))
}
TypeScript / Node (lengkap di docs/examples/sign.ts):
import { createHmac } from "node:crypto";
export function sign(secret: string, ts: string, method: string, pathAndQuery: string, body: string): string {
return createHmac("sha256", secret)
.update(`${ts}.${method.toUpperCase()}.${pathAndQuery}.${body}`)
.digest("hex");
}
Aturan
- Timestamp harus dalam ±5 menit dari jam Payroute. Pastikan server Anda sinkron NTP.
- Buat signature baru untuk setiap request. Satu signature hanya berlaku untuk request itu (body, path, waktu).
- Semua kegagalan — key tidak dikenal, client nonaktif, timestamp kedaluwarsa, signature salah, header kurang — dijawab sama:
401 invalid_signature. Alasan detailnya ada di log Payroute (sebutrequest_iddari body error saat minta bantuan). - Simpan
secretdi secret manager / env, jangan di repo atau di aplikasi kiosk. Kalau secret bocor, minta tim Payroute me-rotate-nya; secret lama langsung tidak berlaku. - Body maksimal 64 KB.