Validating FIAT Payment webhooks
Webhooks are critical for real-time notifications within our platform, enabling automated updates related to FIAT payment events. When such events occur, BVNK makes an HTTP request to the URL configured by the user for the webhooks.
Acknowledging Webhooks
The webhooks service expects to receive a 200 HTTP status code as an acknowledgment of a successfully received and validated webhook. This response should be sent immediately upon receipt of the webhook to confirm its delivery and avoid timeout errors.
Webhook Validation
Webhooks include an x-signature header, which enables servers to authenticate the request using a secret key specific to your server setup.
Signature Calculation with HMAC
The process to calculate and verify the signature using HMAC
Capture the raw payload: Do not parse the incoming HTTP request into an object or re-serialize it. Instead, capture the raw payload as a string to ensure that the signature verification process is accurate.
import hmac
import hashlib
import base64
def main():
# Public key provided from webhook configuration
secret_key = "your_secret_key_here"
# Signature received from webhook (Base64 encoded)
signature_base64 = "expected_signature_base64_here"
# Body of the request
body = "hello world"
# Create HMAC-SHA256 using the secret key
computed_hmac = hmac.new(
key=secret_key.encode('utf-8'),
msg=body.encode('utf-8'),
digestmod=hashlib.sha256
)
# Get the digest and encode to Base64
computed_hmac_base64 = base64.b64encode(computed_hmac.digest()).decode('utf-8')
# Compare the computed signature with the received signature
verified = signature_base64 == computed_hmac_base64
print(f"Computed HMAC: {computed_hmac_base64}")
print(f"Signature is valid: {verified}")
if __name__ == "__main__":
main()
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.nio.charset.StandardCharsets;
public class JavaHmacSignatureExample {
public static void main(String[] args) throws Exception {
// Public key provided from webhook configuration
String secretKey = "your_secret_key_here";
// Signature received from webhook (Base64 encoded)
String signatureBase64 = "expected_signature_base64_here";
// Body of the request
String body = "hello world";
// Create HMAC-SHA256 instance
Mac hmac = Mac.getInstance("HmacSHA256");
// Initialize with the secret key
SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
hmac.init(keySpec);
// Compute the HMAC
byte[] computedHmac = hmac.doFinal(body.getBytes(StandardCharsets.UTF_8));
// Encode to Base64
String computedHmacBase64 = Base64.getEncoder().encodeToString(computedHmac);
// Compare the computed signature with the received signature
boolean verified = signatureBase64.equals(computedHmacBase64);
System.out.println("Computed HMAC: " + computedHmacBase64);
System.out.println("Signature is valid: " + verified);
}
}
const crypto = require('crypto');
// Function to verify HMAC signatures
function verifyHmacSignature(payload, signature, secretKey) {
// Create an HMAC-SHA256 instance with the secret key
const hmac = crypto.createHmac('sha256', secretKey);
// Update HMAC with the payload
hmac.update(payload);
// Generate the computed HMAC signature (in Base64)
const computedSignature = hmac.digest('base64');
// Compare the computed signature with the received signature
console.log('Computed signature:', computedSignature);
console.log('Received signature:', signature);
return crypto.timingSafeEqual(
Buffer.from(computedSignature, 'utf-8'),
Buffer.from(signature, 'utf-8')
);
}
// Example usage
const secretKey = 'your_secret_key_here';
const payload = 'hello world';
const receivedSignature = 'expected_signature_base64_here'; // This would come from the webhook
try {
const isValid = verifyHmacSignature(payload, receivedSignature, secretKey);
console.log('Signature is valid:', isValid);
} catch (error) {
console.error('Error during signature verification:', error.message);
}
Updated 16 days ago