> ## Documentation Index
> Fetch the complete documentation index at: https://developers.nateq.io/llms.txt
> Use this file to discover all available pages before exploring further.

# حزمة PHP

> أرسل واقرأ البريد الإلكتروني من PHP، مع دعم من الدرجة الأولى لـ Laravel وبدون أي اعتماديات مطلوبة.

عميل PHP الرسمي لواجهة برمجة تطبيقات Nateq. نواة مستقلة عن أُطر العمل، مع مزوّد
خدمة (service provider) وواجهة `Nateq` يُكتشفان تلقائياً في Laravel. بدون أي
اعتماديات مطلوبة وقت التشغيل، لذا لن يتعارض مع إصدار Guzzle في تطبيقك.

<CardGroup cols={2}>
  <Card title="Packagist" icon="package" href="https://packagist.org/packages/nateq/sdk">
    nateq/sdk
  </Card>

  <Card title="GitHub" icon="git-branch" href="https://github.com/nateq-ai/sdk-php">
    الكود المصدري والمشكلات
  </Card>
</CardGroup>

## المتطلبات

* PHP الإصدار 8.2 أو أحدث
* الامتدادان `ext-curl` و `ext-json`
* Laravel 11 أو 12 (اختياري — تعمل الحزمة بدونه)

## التثبيت

```bash theme={null}
composer require nateq/sdk
```

في Laravel، يُكتشف مزوّد الخدمة وواجهة `Nateq` تلقائياً. لا شيء تحتاج لتسجيله.

## البدء السريع

أنشئ مفتاح API بنطاق `emails:send`، ثم ضعه في بيئتك:

```bash theme={null}
NATEQ_API_KEY=tg_live_your_key_here
```

```php theme={null}
use Nateq\Sdk\Nateq;

$nateq = new Nateq(); // reads NATEQ_API_KEY

$result = $nateq->emails()->send(
    toEmails: ['customer@example.com'],
    subject: 'Welcome aboard',
    htmlBody: '<p>Thanks for signing up.</p>',
);

echo $result->id;
```

في Laravel، احصل على العميل من الحاوية بدلاً من إنشائه:

```php theme={null}
use Nateq\Sdk\Nateq;

class WelcomeMailer
{
    public function __construct(private Nateq $nateq) {}

    public function send(User $user): void
    {
        $this->nateq->emails()->send(
            toEmails: [$user->email],
            subject: 'Welcome aboard',
            htmlBody: view('mail.welcome', ['user' => $user])->render(),
        );
    }
}
```

## المصادقة

تقرأ الحزمة `NATEQ_API_KEY` افتراضياً. مرّره صراحةً لتحميله من مدير أسرار، أو
للتعامل مع أكثر من مؤسسة:

```php theme={null}
$nateq = new Nateq(apiKey: $secrets->get('nateq_api_key'));
```

يتم التحقق من المفتاح محلياً قبل أي طلب، لذا يفشل المفتاح الخاطئ فوراً بدلاً من
إرساله عبر الشبكة.

| النطاق        | مطلوب لـ           |
| ------------- | ------------------ |
| `emails:send` | `send()`           |
| `emails:read` | `get()` و `list()` |

<Warning>
  تُخفي `var_dump()` و `json_encode()` وصفحات أخطاء أُطر العمل المفتاحَ، كما أن
  محاولة تسلسل (serialize) العميل تُطلق خطأً عن قصد. لكن `print_r()` و
  `var_export()` لا تملكان أي آلية إخفاء في PHP و**ستطبعان** المفتاح — لا تمرّر
  العميل إلى أي منهما.
</Warning>

## إرسال البريد الإلكتروني

تعود `send()` بمجرد قبول الواجهة للرسالة. أما التسليم فيحدث بعد ذلك — العودة
بنجاح تعني أن Nateq تحمّلت مسؤولية الرسالة، لا أنها وصلت.

```php theme={null}
$result = $nateq->emails()->send(
    toEmails: ['a@example.com', 'b@example.com'],
    subject: 'Quarterly update',
    htmlBody: '<p>Hello</p>',
    plainTextBody: 'Hello',
    fromEmail: 'hello@yourdomain.com', // must be a verified address
    fromName: 'Acme Support',
    ccEmails: ['c@example.com'],
    bccEmails: ['d@example.com'],
    replyTo: 'support@yourdomain.com',
    ticketId: '…',
    attachmentIds: ['…'],
    headers: ['X-Campaign' => 'q3'],
);

$result->id;
$result->status; // EmailStatus::Pending
```

قدّم `htmlBody` أو `plainTextBody` أو كليهما. احذف `fromEmail` و
`emailAddressId` لاستخدام عنوان مؤسستك الافتراضي المُوثَّق.

## قراءة البريد الإلكتروني

```php theme={null}
$email = $nateq->emails()->get($result->id);

$email->status;       // EmailStatus::Delivered
$email->openCount;
$email->bounceReason;
```

`EmailStatus` هو تعداد (enum) مزوّد بدوال مساعدة، حتى لا تحفظ أي من الحالات
التسع يُعدّ نجاحاً:

```php theme={null}
if ($email->status->isFailure()) {   // bounced | failed | rejected
    Log::warning('Email did not arrive', ['reason' => $email->bounceReason]);
}

$email->status->isDelivered(); // delivered | opened | clicked
$email->status->isPending();   // pending | sending | sent
```

اعرض قائمة الرسائل المُرسلة، الأحدث أولاً. النتيجة قابلة للعدّ والتكرار:

```php theme={null}
use Nateq\Sdk\Types\EmailStatus;

$page = $nateq->emails()->list(
    status: EmailStatus::Bounced,
    toEmail: 'customer@example.com',
    limit: 25,
);

foreach ($page as $email) {
    echo $email->subject;
}

$page->total;
$page->hasMore();
$page->nextOffset(); // feed straight back into list(offset: …)
```

القيمة الافتراضية لـ `limit` هي 50 والحد الأقصى 100.

<Note>
  تُرجع الواجهة حقولاً أكثر مما تمثّله الحزمة. أي حقل غير مُتاح كخاصية يبقى
  موجوداً في `$email->raw`، لذا يعمل الحقل الجديد دون ترقية الحزمة.
</Note>

## الأخطاء

كل الأخطاء ترث من `NateqException`، لذا تكفي كتلة `catch` واحدة:

```php theme={null}
use Nateq\Sdk\Exceptions\NateqException;
use Nateq\Sdk\Exceptions\PermissionException;
use Nateq\Sdk\Exceptions\RateLimitException;
use Nateq\Sdk\Exceptions\ValidationException;

try {
    $nateq->emails()->send(/* … */);
} catch (ValidationException $e) {
    // 400/422 — the request was wrong. Don't retry it unchanged.
} catch (PermissionException $e) {
    $e->errorCode; // "INSUFFICIENT_SCOPE"
} catch (RateLimitException $e) {
    $e->retryAfter; // seconds
} catch (NateqException $e) {
    $e->status;
    $e->requestId; // quote this to support
}
```

| الاستثناء                 | متى                                       |
| ------------------------- | ----------------------------------------- |
| `ValidationException`     | 400/422، أو وسائط خاطئة تُكتشف قبل أي طلب |
| `AuthenticationException` | 401 — مفتاح مفقود أو غير صالح أو مُلغى    |
| `PermissionException`     | 403 — المفتاح صالح لكنه غير مصرّح له      |
| `NotFoundException`       | 404                                       |
| `RateLimitException`      | 429 — يحمل `retryAfter`                   |
| `ServerException`         | 5xx                                       |
| `TimeoutException`        | تجاوز الطلب المهلة المحددة                |
| `ConnectionException`     | DNS أو TLS أو المقبس أو انقطاع الاتصال    |
| `ConfigurationException`  | إعداد خاطئ للحزمة — يُطلَق قبل أي طلب     |

راجع [الأخطاء](/ar/errors) لرموز الواجهة الأساسية.

## إعادة المحاولة والإرسال المُكرر

تُعاد عمليات القراءة (`get` و `list`) تلقائياً عند انتهاء المهلة أو أخطاء 5xx أو
فشل الاتصال، مع تراجع تدريجي يحترم `Retry-After`.

**أما `send()` فلا.** لا تملك الواجهة مفتاح تكرار (idempotency key)، لذا فإن
عملية إرسال تفشل *بعد* وصولها إلى الخادم قد تكون قد أُرسلت فعلاً. إعادتها قد
تُرسل رسالتين إلى عميلك، والحزمة لن تتخذ هذا القرار نيابةً عنك.

الاستثناء هو **429**، الذي تُطلقه الواجهة قبل إرسال أي شيء — لذا تُعاد المحاولة،
لأن التكرار مستحيل.

إذا أطلقت `send()` خطأ `TimeoutException` أو `ServerException`، فالنتيجة غير
معروفة فعلاً. تحقق منها قبل إعادة المحاولة:

```php theme={null}
use Nateq\Sdk\Exceptions\ServerException;
use Nateq\Sdk\Exceptions\TimeoutException;

try {
    $nateq->emails()->send(toEmails: [$user->email], subject: $subject, htmlBody: $html);
} catch (TimeoutException|ServerException $e) {
    $recent = $nateq->emails()->list(toEmail: $user->email, limit: 5);
    // decide based on what's there
}
```

## Laravel

انشر ملف الإعدادات فقط إذا أردت تعديله:

```bash theme={null}
php artisan vendor:publish --tag=nateq-config
```

```php theme={null}
// config/nateq.php
return [
    'api_key' => env('NATEQ_API_KEY'),
    'base_url' => env('NATEQ_BASE_URL', 'https://api.nateq.io/api/v1'),
    'timeout' => env('NATEQ_TIMEOUT', 30),
    'max_retries' => env('NATEQ_MAX_RETRIES', 2),
];
```

احقن العميل، أو استخدم الواجهة (facade):

```php theme={null}
use Nateq\Sdk\Laravel\Facades\Nateq;

Nateq::emails()->send(
    toEmails: ['customer@example.com'],
    subject: 'Welcome aboard',
    htmlBody: '<p>Thanks for signing up.</p>',
);
```

### الإرسال من مهمة في الطابور

البريد الإلكتروني هو استدعاء شبكي لطرف ثالث. تنفيذه داخل الطلب يربط زمن استجابتك
بزمن استجابتنا.

```php theme={null}
class SendWelcomeEmail implements ShouldQueue
{
    use Queueable;

    public function __construct(public User $user) {}

    // Do not retry blindly: the send may have succeeded before the failure.
    public $tries = 1;

    public function handle(Nateq $nateq): void
    {
        $nateq->emails()->send(
            toEmails: [$this->user->email],
            subject: 'Welcome aboard',
            htmlBody: view('mail.welcome', ['user' => $this->user])->render(),
        );
    }
}
```

<Warning>
  احصل على العميل عبر `handle()` كما في الأعلى — ولا تخزّنه أبداً كخاصية في
  المهمة. تُسلسَل خصائص المهمة داخل حمولة الطابور، والعميل يرفض التسلسل تحديداً
  حتى لا ينتهي مفتاح API الخاص بك مخزّناً في Redis كنص صريح.
</Warning>

إذا غيّرت `NATEQ_API_KEY` ولم يحدث شيء، فلديك إعدادات مُخزّنة مؤقتاً:

```bash theme={null}
php artisan config:clear
```

## الاختبار

لا تستدعِ الواجهة الحقيقية من مجموعة اختباراتك. نفّذ `HttpClient` بنسخة وهمية:

```php theme={null}
use Nateq\Sdk\Http\{HttpClient, Request, Response};

$fake = new class implements HttpClient {
    public array $requests = [];

    public function send(Request $request, float $timeoutSeconds): Response
    {
        $this->requests[] = $request;

        return new Response(201, [], json_encode([
            'id' => 'test-id', 'status' => 'pending', 'createdAt' => 'now',
        ]));
    }
};
```

في Laravel، اربطها في الحاوية وسيلتقطها العميل — بما في ذلك عبر الواجهة (facade):

```php theme={null}
$this->app->instance(HttpClient::class, $fake);

Nateq::emails()->send(toEmails: ['a@example.com'], subject: 'Hi', htmlBody: '<p>Hi</p>');

$this->assertCount(1, $fake->requests);
```

نفس هذه النقطة تتيح توجيه الطلبات عبر منظومتك الخاصة — Guzzle أو Symfony
HttpClient أو وكيل صادر (proxy).
