S
ShadhinPay Docs

Webhooks API

Receive real-time notifications for payment events

Webhooks

Webhooks allow you to receive real-time notifications when events occur in your ShadhinPay account.

How Webhooks Work

  1. You provide a callback_url when creating a payment
  2. When the payment status changes, we send a POST request to your URL
  3. Your server processes the event and returns a 200 response

Event Types

EventDescription
payment.initiatedPayment was created
payment.processingCustomer started payment
payment.completedPayment successful
payment.failedPayment failed
payment.cancelledPayment cancelled
payment.expiredPayment link expired
payment.refundedPayment was refunded
invoice.paidInvoice was paid
invoice.overdueInvoice became overdue

Webhook Payload

All webhooks include these fields:

Webhook Payload
{
  "event": "payment.completed",
  "timestamp": "2024-01-01T12:05:00Z",
  "data": {
    "payment_id": "SP_1234567890",
    "status": "COMPLETED",
    "amount": 1500,
    "currency": "BDT",
    "transaction_id": "TXN_ABC123",
    "payment_method": "bkash",
    "customer_phone": "8801712345678",
    "metadata": {
      "order_id": "ORD-12345"
    }
  },
  "signature": "sha256=abc123..."
}

Verifying Signatures

Important: Always verify webhook signatures before processing events to prevent fraud.

We sign all webhooks using HMAC-SHA256. Here's how to verify:

JavaScript
const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secretKey) {
  const expectedSignature = crypto
    .createHmac('sha256', secretKey)
    .update(JSON.stringify(payload))
    .digest('hex');

  return `sha256=${expectedSignature}` === signature;
}

// In your webhook handler
app.post('/webhook', (req, res) => {
  const signature = req.headers['x-shadhinpay-signature'];
  const isValid = verifyWebhookSignature(req.body, signature, SECRET_KEY);

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

  // Process the webhook
  const { event, data } = req.body;

  switch (event) {
    case 'payment.completed':
      // Fulfill the order
      break;
    case 'payment.failed':
      // Handle failure
      break;
  }

  res.status(200).send('OK');
});
Python
import hmac
import hashlib
import json

def verify_webhook_signature(payload, signature, secret_key):
    expected = hmac.new(
        secret_key.encode(),
        json.dumps(payload).encode(),
        hashlib.sha256
    ).hexdigest()

    return f"sha256={expected}" == signature

# In your webhook handler
@app.route('/webhook', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-ShadhinPay-Signature')

    if not verify_webhook_signature(request.json, signature, SECRET_KEY):
        return 'Invalid signature', 401

    event = request.json['event']
    data = request.json['data']

    if event == 'payment.completed':
        # Fulfill the order
        pass

    return 'OK', 200

Retry Policy

If your webhook endpoint returns an error (non-2xx status), we'll retry:

AttemptDelay
1Immediate
230 seconds
35 minutes
430 minutes
52 hours

After 5 failed attempts, we mark the webhook as failed and stop retrying.

Best Practices

  1. Return 200 quickly - Process webhooks asynchronously
  2. Verify signatures - Always validate before processing
  3. Handle duplicates - Use payment_id for idempotency
  4. Log everything - Store webhook payloads for debugging
  5. Use HTTPS - We only send webhooks to secure endpoints

Testing Webhooks

Use tools like ngrok to test webhooks locally:

# Start ngrok tunnel
ngrok http 3000

# Use the ngrok URL as your callback_url
# https://abc123.ngrok.io/webhook

On this page