PrestaShop Webhook Integration
Overview
Section titled “Overview”This guide explains how to configure and use PrestaShop webhooks with OpenLinker. PrestaShop webhooks allow real-time synchronization of products, inventory, and orders.
Prerequisites
Section titled “Prerequisites”- PrestaShop Installation: PrestaShop 1.7+ with webhook module installed
- OpenLinker Connection: Active PrestaShop connection configured in OpenLinker
- Webhook Secret: Shared secret configured in both PrestaShop and OpenLinker
Configuration
Section titled “Configuration”1. Create Connection in OpenLinker
Section titled “1. Create Connection in OpenLinker”POST /connections{ "name": "My PrestaShop Store", "platformType": "prestashop", "adapterKey": "prestashop.webservice.v1", "status": "active", "credentials": { "apiUrl": "https://your-store.com/api", "apiKey": "your-api-key" }}Save the connectionId from the response.
2. Configure Webhook Secret
Section titled “2. Configure Webhook Secret”Set the webhook secret as an environment variable:
# Connection-specific (recommended)export OPENLINKER_WEBHOOK_SECRET__PRESTASHOP__<CONNECTION_ID>=your-secret-key
# Or provider-level (fallback)export OPENLINKER_WEBHOOK_SECRET__PRESTASHOP=your-secret-keyNote: Replace <CONNECTION_ID> with your actual connection ID (uppercase, no dashes).
3. Install PrestaShop Webhook Module
Section titled “3. Install PrestaShop Webhook Module”Install the OpenLinker module in your PrestaShop instance:
For detailed installation instructions, see: PrestaShop Module README
Quick Start:
- Upload module ZIP to PrestaShop (or use bind-mount for development)
- Install module via PrestaShop backoffice: Modules → Module Manager
- Configure module settings (see module README for details)
4. Configure PrestaShop Webhook Module
Section titled “4. Configure PrestaShop Webhook Module”In your PrestaShop webhook module configuration:
- Webhook URL:
https://your-openlinker-instance.com/webhooks/prestashop/<CONNECTION_ID> - Secret Key: Same secret as configured in OpenLinker
- Events: Select events to subscribe to (see Supported Events below)
Supported Events
Section titled “Supported Events”Product Events
Section titled “Product Events”product.saved
Section titled “product.saved”Triggered when a product is created or updated.
Payload:
{ "schemaVersion": 1, "eventId": "prestashop-product-12345", "eventType": "product.saved", "occurredAt": "2025-01-01T12:00:00.000Z", "object": { "type": "product", "externalId": "12345" }, "payload": { "name": "Product Name", "reference": "PROD-001", "price": 29.99 }}Result: Triggers master.product.syncByExternalId job to fetch full product data via PrestaShop WebService API.
Inventory Events
Section titled “Inventory Events”stock.changed
Section titled “stock.changed”Triggered when product stock level changes.
Payload:
{ "schemaVersion": 1, "eventId": "prestashop-stock-12345", "eventType": "stock.changed", "occurredAt": "2025-01-01T12:00:00.000Z", "object": { "type": "stock", "externalId": "12345" }, "payload": { "quantity": 100 }}Result: Triggers prestashop.inventory.syncByExternalId job to sync inventory levels.
Order Events
Section titled “Order Events”order.created
Section titled “order.created”Triggered when a new order is created.
Payload:
{ "schemaVersion": 1, "eventId": "prestashop-order-67890", "eventType": "order.created", "occurredAt": "2025-01-01T12:00:00.000Z", "object": { "type": "order", "externalId": "67890" }, "payload": { "total": 99.99, "currency": "EUR" }}Result: Triggers prestashop.order.syncByExternalId job to fetch full order data.
order.status_changed
Section titled “order.status_changed”Triggered when order status changes.
Payload:
{ "schemaVersion": 1, "eventId": "prestashop-order-status-67890", "eventType": "order.status_changed", "occurredAt": "2025-01-01T12:00:00.000Z", "object": { "type": "order", "externalId": "67890" }, "payload": { "status": "shipped" }}Result: Triggers prestashop.order.syncByExternalId job to sync order status.
Webhook Signature Generation
Section titled “Webhook Signature Generation”PrestaShop webhook module must generate signatures using the same scheme as OpenLinker:
const crypto = require('crypto');
function generateSignature(secret, timestamp, rawBody) { const signedPayload = timestamp + '.' + rawBody; return crypto .createHmac('sha256', secret) .update(signedPayload) .digest('hex');}
// Usageconst timestamp = Date.now().toString();const rawBody = JSON.stringify(payload);const signature = generateSignature(secret, timestamp, rawBody);
// Headersheaders['X-OpenLinker-Timestamp'] = timestamp;headers['X-OpenLinker-Signature'] = `sha256=${signature}`;Testing
Section titled “Testing”Manual Test
Section titled “Manual Test”-
Generate signature:
Terminal window node scripts/generate-webhook-signature.js your-secret-key -
Send test webhook:
Terminal window curl -X POST http://localhost:3000/webhooks/prestashop/<CONNECTION_ID> \-H "Content-Type: application/json" \-H "X-OpenLinker-Timestamp: <TIMESTAMP>" \-H "X-OpenLinker-Signature: sha256=<SIGNATURE>" \-d '{"schemaVersion": 1,"eventId": "test-event-123","eventType": "product.saved","occurredAt": "2025-01-01T12:00:00.000Z","object": {"type": "product","externalId": "12345"}}' -
Verify:
- Check application logs for “Published webhook event”
- Check Redis stream:
XREAD STREAMS events.inbound.webhooks 0 - Check job queue:
XREAD STREAMS jobs.sync 0
Event Deduplication
Section titled “Event Deduplication”Why Multiple Hook Fires Occur
Section titled “Why Multiple Hook Fires Occur”PrestaShop’s actionProductSave hook (and other hooks) can fire multiple times during a single product save operation. This is expected PrestaShop behavior, not a bug.
Common scenarios:
- Product saved for multiple languages (2 languages = 2+ hook fires)
- Product saved for multiple shops (multi-shop setup)
- Product saved in multiple phases (main record, attributes, categories, stock, images, SEO)
- Hook called from multiple places in PrestaShop core code
Example: Saving a product with 2 languages can trigger the hook 6 times within the same second, all for the same product ID.
How Deduplication Works
Section titled “How Deduplication Works”The PrestaShop webhook module implements automatic deduplication to prevent duplicate events:
-
Deterministic Event IDs: Event IDs are generated deterministically based on:
- Provider + Connection ID + Event Type + Object Type + External ID + Time Window (rounded to nearest minute)
-
Database-Level Deduplication: Uses
INSERT IGNOREwith a unique constraint onevent_id:- If the same event ID is inserted multiple times, only the first insert succeeds
- Subsequent inserts are silently ignored (no error, no duplicate)
-
Time Window: Events within the same minute for the same object generate the same event ID, preventing duplicates from rapid hook fires.
Result: Even if a hook fires 6 times in one second, only 1 event is created and sent to OpenLinker.
Note: Time windows use PrestaShop server timezone. Events created in different minutes are correctly treated as separate events (this is expected behavior for legitimate separate save operations).
Verification
Section titled “Verification”To verify deduplication is working:
-- Check for duplicate events (should return 0 rows)SELECT event_id, COUNT(*) as countFROM ps_openlinker_webhook_outboxGROUP BY event_idHAVING count > 1;
-- Check events for a specific product (should see only 1 per minute)SELECT id, event_type, external_id, created_atFROM ps_openlinker_webhook_outboxWHERE external_id = '23' AND event_type = 'product.saved'ORDER BY created_at DESC;Expected: One event per product save operation, even if the hook fired multiple times.
Troubleshooting
Section titled “Troubleshooting”401 Unauthorized
Section titled “401 Unauthorized”Possible causes:
- Invalid signature (check secret key matches)
- Timestamp out of window (check system clock sync)
- Signature computed on wrong body (must use exact raw bytes)
Solution: Verify signature generation matches OpenLinker’s scheme exactly.
404 Not Found
Section titled “404 Not Found”Possible causes:
- Connection ID doesn’t exist
- Connection is disabled
- Provider mismatch (URL says
prestashopbut connection isallegro)
Solution: Check connection exists and is active with correct platformType.
Events Not Processing
Section titled “Events Not Processing”Possible causes:
- Handler not running (check application logs)
- Consumer group not created (check logs for “Created consumer group”)
- Redis connection issues
Solution: Check application logs and Redis connectivity.
Duplicate Events in Database
Section titled “Duplicate Events in Database”Possible causes:
- Event ID generation changed (should be deterministic)
- Time window logic issue
- Database unique constraint missing
Solution:
- Verify
event_idcolumn has unique constraint:SHOW CREATE TABLE ps_openlinker_webhook_outbox; - Check event IDs are deterministic (same inputs = same ID)
- Verify
INSERT IGNOREis being used inOutboxRepository::enqueueEvent()
Best Practices
Section titled “Best Practices”-
Use Connection-Specific Secrets: Prefer connection-specific secrets over provider-level for better security isolation.
-
Monitor Webhook Delivery: Set up alerts for signature verification failures and duplicate events.
-
Idempotent Event IDs: Use deterministic event IDs (e.g.,
prestashop-product-{id}) to enable proper deduplication. -
Minimal Payloads: Keep webhook payloads minimal. Full data is fetched via adapter APIs during sync jobs.
-
Error Handling: Implement retry logic in PrestaShop webhook module for transient failures (5xx responses).
Related Documentation
Section titled “Related Documentation”- Webhook Overview - OpenLinker webhook ingestion system
- PrestaShop Module README - Module installation and configuration guide
- Architecture Overview - System architecture