التوقيع عبر API · White-label

وقّع داخل تطبيقك — بعلامتك أنت، دون تحويل المستخدم لأي موقع خارجي

ادمج صفحة توقيع كاملة داخل منتجك: بشعارك وألوانك ونطاقك أنت. تُصدِر من خادمك جلسة توقيع قصيرة العمر، تُضمّنها في iframe أو تُعيد التوجيه إليها، وتستقبل الأحداث لحظيًا عبر postMessage والـWebhooks — بينما يبقى المفتاح السرّي في خادمك وحده. التوقيع، وتأكيد الهوية، والتوقيع المؤهّل، كلّها داخل تدفّق واحد لا يغادر تطبيقك.

تضمين iframe أو إعادة توجيه نطاق توقيع خاص بك تأكيد هوية وQES داخل التدفّق
wthaiq:signed signing_session app.yourbrand.com تفعيل الحساب راجِع الاتفاقية ووقّعها لإتمام التسجيل مُضمَّن · بعلامتك شعارك هنا وقّع هنا تأكيد التوقيع
نماذج التكامل

ثلاث طرق لدمج التوقيع — اختر ما يناسب تطبيقك

الأساس واحد في الثلاثة: أنت تنشئ signature_request على خادمك، ثم تحصل على رابط توقيع للموقّع. يختلف النموذج في كيف يصل المستخدم إلى ذلك الرابط.

1
hosted

الصفحة المستضافة

حوّل المستخدم إلى signing_url الجاهز في حقل الموقّع. أسرع طريقة للإطلاق: صفحة توقيع كاملة تستضيفها «وثائق» على نطاقك المخصّص، بلا كود واجهة.

  • صفر كود في المتصفّح — رابط مباشر
  • يعمل على النطاق المخصّص sign.yourbrand.com
  • مثالي للروابط عبر البريد أو الرسائل
2
embedded / iframe

التضمين داخل التطبيق

أصدِر signing_session قصيرة العمر وحمّلها داخل iframe في صفحتك. يبقى المستخدم في مكانه تمامًا، وتستقبل أحداثه لحظيًا عبر postMessage.

  • أعلى معدّل إكمال — دون مغادرة الصفحة
  • أحداث wthaiq:viewed / signed / declined
  • رمز عميل قصير العمر — لا مفتاح سرّي
3
redirect

إعادة التوجيه

حوّل المتصفّح إلى صفحة التوقيع، ثم أعِده تلقائيًا إلى success_url بعد التوقيع أو cancel_url عند الإلغاء. حلٌّ وسط بين البساطة والتحكّم.

  • عودة مضمونة إلى مسار تطبيقك
  • مناسب لبيئات لا تسمح بالـiframe
  • يُعاد المرجع في متغيّر الرابط
جلسة التوقيع

توليد جلسة توقيع قصيرة العمر

لا تُرسِل مفتاحك السرّي إلى المتصفّح أبدًا. بدلًا من ذلك، يستدعي خادمك هذه النقطة لسكّ رمز جلسة مؤقّت وموقّع واحد فقط، آمن للإرسال إلى الواجهة.

POST /v1/signers/{id}/signing_session

يسكّ رابط توقيع مُضمَّنًا ورمز عميل قصير العمر لموقّع بعينه، ضمن نمط White-label.

المُعامِلالوصف
mode
string اختياري
نمط الجلسة: embedded (افتراضي، للتضمين في iframe) أو redirect (لإعادة التوجيه).
allowed_origins
string[] اختياري
قائمة النطاقات المسموح لها بتضمين الجلسة. تُرفض أي أصول (origins) خارجها. مطلوبة عمليًا مع embedded.
success_url
string اختياري
رابط العودة بعد التوقيع في نمط redirect. يدعم القالب {signature_request}.
cancel_url
string اختياري
رابط العودة عند إلغاء المستخدم أو انتهاء الجلسة.
expires_in
integer اختياري
عمر الرمز بالثواني (بين 300 و 3600، الافتراضي 3600). كلّما قصُر كان أأمن.
لماذا جلسة قصيرة العمر بدل المفتاح السرّي؟ المفتاح sk_ يملك صلاحية كاملة على حسابك — إنشاء طلبات، تنزيل مستندات موقّعة، إدارة الـWebhooks. لو تسرّب من المتصفّح لتحكّم فيه أي طرف. أمّا رمز الجلسة فمقيّد بموقّع واحد، وبصلاحية «التوقيع» فقط، ولمدة دقائق معدودة، وعلى نطاقات محدّدة — فحتى لو التُقِط، لا يفتح إلا ما سُكّ لأجله ثم ينتهي.
cURL Node Response
signing_session.sh
curl -X POST https://wthaiq.com/api/v1/signers/sgr_9fA2/signing_session \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "embedded",
    "allowed_origins": ["https://app.acme-pay.com"],
    "expires_in": 900
  }'
server.js
import Wthaiq from '@wthaiq/node';
const wt = new Wthaiq(process.env.WTHAIQ_SECRET_KEY);

// على الخادم فقط — المفتاح السرّي لا يغادر خادمك
const session = await wt.signers.createSigningSession('sgr_9fA2', {
  mode: 'embedded',
  allowed_origins: ['https://app.acme-pay.com'],
  expires_in: 900
});

// أرسِل هذا فقط إلى المتصفّح — لا شيء غيره
res.json({ signing_url: session.signing_url });
200 OK · signing_session
{
  "object": "signing_session",
  "signer": "sgr_9fA2",
  "signature_request": "sr_3n8Kd2Qa1V",
  "signing_url": "https://sign.acme-pay.com/s/uZ8xR2Kq?t=cst_live_9aF2xQ7bV3",
  "client_token": "cst_live_9aF2xQ7bV3",
  "mode": "embedded",
  "expires_at": 1754000900,
  "created_at": 1754000000
}
White-label

التخصيص الكامل للعلامة

صفحة التوقيع لك بالكامل. تُضبط إعدادات العلامة على مستوى الحساب افتراضيًا، ويمكن تجاوزها لكل طلب توقيع عبر كائن branding داخل جسم الطلب.

branding.json
{
  "branding": {
    "logo_url": "https://cdn.acme-pay.com/logo.svg",
    "brand_color": "#0B5FFF",
    "signing_domain": "sign.acme-pay.com",
    "email_from_name": "Acme Pay",
    "email_from_address": "sign@acme-pay.com",
    "remove_wthaiq_branding": true,
    "locale": "ar",
    "support_email": "support@acme-pay.com"
  }
}
الحقلالوصف
logo_urlشعارك المعروض أعلى صفحة التوقيع وفي رأس رسائل البريد. يُفضَّل SVG أو PNG شفّاف.
brand_colorلون علامتك الأساسي (HEX)؛ يُطبَّق على الأزرار والروابط ومؤشّرات التقدّم.
signing_domainنطاق التوقيع المخصّص، مثل sign.acme-pay.com. يُثبَت عبر سجل CNAME وشهادة TLS تُصدَر تلقائيًا.
email_from_nameاسم المُرسِل الظاهر في رسائل الدعوة والتذكير — يظهر باسم علامتك لا باسم «وثائق».
email_from_addressعنوان المُرسِل على نطاقك، بعد ضبط سجلّات SPF وDKIM لضمان التسليم.
remove_wthaiq_brandingعند true تُزال عبارة «مدعوم من وثائق» من الصفحة والبريد (متاح للخطط التي تدعم White-label الكامل).
localeلغة الواجهة الافتراضية للموقّع، مثل ar.
support_emailعنوان الدعم الذي يظهر للموقّع عند احتياجه المساعدة.
النطاق المخصّص يجعل شريط العنوان نفسه يعرض sign.acme-pay.com بدل نطاق «وثائق» — فلا يرى المستخدم أي إشارة إلى مزوّد خارجي في أي لحظة من رحلة التوقيع.
التضمين

التضمين في صفحتك — iframe وأحداث postMessage

حمّل signing_url الناتج عن الجلسة داخل iframe، ثم استمع لأحداث النافذة عبر postMessage لتحديث واجهتك لحظيًا.

embed.html
<iframe
  id="wthaiq-frame"
  src="https://sign.acme-pay.com/s/uZ8xR2Kq?t=cst_live_9aF2xQ7bV3"
  allow="camera; microphone"
  style="width:100%;height:760px;border:0;border-radius:16px">
</iframe>

&lt;!-- allow: camera/microphone مطلوبة لخطوة تأكيد الهوية داخل الإطار --&gt;
listen.js
window.addEventListener('message', function (e) {
  // تحقّق من مصدر الرسالة دائمًا قبل الوثوق بها
  if (e.origin !== 'https://sign.acme-pay.com') return;

  const evt = e.data;
  switch (evt.type) {
    case 'wthaiq:viewed':
      // فتح الموقّع المستند
      console.log('viewed', evt.signer);
      break;
    case 'wthaiq:signed':
      // اكتمل التوقيع — أغلِق الإطار وابدأ التفعيل
      onSigned(evt.signature_request);
      break;
    case 'wthaiq:declined':
      // رفض الموقّع — سجّل السبب وأعِد التوجيه
      onDeclined(evt.reason);
      break;
  }
});
wthaiq:viewed

يُطلَق عند فتح الموقّع المستند لأول مرة داخل الإطار — استخدمه لتتبّع بدء الرحلة.

wthaiq:signed

يُطلَق فور إتمام التوقيع بنجاح، ويحمل signature_request وsigner.

wthaiq:declined

يُطلَق عند رفض الموقّع أو تعذّر إتمامه، ويحمل حقل reason.

أحداث المتصفّح مقابل الـWebhooks. أحداث postMessage مثالية لتحديث الواجهة فورًا، لكنها ليست مصدر الحقيقة — يمكن أن يُغلق المستخدم التبويب قبل وصولها. اعتمِد دائمًا على الـWebhook signature_request.completed على خادمك كإشارة نهائية موثوقة قبل تفعيل أي إجراء حسّاس.

إعادة التوجيه بدل التضمين

إن لم يناسبك الـiframe، اسكّ الجلسة بنمط redirect مع success_url وcancel_url، ثم حوّل المتصفّح إلى signing_url. تعيد «وثائق» المستخدم إلى مسارك تلقائيًا مع المرجع في متغيّر الرابط.

redirect.sh
curl -X POST https://wthaiq.com/api/v1/signers/sgr_9fA2/signing_session \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "redirect",
    "success_url": "https://app.acme-pay.com/onboarding/done?sr={signature_request}",
    "cancel_url": "https://app.acme-pay.com/onboarding/canceled"
  }'
الهوية والتوقيع المؤهّل

تأكيد الهوية وQES — داخل التدفّق ذاته

لا يغادر المستخدم إطارك ليؤكّد هويته أو يوقّع بشهادة مؤهّلة. تظهر هاتان الخطوتان مضمّنتين قبل التوقيع مباشرة، وهذا ما يراه المستخدم في كلٍّ منهما.

AES · Didit

تأكيد الهوية قبل التوقيع

عند تفعيل require_identity على الموقّع، يعرض الإطار خطوة تحقّق كاملة قبل أن تُفتح صفحة التوقيع — ويُربط قرار التحقّق بالتوقيع نفسه.

ما يراه المستخدم
  • تصوير المستند الرسمييطلب الإطار التقاط صورة بطاقة الهوية أو جواز السفر عبر الكاميرا مباشرة داخل صفحتك.
  • مطابقة وجه حيّةيُطلب من المستخدم تحريك وجهه أمام الكاميرا للتأكّد من أنه شخص حيّ حاضر، ومطابقته بصورة المستند.
  • «جارٍ التحقق» ثم اعتماديظهر مؤشّر معالجة، ثم علامة نجاح خضراء — وتصبح حالة الموقّع identity_verified.
  • فتح صفحة التوقيعبعد الاعتماد فقط تُتاح منطقة التوقيع، فيوقّع المستخدم وقد ارتبط توقيعه بهوية مؤكّدة.
QES · token agent

التوقيع المؤهّل بالتوكن

للتوقيع المؤهّل (method: "token")، يجري التوقيع على شهادة الأجهزة عبر وكيل محلّي على جهاز المستخدم عند 127.0.0.1:8899 — المفتاح الخاص لا يغادر التوكن إطلاقًا (PAdES مؤجّل/مُجزّأ).

ما يراه المستخدم
  • اكتشاف الوكيل والتوكنتتحقّق الصفحة من تشغيل الوكيل المحلّي ووجود التوكن موصولًا، وتعرض «تمّ اكتشاف التوكن».
  • تجهيز التوقيع على الخادميحسب الخادم signedAttributes (بصمة SHA-256 للمستند) ويرسل الهاش فقط — لا مفتاح خاص هنا.
  • إدخال رمز PIN على الجهازيطلب الوكيل رمز التوكن، فيُدخله المستخدم ليأذن بعملية توقيع واحدة على الجهاز.
  • التوقيع محليًا ثم دمج CMSيوقّع التوكن الهاش ويعيد بنية CMS، فيدمجها الخادم في المستند ويُكمل PAdES حتى LTA.
qes-deferred.js
// 1) الخادم يجهّز signedAttributes (هاش المستند) — لا مفتاح خاص
const prep = await wt.signers.prepareTokenSignature('sgr_9fA2');
// prep.digest = SHA-256 لـ signedAttributes (ESS signingCertificateV2)

// 2) داخل المتصفّح: الوكيل المحلّي يوقّع على الجهاز
const signed = await fetch('https://127.0.0.1:8899/sign', {
  method: 'POST',
  body: JSON.stringify({ digest: prep.digest, alg: 'SHA256withRSA' })
}).then(r => r.json());
// المفتاح لا يغادر التوكن؛ يعيد الوكيل بنية CMS (adbe.pkcs7.detached)

// 3) الخادم يدمج CMS في المستند ويُنهي PAdES (LT/LTA)
await wt.signers.completeTokenSignature('sgr_9fA2', { cms: signed.cms });
الخطوتان تظهران بنفس علامتك ولغتك داخل الإطار. راجِع صفحة الحجّية لتفصيل مستويات SES / AES / QES وسلّم صيغ PAdES من B حتى LTA.
التدفّق الكامل

من الإنشاء إلى المستند الموقّع — خطوة بخطوة

رحلة تضمين واحدة كاملة: أنشئ الطلب على خادمك، ضمِّن التوقيع، انتظر إشارة الاكتمال الموثوقة، ثم نزّل المستند المختوم.

  1. أنشئ طلب توقيع مُضمَّن

    على خادمك، أنشئ signature_request بموقّعيه ومصدره وسويّته القانونية.

    POST /v1/signature_requests
  2. احصل على رابط التوقيع

    خذ signing_url من الموقّع، أو اسكّ signing_session قصيرة العمر للتضمين.

    signers[0].signing_url
  3. ضمِّن في صفحتك

    حمّل الرابط في iframe واستمع لأحداث postMessage لتحديث الواجهة.

  4. يُكمل المستخدم التوقيع

    يؤكّد هويته (AES) عند اللزوم ثم يوقّع داخل إطارك دون مغادرة تطبيقك.

  5. استقبل الـWebhook الموثوق

    يستقبل خادمك حدث الاكتمال — هذه هي الإشارة النهائية لتفعيل ما بعد التوقيع.

    signature_request.completed
  6. نزّل المستند المختوم

    اطلب نسخة الـPDF النهائية بعد اكتمال الطلب؛ ودليلها التشفيري المستقل هو محضر الأدلّة المختوم (Ed25519 + ختم RFC 3161).

    GET /v1/signature_requests/{id}/download
1 · إنشاء 5 · Webhook 6 · تنزيل
create.sh
curl -X POST https://wthaiq.com/api/v1/signature_requests \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 5f3c9c2e-2b7a-4a1e-9c2f-1d3e4f5a6b7c" \
  -d '{
    "title": "اتفاقية تاجر — Acme Pay",
    "source": {"type":"template","template_id":"tpl_merchant_agreement"},
    "legal_level": "aes",
    "signers": [
      {"name":"سلمى حسن","email":"salma@example.com","type":"individual",
       "method":"draw","require_identity":true}
    ],
    "metadata": {"merchant_id":"M-88213"}
  }'
webhook.json
{
  "id": "evt_2M8pQ",
  "object": "event",
  "type": "signature_request.completed",
  "created_at": 1754500000,
  "livemode": true,
  "data": {
    "object": {
      "id": "sr_3n8Kd2Qa1V",
      "object": "signature_request",
      "status": "completed",
      "reference": "WTQ-000123",
      "download_url": "https://wthaiq.com/api/v1/signature_requests/sr_3n8Kd2Qa1V/download"
    }
  }
}
download.sh
# متاح فقط بعد أن تصبح الحالة completed
curl https://wthaiq.com/api/v1/signature_requests/sr_3n8Kd2Qa1V/download \
  -H "Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Wthaiq-Version: 2026-07-01" \
  -o agreement-signed.pdf

# الناتج: application/pdf نهائي مختوم · الدليل التشفيري المستقل: محضر الأدلّة عبر GET /api/evidence.php?scope=api&doc=<id>
الأمان

قواعد أمان التضمين

التضمين آمن ما دمت تفصل بين ما يبقى على الخادم وما يصل إلى المتصفّح.

لا تُرسِل sk_ إلى المتصفّح أبدًا

المفتاح السرّي يبقى على الخادم حصريًّا. المتصفّح لا يرى إلا رابط التوقيع ورمز الجلسة القصير.

عمر رمز قصير (TTL)

اجعل expires_in بأقصر مدة كافية (300 إلى 3600 ثانية). الرمز المنتهي عديم الأثر حتى لو التُقِط.

قائمة نطاقات مسموح بها

حدّد allowed_origins بنطاقاتك فقط. تُرفض محاولة تضمين الجلسة من أي أصل خارج القائمة.

تحقّق من الأحداث على الخادم

لا تفعّل إجراءً حسّاسًا بناءً على postMessage وحده؛ اعتمِد الـWebhook الموقّع بـwhsec_ كمصدر الحقيقة.

أسئلة شائعة

أسئلة المطوّرين حول التضمين

هل يرى المستخدم أي إشارة إلى «وثائق» أثناء التوقيع؟

لا، إن فعّلت White-label الكامل. تظهر صفحة التوقيع بشعارك وألوانك وعلى نطاقك المخصّص sign.yourbrand.com، وتُرسل رسائل البريد باسم علامتك، وتُزال عبارة «مدعوم من وثائق» عند ضبط remove_wthaiq_branding: true. يبقى المستخدم داخل تجربتك من البداية للنهاية.

لماذا أسكّ signing_session بدل تمرير المفتاح السرّي؟

لأن sk_ يملك صلاحية كاملة على حسابك، ولا يجوز أن يصل إلى المتصفّح إطلاقًا. رمز الجلسة قصير العمر مقيّد بموقّع واحد وبصلاحية التوقيع فقط ولنطاقات محدّدة ولدقائق معدودة — فحتى لو التُقِط، لا يفتح إلا ما سُكّ لأجله ثم ينتهي تلقائيًا.

ما الفرق بين أحداث postMessage والـWebhooks؟

أحداث المتصفّح مثل wthaiq:signed لتحديث الواجهة لحظيًا، لكنها قد لا تصل إن أغلق المستخدم التبويب. الـWebhook مثل signature_request.completed يصل إلى خادمك بشكل موثوق وموقّع بـwhsec_، وهو مصدر الحقيقة الذي تبني عليه أي إجراء حسّاس مثل التفعيل.

كيف يعمل التوقيع المؤهّل QES داخل الإطار؟

عبر وكيل توقيع محلّي على جهاز المستخدم عند 127.0.0.1:8899. يجهّز الخادم بصمة signedAttributes ويرسل الهاش فقط، فيُدخل المستخدم رمز التوكن ويوقّع الجهاز محليًّا — المفتاح الخاص لا يغادر التوكن — ثم يُدمج CMS في المستند لإكمال PAdES حتى LTA. هذا هو التوقيع المؤجّل/المُجزّأ.

ماذا لو لم تسمح بيئتنا باستخدام الـiframe؟

استخدم نمط redirect: اسكّ الجلسة مع success_url وcancel_url، ثم حوّل المتصفّح إلى signing_url. تعيد «وثائق» المستخدم إلى مسارك تلقائيًا مع المرجع في الرابط. أو استخدم الصفحة المستضافة مباشرة عبر signing_url على نطاقك المخصّص.

وقّع داخل تطبيقك — بعلامتك أنت.

ابدأ بجلسة توقيع واحدة خلال دقائق، ثم ضمِّن تجربة توقيع كاملة بعلامتك ونطاقك دون تحويل أي مستخدم إلى موقع خارجي.