PHPackages                             mahmoud-hamed/laravel-nafith - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. mahmoud-hamed/laravel-nafith

ActiveLibrary[API Development](/categories/api)

mahmoud-hamed/laravel-nafith
============================

Laravel package for Nafith API integration, sanad creation, webhook verification, and status sync

v1.0.2(1mo ago)01MITPHPPHP ^8.2CI passing

Since Jul 10Pushed 1mo agoCompare

[ Source](https://github.com/mahmoud-hamed/laravel-nafith)[ Packagist](https://packagist.org/packages/mahmoud-hamed/laravel-nafith)[ Docs](https://github.com/mahmoud-hamed/laravel-nafith)[ RSS](/packages/mahmoud-hamed-laravel-nafith/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (9)Versions (3)Used By (0)

Laravel Nafith
==============

[](#laravel-nafith)

[GitHub](https://github.com/mahmoud-hamed/laravel-nafith) | [Packagist](https://packagist.org/packages/mahmoud-hamed/laravel-nafith) | [Issues](https://github.com/mahmoud-hamed/laravel-nafith/issues)

Laravel Nafith is a Laravel package for integrating with the [Nafith](https://nafith.sa/) Electronic Promissory Note (Sanad) platform.

This Nafith Laravel package handles OAuth2 authentication, sanad creation, sanad closing, status sync, and HMAC-SHA256 webhook verification. It also provides a database-backed tracking model that works with **any** Eloquent model, including models that use string or UUID primary keys.

Features
--------

[](#features)

- Laravel package for Nafith API integration
- Nafith sanad creation and closing workflows
- Signed webhook verification for Nafith callbacks
- Status sync command for pending and approved sanads
- Database tracking with polymorphic Eloquent relations
- Encrypted storage for sensitive debtor and payload data

Why This Package
----------------

[](#why-this-package)

- Speeds up Nafith integration in Laravel 11 and Laravel 12 apps
- Keeps webhook handling and signature verification in one place
- Gives you a reusable Nafith service layer instead of one-off API code
- Works well for Saudi fintech and promissory note workflows

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 11.x | 12.x
- A Nafith merchant account (sandbox or production)

Installation
------------

[](#installation)

### 1. Require the package

[](#1-require-the-package)

```
# From Packagist
composer require mahmoud-hamed/laravel-nafith

# From a local path (development)
composer require mahmoud-hamed/laravel-nafith --repositories='[{"type":"path","url":"../laravel-nafith"}]'
```

Laravel's auto-discovery registers the service provider automatically.

### 2. Publish config and migration

[](#2-publish-config-and-migration)

```
php artisan vendor:publish --tag=nafith-config
php artisan vendor:publish --tag=nafith-migrations
```

This creates:

- `config/nafith.php` -- all API credentials and settings
- `database/migrations/xxxx_create_nafith_sanads_table.php` -- the tracking table

### 3. Run the migration

[](#3-run-the-migration)

```
php artisan migrate
```

### 4. Add environment variables

[](#4-add-environment-variables)

Add these to your `.env`:

```
NAFITH_BASE_URL=https://sandbox.nafith.sa/
NAFITH_CLIENT_ID=your-client-id
NAFITH_CLIENT_SECRET=your-client-secret
NAFITH_SIGN_SECRET=your-sign-secret
NAFITH_SCOPE=read write
NAFITH_SIGNATURE_HOST=nafith.sa
NAFITH_TOKEN_CACHE_KEY=nafith_access_token
NAFITH_TOKEN_TTL=3500
NAFITH_CITY_OF_ISSUANCE=1
NAFITH_CITY_OF_PAYMENT=1
NAFITH_APPROVAL_WINDOW_HOURS=6
NAFITH_MAX_APPROVE_DURATION=360
NAFITH_CURRENCY=SAR
NAFITH_TIMEOUT=30
NAFITH_RETRY_TIMES=1
NAFITH_RETRY_SLEEP=500
NAFITH_WEBHOOK_URI=nafith/callback
NAFITH_WEBHOOK_PREFIX=
NAFITH_LOG_SENSITIVE_DATA=false

```

### 5. Add log channels (recommended)

[](#5-add-log-channels-recommended)

Add these to your `config/logging.php` under the `'channels'` array:

```
'nafith' => [
    'driver' => 'daily',
    'path'   => storage_path('logs/nafith.log'),
    'level'  => env('LOG_NAFITH_LEVEL', 'info'),
    'days'   => 14,
],
'nafith_webhook' => [
    'driver' => 'daily',
    'path'   => storage_path('logs/nafith-webhook.log'),
    'level'  => env('LOG_NAFITH_WEBHOOK_LEVEL', 'info'),
    'days'   => 14,
],
```

Sensitive payload data is redacted from logs by default. Only enable `NAFITH_LOG_SENSITIVE_DATA=true` for short-lived local debugging.

Quick Start
-----------

[](#quick-start)

### Creating a Sanad

[](#creating-a-sanad)

```
use MahmoudHamed\Nafith\Services\NafithService;

$nafithService = app(NafithService::class);

$sanad = $nafithService->createSanad([
    'debtor_national_id' => '1000000001',
    'debtor_phone'       => '+966500000000',
    'debtor_name'        => 'Ahmad Ali',
    'amount'             => 30000.00,
    'currency'           => 'SAR',
    'reference_id'       => 'ORDER-12345',
    'nafithable_type'    => App\Models\Order::class,
    'nafithable_id'      => 12345,
]);

// $sanad is a MahmoudHamed\Nafith\Models\NafithSanad instance
echo $sanad->sanad_number;  // e.g. "1010062627658168"
echo $sanad->status;         // "pending"
```

### Closing a Sanad

[](#closing-a-sanad)

```
$nafithService->closeSanad($sanad);
// Automatically sends CLOSED if approved, CANCELLED_BY_CREDITOR otherwise
```

### Syncing Status from Nafith API

[](#syncing-status-from-nafith-api)

```
$freshSanad = $nafithService->syncStatus($sanad);
// Returns updated model if status changed, null if unchanged
```

### Running the Artisan Command

[](#running-the-artisan-command)

```
# Sync all pending/approved sanads from the Nafith API
php artisan nafith:sync-statuses

# Process in smaller chunks
php artisan nafith:sync-statuses --chunk=50
```

### Scheduling the Sync Command

[](#scheduling-the-sync-command)

Add to `routes/console.php`:

```
use Illuminate\Support\Facades\Schedule;

Schedule::command('nafith:sync-statuses')->everyFiveMinutes();
```

Webhook Setup
-------------

[](#webhook-setup)

The package automatically registers a webhook endpoint at:

```
POST {your-domain}/nafith/callback

```

Configure the URI and middleware in `config/nafith.php`:

```
'webhook_uri'       => 'nafith/callback',  // The path
'webhook_prefix'    => '',                  // Optional prefix (e.g. 'api')
'webhook_middleware' => ['throttle:60,1'],  // Public endpoint protection
```

### Webhook Flow

[](#webhook-flow)

1. Nafith sends a POST to your webhook URL
2. The package verifies the HMAC-SHA256 signature
3. Parses the payload and normalizes the status
4. Dispatches `NafithWebhookReceived` event
5. The default listener updates the `NafithSanad` record in the database

### Listening for Webhooks

[](#listening-for-webhooks)

The default listener handles database updates automatically. To add custom behavior (e.g., send notifications), listen to the event:

```
use MahmoudHamed\Nafith\Events\NafithWebhookReceived;

// In your EventServiceProvider or via Event::listen()
Event::listen(NafithWebhookReceived::class, function (NafithWebhookReceived $event) {
    $data = $event->data;

    // $data['status']        -- normalized status (e.g. 'approved')
    // $data['sanad_number']  -- the Sanad number
    // $data['group_id']      -- the Sanad group ID
    // $data['reference_id']  -- your reference ID
    // $data['payload']       -- the full raw webhook payload

    if ($data['status'] === 'approved') {
        // Send notification, update order status, etc.
    }
});
```

Using the NafithSanad Model
---------------------------

[](#using-the-nafithsanad-model)

The `NafithSanad` model uses a polymorphic `nafithable` relation, so it works with **any** Eloquent model.

Sensitive debtor fields and stored payload snapshots are encrypted at rest and hidden from JSON serialization by default.

### Adding the Relationship to Your Model

[](#adding-the-relationship-to-your-model)

```
use MahmoudHamed\Nafith\Models\NafithSanad;

class Order extends Model
{
    public function nafithSanads()
    {
        return $this->morphMany(NafithSanad::class, 'nafithable');
    }
}
```

### Querying Sanads

[](#querying-sanads)

```
use MahmoudHamed\Nafith\Models\NafithSanad;
use MahmoudHamed\Nafith\NafithStatus;

// All pending sanads
$sanads = NafithSanad::pending()->get();

// All expired pending sanads (past approval window)
$sanads = NafithSanad::expiredPending()->get();

// Sanads for a specific model
$sanads = NafithSanad::forModel(App\Models\Order::class, $orderId)->get();

// Check status
$sanad->isNafithApproved();  // true/false
$sanad->isNafithPending();   // true/false
$sanad->isExpired();         // true/false
$sanad->canClose();          // true/false
```

### Getting the Parent Model

[](#getting-the-parent-model)

```
$sanad = NafithSanad::find(1);
$parentModel = $sanad->nafithable; // Returns the related Order, Bond, etc.
```

Using the Low-Level API Client
------------------------------

[](#using-the-low-level-api-client)

If you need direct API access without the service layer:

```
use MahmoudHamed\Nafith\Nafith;

$nafith = app(Nafith::class);

// Create a Sanad
$response = $nafith->createSingleSanad([
    'debtor' => ['national_id' => '1000000001'],
    'city_of_issuance' => '1',
    'debtor_phone_number' => '+966500000000',
    'total_value' => 30000,
    'currency' => 'SAR',
    'max_approve_duration' => 360,
    'reference_id' => 'MY-REF-123',
    'sanad' => [
        ['due_type' => 'upon request', 'total_value' => 30000, 'reference_id' => 'SANAD-1'],
    ],
]);

// Close a Sanad
$response = $nafith->closeSanad('group-id-123', 'closed');

// Get status
$response = $nafith->getSanadStatus('group-id-123');

// Verify a webhook signature
$valid = $nafith->verifyWebhook($signature, $timestamp, $rawBody, $path);

// Normalize any status string
$normalized = $nafith->normalizeStatus('accept'); // returns 'approved'
```

NafithStatus Enum
-----------------

[](#nafithstatus-enum)

```
use MahmoudHamed\Nafith\NafithStatus;

NafithStatus::PENDING;                // 'pending'
NafithStatus::APPROVED;               // 'approved'
NafithStatus::REJECTED_BY_DEBTOR;     // 'rejected_by_debtor'
NafithStatus::NO_RESPONSE;            // 'no_response'
NafithStatus::CANCELLED_BY_CREDITOR;  // 'cancelled_by_creditor'
NafithStatus::CLOSED;                 // 'closed'

// Helpers
NafithStatus::PENDING->label();      // English label
NafithStatus::PENDING->labelAr();    // Arabic label
NafithStatus::PENDING->color();      // Bootstrap color
NafithStatus::PENDING->isActive();   // true (pending/approved are active)
NafithStatus::PENDING->isFinal();    // false
NafithStatus::PENDING->canApprove(); // true
NafithStatus::PENDING->canReject();  // true
NafithStatus::PENDING->canCancel();  // true

// From string
NafithStatus::fromString('approved'); // NafithStatus::APPROVED
NafithStatus::fromString(null);       // null
```

Architecture
------------

[](#architecture)

```
./
  src/
    Nafith.php                    -- Core API client (OAuth, HTTP, signatures)
    NafithException.php           -- Exception with HTTP status + body
    NafithStatus.php              -- Backed string enum
    NafithServiceProvider.php     -- Package bootstrap
    Models/NafithSanad.php        -- Polymorphic tracking model
    Services/NafithService.php    -- High-level lifecycle service
    Http/Controllers/
      NafithCallbackController.php -- Webhook endpoint
    Console/Commands/
      SyncNafithStatuses.php       -- Artisan sync command
    Events/
      NafithWebhookReceived.php    -- Webhook event
  config/nafith.php               -- Publishable config
  database/migrations/
    create_nafith_sanads_table.php -- Publishable migration
  routes/webhook.php              -- Auto-registered route

```

License
-------

[](#license)

MIT

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance90

Actively maintained with recent releases

Popularity1

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

48d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/330648348ce1c2d5df257edae086377901172afce6a6a5a563860b7bb478646a?d=identicon)[mahmoud-hamed](/maintainers/mahmoud-hamed)

---

Top Contributors

[![mahmoud-hamed](https://avatars.githubusercontent.com/u/61250628?v=4)](https://github.com/mahmoud-hamed "mahmoud-hamed (6 commits)")

---

Tags

laravellaravel-packagewebhookfintechsaudi-arabianafithsanadpromissory-notenafith-apielectronic-promissory-note

### Embed Badge

![Health badge](/badges/mahmoud-hamed-laravel-nafith/health.svg)

```
[![Health](https://phpackages.com/badges/mahmoud-hamed-laravel-nafith/health.svg)](https://phpackages.com/packages/mahmoud-hamed-laravel-nafith)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.4M354](/packages/psalm-plugin-laravel)[api-platform/laravel

API Platform support for Laravel

58190.1k21](/packages/api-platform-laravel)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9922.4M148](/packages/roots-acorn)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k16.3M159](/packages/laravel-pulse)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

265.2k](/packages/aedart-athenaeum)[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.5k31.8M163](/packages/laravel-cashier)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
