How to Test Square Webhooks Locally
What Webhooks Does Square Send?
Square's webhook system notifies your application about events across their commerce platform. Key event types include payment.completed, payment.updated, order.created, order.updated, order.fulfillment.updated, inventory.count.updated, customer.created, customer.updated, catalog.version.updated, and refund.created.
Each webhook payload includes the event type, a merchant_id, event_id for deduplication, and a data object containing the relevant resource. Square also includes created_at timestamps and the object ID so you can fetch the full resource from the API if needed.
Square signs webhooks with an HMAC-SHA256 signature in the x-square-hmacsha256-signature header, computed using your webhook signature key and the notification URL plus the request body. This is important for verifying that requests genuinely come from Square.
Setting Up Square Webhooks
In the Square Developer Dashboard, go to your application and navigate to Webhooks. Click "Add" and enter your ReqPour URL as the notification URL, e.g., https://abc123.reqpour.com/square. Select the event types you want to receive.
Square provides a sandbox environment with test API credentials. Configure your webhook subscription to point to your ReqPour endpoint, then use the Square sandbox to create test payments and orders. Each event flows through ReqPour where you can inspect it.
Note the webhook signature key that Square provides — you will need this to verify webhook signatures. Store it in an environment variable. During development, you can use the ReqPour dashboard to see the signature header value and compare it with your computed hash.
Handling Square Webhooks
Here is a handler for Square webhook events:
const crypto = require('crypto');const SQUARE_WEBHOOK_SIGNATURE_KEY = process.env.SQUARE_WEBHOOK_KEY; const NOTIFICATION_URL = 'https://abc123.reqpour.com/square';
app.post('/square', express.json(), (req, res) => { // Verify signature const signature = req.headers['x-square-hmacsha256-signature']; const body = JSON.stringify(req.body); const hmac = crypto .createHmac('sha256', SQUARE_WEBHOOK_SIGNATURE_KEY) .update(NOTIFICATION_URL + body) .digest('base64');
if (signature !== hmac) { return res.status(403).send('Invalid signature'); }
const { type, data } = req.body;
switch (type) { case 'payment.completed': console.log('Payment completed:', data.id); break; case 'order.updated': console.log('Order updated:', data.id); break; case 'inventory.count.updated': console.log('Inventory changed:', data.object.inventory_counts); break; }
res.status(200).send('OK'); }); ```
Note that Square's signature includes the notification URL concatenated with the body. During development, use your ReqPour URL as the notification URL in the HMAC computation.
Testing Square Payment Flows
Square's sandbox provides test card numbers for simulating different payment outcomes. Use these with the Square Web Payments SDK or Terminal API to trigger payment.completed and payment.updated events. The full payment object appears in the ReqPour dashboard where you can examine amounts, card details, and processing fees.
For order management, create orders via the Orders API in sandbox mode. Watch order.created events arrive, then update fulfillment status to trigger order.fulfillment.updated events. The ReqPour request inspector shows the nested order structure with line items, taxes, discounts, and fulfillment details.
Use ReqPour's relay to forward all these events to your local development server:
npx reqpour relay --to http://localhost:4000/webhooks/squareThe relay preserves the full request including headers, so signature verification works correctly on your local server.
Square Webhook Best Practices
Square has a 10-second timeout for webhook responses. Return a 200 status immediately and process events asynchronously. After 14 consecutive failures, Square disables the webhook subscription and you will need to re-enable it manually.
Use the event_id field for idempotency. Square guarantees at-least-once delivery, meaning you may receive the same event more than once. Store processed event IDs and skip duplicates. ReqPour's replay feature is perfect for testing this — replay the same event twice and verify your handler handles the duplicate gracefully.
Square webhooks include only the object ID, not always the full object. For events like catalog.version.updated, you may need to call the Square API to fetch the full catalog data. During development, compare the webhook payload (visible in ReqPour) with the API response to understand what data is and is not included in the webhook.
Related
Get started with ReqPour
Catch, inspect, and relay webhooks to localhost. Free to start, $3/mo for Pro.