Verify webhooks in Go
The github.com/watenga/watenga-go module ships a webhook package that verifies the Watenga-Signature header and returns the parsed event.
Install
bash
go get github.com/watenga/watenga-goConstructEvent
ConstructEvent(rawBody []byte, signatureHeader, secret string, toleranceSeconds int) returns the decoded event (map[string]any) and an error. It recomputes HMAC-SHA256 over `${t}.${rawBody}`, compares it to v1 in constant time, and rejects timestamps outside the tolerance window.
HTTP handler
go
package main
import (
"io"
"net/http"
"os"
"github.com/watenga/watenga-go/webhook"
)
func watengaWebhook(w http.ResponseWriter, r *http.Request) {
rawBody, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "cannot read body", http.StatusBadRequest)
return
}
// ConstructEvent verifies the Watenga-Signature header
// (t=<unix>,v1=<hmac-sha256-hex>) and the 5-minute tolerance window.
// Pass 0 for toleranceSeconds to use the 300s default.
event, err := webhook.ConstructEvent(
rawBody,
r.Header.Get("Watenga-Signature"),
os.Getenv("WATENGA_WEBHOOK_SECRET"),
0,
)
if err != nil {
http.Error(w, "invalid signature", http.StatusBadRequest)
return
}
// event is the parsed envelope: { id, object, type, created, data }
if event["type"] == "payment.capture.completed" {
data, _ := event["data"].(map[string]any)
// fulfil the order using data["id"]
_ = data
}
w.WriteHeader(http.StatusOK)
}Use the raw body
Always pass the unparsed request body to
ConstructEvent. Any re-serialisation changes the bytes and breaks signature verification. See the Webhooks reference for the signing scheme.