Webhooks Controller
Source: internal/api/v1/reviews/controllers/webhook.controller.js
Service: webhook service
Module: Reviews
Overview
The Webhooks controller handles external integration webhooks that trigger review requests. It processes payment completion events from Stripe and order events from Square to automatically send review requests to customers.
Key Capabilities
- Stripe Charge Webhook - Process completed payment events
- Square Order Webhook - Process completed order events
- Auto Review Request - Trigger review requests on purchase
- Payment Validation - Verify webhook authenticity
MongoDB Collections
| Collection | Operations | Purpose |
|---|---|---|
review-requests | CREATE | Track triggered review requests |
review-auto-request-config | READ | Get automation settings |
Service Methods
1. newCharge()
Process Stripe charge webhook to trigger review request.
Endpoint: POST /reviews/webhook/stripe/charge
Controller Logic:
const newCharge = catchAsync(async (req, res) => {
const { stripe_billing: stripeBilling } = req.auth;
const chargeDetails = req.body;
await webhookService.newCharge({ chargeDetails, stripeBilling });
res.status(200).json({
success: true,
message: 'SUCCESS',
});
});
Webhook Payload (from Stripe):
{
"id": "ch_abc123",
"object": "charge",
"amount": 5000,
"currency": "usd",
"customer": "cus_xyz",
"description": "Payment for Order #1234",
"metadata": {
"account_id": "acc_123",
"order_id": "order_456"
},
"status": "succeeded"
}
Processing Logic:
- Extract charge details from webhook
- Verify Stripe signature (security)
- Check if charge succeeded
- Get account_id from metadata
- Check auto-review-request config
- Validate delay and conditions
- Schedule review request job
- Return success
Response:
{
"success": true,
"message": "SUCCESS"
}
2. newSquareupOrder()
Process Square order webhook to trigger review request.
Endpoint: POST /reviews/webhook/square/order
Controller Logic:
const newSquareupOrder = catchAsync(async (req, res, next) => {
let squareUpInfo = req.body;
await webhookService.newSquareupOrder({ squareUpInfo });
res.status(200).json({
success: true,
message: 'SUCCESS',
});
});
Webhook Payload (from Square):
{
"merchant_id": "merchant_123",
"type": "order.created",
"event_id": "event_abc",
"created_at": "2024-12-08T10:00:00Z",
"data": {
"type": "order",
"id": "order_xyz",
"object": {
"order": {
"id": "order_xyz",
"location_id": "loc_123",
"customer_id": "cust_456",
"state": "COMPLETED",
"total_money": {
"amount": 5000,
"currency": "USD"
}
}
}
}
}
Processing Logic:
- Extract order details
- Verify Square signature
- Check order state (must be COMPLETED)
- Map location_id to account_id
- Get customer details
- Check auto-review config
- Schedule review request
- Return success
Response:
{
"success": true,
"message": "SUCCESS"
}
Webhook Security
Stripe Signature Verification
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const sig = req.headers['stripe-signature'];
const event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);
Square Signature Verification
const crypto = require('crypto');
const signature = req.headers['x-square-signature'];
const isValid = crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature));
Business Logic Flow
sequenceDiagram
participant Stripe/Square
participant Webhook
participant Service
participant MongoDB
participant QueueManager
Stripe/Square->>Webhook: POST /webhook (charge/order)
Webhook->>Webhook: Verify Signature
Webhook->>Service: newCharge/newSquareupOrder
Service->>MongoDB: Find auto-request config
MongoDB-->>Service: Config with delay_hours
Service->>MongoDB: Check recent requests
Service->>Service: Validate conditions
Service->>QueueManager: Schedule review request
QueueManager-->>Service: Job ID
Service-->>Webhook: Success
Webhook-->>Stripe/Square: 200 OK
Note over QueueManager: Wait delay_hours
QueueManager->>Service: Execute review request
Service->>Service: Get customer email/phone
Service->>Service: Generate review links
Service->>Service: Send email/SMS
Trigger Conditions
When Review Request Sends
- Charge Succeeded: Payment must be successful
- Config Enabled: auto-review-request must be active
- Delay Passed: Configured delay_hours elapsed
- Min Amount: Order meets minimum value (if set)
- Not Recent: Customer not requested in last 30 days
- Not Refunded: Order not refunded (if check enabled)
When Request Skipped
- Payment failed or pending
- Auto-request disabled for account
- Customer requested recently
- Order value below minimum
- Order refunded
- No customer email/phone found
Edge Cases & Error Handling
1. Invalid Webhook Signature:
// Stripe throws error if signature invalid
throw new Error('Invalid webhook signature');
// Returns 400 Bad Request
2. Missing account_id:
// Cannot process without account mapping
throw new Error('account_id not found in metadata');
3. No Auto-Request Config:
// Silently skip if not configured
if (!config || !config.enabled) {
return; // No error, just skip
}
4. Duplicate Webhook:
// Idempotency check using event_id
const existing = await ReviewRequest.findOne({ event_id });
if (existing) {
return { success: true, message: 'Already processed' };
}
5. Customer Not Found:
// Skip if cannot find customer details
if (!customer || (!customer.email && !customer.phone)) {
console.log('Customer contact info missing, skipping');
return;
}
Webhook URLs
Production Endpoints
Stripe Webhooks:
POST https://api.dashclicks.com/api/v1/v1/reviews/webhook/stripe/charge
Square Webhooks:
POST https://api.dashclicks.com/api/v1/v1/reviews/webhook/square/order
Testing Webhooks
Stripe CLI:
stripe listen --forward-to localhost:5002/api/v1/v1/reviews/webhook/stripe/charge
stripe trigger charge.succeeded
Square Sandbox:
# Use Square Sandbox webhook URLs
# Test events via Square Developer Dashboard
Authorization
Authentication:
- ❌ No JWT required (external webhooks)
- ✅ Signature verification required
- ✅ HTTPS only
Security Measures:
- Signature Verification: Validates webhook source
- HTTPS Requirement: Encrypted transmission
- Idempotency: Prevents duplicate processing
- Rate Limiting: Prevents abuse
Integration Points
Triggered By:
- Stripe: Payment completion events
- Square: Order fulfillment events
- Custom Integrations: Can POST to webhook URL
Triggers:
- Queue Manager: Schedules delayed review request
- Notification Service: Sends email/SMS
- Review Requests Collection: Creates tracking record
Important Notes
Webhook Response Time
- ⚠️ Must respond within 5 seconds
- Use async processing for heavy operations
- Return 200 immediately
- Process in background queue
Retry Logic
- Stripe retries failed webhooks (exponential backoff)
- Square retries up to 3 times
- Return 200 even if skipping request
- Only return error if webhook processing fails
Metadata Requirements
Stripe Charges:
// Include in charge metadata
{
"account_id": "acc_123",
"customer_email": "customer@example.com",
"customer_phone": "+1234567890"
}
Square Orders:
// Map location_id to account_id in database
await LocationMapping.findOne({ location_id: 'loc_123' });
Related Documentation
- Auto Review Request - Configuration
- Request Review - Manual requests
- Config Controller - Platform setup
Version: 1.0
Last Updated: December 2024
Status: ✅ Production Ready