ThunderPhone 2.0 ఇప్పుడు అందుబాటులో ఉంది.మీరే ప్రారంభించవచ్చు—నిమిషానికి 2¢ నుంచి.ప్రకటనను చదవండి

Operations

వెబ్‌హుక్ సంతకాలను ధృవీకరించండి

ThunderPhone పంపే ప్రతి వెబ్‌హుక్ మరియు టూల్ అభ్యర్థనకు టైమ్‌స్టాంప్‌తో సంతకం చేయబడుతుంది. ఇక్కడ ఉన్న విధానం లేదా SDK సహాయకంతో దాన్ని ధృవీకరించి, ఐదు నిమిషాల కంటే పాత మళ్లీ పంపిన అభ్యర్థనలను తిరస్కరించండి.

మేము మీ సర్వర్‌కు పంపే ప్రతి అభ్యర్థనలో (వెబ్‌హుక్ డెలివరీలు, లెగసీ సింగిల్-URL వెబ్‌హుక్ మరియు టూల్-ఎండ్‌పాయింట్ కాల్‌లు) రెండు సంతకం హెడర్‌లు ఉంటాయి. రెండూ ఒకే సీక్రెట్‌తో HMAC-SHA256ను ఉపయోగిస్తాయి.

హెడర్సంతకం చేసే అంశాలురీప్లే రక్షణ
X-ThunderPhone-Signature-V2 (సిఫార్సు చేయబడింది)టైమ్‌స్టాంప్, మెథడ్, పాత్, క్వెరీ మరియు బాడీ5 నిమిషాల కంటే పాత అభ్యర్థనలను తిరస్కరించండి
X-ThunderPhone-Signatureబాడీ మాత్రమేలేదు: క్యాప్చర్ చేసిన అభ్యర్థన ఎప్పటికీ ధృవీకరించబడుతుంది

కొత్త ఇంటిగ్రేషన్‌లలో X-ThunderPhone-Signature-V2ను ధృవీకరించండి మరియు ఇప్పటికే ఉన్న వాటిని దీనికి మార్చండి. X-ThunderPhone-Signature ఇప్పటికీ ప్రతి అభ్యర్థనతో మార్పులేకుండా పంపబడుతుంది, కాబట్టి దానిపై నిర్మించిన ధృవీకరణ సాధనాలు పనిచేస్తూనే ఉంటాయి. దాన్ని పంపడం ఆపే ముందు మేము ఒక తేదీని ప్రకటిస్తాము.

సంతకం V2

X-ThunderPhone-Signature-V2: t=1767225600,v1=03a89bec521f78d55727237ca4713bb0b8dcdf2e09e450865b5b5637b80c1c4e
  • t అనేది మేము అభ్యర్థన పంపిన సమయానికి సెకన్లలోని Unix సమయం. మళ్లీ ప్రయత్నించిన డెలివరీని పంపినప్పుడు, దాని స్వంత tతో మళ్లీ సంతకం చేస్తాము.
  • v1 అనేది మీ సీక్రెట్‌తో కీ చేయబడిన దిగువ సంతకం చేసిన స్ట్రింగ్ యొక్క హెక్స్ HMAC-SHA256.

ఈ విలువ కామాలతో వేరు చేసిన key=value జతల జాబితా. మీకు తెలియని కీలను విస్మరించండి, మరియు ఏదైనా v1 సరిపోలితే అభ్యర్థనను ఆమోదించండి: భవిష్యత్తు మార్పులో మేము రెండవ సంతకాన్ని జోడించవచ్చు.

సంతకం చేసిన స్ట్రింగ్

నాలుగు ఫీల్డ్‌లు ఉంటాయి; చివరిది మినహా ప్రతి ఫీల్డ్ తర్వాత ఒక న్యూ లైన్ (\n, బైట్ 0x0A) ఉంటుంది:

<t>\n<METHOD>\n<path and query>\n<body>
ఫీల్డ్విలువ
tహెడర్‌లోని t విలువ
METHODపెద్ద అక్షరాల్లోని HTTP మెథడ్: POST, GET, PUT, PATCH లేదా DELETE
path and queryHTTP అభ్యర్థన లైన్‌లోని అభ్యర్థన టార్గెట్: పాత్, ఆపై ?, మరియు ఉంటే క్వెరీ స్ట్రింగ్. స్కీమ్, హోస్ట్ లేదా ఫ్రాగ్మెంట్ ఉండవు. దాన్ని డీకోడ్ చేయకుండా లేదా పరామితులను మళ్లీ క్రమబద్ధీకరించకుండా, వచ్చినట్లుగానే ఉపయోగించండి
bodyముడి అభ్యర్థన బాడీ. GET మరియు DELETE టూల్ కాల్‌లకు బాడీ ఉండదు, కాబట్టి వాటి సంతకం చేసిన స్ట్రింగ్ పాత్ తర్వాతి న్యూ లైన్‌తో ముగుస్తుంది

ధృవీకరించడానికి:

  1. ఏదైనా పార్సింగ్‌కు ముందు హెడర్, మెథడ్, ముడి పాత్ మరియు క్వెరీ, అలాగే ముడి బాడీని చదవండి.
  2. హెడర్ నుండి t మరియు ప్రతి v1 తీసుకోండి.
  3. t మీ గడియారం కంటే ముందు లేదా తర్వాత 300 సెకన్ల కంటే ఎక్కువగా ఉంటే అభ్యర్థనను తిరస్కరించండి.
  4. మీ సీక్రెట్‌తో సంతకం చేసిన స్ట్రింగ్ యొక్క హెక్స్ HMAC-SHA256ను గణించండి.
  5. దాన్ని స్థిర సమయంలో v1తో పోల్చండి. ఏదీ సరిపోలకపోతే 401తో స్పందించండి.

ఉదాహరణ

సీక్రెట్ whsec_example_4c1f2a9b. మేము ఈ అభ్యర్థనను t = 1767225600 వద్ద (2026-01-01 00:00:00 UTC) పంపుతాము:

POST /tools/lookup?region=us HTTP/1.1
Host: api.example.com
Content-Type: application/json
X-ThunderPhone-Signature-V2: t=1767225600,v1=03a89bec521f78d55727237ca4713bb0b8dcdf2e09e450865b5b5637b80c1c4e
 
{"customer_id":"cust_123"}

సంతకం చేసిన స్ట్రింగ్ బాడీ తర్వాత న్యూ లైన్ లేకుండా ఈ నాలుగు లైన్‌లు:

1767225600
POST
/tools/lookup?region=us
{"customer_id":"cust_123"}

దీని HMAC-SHA256 03a89bec521f78d55727237ca4713bb0b8dcdf2e09e450865b5b5637b80c1c4e. దీనికి వ్యతిరేకంగా మీ ఇంప్లిమెంటేషన్‌ను తనిఖీ చేయండి లేదా opensslతో దీన్ని పునరుత్పత్తి చేయండి:

printf '1767225600\nPOST\n/tools/lookup?region=us\n{"customer_id":"cust_123"}' \
  | openssl dgst -sha256 -hmac 'whsec_example_4c1f2a9b'

అదే సెకనులో /tools/lookup?customer_id=cust_123కు చేసే GET టూల్ కాల్‌కు ఖాళీ బాడీ ఉంటుంది. దాని సంతకం చేసిన స్ట్రింగ్ 1767225600\nGET\n/tools/lookup?customer_id=cust_123\n, మరియు దాని సంతకం 3ce214cb0b8f2bf39b1d19d4e7265df9dbaff5a5f838d3cc6c753a8f7a4ad280.

SDKలతో

Python మరియు TypeScript SDKలు ఒక హెల్పర్‌ను కలిగి ఉంటాయి (thunderphone 0.2.0 లేదా తర్వాతి వెర్షన్, @thunderphone/sdk 0.2.0 లేదా తర్వాతి వెర్షన్). ఇది 5 నిమిషాల విండోను తనిఖీ చేస్తుంది, స్థిర సమయంలో పోల్చుతుంది, మరియు డౌన్‌టైమ్ లేకుండా రొటేట్ చేయడానికి సీక్రెట్‌ల జాబితాను ఆమోదిస్తుంది.

Python
from thunderphone import verify_signature
 
ok = verify_signature(
    header,            # the X-ThunderPhone-Signature-V2 value
    secret=SECRET,     # or [NEW_SECRET, OLD_SECRET] while rotating
    method=method,     # "POST"
    path=path,         # "/tools/lookup?region=us", exactly as received
    body=raw_body,     # bytes, before any JSON parsing
    tolerance=300,     # optional; seconds
)
Node.js
import { verifySignature } from "@thunderphone/sdk";
 
const ok = verifySignature({
  header,               // the X-ThunderPhone-Signature-V2 value
  secret: SECRET,       // or [NEW_SECRET, OLD_SECRET] while rotating
  method,               // "POST"
  path,                 // "/tools/lookup?region=us", exactly as received
  body: rawBody,        // Buffer or string, before any JSON parsing
  toleranceSeconds: 300, // optional
});

SDK లేకుండా

Python
import hashlib
import hmac
import time
 
TOLERANCE_SECONDS = 300
 
 
def verify_v2(header: str, secret: str, method: str, path: str, body: bytes) -> bool:
    """path is the path plus ?query exactly as received; body is the raw body."""
    timestamp, signatures = None, []
    for item in (header or "").split(","):
        key, _, value = item.strip().partition("=")
        if key == "t" and value.isdigit() and len(value) <= 15:
            timestamp = int(value)
        elif key == "v1":
            signatures.append(value)
    if timestamp is None or abs(time.time() - timestamp) > TOLERANCE_SECONDS:
        return False
    signed = f"{timestamp}\n{method.upper()}\n{path}\n".encode() + body
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return any(hmac.compare_digest(expected.encode(), s.encode()) for s in signatures)
Node.js
import crypto from "node:crypto";
 
const TOLERANCE_SECONDS = 300;
 
// path is the path plus ?query exactly as received; body is the raw Buffer.
export function verifyV2(header, secret, method, path, body) {
  let timestamp;
  const signatures = [];
  for (const item of (header || "").split(",")) {
    const [key, value = ""] = item.trim().split("=", 2);
    if (key === "t" && /^\d+$/.test(value)) timestamp = Number(value);
    else if (key === "v1") signatures.push(value);
  }
  if (timestamp === undefined) return false;
  if (Math.abs(Date.now() / 1000 - timestamp) > TOLERANCE_SECONDS) return false;
  const expected = Buffer.from(
    crypto
      .createHmac("sha256", secret)
      .update(`${timestamp}\n${method.toUpperCase()}\n${path}\n`)
      .update(body)
      .digest("hex"),
  );
  return signatures.some((sig) => {
    const candidate = Buffer.from(sig);
    return (
      candidate.length === expected.length &&
      crypto.timingSafeEqual(candidate, expected)
    );
  });
}

ఫ్రేమ్‌వర్క్ అనుసంధానం

ప్రతి ఉదాహరణ రా పాత్ మరియు క్వెరీని, అలాగే రా బాడీని చదివి, తర్వాత SDK హెల్పర్‌ను కాల్ చేస్తుంది.

FastAPI
import json
 
from fastapi import FastAPI, HTTPException, Request
from thunderphone import verify_signature
 
app = FastAPI()
 
 
def raw_path_and_query(request: Request) -> str:
    path = (request.scope.get("raw_path") or request.url.path.encode()).decode("latin-1")
    query = request.scope.get("query_string", b"").decode("latin-1")
    return f"{path}?{query}" if query else path
 
 
@app.post("/thunderphone-webhook")
async def hook(request: Request):
    body = await request.body()  # raw bytes, NOT request.json()
    if not verify_signature(
        request.headers.get("X-ThunderPhone-Signature-V2"),
        secret=SECRET,
        method=request.method,
        path=raw_path_and_query(request),
        body=body,
    ):
        raise HTTPException(status_code=401)
    event = json.loads(body)
    # … dispatch on event["type"] …
    return {"ok": True}
Express
import express from "express";
import { verifySignature } from "@thunderphone/sdk";
 
const app = express();
 
app.post(
  "/thunderphone-webhook",
  // IMPORTANT: parse as raw; do NOT use express.json() here.
  express.raw({ type: "application/json" }),
  (req, res) => {
    const ok = verifySignature({
      header: req.header("X-ThunderPhone-Signature-V2"),
      secret: process.env.WEBHOOK_SECRET,
      method: req.method,
      path: req.originalUrl, // path and query as received
      body: req.body,
    });
    if (!ok) return res.sendStatus(401);
    const event = JSON.parse(req.body.toString("utf8"));
    // … dispatch on event.type …
    res.sendStatus(204);
  },
);
Django
import json
 
from django.http import HttpResponseForbidden, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from thunderphone import verify_signature
 
 
@csrf_exempt
@require_POST
def hook(request):
    if not verify_signature(
        request.headers.get("X-ThunderPhone-Signature-V2"),
        secret=SECRET,
        method=request.method,
        path=request.get_full_path(),
        body=request.body,  # raw bytes
    ):
        return HttpResponseForbidden("invalid signature")
    event = json.loads(request.body)
    # … dispatch on event["type"] …
    return JsonResponse({"ok": True})

req.originalUrl (Express), అలాగే ASGI యొక్క raw_path మరియు query_string (FastAPI), అందినట్లుగానే ఉన్న టార్గెట్‌ను ఉపయోగిస్తాయి. Django యొక్క get_full_path() మరియు Flask యొక్క request.path ముందుగా పాత్‌ను డీకోడ్ చేస్తాయి, కాబట్టి సాధారణ పాత్‌లకు అదే స్ట్రింగ్ లభిస్తుంది. మీ ఎండ్‌పాయింట్ పాత్‌లో %2F, ; లేదా = వంటి శాతం-ఎన్‌కోడ్ చేసిన లేదా రిజర్వ్ చేసిన అక్షరాలు ఉంటే, బదులుగా మీ సర్వర్ అందించే రా టార్గెట్‌ను చదవండి (ఉదాహరణకు, Gunicornలో request.META["RAW_URI"]).

ఏ సీక్రెట్?

అదే రహస్యం రెండు హెడర్‌లపై సంతకం చేస్తుంది.

మూలంసీక్రెట్
Webhook ఎండ్‌పాయింట్ (/v1/developer/webhook-endpoints)సృష్టించే సమయంలో ఒక్కసారి తిరిగి ఇచ్చే ప్రతి-ఎండ్‌పాయింట్ secret (48 హెక్స్ అక్షరాలు)
లెగసీ సింగిల్-URL webhookGET /v1/webhookలో తిరిగి ఇచ్చే ప్రతి-ఆర్గనైజేషన్ secret
Tool-endpoint ఇన్‌వొకేషన్ (మీ endpoint.urlకు నేరుగా చేసే కాల్)ఆర్గనైజేషన్-స్థాయి webhook సీక్రెట్ (లెగసీ సింగిల్-URL webhookకు ఉన్నదే) — ప్రతి-ఎండ్‌పాయింట్ సీక్రెట్ కాదు

సీక్రెట్‌ను మీ సీక్రెట్ మేనేజర్ లేదా env varలో నిల్వ చేయండి — దాన్ని ఎప్పుడూ కమిట్ చేయవద్దు.

టూల్ కాల్‌లను ధృవీకరించడం

ఏజెంట్ మీ ఫంక్షన్ టూల్స్లో ఒకదాన్ని నేరుగా అమలు చేసినప్పుడు (ఆ టూల్‌కు endpoint ఉన్నప్పుడు), అభ్యర్థన మీ కాన్ఫిగర్ చేసిన endpoint.headersతో పాటు ఈ ThunderPhone హెడర్‌లను కలిగి ఉంటుంది:

  • X-ThunderPhone-Call-ID — కొనసాగుతున్న కాల్ యొక్క సంఖ్యా ఐడి.
  • X-ThunderPhone-Signature-V2 — మీ సంస్థ-స్థాయి వెబ్‌హుక్ సీక్రెట్తో కీ చేయబడిన, పైన ఉన్న టైమ్‌స్టాంప్ చేసిన సిగ్నేచర్.
  • X-ThunderPhone-Signature — అదే కీతో ఉన్న పాత బాడీ-మాత్రమే సిగ్నేచర్.

వెబ్‌హుక్‌లతో పోలిస్తే రెండు తేడాలు ఉన్నాయి:

  1. GET / DELETE టూల్స్‌కు బాడీ ఉండదు. ఆర్గ్యుమెంట్‌లు క్వెరీ పరామితులుగా వెళ్తాయి. V2 సిగ్నేచర్ వాటిని పాత్ మరియు క్వెరీ ద్వారా కవర్ చేస్తుంది, అలాగే బాడీ ఫీల్డ్ ఖాళీగా ఉంటుంది. రా క్వెరీ స్ట్రింగ్‌తో ధృవీకరించండి: పరామితులను పార్స్ చేసి కొత్త క్వెరీ స్ట్రింగ్‌ను నిర్మించడం వాటి క్రమాన్ని లేదా ఎన్‌కోడింగ్‌ను మార్చవచ్చు. లెగసీ హెడర్ ఖాళీ బైట్ స్ట్రింగ్‌పై సైన్ చేస్తుంది.
  2. లెగసీ వెబ్‌హుక్ కాన్ఫిగర్ చేయని సంస్థలకు సంస్థ సీక్రెట్ ఉండదు. ఆ సందర్భంలో టూల్ కాల్‌లు X-ThunderPhone-Call-IDను మాత్రమే కలిగి ఉంటాయి; సిగ్నేచర్ హెడర్‌లు ఉండవు. సైనింగ్ సీక్రెట్ పొందడానికి లెగసీ వెబ్‌హుక్‌ను కాన్ఫిగర్ చేయండి (PUT /v1/webhook), లేదా endpoint.headers ద్వారా మీ స్వంత హెడర్‌తో టూల్ కాల్‌లను ప్రామాణీకరించండి.
@app.api_route("/tools/search-appointments", methods=["GET", "POST"])
async def tool(request: Request):
    body = await request.body()  # b"" for GET/DELETE tools
    if not verify_signature(
        request.headers.get("X-ThunderPhone-Signature-V2"),
        secret=ORG_WEBHOOK_SECRET,
        method=request.method,
        path=raw_path_and_query(request),
        body=body,
    ):
        raise HTTPException(status_code=401)
    call_id = request.headers.get("X-ThunderPhone-Call-ID", "")
    args = json.loads(body) if body else dict(request.query_params)
    ...

Expressలో, బాడీ పార్సర్ GETపై req.bodyను ఖాళీ ఆబ్జెక్ట్‌గా ఉంచుతుంది; కాబట్టి Buffer.isBuffer(req.body) ? req.body : Buffer.alloc(0)ను పంపండి.

వెబ్‌హుక్-మోడ్ టూల్ డిస్పాచ్ (endpoint లేని టూల్‌లు, మీ సంస్థ వెబ్‌హుక్‌కు telephony.tool / web.toolగా పంపబడతాయి) సాధారణ సైన్డ్ వెబ్‌హుక్ — పైన ఉన్న ప్రామాణిక విధానం వర్తిస్తుంది. రెండు అభ్యర్థన ఆకృతుల కోసం ఫంక్షన్ టూల్స్ చూడండి.

లెగసీ సంతకం: X-ThunderPhone-Signature

అసలు హెడర్ అనేది అదే సీక్రెట్‌తో, కేవలం raw body యొక్క హెక్స్ HMAC-SHA256. దీనిలో టైమ్‌స్టాంప్ ఉండదు కాబట్టి, ఒక సంతకం చేసిన రిక్వెస్ట్‌ను పట్టుకున్న ఎవరైనా దాన్ని మళ్లీ పంపవచ్చు. ఇంటిగ్రేషన్‌లు V2కి మారుతున్న సమయంలో కూడా ఇది ప్రతి రిక్వెస్ట్‌లో పంపబడుతుంది.

  1. raw రిక్వెస్ట్ bodyని చదవండి — మేము మీకు పంపిన ఖచ్చితమైన బైట్‌లు.
  2. hmac_sha256(secret, body).hexdigest()ను గణించండి.
  3. X-ThunderPhone-Signatureతో స్థిర సమయంలో పోల్చండి.

మేము ప్రసారం చేసే ఖచ్చితమైన బైట్‌లకే సంతకం చేస్తాము. ఆ బైట్‌లు payload యొక్క కానానికల్ JSON సీరియలైజేషన్ కూడా — కీలు అక్షర క్రమంలో అమర్చబడినవి, ఖాళీలు లేని కాంపాక్ట్ సెపరేటర్‌లు (, మరియు :), UTF-8. మీ ఫ్రేమ్‌వర్క్ పార్స్ చేసిన JSONను మాత్రమే అందిస్తే, దాన్ని కానానికల్‌గా మళ్లీ సీరియలైజ్ చేసి దానిపై HMAC చేయండి:

# Equivalent to hashing the raw body:
import json
canonical = json.dumps(payload, separators=(",", ":"), sort_keys=True).encode("utf-8")

raw bodyని ఉపయోగించడం ఉత్తమం — ఇది ఒక దశ తక్కువగా ఉంటుంది మరియు కొన్ని భాషల్లోని JSON సంఖ్య రౌండ్-ట్రిప్పింగ్ ప్రత్యేకతల ప్రభావానికి లోనుకాదు. V2లోని body ఫీల్డ్‌కూ ఇదే వర్తిస్తుంది.

Python
import hashlib
import hmac
 
 
def verify(body: bytes, signature: str, secret: str) -> bool:
    """Constant-time HMAC-SHA256 verification."""
    expected = hmac.new(
        secret.encode("utf-8"),
        body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature or "")
Node.js
import crypto from "node:crypto";
 
export function verify(body, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(body)
    .digest("hex");
  if (!signature || expected.length !== signature.length) return false;
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature),
  );
}
Go
package webhook
 
import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
)
 
func Verify(body []byte, signature, secret string) bool {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(body)
    expected := hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(expected), []byte(signature))
}
Ruby
require "openssl"
 
def verify(body, signature, secret)
  expected = OpenSSL::HMAC.hexdigest("SHA256", secret, body)
  Rack::Utils.secure_compare(expected, signature.to_s)
end

సాధారణ సమస్యలు

గడియార వ్యత్యాసం

5-నిమిషాల వ్యవధిని మీ సర్వర్ గడియారానికి సంబంధించి కొలుస్తారు. దానిని NTPతో సమకాలీకరించి ఉంచండి; 5 నిమిషాల కంటే ఎక్కువ వ్యత్యాసం ఉన్న గడియారం ప్రతి అభ్యర్థనను తిరస్కరిస్తుంది.

ప్రాక్సీ పాత్‌ను మళ్లీ వ్రాస్తుంది

సంతకం చేసిన పాత్ మేము అభ్యర్థించినదే: మీరు ThunderPhoneలో కాన్ఫిగర్ చేసిన URL యొక్క పాత్. మీ అప్లికేషన్ అభ్యర్థనను చూడకముందే రివర్స్ ప్రాక్సీ లేదా API గేట్‌వే ఉపసర్గను తొలగించినా లేదా జోడించినా, ధృవీకరించే ముందు మేము పంపిన పాత్‌ను మళ్లీ నిర్మించండి (ఉదాహరణకు, ఉపసర్గను తిరిగి జోడించండి).

డిఫాల్ట్ ఫార్మాటింగ్‌తో మళ్లీ సీరియలైజ్ చేయడం

బాడీని పార్స్ చేసి, మీ JSON లైబ్రరీ డిఫాల్ట్‌లతో (, / : తర్వాత ఖాళీలు, చొప్పించిన క్రమంలోని కీలు) మళ్లీ డంప్ చేయడం వల్ల వేర్వేరు బైట్‌లు ఉత్పత్తి అవుతాయి మరియు HMAC విఫలమవుతుంది. రా బాడీని ధృవీకరించండి — లేదా మీరు తప్పనిసరిగా మళ్లీ సీరియలైజ్ చేయాల్సి ఉంటే, మా కానానికల్ రూపంతో ఖచ్చితంగా సరిపోల్చండి: క్రమబద్ధీకరించిన కీలు, కాంపాక్ట్ సెపరేటర్లు, UTF-8.

ఫ్రేమ్‌వర్క్ JSONను స్వయంచాలకంగా పార్స్ చేస్తుంది

Express యొక్క express.json() మిడిల్‌వేర్ బాడీ స్ట్రీమ్‌ను వినియోగిస్తుంది మరియు మీరు రా బైట్‌లను కోల్పోతారు. వెబ్‌హుక్ రూట్‌లో ప్రత్యేకంగా express.raw() ఉపయోగించండి లేదా ప్రీ-మిడిల్‌వేర్‌లో రా బాడీని బఫర్ చేయండి. NestJS / Koa కోసం కూడా ఇదే వర్తిస్తుంది — వాటి "రా బాడీ" డాక్యుమెంటేషన్‌ను చూడండి.

టైమింగ్‌కు సురక్షితం కాని పోలిక

JSలో expected === signature లేదా Pythonలో expected == signature టైమింగ్-వేరియబుల్ పోలికలు. వరుసగా crypto.timingSafeEqual లేదా hmac.compare_digest ఉపయోగించండి. పనితీరు తేడా ఏమాత్రం ఉండదు.

టూల్ ఎండ్‌పాయింట్లకు తప్పు రహస్యం

ప్రత్యక్ష టూల్-ఎండ్‌పాయింట్ కాల్‌లు సంస్థ-స్థాయి వెబ్‌హుక్ రహస్యం (GET /v1/webhook)తో సంతకం చేయబడతాయి — /v1/developer/webhook-endpointsలోని ఏ ఎండ్‌పాయింట్-స్థాయి రహస్యంతో కాదు. అదే హెల్పర్‌ను మళ్లీ ఉపయోగించండి, కానీ టూల్ రూట్‌లలో దానికి సంస్థ రహస్యాన్నే అందిస్తున్నారని నిర్ధారించుకోండి.

GET/DELETE టూల్‌లలో క్వెరీ స్ట్రింగ్‌లు

V2 క్వెరీ స్ట్రింగ్ పంపిన విధంగానే దానిపై సంతకం చేస్తుంది. లెగసీ హెడర్ ఖాళీ బాడీపై సంతకం చేస్తుంది, క్వెరీ స్ట్రింగ్‌పై ఎప్పుడూ కాదు; కాబట్టి లెగసీ హెడర్ కోసం URLను హ్యాష్ చేయడం ఎప్పటికీ సరిపోలదు.

వ్యవధిలోని నకిలీలు

టైమ్‌స్టాంప్ 5 నిమిషాల కంటే పాత రీప్లేలను అడ్డుకుంటుంది, ఆ వ్యవధిలోని రెండో కాపీని కాదు. ఎండ్‌పాయింట్ డెలివరీలు కనీసం ఒకసారి జరుగుతాయి, కాబట్టి అదే ఈవెంట్‌కు మళ్లీ ప్రయత్నించినది కూడా రావచ్చు; ఎన్వలప్‌లోని event_id ఆధారంగా నకిలీలను తొలగించండి.

సరిపోలనప్పుడు 401ని తిరిగి ఇవ్వకపోవడం

ధృవీకరణ విఫలమైనప్పుడు 200ని తిరిగి ఇవ్వడం హ్యాండ్లర్‌ను రీప్లే లక్ష్యంగా మారుస్తుంది. ధృవీకరణ విఫలమైతే ఎల్లప్పుడూ 2xx కాని ప్రతిస్పందన ఇవ్వండి.


తదుపరి దశలు