Finix
    Finix
    • Finix API Documentation
    • Introduction
    • Signature
    • Signature Verification
    • India
      • Payment
        • Initiate
        • Fetch
        • Webhook
      • Payout
        • Initiate
        • Fetch
        • Webhook
    • Nigeria
      • Payment
        • Initiate
        • Fetch
        • Webhook
      • Payout
        • Initiate
        • Fetch
        • Webhook
        • Bank Code
    • Balance
      POST
    • USDT Rate
      POST

    Signature Verification

    RSA-SHA512 Webhook Signature Verification Mechanism#

    1. Purpose of Signature Verification#

    To ensure the authenticity, integrity, and reliability of webhook notifications from the Finix platform, merchants must verify the Signature field included in the webhook HTTP headers using RSA with SHA512 (SHA512withRSA).

    2. Usage Scenarios#

    Whenever the Finix platform sends a webhook to notify merchants of transaction status or updates, the merchant is required to verify the digital signature in the request header to confirm the source and content have not been tampered with.

    3. Signature Structure and Rules#

    Finix includes a Signature field in the webhook request header. The merchant must compute the verification string using the request Body and Timestamp header, and validate it using Finix's public key.
    1
    Signature Algorithm
    Hash Algorithm: SHA512 (Secure Hash Algorithm 512-bit)
    Encryption Algorithm:RSA
    Full Signature Scheme: SHA512withRSA
    Encoding: Base64
    2
    Verification String Format
    The signature string is constructed as:
    string_to_verify = SHA512(body) + Timestamp
    Notes:
    body: The raw JSON string from the webhook request body (field order must be preserved)
    timestamp: The Unix timestamp from the request header (in seconds)
    The URL is not included in the signature
    Example body:
    {"status":"SUCCESS","orderId":"ABC123","amount":100}
    SHA512 hash result (lowercase hex):
    09ec4d91b7dd1fce70320b09f3fd7e98cd66efc8f918bb56e98a37c6b179f7d24fc4e43c0a614f8b9e5e95e410af0c0fd9d5f40ad8e38b30a9ad512b48c9c0e7
    Timestamp:
    1699447297
    String to verify:
    09ec4d91b7dd1fce70320b09f3fd7e98cd66efc8f918bb56e98a37c6b179f7d24fc4e43c0a614f8b9e5e95e410af0c0fd9d5f40ad8e38b30a9ad512b48c9c0e71699447297
    3
    Public Key Verification
    Use Finix’s public RSA key to verify the signature using the SHA512withRSA algorithm
    The Signature value from the header must be Base64-decoded before verification
    If the signature is valid, the webhook is considered authentic

    4. Security Considerations#

    Finix uses its private key to sign the webhook data. The merchant uses the public key to verify it.
    This ensures that:
    The webhook is genuinely from Finix
    The data has not been tampered with
    The request is not replayed or forged

    5. Important Notes#

    Field case sensitivity matters — the signature string must match exactly
    Keep the JSON body unchanged — avoid reformatting, reordering fields, or adding whitespace
    Signature is Base64-encoded
    Timestamp must be extracted from the request headers

    6. Code Examples#

    Java
    Python
    PHP
    C#(.NET Core)
    Go
    import java.nio.charset.StandardCharsets;
    import java.security.*;
    import java.security.spec.X509EncodedKeySpec;
    import java.util.Base64;
    
    public class CallbackVerifier {
        public static void main(String[] args) throws Exception {
            String body = "{\"status\":\"SUCCESS\",\"orderId\":\"ABC123\",\"amount\":100}";
            String timestamp = "1699447297";
            String signatureBase64 = "REQUEST_HEADER_SIGNATURE_VALUE";
    
            String bodyHash = sha512Hex(body);
            String stringToVerify = bodyHash + timestamp;
    
            String publicKeyPem = "-----BEGIN PUBLIC KEY-----\nYOUR_FINIX_PUBLIC_KEY_HERE\n-----END PUBLIC KEY-----";
            PublicKey publicKey = loadPublicKey(publicKeyPem);
    
            boolean isValid = verifySignature(publicKey, stringToVerify, signatureBase64);
            System.out.println("Signature valid: " + isValid);
        }
    
        public static String sha512Hex(String data) throws Exception {
            MessageDigest md = MessageDigest.getInstance("SHA-512");
            byte[] digest = md.digest(data.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for (byte b : digest) sb.append(String.format("%02x", b));
            return sb.toString();
        }
    
        public static PublicKey loadPublicKey(String pem) throws Exception {
            pem = pem.replace("-----BEGIN PUBLIC KEY-----", "")
                     .replace("-----END PUBLIC KEY-----", "")
                     .replaceAll("\\s+", "");
            byte[] decoded = Base64.getDecoder().decode(pem);
            X509EncodedKeySpec spec = new X509EncodedKeySpec(decoded);
            return KeyFactory.getInstance("RSA").generatePublic(spec);
        }
    
        public static boolean verifySignature(PublicKey publicKey, String data, String signatureBase64) throws Exception {
            byte[] signatureBytes = Base64.getDecoder().decode(signatureBase64);
            Signature verifier = Signature.getInstance("SHA512withRSA");
            verifier.initVerify(publicKey);
            verifier.update(data.getBytes(StandardCharsets.UTF_8));
            return verifier.verify(signatureBytes);
        }
    }
    
    Previous
    Signature
    Next
    Initiate
    Built with