ThunderPhone 2.0 ਹੁਣ ਲਾਈਵ ਹੈ।ਸੈਲਫ਼-ਸਰਵਿਸ—2¢/ਮਿੰਟ ਤੋਂਐਲਾਨ ਪੜ੍ਹੋ

Operations

ਵੈੱਬਹੁੱਕ ਦਸਤਖਤਾਂ ਦੀ ਤਸਦੀਕ ਕਰੋ

ThunderPhone ਵੱਲੋਂ ਭੇਜੀ ਹਰ ਵੈੱਬਹੁੱਕ ਅਤੇ ਟੂਲ ਬੇਨਤੀ

ਹਰ ਬੇਨਤੀ ਜੋ ਅਸੀਂ ਤੁਹਾਡੇ ਸਰਵਰ ਨੂੰ ਭੇਜਦੇ ਹਾਂ — ਵੈੱਬਹੁੱਕ ਡਿਲਿਵਰੀਆਂ ਅਤੇ ਟੂਲ-ਐਂਡਪੁਆਇੰਟ ਇਨਵੋਕੇਸ਼ਨ — ਵਿੱਚ X-ThunderPhone-Signature ਹੈਡਰ ਵਿੱਚ HMAC-SHA256 ਦਸਤਖ਼ਤ ਹੁੰਦਾ ਹੈ। ਤਸਦੀਕ ਨੂੰ ਇੱਕ ਵਾਰ ਸਹੀ ਕਰੋ ਅਤੇ ਉਹੀ ਹੈਲਪਰ ਹਰ ਹੈਂਡਲਰ ਵਿੱਚ ਵਰਤੋ।

ਐਲਗੋਰਿਦਮ

  1. ਕੱਚੀ ਬੇਨਤੀ ਬਾਡੀ ਪੜ੍ਹੋ — ਉਹੀ ਸਹੀ ਬਾਈਟਾਂ ਜੋ ਅਸੀਂ ਤੁਹਾਨੂੰ POST ਕੀਤੀਆਂ ਹਨ।
  2. hmac_sha256(secret, body).hexdigest() ਗਣਨਾ ਕਰੋ।
  3. X-ThunderPhone-Signature ਨਾਲ ਸਥਿਰ ਸਮੇਂ ਵਿੱਚ ਤੁਲਨਾ ਕਰੋ। (ਸਧਾਰਨ ਸਟਰਿੰਗ ਤੁਲਨਾ ਟਾਈਮਿੰਗ ਜਾਣਕਾਰੀ ਲੀਕ ਕਰਦੀ ਹੈ।)

ਅਸੀਂ ਬਿਲਕੁਲ ਉਹੀ ਬਾਈਟਾਂ ਸਾਈਨ ਕਰਦੇ ਹਾਂ ਜੋ ਅਸੀਂ ਭੇਜਦੇ ਹਾਂ, ਇਸ ਲਈ ਕੱਚੀ ਬਾਡੀ ਦੀ ਤਸਦੀਕ ਹਮੇਸ਼ਾਂ ਕੰਮ ਕਰਦੀ ਹੈ। ਉਹ ਬਾਈਟਾਂ ਪੇਲੋਡ ਦਾ ਕੈਨੋਨਿਕਲ JSON ਸੀਰੀਅਲਾਈਜ਼ੇਸ਼ਨ ਵੀ ਹਨ — ਕੁੰਜੀਆਂ ਵਰਣਮਾਲਾ ਅਨੁਸਾਰ ਕ੍ਰਮਬੱਧ, ਸੰਖੇਪ ਸੈਪਰੇਟਰ (ਬਿਨਾਂ ਖਾਲੀ ਥਾਵਾਂ ਦੇ , ਅਤੇ :), UTF-8। ਜਦੋਂ ਤੁਹਾਡਾ ਫਰੇਮਵਰਕ ਸਿਰਫ਼ ਪਾਰਸ ਕੀਤਾ JSON ਦਿਖਾਉਂਦਾ ਹੋਵੇ, ਤਾਂ ਇਹ ਤੁਹਾਨੂੰ ਦੂਜਾ, ਪੂਰੀ ਤਰ੍ਹਾਂ ਸਮਾਨ ਤਰੀਕਾ ਦਿੰਦਾ ਹੈ: ਕੈਨੋਨਿਕਲ ਰੂਪ ਵਿੱਚ ਮੁੜ-ਸੀਰੀਅਲਾਈਜ਼ ਕਰੋ ਅਤੇ ਉਸ ਦਾ HMAC ਬਣਾਓ।

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

ਕੱਚੀ ਬਾਡੀ ਨੂੰ ਤਰਜੀਹ ਦਿਓ — ਇਸ ਵਿੱਚ ਇੱਕ ਕਦਮ ਘੱਟ ਹੈ ਅਤੇ ਕੁਝ ਭਾਸ਼ਾਵਾਂ ਵਿੱਚ JSON ਨੰਬਰਾਂ ਨੂੰ ਮੁੜ-ਰੂਪਾਂਤਰਿਤ ਕਰਨ ਦੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਤੋਂ ਪ੍ਰਭਾਵਿਤ ਨਹੀਂ ਹੁੰਦੀ।

ਕਿਹੜਾ ਸੀਕ੍ਰੇਟ?

ਸਰੋਤਸੀਕ੍ਰੇਟ
ਵੈੱਬਹੁੱਕ ਐਂਡਪੁਆਇੰਟ (/v1/developer/webhook-endpoints)ਬਣਾਉਣ ਵੇਲੇ ਇੱਕ ਵਾਰ ਵਾਪਸ ਕੀਤਾ ਗਿਆ ਪ੍ਰਤੀ-ਐਂਡਪੁਆਇੰਟ secret (48 ਹੈਕਸ ਅੱਖਰ)
ਪੁਰਾਣਾ ਸਿੰਗਲ-URL ਵੈੱਬਹੁੱਕGET /v1/webhook ਉੱਤੇ ਵਾਪਸ ਕੀਤਾ ਗਿਆ ਪ੍ਰਤੀ-ਸੰਗਠਨ secret
ਟੂਲ-ਐਂਡਪੁਆਇੰਟ ਇਨਵੋਕੇਸ਼ਨ (ਤੁਹਾਡੇ endpoint.url ਨੂੰ ਸਿੱਧੀ ਕਾਲ)ਸੰਗਠਨ-ਪੱਧਰ ਦਾ ਵੈੱਬਹੁੱਕ ਸੀਕ੍ਰੇਟ (ਪੁਰਾਣੇ ਸਿੰਗਲ-URL ਵੈੱਬਹੁੱਕ ਵਾਲਾ ਹੀ) — ਪ੍ਰਤੀ-ਐਂਡਪੁਆਇੰਟ ਸੀਕ੍ਰੇਟ ਨਹੀਂ

ਸੀਕ੍ਰੇਟ ਨੂੰ ਆਪਣੇ ਸੀਕ੍ਰੇਟ ਮੈਨੇਜਰ ਜਾਂ env var ਵਿੱਚ ਸਟੋਰ ਕਰੋ — ਇਸ ਨੂੰ ਕਦੇ ਵੀ ਕਮਿਟ ਨਾ ਕਰੋ।

ਹਵਾਲਾ ਇੰਪਲੀਮੈਂਟੇਸ਼ਨ

ਸਾਰੇ ਚਾਰ ਕੱਚੀ ਬੇਨਤੀ ਬਾਡੀ ਦੀ ਤਸਦੀਕ ਕਰਦੇ ਹਨ:

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

ਫਰੇਮਵਰਕ-ਵਿਸ਼ੇਸ਼ ਵਾਇਰਿੰਗ

FastAPI
from fastapi import FastAPI, HTTPException, Request
 
app = FastAPI()
 
@app.post("/thunderphone-webhook")
async def hook(request: Request):
    body = await request.body()           # raw bytes, NOT request.json()
    sig = request.headers.get("X-ThunderPhone-Signature", "")
    if not verify(body, sig, SECRET):
        raise HTTPException(status_code=401)
 
    import json
    event = json.loads(body)
    # … dispatch on event["type"] …
    return {"ok": True}
Express
import express from "express";
 
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 sig = req.header("X-ThunderPhone-Signature") || "";
    if (!verify(req.body, sig, process.env.WEBHOOK_SECRET)) {
      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 JsonResponse, HttpResponseForbidden
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
 
 
@csrf_exempt
@require_POST
def hook(request):
    body = request.body  # raw bytes
    sig = request.headers.get("X-ThunderPhone-Signature", "")
    if not verify(body, sig, SECRET):
        return HttpResponseForbidden("invalid signature")
    event = json.loads(body)
    # … dispatch on event["type"] …
    return JsonResponse({"ok": True})

ਟੂਲ ਕਾਲਾਂ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨਾ

ਜਦੋਂ ਏਜੰਟ ਤੁਹਾਡੇ ਕਿਸੇ ਫੰਕਸ਼ਨ ਟੂਲ ਨੂੰ ਸਿੱਧਾ ਕਾਲ ਕਰਦਾ ਹੈ (ਟੂਲ ਵਿੱਚ ਇੱਕ endpoint ਹੁੰਦਾ ਹੈ), ਤਾਂ ਬੇਨਤੀ ਤੁਹਾਡੇ ਕਨਫਿਗਰ ਕੀਤੇ endpoint.headers ਦੇ ਨਾਲ ਦੋ ThunderPhone ਹੈਡਰ ਲੈ ਕੇ ਜਾਂਦੀ ਹੈ:

  • X-ThunderPhone-Call-ID — ਲਾਈਵ ਕਾਲ ਦੀ ਸੰਖਿਆਤਮਕ ਆਈਡੀ।
  • X-ThunderPhone-Signature — ਬੇਨਤੀ-ਬਾਡੀ ਦੇ ਸਟੀਕ ਬਾਈਟਾਂ ਉੱਤੇ, ਤੁਹਾਡੇ ਸੰਗਠਨ-ਪੱਧਰੀ ਵੈੱਬਹੁੱਕ ਸੀਕਰਿਟ ਨਾਲ ਕੀਡ ਕੀਤਾ HMAC-SHA256।

ਉਹੀ verify() ਹੈਲਪਰ ਬਿਨਾਂ ਕਿਸੇ ਬਦਲਾਅ ਦੇ ਕੰਮ ਕਰਦਾ ਹੈ, ਦੋ ਗੱਲਾਂ ਦੇ ਨਾਲ:

  1. GET / DELETE ਟੂਲਾਂ ਦੀ ਕੋਈ ਬਾਡੀ ਨਹੀਂ ਹੁੰਦੀ। ਆਰਗੂਮੈਂਟ ਕਵੇਰੀ ਪੈਰਾਮੀਟਰਾਂ ਵਜੋਂ ਜਾਂਦੇ ਹਨ, ਅਤੇ ਸਿਗਨੇਚਰ ਦੀ ਗਣਨਾ ਖਾਲੀ ਬਾਈਟ ਸਟ੍ਰਿੰਗ ਉੱਤੇ ਕੀਤੀ ਜਾਂਦੀ ਹੈ — ਇਸ ਲਈ verify(b"", sig, secret) (Python) ਜਾਂ verify(Buffer.alloc(0), sig, secret) (Node) ਵਰਤੋ। ਕਵੇਰੀ ਸਟ੍ਰਿੰਗ ਨੂੰ ਹੈਸ਼ ਨਾ ਕਰੋ।
  2. ਜਿਨ੍ਹਾਂ ਸੰਗਠਨਾਂ ਵਿੱਚ ਲੇਗੇਸੀ ਵੈੱਬਹੁੱਕ ਕਨਫਿਗਰ ਨਹੀਂ ਹੈ, ਉਨ੍ਹਾਂ ਕੋਲ ਸੰਗਠਨ ਸੀਕਰਿਟ ਨਹੀਂ ਹੁੰਦਾ। ਉਸ ਸਥਿਤੀ ਵਿੱਚ ਟੂਲ ਕਾਲਾਂ ਵਿੱਚ ਸਿਰਫ਼ X-ThunderPhone-Call-ID ਹੁੰਦਾ ਹੈ ਅਤੇ ਕੋਈ ਸਿਗਨੇਚਰ ਹੈਡਰ ਨਹੀਂ ਹੁੰਦਾ। ਸਾਈਨਿੰਗ ਸੀਕਰਿਟ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ ਲੇਗੇਸੀ ਵੈੱਬਹੁੱਕ (PUT /v1/webhook) ਕਨਫਿਗਰ ਕਰੋ, ਜਾਂ endpoint.headers ਰਾਹੀਂ ਆਪਣੇ ਹੈਡਰ ਨਾਲ ਟੂਲ ਕਾਲਾਂ ਨੂੰ ਪ੍ਰਮਾਣਿਤ ਕਰੋ।
@app.post("/tools/search-appointments")
async def tool(request: Request):
    body = await request.body()  # b"" for GET/DELETE tools
    sig = request.headers.get("X-ThunderPhone-Signature", "")
    call_id = request.headers.get("X-ThunderPhone-Call-ID", "")
    if not verify(body, sig, ORG_WEBHOOK_SECRET):
        raise HTTPException(status_code=401)
    args = json.loads(body)
    ...

ਵੈੱਬਹੁੱਕ-ਮੋਡ ਟੂਲ ਡਿਸਪੈਚ (ਬਿਨਾਂ endpoint ਵਾਲੇ ਟੂਲ, ਜੋ ਤੁਹਾਡੇ ਸੰਗਠਨ ਦੇ ਵੈੱਬਹੁੱਕ ਨੂੰ telephony.tool / web.tool ਵਜੋਂ ਭੇਜੇ ਜਾਂਦੇ ਹਨ) ਇੱਕ ਆਮ ਸਾਈਨ ਕੀਤਾ ਵੈੱਬਹੁੱਕ ਹੈ — ਉੱਪਰ ਦਿੱਤੀ ਮਿਆਰੀ ਵਿਧੀ ਲਾਗੂ ਹੁੰਦੀ ਹੈ। ਦੋਵੇਂ ਬੇਨਤੀ ਰੂਪਾਂ ਲਈ ਫੰਕਸ਼ਨ ਟੂਲ ਵੇਖੋ।

ਆਮ ਸਮੱਸਿਆਵਾਂ

ਡਿਫੌਲਟ ਫਾਰਮੈਟਿੰਗ ਨਾਲ ਮੁੜ-ਸੀਰੀਅਲਾਈਜ਼ ਕਰਨਾ

ਬਾਡੀ ਨੂੰ ਪਾਰਸ ਕਰਕੇ ਆਪਣੀ 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 ਦੇ ਕਿਸੇ ਪ੍ਰਤੀ-ਐਂਡਪੁਆਇੰਟ ਸੀਕਰਟ ਨਾਲ। ਉਹੀ verify() ਫੰਕਸ਼ਨ ਦੁਬਾਰਾ ਵਰਤੋ, ਪਰ ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਤੁਸੀਂ ਟੂਲ ਰੂਟਾਂ ਉੱਤੇ ਇਸਨੂੰ ਸੰਗਠਨ ਦਾ ਸੀਕਰਟ ਦਿੰਦੇ ਹੋ।

GET/DELETE ਟੂਲਾਂ ਉੱਤੇ ਕਵੇਰੀ ਸਟ੍ਰਿੰਗ ਨੂੰ ਹੈਸ਼ ਕਰਨਾ

ਬਾਡੀ-ਰਹਿਤ ਟੂਲ ਮੈਥਡਾਂ ਲਈ ਸਿਗਨੇਚਰ ਖਾਲੀ ਬਾਈਟ ਸਟ੍ਰਿੰਗ ਨੂੰ ਕਵਰ ਕਰਦਾ ਹੈ, ਜਿਸ ਨਾਲ ਇੱਕ ਸਾਰਵਭੌਮ ਤਰੀਕਾ ਬਣਿਆ ਰਹਿੰਦਾ ਹੈ: ਰਾਅ ਰਿਕਵੇਸਟ ਬਾਡੀ ਦਾ HMAC ਕਰੋ, ਭਾਵੇਂ ਉਹ ਜੋ ਵੀ ਹੋਵੇ। URL ਜਾਂ ਕਵੇਰੀ ਸਟ੍ਰਿੰਗ ਨੂੰ ਹੈਸ਼ ਕਰਨਾ ਕਦੇ ਮੇਲ ਨਹੀਂ ਖਾਏਗਾ।

ਮੇਲ ਨਾ ਹੋਣ ਉੱਤੇ 401 ਵਾਪਸ ਨਾ ਕਰਨਾ

ਅਸਫਲ ਪੁਸ਼ਟੀ ਉੱਤੇ 200 ਵਾਪਸ ਕਰਨ ਨਾਲ ਹੈਂਡਲਰ ਰੀਪਲੇ ਟਾਰਗੇਟ ਬਣ ਜਾਂਦਾ ਹੈ। ਪੁਸ਼ਟੀ ਅਸਫਲ ਹੋਣ 'ਤੇ ਹਮੇਸ਼ਾ ਗੈਰ-2xx ਜਵਾਬ ਦਿਓ।


ਅਗਲੇ ਕਦਮ