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

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

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

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

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

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

النوع يستخدمه الأجزاء الموقّعة، بهذا الترتيب تحديدًا
قراءة كل قراءة GET مصادَق عليها @method @authority @path @query x-anis-date
تغيير الكشف والتشخيص وتغييرات الوصول والشبكات وPATCH لمستلم الشحن المباشر وتغييرات إدارة الإشعارات @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. لا تتحقق أنيس من المدة بينهما: فهي ترفض قيمة created التي تتقدّم على ساعتها بأكثر من 30 ثانية تقريبًا أو تتأخر عنها بأكثر من 5 دقائق، وترفض expires الذي مضى وقته. اجعل expires يساوي created مضافًا إليه 60 ثانية أو أقل (كما تفعل حزم SDK) — انظر قسم «الساعة» أدناه.
  • keyid هو معرّف المفتاح الذي حصلت عليه عند تسجيل المفتاح: UUID بأحرف صغيرة مع الشرطات.
  • alg هو دائمًا ecdsa-p256-sha256. لا يوجد ما يُتفاوض عليه.
  • في استدعاءات التغيير وطلبات الشراء، أضف ;nonce="<the Nonce header>" في النهاية، ويجب أن يساوي قيمة ترويسة Nonce. في استدعاء القراءة يجب ألا يكون موجودًا. أي عدم تطابق يُرفض بالرمز malformed_signed_request قبل أن يُتحقق من التوقيع أصلًا.

لإخفاقات التوقيع أحد ردّين، عن قصد. الاستدعاء غير سليم البنية — ترويسة توقيع مفقودة أو لا يمكن قراءتها، أو أجزاء موقّعة ليست ما يحتاجه المسار أو ليست بترتيبه، أو بصمة محتوى لا يمكن قراءتها أو لا تصف المحتوى المُرسَل — يُرفض بالرمز malformed_signed_request (400)، قبل أن تنظر أنيس في أي مفتاح. أما الاستدعاء السليم البنية الذي لا يصحّ توقيعه — نصّ توقيع خاطئ، أو توقيع بصيغة DER، أو وقت قديم، أو مفتاح غير معروف أو غير نشط — فيكون ردّه invalid_credentials (401).

نصّ التوقيع

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

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