# متطلبات الأمان

كل تطبيق مثبّت على متجر `DZBuild` يملك رمزا يصل إلى طلبيات التاجر أو زبائنه أو منتجاته. تنطبق هذه القواعد على كل التطبيقات، في وضع التجربة وبعد الاعتماد. يمكن لـ `DZBuild` إيقاف التطبيق الذي يخالفها، والتطبيق الموقوف يتلقى `403 app_suspended` في كل استدعاء.

## عناوين إعادة التوجيه[​](#عناوين-إعادة-التوجيه "رابط مباشر إلى عناوين إعادة التوجيه")

سجّل من 1 إلى 5 عناوين إعادة توجيه في منصة المطورين. يجب أن يكون كل عنوان:

* بـ `https`،
* بطول 512 حرفا على الأكثر،
* بلا اسم مستخدم ولا كلمة مرور ولا `#fragment`،
* بلا حرف البدل `*`.

تقارن `DZBuild` قيمة `redirect_uri` في كل طلب بالقائمة المسجّلة حرفا بحرف. العنوانان `https://app.example.com/callback` و `https://app.example.com/callback/` مختلفان. لا تقبل منصة المطورين إلا `https`، لذا اختبر مسار التثبيت على عنوان `workers.dev` أو عبر نفق `https` وسجّل عنوانه.

أنشئ قيمة `state` عشوائية جديدة لكل طلب تفويض، واربطها بجلسة التاجر، وارفض كل عودة لا تطابق فيها `state`.

## سرّ العميل ورموز الوصول[​](#سرّ-العميل-ورموز-الوصول "رابط مباشر إلى سرّ العميل ورموز الوصول")

يظهر سرّ العميل (`dzas_` يليه 48 حرفا ست عشريا) مرة واحدة، عند إنشاء التطبيق أو تجديد السرّ. لا تحتفظ `DZBuild` إلا ببصمته. استعمله على خادمك فقط: لا في متصفح، ولا في تطبيق هاتف، ولا في مستودع عام، ولا في سطر سجلّ.

إذا تسرّب السرّ، جدّده في منصة المطورين. يتوقف السرّ القديم فورا، وتبقى رموز الوصول الصادرة من قبل صالحة.

رموز الوصول (`dzpk_live_...`) تمنح الوصول إلى الواجهة الذي وافق عليه التاجر، دون تاريخ انتهاء. احفظها على الخادم، مشفّرة عند التخزين، رمزا لكل متجر. لا تملك التطبيقات نقطة وصول لإلغاء رمز. إذا تسرّب رمز، اطلب من التاجر إعادة تثبيت تطبيقك على ذلك المتجر: يحلّ الرمز الجديد محلّ القديم فورا. ويمكن للتاجر أيضا إزالة التطبيق، فيُلغى الرمز.

## التحقق من توقيع إشعارات `webhook`[​](#التحقق-من-توقيع-إشعارات-webhook "رابط مباشر إلى التحقق-من-توقيع-إشعارات-webhook")

كل إشعار `webhook` ترسله `DZBuild` إلى تطبيقك موقّع بمفتاح التوقيع الخاص بالتطبيق، وهو سلسلة ست عشرية من 64 حرفا تستطيع عرضها وتجديدها في منصة المطورين. يحمل الطلب `X-DZ-Timestamp` و `X-DZ-Event` و `X-DZ-Delivery` و:

```
X-DZ-Signature: t=1758880000,v1=5d41402abc4b2a76b9719d911017c592...
```

احسب `HMAC-SHA256` بمفتاح التوقيع على الطابع الزمني ثم نقطة ثم جسم الطلب الخام، وقارنه بـ `v1` بمقارنة ثابتة الزمن، وارفض كل طلب مضى على طابعه الزمني أكثر من 5 دقائق. نقاط استقبال التطبيقات لا تستقبل أبدا الترويسة `X-DZ-Token`، فالتوقيع هو الدليل الوحيد على أن الطلب صادر من `DZBuild`. تشرح صفحة [إشعارات Webhook](https://dzbuild.dev/ar/ar/webhooks.md) المخطط كاملا مع شيفرة بعدة لغات.

سرّ توقيع واحد يغطي كل المتاجر التي ثبّتت تطبيقك. تجديده يغيّر المفتاح لها كلها دفعة واحدة.

## التحقق من رموز الفتح[​](#التحقق-من-رموز-الفتح "رابط مباشر إلى التحقق من رموز الفتح")

عندما يفتح التاجر تطبيقك من لوحة تحكم `DZBuild`، تعيد `DZBuild` توجيه المتصفح إلى عنوان الفتح الخاص بك مع معامل الاستعلام `dz_launch`. يجب أن يستعمل عنوان الفتح `https`. المعامل رمز `JWT` موقّع بخوارزمية `HS256` بمفتاح التوقيع الخاص بالتطبيق: السلسلة ذات 64 حرفا كما تعرضها منصة المطورين تماما.

| الحقل        | القيمة                                                |
| ------------ | ----------------------------------------------------- |
| `iss`        | `dzbuild`                                             |
| `aud`        | قيمة `client_id` الخاصة بك.                           |
| `sub`        | معرّف مستخدم `DZBuild` الذي فتح التطبيق، كسلسلة نصية. |
| `store_id`   | المتجر الذي فُتح منه التطبيق.                         |
| `install_id` | تثبيتك على ذلك المتجر.                                |
| `is_owner`   | `true` لمالك المتجر، و `false` لعضو الفريق.           |
| `iat`        | وقت الإصدار، بثواني `Unix`.                           |
| `exp`        | `iat` زائد 300 ثانية.                                 |
| `jti`        | 16 حرفا ست عشريا عشوائيا.                             |

لا تقبل الرمز إلا إذا طابق التوقيع، وذكرت الترويسة `HS256`، وكانت `iss` تساوي `dzbuild`، و `aud` تساوي معرّف العميل الخاص بك، ولم يحن `exp` بعد. ثم ارفض كل `jti` سبق أن قبلته خلال آخر 5 دقائق. بعد قراءة الرمز، أعد التوجيه إلى عنوان لا يحمله، حتى يبقى خارج سجلّ المتصفح والسجلات.

رمز الفتح يثبت من فتح التطبيق ومن أي متجر. لا يستدعي الواجهة: استعمل رمز الوصول الذي حفظته لـ `store_id` و `install_id` هذين.

بلغة PHP:

```
<?php

/** Claims of a valid dz_launch token, or null. $clientId is your app's client_id. */

function verifyLaunchToken(string $jwt, string $signingSecret, string $clientId): ?array

{

    $parts = explode('.', $jwt);

    if (count($parts) !== 3) {

        return null;

    }

    [$head, $body, $sig] = $parts;

    $b64 = static fn(string $s): string|false => base64_decode(strtr($s, '-_', '+/'), true);

    $expected = rtrim(strtr(base64_encode(hash_hmac('sha256', "$head.$body", $signingSecret, true)), '+/', '-_'), '=');

    if (!hash_equals($expected, $sig)) {

        return null;

    }

    $header = json_decode((string) $b64($head), true);

    $claims = json_decode((string) $b64($body), true);

    if (($header['alg'] ?? '') !== 'HS256' || !is_array($claims)) {

        return null;

    }

    $now = time();

    if (($claims['iss'] ?? '') !== 'dzbuild' || ($claims['aud'] ?? '') !== $clientId

        || !is_int($claims['exp'] ?? null) || $claims['exp'] < $now || ($claims['iat'] ?? 0) > $now + 60) {

        return null;

    }

    return $claims; // Then refuse a jti you have already seen in the last 5 minutes.

}
```

بلغة Node.js:

```
const crypto = require('node:crypto');



// Claims of a valid dz_launch token, or null. clientId is your app's client_id.

function verifyLaunchToken(jwt, signingSecret, clientId) {

  const parts = String(jwt).split('.');

  if (parts.length !== 3) return null;

  const [head, body, sig] = parts;

  const expected = crypto.createHmac('sha256', signingSecret).update(`${head}.${body}`).digest();

  const given = Buffer.from(sig, 'base64url');

  if (given.length !== expected.length || !crypto.timingSafeEqual(given, expected)) return null;

  const header = JSON.parse(Buffer.from(head, 'base64url').toString('utf8'));

  const claims = JSON.parse(Buffer.from(body, 'base64url').toString('utf8'));

  const now = Math.floor(Date.now() / 1000);

  if (header.alg !== 'HS256' || claims.iss !== 'dzbuild' || claims.aud !== clientId) return null;

  if (!Number.isInteger(claims.exp) || claims.exp < now || claims.iat > now + 60) return null;

  return claims; // Then refuse a jti you have already seen in the last 5 minutes.

}
```

## حذف بيانات المتجر بعد إزالة التطبيق[​](#حذف-بيانات-المتجر-بعد-إزالة-التطبيق "رابط مباشر إلى حذف بيانات المتجر بعد إزالة التطبيق")

عندما يزيل التاجر تطبيقك، تلغي `DZBuild` رمز المتجر، وتُسقط إرساليات `webhook` التي كانت لا تزال تنتظر ذلك التثبيت، وترسل حدثا واحدا `app.uninstalled` إلى عنوان `webhook` الخاص بك إذا تم التحقق منه وكان نشطا:

```
{

  "id": "evt_...",

  "event": "app.uninstalled",

  "created_at": "2026-09-26T10:15:00+01:00",

  "store_id": 141,

  "data": {

    "install_id": 57,

    "client_id": "dzapp_0123456789abcdef0123",

    "store_id": 141,

    "uninstalled_at": "2026-09-26T09:15:00+00:00"

  }

}
```

احذف البيانات التي تحتفظ بها لذلك المتجر خلال 30 يوما من الحدث: الطلبيات والزبائن والمنتجات وكل نسخة من رمز الوصول. إذا لم يكن لتطبيقك عنوان `webhook` تم التحقق منه، فالرد `401` على رمز المتجر هو إشارتك.

## اطلب أقل الصلاحيات[​](#اطلب-أقل-الصلاحيات "رابط مباشر إلى اطلب أقل الصلاحيات")

اطلب فقط [الصلاحيات](https://dzbuild.dev/ar/ar/scopes.md) التي تستعملها ميزاتك: لا صلاحية كتابة على بيانات تكتفي بقراءتها، ولا `customers:read` لتطبيق لا يعرض أي زبون. تقارن `DZBuild` الصلاحيات المطلوبة بما تقوله بطاقة تطبيقك عنه، ويرى التاجر في شاشة الموافقة سطرًا واحدًا لكل مورد قبل أن يوافق.

## لا استخراج آلي للبيانات[​](#لا-استخراج-آلي-للبيانات "رابط مباشر إلى لا استخراج آلي للبيانات")

لا تصل إلى بيانات المتجر إلا عبر واجهة `REST` وإشعارات `webhook`. لا تؤتمت لوحة تحكم `DZBuild`، ولا تدخل بحساب التاجر، ولا تستخرج صفحات لوحة التحكم أو واجهة المتجر. لا تطلب أبدا من التاجر كلمة مرور `DZBuild` ولا مفتاح `API` خاصا بالتاجر.

## الرد على بريد الدعم[​](#الرد-على-بريد-الدعم "رابط مباشر إلى الرد على بريد الدعم")

سجّل عنوان بريد دعم تقرؤه فعلا، وأجب التجار الذين يراسلونه. تعرضه شاشة الموافقة وصفحة التطبيق في لوحة تحكم التاجر. أما قرارات المراجعة فترسلها `DZBuild` إلى بريد حسابك، لا إلى هذا العنوان.
