Set up and consume RMZ webhooks to react to store events in real time.
This guide walks through the complete process of setting up an RMZ webhook, building a receiver endpoint, verifying signatures, and handling common scenarios.
Your endpoint must accept HTTP POST requests, verify the signature, process the payload, and return a 200 status quickly.
const express = require("express");const crypto = require("crypto");const app = express();// IMPORTANT: Use raw body for signature verificationapp.post( "/webhooks/rmz", express.raw({ type: "application/json" }), async (req, res) => { const signature = req.headers["signature"]; const requestId = req.headers["x-rmz-request-id"]; const rawBody = req.body.toString(); // 1. Verify signature const expectedSig = crypto .createHmac("sha256", process.env.RMZ_WEBHOOK_SECRET) .update(rawBody) .digest("hex"); if (!crypto.timingSafeEqual( Buffer.from(signature || ""), Buffer.from(expectedSig) )) { console.error("Invalid webhook signature"); return res.status(401).json({ error: "Invalid signature" }); } // 2. Parse payload const payload = JSON.parse(rawBody); console.log(`Received ${payload.event} (request: ${requestId})`); // 3. Respond immediately res.status(200).json({ received: true }); // 4. Process asynchronously processWebhook(payload, requestId); });async function processWebhook(payload, requestId) { switch (payload.event) { case "order.created": const order = payload.data; console.log(`New order #${order.id} from ${order.customer.firstName}`); console.log(`Amount: ${order.total}, Method: ${order.transaction.payment_method}`); // Your business logic here... break; case "order.status.changed": const updated = payload.data; const currentStatus = updated.status.status; console.log(`Order #${updated.id} status changed to ${currentStatus}`); break; }}app.listen(3000);
If you are using Laravel, make sure to exclude the webhook route from CSRF verification by adding it to the $except array in App\Http\Middleware\VerifyCsrfToken.
Webhooks may be delivered more than once. Use the X-RMZ-REQUEST-ID header to detect and ignore duplicates:
// In-memory for demo — use Redis or a database in productionconst processedIds = new Set();function isProcessed(requestId) { if (processedIds.has(requestId)) { return true; } processedIds.add(requestId); return false;}// In your webhook handler:if (isProcessed(requestId)) { console.log(`Duplicate webhook ${requestId}, skipping`); return res.status(200).json({ received: true });}
Store processed request IDs in Redis with a 7-day TTL for automatic cleanup. In a database, use a unique constraint on the request ID column and catch the duplicate key error.