توقيع الاستدعاءات

هل تستخدم حزمة SDK؟

حزم SDK تقوم بكل هذا نيابةً عنك. هذه الصفحة لمن يكتب عميله الخاص، أو لفهم سبب رفضٍ ما.

كل استدعاء، باستثناء تسجيل المفتاح والمفاتيح العامة لأنيس، يُوقَّع باستخدام HTTP Message Signatures (RFC 9421)، بخوارزمية ECDSA P-256 مع SHA-256. أنت توقّع نصًّا محددًا بدقة — نصّ التوقيع — يُبنى من أجزاء الاستدعاء. يعيد أنيس بناء النصّ نفسه مما استلمه، ويتحقق من توقيعك مقابله. إذا اختلف بايت واحد يُرفض الاستدعاء بالرمز invalid_credentials، دون أي إشارة إلى البايت المختلف، وذلك عن قصد.

الأنواع الثلاثة للاستدعاءات الموقّعة

المسار هو الذي يحدد الأجزاء التي توقّعها — راجع صفحة كل مسار في مرجع API.

النوع يستخدمه الأجزاء الموقّعة، بهذا الترتيب تحديدًا
قراءة كل استدعاء GET @method @authority @path @query x-anis-date
تغيير عمليتا الكشف والفحص الذاتي للتوقيع @method @authority @path @query content-digest nonce x-anis-date
طلب شراء تقديم طلب شراء @method @authority @path @query content-digest nonce idempotency-key x-anis-date

يأتي idempotency-key قبل x-anis-date، لا بعده.

الترويسات التي ترسلها

الترويسة في القيمة
X-Anis-Date كل استدعاء موقّع الوقت بصيغة ISO-8601 بتوقيت UTC: 2026-09-19T08:00:00Z
Content-Digest استدعاءات التغيير وطلبات الشراء sha-256=:<base64 of SHA-256 of the exact body bytes>:
Nonce استدعاءات التغيير وطلبات الشراء قيمة عشوائية جديدة لكل استدعاء (تستخدم حزم SDK 128 بتًا عشوائيًا بترميز base64url)
Idempotency-Key طلبات الشراء معرّف العملية الخاص بك: UUID بأحرف صغيرة مع الشرطات
Signature-Input كل استدعاء موقّع المعاملات المبيّنة أدناه
Signature كل استدعاء موقّع sig1=:<base64 of the 64-byte signature>:

المعاملات

sig1=("@method" "@authority" "@path" "@query" "x-anis-date");created=1789804800;expires=1789804860;keyid="3f2a9c14-8d6e-4b21-9f07-5c8ab2d61e43";alg="ecdsa-p256-sha256"
  • التسمية (label) هي دائمًا sig1، في كلٍّ من Signature-Input وSignature.
  • created وexpires بالثواني وفق توقيت Unix. يسمح أنيس بـ 300 ثانية كحدّ أقصى بينهما؛ استخدم 60 ثانية أو أقل (كما تفعل حزم SDK) — انظر قسم «الساعة» أدناه.
  • keyid هو معرّف المفتاح الذي حصلت عليه عند تسجيل المفتاح: UUID بأحرف صغيرة مع الشرطات.
  • alg هو دائمًا ecdsa-p256-sha256. لا يوجد ما يُتفاوض عليه.
  • في استدعاءات التغيير وطلبات الشراء، أضف ;nonce="<the Nonce header>" في النهاية، ويجب أن يساوي قيمة ترويسة Nonce. في استدعاء القراءة يجب ألا يكون موجودًا. أي عدم تطابق يُرفض بالرمز invalid_credentials قبل أن يُتحقق من التوقيع أصلًا.

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

نصّ التوقيع

سطر واحد لكل جزء موقّع بالشكل "name": value، وينتهي كل سطر بمحرف سطر جديد — ثم سطر أخير لـ "@signature-params"، دون محرف سطر جديد بعده. مثال لطلب شراء:

"@method": POST
"@authority": partners.anis.ly
"@path": /v1/wallets/2f1c8a94-6d37-4e52-b8a1-0c9e5d3f7b26/orders
"@query": ?
"content-digest": sha-256=:3AuIAnFFepkxwk6edx9OofFKHz5L8cSdZNVmztycemo=:
"nonce": b2F1dGgtbm9uY2UtMDAx
"idempotency-key": 9b2e4f17-3c6a-4d58-b0e1-7a5c8d2f6b34
"x-anis-date": 2026-09-19T08:00:00Z
"@signature-params": ("@method" "@authority" "@path" "@query" "content-digest" "nonce" "idempotency-key" "x-anis-date");created=1789804800;expires=1789804860;keyid="3f2a9c14-8d6e-4b21-9f07-5c8ab2d61e43";alg="ecdsa-p256-sha256";nonce="b2F1dGgtbm9uY2UtMDAx"
الجزء القيمة
@method الطريقة (method) بأحرف كبيرة
@authority اسم المضيف — مع المنفذ إن لم يكن المنفذ الافتراضي للبروتوكول — بأحرف صغيرة
@path المسار تمامًا كما أُرسل. كل مسار يتكوّن من كلمات ثابتة ومعرّفات UUID، لذا لا يحتاج أبدًا إلى ترميز النسبة المئوية (percent-encoding)
@query ? متبوعة بسلسلة الاستعلام تمامًا كما أُرسلت. عند غياب الاستعلام يُوقَّع ?
content-digest, nonce, idempotency-key, x-anis-date قيمة الترويسة تمامًا كما أُرسلت

وقّع بايتات نصّ التوقيع بترميز UTF-8 باستخدام ECDSA P-256 وSHA-256. أرسل التوقيع بصيغته ذات 64 بايتًاr ثم s، كلٌّ منهما 32 بايتًا — وليس بصيغة DER أبدًا.

المزالق

  • عند غياب الاستعلام يُوقَّع ?، لا سلسلة فارغة.
  • يُوقَّع الاستعلام تمامًا كما أُرسل — دون إعادة ترتيب، أو إعادة ترميز، أو إعادة بناء من القيم بعد تحليلها. مؤشر التصفح (paging cursor) يُعاد تمامًا كما استلمته.
  • اسم المضيف بأحرف صغيرة. المضيف المكتوب بأحرف كبيرة وصغيرة مختلطة يُنتج نصًّا موقّعًا لا يعيد أنيس بناءه أبدًا.
  • كل استدعاء تغيير يحسب البصمة لما يرسله بالضبط. الكشف لا يرسل محتوى على الإطلاق — ولا حتى {} — لذا فبصمته هي بصمة صفر بايت: sha-256=:47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=:. أما الفحص الذاتي للتوقيع فيرسل {} بالضبط: sha-256=:RBNvo1WzZ4oRRq0W9+hknpT7T8If536DEMBg9hyq/4o=:. وطلب الشراء يحسب البصمة لمحتواه بصيغة JSON — احسب البصمة بعد أن يصبح المحتوى نهائيًا، وأرسل تلك البايتات نفسها بالضبط.
  • التوقيع 64 بايتًا، وليس DER أبدًا. تُنتج ‎.NET وWebCrypto الصيغة ذات 64 بايتًا. أما openssl_sign في PHP ومكتبة cryptography في Python فتُنتجان DER وتحتاجان إلى تحويل؛ والتوقيع الذي يتراوح طوله بين 70 و72 بايتًا هو DER.

الساعة

يقبل أنيس التوقيع الذي لا تتجاوز قيمة created فيه نحو 30 ثانية تقدّمًا على ساعته أو 5 دقائق تأخّرًا عنها، والذي لم يحن بعدُ وقت expires فيه. تحمل ردود أنيس وقتها الخاص، ولا يقبل العميل الردّ إلا إذا كان ضمن 60 ثانية من ساعته هو (انظر التحقق من الردود).

لهذا ينبغي ألا يتجاوز عمر التوقيع 60 ثانية: مع نافذة أطول، قد تتسبب ساعة خادم متأخرة بضع دقائق في قبول طلب شراء وإتمامه، ثم في التخلّص من الردّ الذي يحمل رموز البطاقات باعتباره قديمًا جدًا. أبقِ ساعة خادمك متزامنة.

عندما لا يجتاز التوقيع التحقق

استدعِ فحص توقيعك. يُبلغك بالطريقة (method) والعنوان والمسار والاستعلام التي بنى أنيس عليها نصّ التوقيع، وبالمفتاح الذي وجده. قارنها بنصّ التوقيع لديك، سطرًا بسطر.

وقبل أن يستدعي عميلك أنيس أصلًا، اختبره على أمثلة اختبار التوقيع: ففيها نصّ التوقيع الدقيق لكل نوع من الاستدعاءات الموقّعة.