m2pfintech
Webhook Guide

Webhook Integration

Webhook payload formats, retry policy, signature verification, and security requirements.

Technical guide for receiving and processing webhook callbacks from the M2P Prepaid Platform.


Requirements

Your webhook endpoint must satisfy:

RequirementDetail
ProtocolHTTPS only
TLS VersionTLS 1.2 or higher
Response TimeReturn HTTP 200 within 30 seconds
IdempotencyHandle duplicate deliveries gracefully
Response CodeAny non-2xx response triggers retry

Payload Formats

{
  "eventName": "TRANSACTION_NOTIFICATION",
  "eventTimestamp": "2026-02-12T14:30:00.000Z",
  "tenant": "TENANT_001",
  "data": {
    "transactionId": "TXN_20260212_001",
    "entityId": "ENTITY_123",
    "kitNumber": "650527XXXX1234",
    "transactionType": "DEBIT",
    "transactionOrigin": "POS",
    "amount": 5000.00,
    "currencyCode": "INR",
    "balanceAfter": 15000.00,
    "merchantName": "Amazon India",
    "mcc": "5411",
    "rrn": "123456789012",
    "status": "SUCCESS",
    "timestamp": "2026-02-12T14:30:00.000Z"
  }
}
{
  "eventName": "KIT_STATUS_UPDATE",
  "eventTimestamp": "2026-02-12T14:30:00.000Z",
  "tenant": "TENANT_001",
  "data": {
    "kitNumber": "650527XXXX1234",
    "entityId": "ENTITY_123",
    "previousStatus": "INACTIVE",
    "currentStatus": "ACTIVE",
    "action": "ACTIVATION",
    "timestamp": "2026-02-12T14:30:00.000Z"
  }
}
{
  "eventName": "KIT_EXPIRY_ADVANCE_NOTIFICATION",
  "eventTimestamp": "2026-02-12T14:30:00.000Z",
  "tenant": "TENANT_001",
  "data": {
    "kitNumber": "650527XXXX1234",
    "entityId": "ENTITY_123",
    "expiryDate": "2026-03-15",
    "daysToExpiry": 30,
    "timestamp": "2026-02-12T14:30:00.000Z"
  }
}

Retry Policy

AttemptDelayCumulative Wait
1st retry60 seconds1 minute
2nd retry120 seconds3 minutes
3rd retry300 seconds8 minutes
After 3 failuresEvent dropped

Retries are triggered on:

  • HTTP 4xx / 5xx responses
  • Connection timeout (30s)
  • Connection refused

Events are not persisted after max retries. Configure alerting on your webhook failure rates and implement a polling fallback for critical events.


Security

IP Whitelisting

Whitelist M2P webhook delivery IPs on your firewall. Contact your M2P account manager for the current IP list.

Signature Verification

Every webhook includes an X-M2P-Signature header containing an HMAC-SHA256 signature of the payload.

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// Express.js handler
app.post('/webhooks/m2p', (req, res) => {
  const signature = req.headers['x-m2p-signature'];
  if (!verifyWebhookSignature(req.body, signature, WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  // Process asynchronously
  processWebhookAsync(req.body);
  res.status(200).send('OK');
});
import hmac
import hashlib
import json

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

# Flask handler
@app.route('/webhooks/m2p', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-M2P-Signature')
    if not verify_webhook_signature(request.json, signature, WEBHOOK_SECRET):
        return 'Invalid signature', 401
    process_webhook_async(request.json)
    return 'OK', 200
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;

public boolean verifySignature(String payload, String signature, String secret) {
    Mac mac = Mac.getInstance("HmacSHA256");
    mac.init(new SecretKeySpec(secret.getBytes(), "HmacSHA256"));
    byte[] hash = mac.doFinal(payload.getBytes());
    String expected = bytesToHex(hash);
    return MessageDigest.isEqual(
        expected.getBytes(), signature.getBytes()
    );
}

Process Asynchronously

Return HTTP 200 immediately upon receiving the webhook. Process the payload asynchronously to avoid timeouts.

Receive webhook → Validate signature → Store event → Return 200 → Process later

Kafka Integration

For high-throughput requirements, consume events directly via Kafka instead of webhooks.

Topic CategoryContentFormat
Transaction eventsAll financial transactionsJSON
Kit status eventsCard lifecycle changesJSON
Customer eventsCustomer profile changesJSON

Direct Kafka integration requires dedicated connectivity setup. Contact your M2P account manager for Kafka endpoint configuration.

Internal Event Flow

On this page