# المصادقة وتوقيع الطلبات

> قم بتوقيع طلبات API باستخدام HMAC-SHA256 مع project UUID وAPI key الخاص بك.

يجب أن يحمل كل طلب API (باستثناء webhooks الواردة) project UUID الخاص بك وتوقيع الطلب. يثبت التوقيع أن الطلب صادر منك وأن لا أحد قام بتعديله أثناء النقل.

## مفاتيح API

يستخدم 2328.io **مفتاحين** يشتركان في خوارزمية التوقيع نفسها لكنهما يغطيان نقاط نهاية مختلفة:

| المفتاح | يُستخدم لـ |
|-----|----------|
| **API key** | المدفوعات، المحافظ الثابتة، الرصيد، أسعار الصرف، والتحقق من webhooks المدفوعات/المحافظ الثابتة |
| **Payout API key** | جميع نقاط النهاية `/v1/payout/*` والتحقق من webhooks السحب |

كلا المفتاحين موجودان في إعدادات مشروعك على [2328.io](https://2328.io). الأمثلة أدناه تذكر "API key" بشكل عام — استبدله بالمفتاح المناسب لنقطة النهاية التي تستدعيها.

> **INFO:** **لا تخلط أبدًا** بين المفتاحين: توقيع طلب سحب باستخدام API key العادي (أو طلب دفع باستخدام Payout key) يُرجع خطأ توقيع.

## الرؤوس المطلوبة

| الرأس | النوع | مطلوب | الوصف |
|--------|------|----------|-------------|
| `Content-Type` | string | نعم | دائمًا `application/json` |
| `project` | string | نعم | project UUID الخاص بك |
| `sign` | string | نعم | توقيع HMAC-SHA256 للطلب، محسوب باستخدام API key الخاص بك |
| `User-Agent` | string | نعم | يحدد تطبيقك (مثل `MyShop/1.4 (+https://myshop.example)`). قد يتم حظر الطلبات بدون `User-Agent`. |

## كيف يعمل التوقيع

اعتبر التوقيع بمثابة بصمة لمحتوى الطلب. يتم بناؤه عن طريق:

1. تسلسل المحتوى إلى JSON (مضغوط — بدون مسافات إضافية).
2. ترميز Base64 لذلك JSON. هذه الخطوة تطبّع المدخلات عبر اللغات — بمجرد أن تصبح ASCII خالصة، تنتج كل اللغات نفس البايتات لـ HMAC.
3. حساب **HMAC-SHA256** لسلسلة Base64 باستخدام API key الخاص بك، ثم تحويل النتيجة إلى hex بأحرف صغيرة.

بالنسبة لطلبات **GET** وغيرها من الطلبات بدون محتوى، قم بتوقيع سلسلة فارغة بدلًا من JSON.

> **INFO:** توقيع السلسلة الفارغة ثابت لمفتاح API معين. يمكنك تخزينه مؤقتًا إذا قمت بإجراء العديد من استدعاءات GET.

## التطبيقات

<CodeSnippet name="apiSign" langs="php,js,ts,python,go" />

### الطلبات بدون محتوى (GET)

بالنسبة للطلبات بدون محتوى (مثل `GET /v1/payout/status/{uuid}`)، قم بتوقيع سلسلة فارغة:

<CodeSnippet name="apiSignBodyless" langs="curl,php,js,ts,python,go" />

## مثال كامل لطلب

<CodeSnippet name="fullRequestExample" langs="curl,php,js,ts,python,go" />

> **DANGER:** **لا تكشف أبدًا عن API key الخاص بك في كود من جانب العميل.** قم بتوقيع الطلبات على الواجهة الخلفية لديك. تسرّب API key يمنح أي شخص وصولًا كاملًا إلى حساب التاجر.

## التحقق من توقيعات webhook

عندما يرسل لك 2328.io webhook، تعمل نفس الخوارزمية بشكل عكسي:

1. استخرج حقل `sign` من المحتوى.
2. قم بترميز الحقول المتبقية كـ JSON (مضغوط، بدون مسافات).
3. قم بترميز Base64 لتلك السلسلة.
4. احسب `HMAC-SHA256` بالمفتاح المناسب.
5. قارنه بـ `sign` المستلم باستخدام مقارنة **بزمن ثابت** (`hash_equals`، `crypto.timingSafeEqual`، `hmac.compare_digest`، `subtle.ConstantTimeCompare`، `OpenSSL.fixed_length_secure_compare`).

يعتمد مفتاح التوقيع على مصدر webhook:

| Webhook | المفتاح المستخدم للتحقق |
|---------|---------------------|
| webhooks المدفوعات / المحافظ الثابتة (`/v1/payment`، `/v1/static-wallet`) | **API key** |
| webhooks السحب (`/v1/payout`) | **Payout API key** |

> **WARNING:** **أخطاء شائعة في التحقق.** يجب أن ينتج مُرمِّز JSON لديك **نفس البايتات تمامًا** التي أنتجها المُرسِل — وإلا فسيختلف Base64 ولن يتطابق التوقيع.

- **Go**: استخدم `json.NewEncoder` مع `SetEscapeHTML(false)`. الـ `json.Marshal` الافتراضي يهرب `<` و`>` و`&` إلى `<` ويُفسد التوقيع.
- **Python**: مرِّر `ensure_ascii=False` إلى `json.dumps`. بدونها، تُهرَّب الأحرف غير ASCII (السيريلية، الصينية، …) إلى `\uXXXX`.
- **JSON مضغوط**: بدون مسافات بيضاء بين الحقول (`separators=(",", ":")` في Python).
- **ترتيب الحقول** (Go): إن `map[string]any` العادي يُرتِّب المفاتيح عشوائيًا عند إعادة الترميز. استخدم `json.RawMessage` أو struct مُرتَّبة، أو احذف `sign` من البايتات الأصلية.

إذا استمر فشل التحقق، فشغِّل `apiSign` على الحمولة بنفسك — يجب أن ينتج نفس السلسلة السداسية عشرية مثل `sign` المستلم.

> **INFO:** **التوقيع الصحيح لا يمنع إعادة الإرسال.** هو فقط يُثبت أن الـ webhook جاء من 2328.io — ولا يمنع المهاجم من إعادة إرسال webhook *تم اعتراضه* لاحقًا. تحقق دائمًا من عدم التكرار باستخدام `uuid` (أو `txid` للمحافظ الثابتة) قبل قيد الأموال. ارفض الطلب بـ HTTP `401` إذا كان التوقيع مفقودًا أو خاطئًا.

أمثلة الكود الكاملة موجودة في **[Webhook Notifications](/docs/webhooks#verifying-the-signature)**. ومعالجة إعادة المحاولة وقواعد عدم التكرار في [أفضل الممارسات](/docs/webhooks#best-practices).