POST /webhooks/paymentNotify
This is an inbound webhook that Rebell sends to your server when a payment status changes. Configure your webhook URL in the paymentNotifyUrl parameter when creating payments.
This is an inbound webhook - Rebell calls your server to notify you of payment events.
Webhook Payload
Rebell payment identifier
Merchant payment request ID (your idempotency key)
Payment status:
SUCCESS - Payment completed successfully
FAIL - Payment failed
Payment completion time (ISO 8601)
Payment creation time (ISO 8601)
Expected Response
Return a success response to acknowledge receipt:
{
"result": {
"resultCode": "SUCCESS",
"resultStatus": "S",
"resultMessage": "success"
}
}
If your webhook fails to respond with a success status, Rebell will retry the notification. Ensure your handler is idempotent to handle duplicate notifications.
Example Webhook Payload
Successful Payment:
{
"paymentId": "2024011012345678901234",
"paymentRequestId": "RETAIL-20240110-001",
"paymentStatus": "SUCCESS",
"paymentAmount": {
"currency": "EUR",
"value": "2500"
},
"paymentTime": "2024-01-10T14:30:45+01:00",
"paymentCreatedTime": "2024-01-10T14:30:00+01:00"
}
Failed Payment:
{
"paymentId": "2024011012345678901234",
"paymentRequestId": "RETAIL-20240110-001",
"paymentStatus": "FAIL",
"paymentAmount": {
"currency": "EUR",
"value": "2500"
},
"paymentTime": "2024-01-10T14:30:45+01:00"
}
Webhook Handler Implementation
const express = require('express');
const app = express();
app.post('/webhooks/payment', express.json(), async (req, res) => {
// 1. Verify the signature
if (!verifyWebhookSignature(req)) {
console.error('Invalid webhook signature');
return res.status(401).json({
result: { resultStatus: 'F', resultCode: 'INVALID_SIGNATURE' }
});
}
const {
paymentId,
paymentRequestId,
paymentStatus,
paymentAmount,
paymentTime
} = req.body;
// 2. Check for duplicate notification (idempotency)
const existingRecord = await getPaymentRecord(paymentRequestId);
if (existingRecord && existingRecord.webhookProcessed) {
// Already processed, return success
return res.json({
result: { resultStatus: 'S', resultCode: 'SUCCESS' }
});
}
try {
// 3. Process the payment notification
if (paymentStatus === 'SUCCESS') {
await handleSuccessfulPayment({
paymentId,
paymentRequestId,
amount: paymentAmount,
paidAt: paymentTime
});
} else if (paymentStatus === 'FAIL') {
await handleFailedPayment({
paymentId,
paymentRequestId
});
}
// 4. Mark webhook as processed
await markWebhookProcessed(paymentRequestId, paymentId);
// 5. Return success
res.json({
result: {
resultCode: 'SUCCESS',
resultStatus: 'S',
resultMessage: 'success'
}
});
} catch (error) {
console.error('Webhook processing error:', error);
// Return failure - Rebell will retry
res.status(500).json({
result: { resultStatus: 'F', resultCode: 'PROCESS_ERROR' }
});
}
});
async function handleSuccessfulPayment(payment) {
// Update order status
await updateOrderStatus(payment.paymentRequestId, 'paid');
// Trigger fulfillment
await triggerFulfillment(payment.paymentRequestId);
// Send confirmation to customer
await sendPaymentConfirmation(payment);
}
async function handleFailedPayment(payment) {
// Update order status
await updateOrderStatus(payment.paymentRequestId, 'payment_failed');
// Notify customer
await sendPaymentFailureNotification(payment.paymentRequestId);
}
Signature Verification
Always verify the webhook signature before processing:
function verifyWebhookSignature(req) {
const signature = req.headers['signature'];
const clientId = req.headers['client-id'];
const responseTime = req.headers['response-time'];
const body = JSON.stringify(req.body);
// Extract signature value from header
const signatureMatch = signature.match(/signature=([^,]+)/);
if (!signatureMatch) return false;
const signatureValue = signatureMatch[1];
// Build content to verify
const method = 'POST';
const uri = '/webhooks/payment'; // Your webhook path
const contentToVerify = `${method} ${uri}\n${clientId}.${responseTime}.${body}`;
// Verify with Rebell's public key
const verifier = crypto.createVerify('RSA-SHA256');
verifier.update(contentToVerify);
return verifier.verify(REBELL_PUBLIC_KEY, signatureValue, 'base64');
}
Best Practices
Important: The webhook is your source of truth for payment status. Don’t rely solely on the initial API response, especially for asynchronous payments.