Skip to main content

Webhook Signature Verification

To guarantee that incoming webhook requests actually originated from SolydFlow and were not intercepted or modified, every outbound request is cryptographically signed using Timestamped HMAC-SHA256.

[1. Extract t & v1 Headers] ➔ [2. Verify Timestamp < 5 mins] ➔ [3. Hash: timestamp + . + rawBody] ➔ [4. Timing-Safe Compare]

How It Works​

SolydFlow transmits the signature in the X-SolydFlow-Signature HTTP header:

X-SolydFlow-Signature: t=1677638400,v1=5a8d29bf1c...
  • t: UNIX timestamp when the event was generated.
  • v1: HMAC-SHA256 hex digest of the payload.
  • Signing Secret: Starts with whsec_... (Found in Console → API Management).
The 'Raw Body' Rule (Crucial)

To compute the signature correctly, you must hash the raw, unparsed HTTP body bytes exactly as received. If your server framework (Express, FastAPI, Django) parses the JSON into an object first and converts it back to a string, whitespace or key ordering will change, causing verification to fail.


Production Verification Handlers​

src/webhook.js
const express = require('express');
const crypto = require('crypto');
const app = express();

const WEBHOOK_SECRET = process.env.SOLYDFLOW_WEBHOOK_SECRET; // whsec_...

// Use express.raw() to preserve unparsed bytes
app.post('/webhook/solydflow', express.raw({ type: 'application/json' }), (req, res) => {
const sigHeader = req.headers['x-solydflow-signature'];

if (!sigHeader) {
return res.status(401).send('Missing signature header');
}

try {
// 1. Parse header components
const parts = sigHeader.split(',');
const timestamp = parts.find(p => p.startsWith('t=')).split('=')[1];
const signature = parts.find(p => p.startsWith('v1=')).split('=')[1];

// 2. Prevent Replay Attacks (5-minute tolerance)
const fiveMinsAgo = Math.floor(Date.now() / 1000) - 300;
if (parseInt(timestamp, 10) < fiveMinsAgo) {
return res.status(400).send('Webhook expired (replay attack)');
}

// 3. Compute expected signature: timestamp + "." + rawBody
const rawBody = req.body.toString('utf8');
const payloadToSign = `${timestamp}.${rawBody}`;

const expectedSig = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(payloadToSign)
.digest('hex');

// 4. Constant-time comparison to prevent timing attacks
const isValid = crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expectedSig, 'hex')
);

if (!isValid) {
return res.status(401).send('Invalid cryptographic signature');
}

// 5. Signature verified safely; process business logic
const event = JSON.parse(rawBody);
console.log("✅ Verified SolydFlow Event:", event.event);

return res.status(200).json({ received: true });
} catch (err) {
return res.status(400).send('Webhook verification failed');
}
});

Testing Your Implementation​

You can test your signature verification without making a real purchase:

  1. Open the SolydFlow Console.
  2. Go to Project Settings → Outbound Webhooks.
  3. Click "Send Test Webhook".
  4. SolydFlow will immediately dispatch a signed test_event payload to your endpoint.
  5. Check your server logs to confirm that the HMAC signature was accepted with an HTTP 200 OK.