सभी लेख
localhost पर HubSpot webhook को सुरक्षित तरीके से टेस्ट करें
HubSpotwebhookslocalhostCRM integrations

localhost पर HubSpot webhook को सुरक्षित तरीके से टेस्ट करें

बाहरी service सीधे localhost तक नहीं पहुँच सकती। Tunnel public HTTPS URL देता है और असली headers तथा बिना बदला body लोकल server तक भेजता है। JSON parser से पहले raw body रखें। पहले signature और input जाँचें, delivery को durable storage में लिखें और उसके बाद business logic चलाएँ।

HubSpot webhook लोकल ऐप तक कैसे पहुँचता है

बाहरी service सीधे localhost तक नहीं पहुँच सकती। Tunnel public HTTPS URL देता है और असली headers तथा बिना बदला body लोकल server तक भेजता है। >आधिकारिक दस्तावेज़

1. raw body सुरक्षित रखने वाला endpoint बनाएँ

JSON parser से पहले raw body रखें। पहले signature और input जाँचें, delivery को durable storage में लिखें और उसके बाद business logic चलाएँ।

import express from "express";
import crypto from "node:crypto";

const app = express();
const secret = process.env.HUBSPOT_CLIENT_SECRET;
const publicBase = process.env.WEBHOOK_PUBLIC_BASE_URL;

app.post(
  "/webhooks/hubspot",
  express.raw({ type: "application/json", limit: "1mb" }),
  async (req, res) => {
    const signature = req.get("x-hubspot-signature-v3");
    const timestamp = req.get("x-hubspot-request-timestamp");
    const rawBody = req.body.toString("utf8");

    if (!verifyHubSpotV3({
      signature,
      timestamp,
      method: req.method,
      publicUri: `${publicBase}${req.originalUrl}`,
      rawBody,
      secret,
    })) {
      return res.sendStatus(401);
    }

    let events;
    try {
      events = JSON.parse(rawBody);
      if (!Array.isArray(events)) throw new Error("Expected a batch");
      await enqueueBatchIdempotently(events);
    } catch (error) {
      console.error("HubSpot webhook rejected", error);
      return res.sendStatus(500);
    }

    return res.sendStatus(200);
  }
);

app.use(express.json());
app.listen(3000);

पूरा HTTPS URL जोड़ें, केवल ज़रूरी event चुनें और test environment में असली action चलाएँ। secret को repository से बाहर रखें।

2. लोकल port को HTTPS पर उपलब्ध करें

Server शुरू करके दूसरे terminal में tunnel चालू रखें। बिना signature वाले request पर 401 मिलना सही routing और authentication rejection दिखाता है।

npx portpreview 3000
https://example.portpreview.dev/webhooks/hubspot

Server शुरू करके दूसरे terminal में tunnel चालू रखें। बिना signature वाले request पर 401 मिलना सही routing और authentication rejection दिखाता है।

3. HubSpot में webhook कॉन्फ़िगर करें

पूरा HTTPS URL जोड़ें, केवल ज़रूरी event चुनें और test environment में असली action चलाएँ। secret को repository से बाहर रखें। >आधिकारिक दस्तावेज़

केवल ज्ञात type process करें, नए field स्वीकार करें और stable delivery ID से durable deduplication करें।

मूल bytes पर signature verify करें

Webhook secret और specification में दिए exact data से HMAC बनाएँ। constant-time comparison करें और secret को log न करें। >आधिकारिक दस्तावेज़

function decodeHubSpotQuery(uri) {
  const [base, query] = uri.split("?", 2);
  if (query === undefined) return base;
  const map = {
    "%3A": ":", "%2F": "/", "%3F": "?", "%40": "@",
    "%21": "!", "%24": "$", "%27": "'", "%28": "(",
    "%29": ")", "%2A": "*", "%2C": ",", "%3B": ";",
  };
  const decoded = query.replace(
    /%3A|%2F|%3F|%40|%21|%24|%27|%28|%29|%2A|%2C|%3B/g,
    value => map[value]
  );
  return `${base}?${decoded}`;
}

function verifyHubSpotV3(input) {
  if (!input.secret || !input.signature || !input.timestamp) return false;

  const sentAt = Number(input.timestamp);
  if (!Number.isFinite(sentAt) || Math.abs(Date.now() - sentAt) > 300_000) {
    return false;
  }

  const uri = decodeHubSpotQuery(input.publicUri.split("#")[0]);
  const source = `${input.method}${uri}${input.rawBody}${input.timestamp}`;
  const expected = crypto
    .createHmac("sha256", input.secret)
    .update(source, "utf8")
    .digest("base64");

  const actualBuffer = Buffer.from(input.signature, "utf8");
  const expectedBuffer = Buffer.from(expected, "utf8");
  return actualBuffer.length === expectedBuffer.length &&
    crypto.timingSafeEqual(actualBuffer, expectedBuffer);
}

Signature सफल होने के बाद timestamp की freshness जाँचें और host clock sync रखें।

Webhook secret और specification में दिए exact data से HMAC बनाएँ। constant-time comparison करें और secret को log न करें। >व्यावहारिक गाइड

जल्दी जवाब दें और idempotent processing करें

केवल ज्ञात type process करें, नए field स्वीकार करें और stable delivery ID से durable deduplication करें।

200 भेजने से पहले delivery ID reserve करना और job बनाना एक transaction में करें। storage विफल हो तो retry के लिए error लौटाएँ। >व्यावहारिक गाइड

HubSpot webhook की समस्याएँ हल करें

  • Server शुरू करके दूसरे terminal में tunnel चालू रखें। बिना signature वाले request पर 401 मिलना सही routing और authentication rejection दिखाता है।
  • Webhook secret और specification में दिए exact data से HMAC बनाएँ। constant-time comparison करें और secret को log न करें।
  • Routing, headers, signature, response time और idempotency जाँचें। Synthetic request rejection path टेस्ट करने के लिए उपयोगी हैं।
  • POST path, tunnel port, raw body, secret, system clock और database latency जाँचें।

>व्यावहारिक गाइड · >व्यावहारिक गाइड

लोकल और production सुरक्षा checklist

  • HTTPS अनिवार्य करें, size और method सीमित करें, secrets व logs सुरक्षित रखें और पुराने test URL हटाएँ।
  • JSON parser से पहले raw body रखें। पहले signature और input जाँचें, delivery को durable storage में लिखें और उसके बाद business logic चलाएँ।
  • 200 भेजने से पहले delivery ID reserve करना और job बनाना एक transaction में करें। storage विफल हो तो retry के लिए error लौटाएँ।

HTTPS अनिवार्य करें, size और method सीमित करें, secrets व logs सुरक्षित रखें और पुराने test URL हटाएँ।

अक्सर पूछे जाने वाले प्रश्न

localhost पर HubSpot webhook कैसे टेस्ट करें?
Server शुरू करके दूसरे terminal में tunnel चालू रखें। बिना signature वाले request पर 401 मिलना सही routing और authentication rejection दिखाता है।
HubSpot webhook signature कैसे verify करें?
Webhook secret और specification में दिए exact data से HMAC बनाएँ। constant-time comparison करें और secret को log न करें।
HubSpot webhook को दोबारा क्यों भेजता है?
200 भेजने से पहले delivery ID reserve करना और job बनाना एक transaction में करें। storage विफल हो तो retry के लिए error लौटाएँ।
Idempotency के लिए कौन-सी key उपयोग करें?
केवल ज्ञात type process करें, नए field स्वीकार करें और stable delivery ID से durable deduplication करें।