توقيع الاستدعاءات
هل تستخدم حزمة 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) والعنوان والمسار والاستعلام التي بنى أنيس عليها نصّ التوقيع، وبالمفتاح الذي وجده. قارنها بنصّ التوقيع لديك، سطرًا بسطر.
وقبل أن يستدعي عميلك أنيس أصلًا، اختبره على أمثلة اختبار التوقيع: ففيها نصّ التوقيع الدقيق لكل نوع من الاستدعاءات الموقّعة.