Sign In

Webhooks

GrowthBnk POSTs a signed event to your configured URL when a payment reaches a terminal state — verify it, then fulfill.

Setup

Configure your webhook URL in the GrowthBnk dashboard and ensure webhooks are enabled for your account. GrowthBnk sends an HTTP POST with a JSON body; respond with 200 to acknowledge. Any non-200 response (or a timeout) is retried.

Headers

HeaderTypeRequiredDescription
Content-TypestringRequiredAlways application/json.
keystringRequiredYour merchant API key — confirm it matches your account.
hashstringRequiredSHA-256 signature of the event (verify this).

Signature

The hash header is computed exactly like an API request signature:

hash = SHA256( key | transaction_id | transaction_amount | salt )

Use the delivered amount

Compute the expected hash using the transaction_amount exactly as it appears in the payload (a two-decimal string, e.g. "1499.00"). A formatting mismatch will fail verification.

Verify a webhook

Recompute the hash with your salt and compare it to the hash header using a constant-time comparison before trusting the payload:

Verify
import hashlib

def verify_webhook(headers, payload, api_key, api_salt):
    signing = f"{api_key}|{payload['transaction_id']}|{payload['transaction_amount']}|{api_salt}"
    expected = hashlib.sha256(signing.encode("utf-8")).hexdigest()
    # Constant-time compare against the `hash` header
    import hmac
    if not hmac.compare_digest(expected, headers.get("hash", "")):
        raise ValueError("Invalid webhook signature")
    return payload
<?php
function verify_webhook(array $headers, array $payload, string $apiKey, string $apiSalt): array {
    $signing = "{$apiKey}|{$payload['transaction_id']}|{$payload['transaction_amount']}|{$apiSalt}";
    $expected = hash('sha256', $signing);
    $received = $headers['hash'] ?? '';
    if (!hash_equals($expected, $received)) {
        throw new RuntimeException('Invalid webhook signature');
    }
    return $payload;
}
const crypto = require('crypto');

function verifyWebhook(headers, payload, apiKey, apiSalt) {
  const signing = `${apiKey}|${payload.transaction_id}|${payload.transaction_amount}|${apiSalt}`;
  const expected = crypto.createHash('sha256').update(signing).digest('hex');
  const received = headers['hash'] || '';
  const ok = expected.length === received.length &&
    crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
  if (!ok) throw new Error('Invalid webhook signature');
  return payload;
}

Payload

A representative payment_webhook event:

Webhook payload
{
    "event_type": "payment_webhook",
    "transaction_id": "TXN20260707172038123",
    "merchant_transaction_id": "ORDER-2026-0001",
    "transaction_status": "SUCCESS",
    "transaction_amount": "1499.00",
    "currency": "INR",
    "transaction_date": "2026-07-07T17:20:38Z",
    "timestamp": "2026-07-07T17:20:41Z",
    "is_refund": false,
    "customer_details": {
        "customer_name": "Jane Doe",
        "customer_email": "jane.doe@example.com",
        "customer_phone": "9876543210"
    },
    "merchant_details": {
        "merchant_id": "MID-10234",
        "merchant_name": "Example Store"
    },
    "fees": {
        "commission_charges": "0.00",
        "commission_tax": "0.00",
        "service_charges": "0.00",
        "service_tax": "0.00"
    }
}

Key fields

FieldTypeDescription
event_typestringEvent name, e.g. payment_webhook.
transaction_idstringGrowthBnk transaction id.
merchant_transaction_idstringYour reference for the payment.
transaction_statusstringOne of INITIATED, SUCCESS, FAILURE, DROP, CANCELLED.
transaction_amountstringAmount in rupees (two decimals) — used in the signature.
currencystringCurrency code.
transaction_datestringISO-8601 time the payment was created.
timestampstringISO-8601 time the webhook was sent.
is_refundbooleanWhether the event relates to a refund.
customer_detailsobjectCustomer name, email and phone.
merchant_detailsobjectYour merchant id and name.
feesobjectCommission and service charges/taxes.

Best Practices