Gareth Wilson Gareth Wilson

Guide to Solidgate Webhooks: Features and Best Practices

Published


Solidgate webhooks notify your systems about payment activity: an order updates, a chargeback is received, a subscription changes. If you're building payments on Solidgate, webhooks are how you react to these events without polling.

This guide covers how Solidgate webhooks work, the events you'll handle, its unusual signature scheme (get this exactly right), the retry behavior, and the best practices for production.

What are Solidgate webhooks?

Solidgate webhooks are HTTP POSTs delivered to endpoints you configure. Each carries two headers: merchant (your webhook public key, prefixed wh_pk_) and signature (the computed signature). The signature scheme is unusual and easy to get wrong, so the securing section below is the part to focus on.

Solidgate webhook features

FeatureDetails
ConfigurationSolidgate Hub: keys under Developers; endpoints/events under Developers > Channels
Headersmerchant (public key, wh_pk_), signature
Signature schemebase64(hex(HMAC-SHA512(secretKey, publicKey + rawBody + publicKey)))
Keyswh_pk_ (public) / wh_sk_ (secret)
Retries8 attempts: 15m, 30m, 1h, 2h, 4h, 8h, 16h, 24h
Acknowledgement2xx within 30 seconds
SDK@solidgate/node-sdk, solidgate-sdk (no dedicated webhook-verify helper)

Common events

Solidgate event names are dotted and product-scoped:

EventFires when
card_gate.order.updatedA card order updates
card_gate.chargeback.receivedA chargeback is received
alt_gate.order.updatedAn alternative-payment order updates
subscription.updated.v2A subscription updates (the .v2 suffix is real)

Subscribe to the events you process, and consult Solidgate's webhook reference for the full list.

Setting up Solidgate webhooks

Set your webhook keys in Solidgate Hub > Developers, and configure endpoints and events under Developers > Channels. Each delivery carries the merchant (public key) and signature headers you verify with.

Securing Solidgate webhooks

The scheme has a distinctive double-encode. Construct text = publicKey + rawBody + publicKey, compute HMAC-SHA512(secretKey, text), take the hex string of that digest, then base64-encode the hex string (not the raw digest bytes). Compare the result to the signature header. Use the secret key (wh_sk_) and the exact raw body.

const crypto = require("crypto");

const SECRET_KEY = process.env.SOLIDGATE_WEBHOOK_SECRET_KEY; // wh_sk_

function verify(rawBody, publicKey, signature) {
  const text = publicKey + rawBody.toString() + publicKey;
  const hex = crypto.createHmac("sha512", SECRET_KEY).update(text).digest("hex");
  // base64 of the HEX STRING, not the raw digest bytes
  const expected = Buffer.from(hex).toString("base64");

  const a = Buffer.from(expected);
  const b = Buffer.from(signature || "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const publicKey = req.headers["merchant"];
  if (!verify(req.body, publicKey, req.headers["signature"])) {
    return res.sendStatus(401);
  }

  res.sendStatus(200); // acknowledge within 30s
  processQueue.add(JSON.parse(req.body)); // process asynchronously
});

The same in Python (matching Solidgate's official example):

import base64
import hashlib
import hmac
import os

SECRET_KEY = os.environ["SOLIDGATE_WEBHOOK_SECRET_KEY"].encode()  # wh_sk_


def verify(raw_body: bytes, public_key: str, signature: str) -> bool:
    text = (public_key.encode() + raw_body + public_key.encode())
    hmac_hash = hmac.new(SECRET_KEY, text, hashlib.sha512).digest()
    # base64 of the hex string (Solidgate's official construction)
    expected = base64.b64encode(hmac_hash.hex().encode("utf-8")).decode("utf-8")
    return hmac.compare_digest(expected, signature or "")

Solidgate webhook limitations and pain points

The double-encode is easy to get wrong

The Problem: The signature is base64(hex(HMAC-SHA512(...))), base64 of the hex string, not base64 of the raw digest bytes. And the signed text wraps the body in the public key (publicKey + body + publicKey). Almost every "obvious" implementation (base64 of the digest, or signing the body alone) fails.

Why It Happens: Solidgate's official construction double-encodes and brackets the body with the public key.

Workarounds:

  • Follow the construction exactly: text = pk + body + pk, HMAC-SHA512 with wh_sk_, hex, then base64 the hex string.

How Hookdeck Can Help: Hookdeck verifies the Solidgate signature at the edge, so your application receives pre-verified events without reproducing the double-encode.

Raw body sensitivity

The Problem: The body is wrapped into the signed text verbatim, so any re-serialization breaks verification.

Why It Happens: The signature is over the exact raw body between the two public-key copies.

Workarounds:

  • Capture the raw body and verify before parsing.

How Hookdeck Can Help: Hookdeck verifies against the bytes as received, so your app never has to preserve the raw body.

No SDK verify helper

The Problem: The official SDKs exist but don't ship a dedicated webhook-verification helper, so you implement the scheme yourself.

Why It Happens: The SDKs target the payment APIs, not webhook ingestion.

Workarounds:

  • Use the constant-time implementation above, centralized in one place.

How Hookdeck Can Help: Hookdeck verifies the signature at the edge, so your app receives pre-verified events without hand-rolled crypto.

Retries and duplicates

The Problem: Solidgate retries 8 times (15m through 24h), so the same event can arrive more than once, and double-processing a payment event is costly.

Why It Happens: At-least-once delivery favors eventual delivery.

Workarounds:

  • Dedupe on an event/order id, make side effects idempotent, and confirm state via the API before releasing value.

How Hookdeck Can Help: Hookdeck deduplicates deliveries at the edge, so retries don't double-process. See our guide to webhook idempotency.

Best practices

Verify with the exact double-encode

text = pk + rawBody + pk, HMAC-SHA512 with wh_sk_, hex, then base64 the hex string; compare in constant time to signature.

Verify against the raw body

Capture the raw body so the wrapped signed text matches.

Acknowledge within 30 seconds, process asynchronously

Return 2xx quickly and defer work to a queue. See why to process webhooks asynchronously.

Dedupe and confirm before settling

Dedupe on an event/order id, and confirm payment state via the API before releasing value.

Make Solidgate webhooks production-ready

Hookdeck verifies the signature, deduplicates, and durably queues every payment event

Conclusion

Solidgate webhooks use an unusual signature: base64(hex(HMAC-SHA512(secretKey, publicKey + rawBody + publicKey))), delivered with merchant and signature headers. The double-encode and the public-key-wrapped body are the things to get exactly right; then verify against the raw body, dedupe across the 8-attempt retry schedule, and confirm state before releasing value.

Hookdeck verifies the signature, deduplicates, and durably queues every event at the edge, so your app only ever processes verified, unique payment events.

Get started with Hookdeck for free and handle Solidgate webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.