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);
}