72 lines
2.1 KiB
JavaScript
Executable File
72 lines
2.1 KiB
JavaScript
Executable File
// routes/privacyLawWebhooks.js
|
|
require('dotenv').config();
|
|
const express = require('express');
|
|
const crypto = require('crypto');
|
|
|
|
const router = express.Router();
|
|
|
|
// Use raw body ONLY for this router so HMAC works
|
|
router.use(express.raw({ type: '*/*' }));
|
|
|
|
const SHOPIFY_API_SECRET = process.env.SHOPIFY_API_SECRET;
|
|
|
|
// ---- helpers ----
|
|
function verifyHmac(rawBody, hmacHeader) {
|
|
if (!SHOPIFY_API_SECRET || !hmacHeader || !rawBody) return false;
|
|
|
|
const digest = crypto
|
|
.createHmac('sha256', SHOPIFY_API_SECRET)
|
|
.update(rawBody) // raw Buffer
|
|
.digest('base64');
|
|
|
|
const generated = Buffer.from(digest, 'utf8');
|
|
const received = Buffer.from(hmacHeader, 'utf8');
|
|
if (generated.length !== received.length) return false;
|
|
|
|
return crypto.timingSafeEqual(generated, received);
|
|
}
|
|
|
|
function parseJsonSafe(buf) {
|
|
try { return JSON.parse(buf.toString('utf8')); }
|
|
catch { return null; }
|
|
}
|
|
|
|
function handleWebhook(req, res, topicName) {
|
|
const hmacHeader = req.header('x-shopify-hmac-sha256');
|
|
const shop = req.header('x-shopify-shop-domain');
|
|
const topic = req.header('x-shopify-topic') || topicName;
|
|
|
|
const isValid = verifyHmac(req.body, hmacHeader);
|
|
|
|
if (!isValid) {
|
|
// You asked for 500 on invalid HMAC. (Best practice is 401.)
|
|
console.error(`[WEBHOOK:${topic}] INVALID HMAC from shop=${shop}`);
|
|
return res.status(401).send('Invalid HMAC');
|
|
}
|
|
|
|
const payload = parseJsonSafe(req.body) || {};
|
|
// Log minimally; avoid logging full PII in real apps
|
|
console.log(`[WEBHOOK:${topic}] shop=${shop}`, payload);
|
|
|
|
// Echo back for your debugging
|
|
return res.status(200).json({ status: 'ok', topic, shop, received: payload });
|
|
}
|
|
|
|
// ---- endpoints ----
|
|
// 1) customers/data_request
|
|
router.post('/customers/data_request', (req, res) =>
|
|
handleWebhook(req, res, 'customers/data_request')
|
|
);
|
|
|
|
// 2) customers/redact
|
|
router.post('/customers/redact', (req, res) =>
|
|
handleWebhook(req, res, 'customers/redact')
|
|
);
|
|
|
|
// 3) shop/redact
|
|
router.post('/shop/redact', (req, res) =>
|
|
handleWebhook(req, res, 'shop/redact')
|
|
);
|
|
|
|
module.exports = router;
|