الانتقال إلى المحتوى الرئيسي
Certyneo
وثائق المطور

شبكات الويب تتلقى أحداث التوقيع في الوقت الحقيقي

قم بتعيين عنوان URL HTTPS في لوحة التحكم Certyneo الخاصة بك وتتلقى POST موقعة HMAC-SHA256 بمجرد حدوث حدث على المظروف: التوقيع، الرفض، انتهاء الصلاحية. 8 أحداث مدعومة، إعادة التجربة على مستوى 6 محاولات، التحقق من التشفير في 8 أسطر من الرموز.

< 5s

متوسط مدة التسليم بعد الحدث

5x

محاولات في حالة الفشل (حتى بعد 9 ساعات)

HMAC-SHA256

خوارزمية توقيع كل طلب

قائمة الأحداث

تتضمن الأحداث الثمانية أدناه دورة حياة كاملة لفاتورة Certyneo. قم بتشغيل الأحداث التي تهمك في الإعدادات → الويب هووك، وتجاهل الآخرين الاشتراك هو حجرة لكل حدث.

الحدثإطلاق
envelope.createdيتم إنشاء غلاف (بواسطة واجهة المستخدم، واجهة برمجة التطبيقات أو قالب) مفيد لمزامنة تسجيل جانب CRM في وقت إنشائه.
envelope.sentيتم إرسال المظروف إلى الموقعين (أول بريد إلكتروني تم إرساله). يمثل بداية دورة التوقيع النشطة.
envelope.completedجميع الموقعين وقعوا. ويمكن الوصول إلى PDF eIDAS المختوم ومتابعة التدقيق عبر `proof_pdf_url` في الحمولة.
envelope.declinedرفض أحد الموقعين الملف. سبب الرفض (إذا قدم من قبل الموقع) في `data.decline_reason`.
envelope.voidedتم إلغاء المظروف من قبل المصدر قبل التوقيع الكامل.
envelope.expiredلقد مرت فترة انتهاء صلاحية المظروف دون توقيع كامل. قائمة الموقعين المفقودين موجودة في `data.missing_signers`.
recipient.signedتم توقيع أحد الموقعين (ولكن ليس بالضرورة جميعهم) مفيد لدورات العمل المتتالية: إطلاق التحول إلى الموقع التالي.
recipient.viewedأحد الموقعين فتح رابط التوقيع دون أن يوقع بعد، مفيد لإعادة التجارة المستهدفة.

شكل الحمولة

جميع الأحداث تتشارك نفس مخطط JSON العلوي: `event`, `envelope_id`, `occurred_at`, `data`. يختلف محتوى `data` حسب الحدث (حقول موثقة حسب الحدث في مرجع API). إليك مثال كامل `envelope.completed`.

{
  "event": "envelope.completed",
  "envelope_id": "env_01HG3K8X9Y2N4P5Q6R7S8T9V0W",
  "occurred_at": "2026-05-27T08:42:13.521Z",
  "data": {
    "title": "Contrat de prestation Acme Corp",
    "status": "completed",
    "created_at": "2026-05-24T14:12:00.000Z",
    "completed_at": "2026-05-27T08:42:13.000Z",
    "signers": [
      {
        "email": "client@acme.example",
        "name": "Alex Client",
        "signed_at": "2026-05-27T08:42:13.000Z",
        "method": "eidas_aes",
        "ip": "203.0.113.42"
      }
    ],
    "proof_pdf_url": "https://certyneo.com/api/envelopes/env_01HG.../proof.pdf"
  }
}

يتم تشفير الحمولة بالرمز UTF-8 ، بدون BOM. يتم حساب توقيع HMAC على الجسم الخام كما تم إرساله لا تغير المساحات ، غالبًا ما يغير إعادة تحليل JSON ترتيب المفاتيح ويكسر التحقق.

تحقق من توقيع HMAC

يتم توقيع كل طلب باستخدام سر الويب هوك الخاص بك (يمكن رؤيته مرة واحدة عند إنشاؤه في لوحة التحكم، ثم يتم تشفيره في الراحة) ، ويتم نقل التوقيع في عنوان `Certyneo-Signature` بتنسيق `t=<timestamp>,v1=<hex_hmac>`. دائماً تحقق من التوقيع قبل معالجة الحمولة بدون هذه الخطوة، يمكن لأي شخص تزوير حدث و الاتصال بمنصبك.

Node.js / TypeScript

import crypto from "node:crypto";

export function verifyCertyneoSignature(
  rawBody: string,
  signatureHeader: string,
  secret: string,
): boolean {
  // Header format: t=<timestamp>,v1=<hex_hmac>
  const parts = Object.fromEntries(
    signatureHeader.split(",").map((p) => p.split("=")),
  );
  const ts = parts.t;
  const sig = parts.v1;
  if (!ts || !sig) return false;

  // Reject events older than 5 minutes (replay protection).
  const age = Math.abs(Date.now() / 1000 - Number(ts));
  if (age > 300) return false;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${ts}.${rawBody}`)
    .digest("hex");

  // timingSafeEqual to mitigate timing attacks.
  return crypto.timingSafeEqual(
    Buffer.from(expected, "hex"),
    Buffer.from(sig, "hex"),
  );
}

Python

import hashlib
import hmac
import time


def verify_certyneo_signature(
    raw_body: bytes, signature_header: str, secret: str
) -> bool:
    """Verify a Certyneo webhook signature.

    Header format: `t=<timestamp>,v1=<hex_hmac>`
    Rejects events older than 5 minutes (replay protection).
    """
    parts = dict(p.split("=") for p in signature_header.split(","))
    ts = parts.get("t")
    sig = parts.get("v1")
    if not ts or not sig:
        return False

    if abs(time.time() - int(ts)) > 300:
        return False

    expected = hmac.new(
        secret.encode("utf-8"),
        f"{ts}.{raw_body.decode('utf-8')}".encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()

    return hmac.compare_digest(expected, sig)

خطأ شائع

لا تستخدم `===` أو `==` لمقارنة التوقيع المتوقع مع التوقيع المستلم. استخدم وظيفة مؤقتة (`crypto.timingSafeEqual` في Node ، `hmac.compare_digest` في Python). خلاف ذلك ، فإن فاصل وقت المقارنة بين اثنين من التوقيعات يكشف السر تدريجياً لمهاجم مريض (هجوم توقيت).

سياسة إعادة التجربة

إذا كانت النتيجة النهائية غير HTTP 2xx (توقيت انتهاء، 5xx، اتصال رفض) ، سنقوم بالرد على النظام الاحتياطي. إجمالاً 6 محاولات في ~ 9 ساعات. بعد 6، يتم وضع علامة `فشل` على الحدث في لوحة تحكم شبكة الإنترنت الخاصة بك ويتم إرسال رسالة تنبيه عبر البريد الإلكتروني.

محاولةالمدة المتراكمةالوقت المنصرم
#100
#2+ 1 min1 min
#3+ 5 min6 min
#4+ 30 min36 min
#5+ 2 h2h 36
#6+ 6 h8h 36

إذا كنت ترغب في إعادة تقديم حدث فشل يدوياً، فإن لوحة التحكم على الويب توفر زر إعادة التسليم على كل تسليم فشل متاح لمدة 7 أيام بعد الفشل النهائي.

اختبار دون إرسال مظروف حقيقي

نقطة النهاية `/v1/webhooks/test` تقبل عنوان URL ونوع حدث، وتضع حمولة وهمية موقعة تمامًا مثل الحملة الحقيقية. مثالية للتحقق من صحة المُدير الخاص بك في التكامل المستمر أو أثناء التطوير المحلي (مع ngrok أو ما يعادلها).

# Trigger a test webhook to your endpoint
curl -X POST https://api.certyneo.com/v1/webhooks/test \
  -H "Authorization: Bearer $CERTYNEO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"event": "envelope.completed", "url": "https://your.app/webhooks/certyneo"}'

6 أساليب يجب اتباعها

  • تحقق من توقيع HMAC قبل أي قراءة للجسم استخدم مقارنة آمنة للوقت.
  • معالجة كل `envelope_id` × `event` مرة واحدة فقط من خلال تخزين المميزات التي تم رؤيتها بالفعل في القاعدة (يمكن أن تعيد التسجيلات نفس الحدث).
  • يجب أن تجيب على HTTP 200 في 5 ثوانٍ كحد أقصى، ثم قم بمعالجة التسلسل غير المتزامن، وإلا سيتم إيقافك وتعتبر محاولة إعادة.
  • تسجيل الجسم الخام + التوقيع الكامل في التحليل الاختياري التحقق من HMAC غالبا ما يفشل على BOM أو مساحة بيضاء غير مرئية.
  • إدخال "موت الرسالة" على التوقيت "الفشل" لتتبعها يدوياً في حالة وقوع حادث على جانب قسمك
  • يجب أن تتسامح مع فترة 5 دقائق بين `t=` و وقت الاستقبال بعد ذلك، و يجب أن ترفض لتغلق المشاركات.

للذهاب إلى أبعد من ذلك

هل أنتم مستعدون لربط أنظمتكم؟

إنشاء حساب مجاني في دقيقتين تطبيق webhook متاح من الخطة الابتدائية (مجانية، 5 غلافات/شهر).