mahmoud-hamed/laravel-nafith 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

mahmoud-hamed/laravel-nafith

Composer 安装命令:

composer require mahmoud-hamed/laravel-nafith

包简介

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

README 文档

README

GitHub | Packagist | Issues

Laravel Nafith is a Laravel package for integrating with the Nafith 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

  • 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

  • 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

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

Installation

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

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

php artisan migrate

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)

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

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

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

Syncing Status from Nafith API

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

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

Add to routes/console.php:

use Illuminate\Support\Facades\Schedule;

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

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

  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

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

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

use MahmoudHamed\Nafith\Models\NafithSanad;

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

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

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

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

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

./
  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

MIT

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 0
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-07-10

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固