Core
Webhooks
Receive real-time notifications for payment and payout events. Verify signatures and handle retries.
Webhooks notify your application when payment events occur. This is the most reliable way to confirm payments.
Webhook Events
| Event | When | Action |
|---|---|---|
payment.completed | Payment successful | Credit wallet, fulfill order |
payment.failed | Payment failed | Update order status |
payout.completed | Payout sent | Mark payout as complete |
payout.failed | Payout failed | Refund wallet, notify user |
payout.reversed | Payout reversed | Refund wallet, investigate |
Webhook Payload
Example payload for a completed payment:
json
{
"id": "evt_abc123",
"type": "payment.completed",
"api_version": "2025-01-15",
"created_at": "2025-01-15T14:20:00Z",
"data": {
"reference": "snippe_ref_abc123",
"status": "completed",
"amount": 2000,
"fee_amount": 50,
"net_amount": 1950,
"completed_at": "2025-01-15T14:20:00Z"
}
}Verify Webhook Signatures
CamelPay signs each webhook with HMAC-SHA256. The signature is sent in the X-Snippe-Signature header.
Always verify webhook signatures to prevent spoofed requests. Use the raw request body and constant-time comparison.
import hmac
import hashlib
def verify_webhook_signature(raw_body, timestamp, signature, secret):
message = f"{timestamp}.{raw_body.decode('utf-8')}"
expected = hmac.new(
secret.encode('utf-8'),
message.encode('utf-8'),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)Handle Events
Return 200 OK immediately and process events asynchronously:
python
from flask import Flask, request, jsonify
import hmac
import hashlib
import json
app = Flask(__name__)
WEBHOOK_SECRET = "your_webhook_secret_here"
@app.route('/webhooks/camelpay', methods=['POST'])
def handle_webhook():
# 1. Verify signature
timestamp = request.headers.get('X-Snippe-Timestamp', '')
signature = request.headers.get('X-Snippe-Signature', '')
raw_body = request.data
message = f"{timestamp}.{raw_body.decode('utf-8')}"
expected = hmac.new(
WEBHOOK_SECRET.encode('utf-8'),
message.encode('utf-8'),
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, signature):
return jsonify({'error': 'Invalid signature'}), 401
# 2. Parse and process event
event = json.loads(raw_body)
event_type = event['type']
event_data = event['data']
if event_type == 'payment.completed':
process_completed_payment(event_data)
elif event_type == 'payment.failed':
process_failed_payment(event_data)
elif event_type == 'payout.completed':
process_completed_payout(event_data)
# 3. Return 200 immediately
return jsonify({'received': True}), 200Idempotency
Webhooks may be delivered multiple times. CamelPay guarantees at-least-once delivery. Deduplicate by storing processed event IDs:
python
processed_events = set()
def handle_webhook(event):
event_id = event['id']
if event_id in processed_events:
return {'status': 'already_processed'}
process_event(event)
processed_events.add(event_id)
return {'status': 'processed'}