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:
| Requirement | Detail |
|---|---|
| Protocol | HTTPS only |
| TLS Version | TLS 1.2 or higher |
| Response Time | Return HTTP 200 within 30 seconds |
| Idempotency | Handle duplicate deliveries gracefully |
| Response Code | Any 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
| Attempt | Delay | Cumulative Wait |
|---|---|---|
| 1st retry | 60 seconds | 1 minute |
| 2nd retry | 120 seconds | 3 minutes |
| 3rd retry | 300 seconds | 8 minutes |
| After 3 failures | Event 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', 200import 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 laterKafka Integration
For high-throughput requirements, consume events directly via Kafka instead of webhooks.
| Topic Category | Content | Format |
|---|---|---|
| Transaction events | All financial transactions | JSON |
| Kit status events | Card lifecycle changes | JSON |
| Customer events | Customer profile changes | JSON |
Direct Kafka integration requires dedicated connectivity setup. Contact your M2P account manager for Kafka endpoint configuration.
