Webhooks Guide
Webhooks push real-time notifications to your server when events occur -- primarily transaction state changes. This is the recommended way to track payments in production.
How It Works
- Register a webhook with your callback URL and a secret
- When a transaction changes state, Neutron POSTs the event to your URL
- Your server verifies the signature and processes the event
- Respond with 200 OK to acknowledge receipt
Setup
Register a Webhook
curl -X POST https://api.neutron.me/api/v2/webhook \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{
"callback": "https://yourapp.com/webhooks/neutron",
"secret": "your-webhook-secret-key"
}'Response:
{
"id": "01e2a3dc-0c34-4e14-92e4-b270c4778d95",
"callback": "https://yourapp.com/webhooks/neutron",
"createdAt": 1747889654376
}Verify Signatures
Every webhook includes an X-Neutronpay-Signature header. Always verify before processing.
Node.js:
const crypto = require('crypto');
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhooks/neutron', (req, res) => {
const signature = req.headers['x-neutronpay-signature'];
const expected = crypto
.createHmac('sha256', 'your-webhook-secret-key')
.update(JSON.stringify(req.body))
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
return res.status(401).send('Invalid signature');
}
res.status(200).send('OK');
// Process asynchronously
switch (req.body.txnState) {
case 'completed':
// Payment received -- fulfill the order
break;
case 'failed':
// Payment failed -- notify the customer
break;
}
});Python:
import hmac
import hashlib
from flask import Flask, request
app = Flask(__name__)
WEBHOOK_SECRET = 'your-webhook-secret-key'
@app.route('/webhooks/neutron', methods=['POST'])
def webhook():
signature = request.headers.get('X-Neutronpay-Signature')
expected = hmac.new(
WEBHOOK_SECRET.encode(),
request.data,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected):
return 'Invalid signature', 401
event = request.json
return 'OK', 200Webhook Payload
{
"txnId": "5e25d2f4-9bca-4b7a-a1ad-2cf056100cb6",
"extRefId": "order-12345",
"txnState": "completed",
"msg": "",
"updatedAt": 1676030467492
}Key States to Handle
| State | Action |
|---|---|
completed | Payment successful -- fulfill the order |
failed | Payment failed -- notify and retry or refund |
expired | Invoice timed out -- prompt customer to retry |
Best Practices
Respond quickly. Return 200 OK immediately, then process in a background job.
Be idempotent. You may receive the same event multiple times. Track processed transaction IDs and skip duplicates.
Use HTTPS. Your callback URL must use HTTPS.
Handle retries. If your endpoint is temporarily unavailable, Neutron retries with exponential backoff.
Managing Webhooks
| Action | Endpoint |
|---|---|
| Create | POST /api/v2/webhook |
| List | GET /api/v2/webhook |
| Update | PUT /api/v2/webhook/webhookId |
| Remove | DELETE /api/v2/webhook/webhookId |
One webhook per account. Use the update endpoint to change URL or secret.
Local Development
Test webhooks locally with a tunnel:
ngrok http 3000
# Then register the ngrok URL as your webhook callbackUpdated 6 days ago
