PHPackages                             nizaamomer/laravel-fib - 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. [Payment Processing](/categories/payments)
4. /
5. nizaamomer/laravel-fib

ActiveLibrary[Payment Processing](/categories/payments)

nizaamomer/laravel-fib
======================

Modern Laravel SDK for First Iraqi Bank (FIB): online payments and payouts, with typed DTOs, multi-account support, and webhook-safe status verification.

v1.0.0(today)00MITPHPPHP ^8.2CI passing

Since Jul 1Pushed todayCompare

[ Source](https://github.com/nizaamomer/laravel-fib)[ Packagist](https://packagist.org/packages/nizaamomer/laravel-fib)[ Docs](https://github.com/nizaamomer/laravel-fib)[ RSS](/packages/nizaamomer-laravel-fib/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (11)Versions (2)Used By (0)

Laravel FIB Payment &amp; Payout SDK
====================================

[](#laravel-fib-payment--payout-sdk)

[![Latest Version on Packagist](https://camo.githubusercontent.com/70562698a3bd797e672f6f8181cfd2daa926b72690a5ac38bbe373ea1a6da242/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6e697a61616d6f6d65722f6c61726176656c2d6669622e7376673f7374796c653d666c61742d737175617265266c6162656c3d5061636b616769737426636f6c6f723d6f72616e6765)](https://packagist.org/packages/nizaamomer/laravel-fib)[![Tests](https://camo.githubusercontent.com/c111d734e4fcceff1ba79f0e6099d478e4a8ca1be1da396e0bf81d1116762ebb/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6e697a61616d6f6d65722f6c61726176656c2d6669622f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d5465737473267374796c653d666c61742d737175617265)](https://github.com/nizaamomer/laravel-fib/actions)[![Total Downloads](https://camo.githubusercontent.com/7bc49c3520d4d6046e9d25247966fcead19f737d273af1efd46842292a07279b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6e697a61616d6f6d65722f6c61726176656c2d6669622e7376673f7374796c653d666c61742d737175617265266c6162656c3d446f776e6c6f61647326636f6c6f723d626c7565)](https://packagist.org/packages/nizaamomer/laravel-fib)[![PHP Version](https://camo.githubusercontent.com/52a25a5fbd5fa7e75c7461131be48211dd5b2b6c9f7d85135dc461edbe361985/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f6e697a61616d6f6d65722f6c61726176656c2d6669622e7376673f7374796c653d666c61742d737175617265266c6162656c3d50485026636f6c6f723d373737626234)](https://packagist.org/packages/nizaamomer/laravel-fib)[![Laravel Version](https://camo.githubusercontent.com/11e569685816c7320414be3556c600e4ffed85d6d93837a325f017cb479df7eb/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c2d3131253230253743253230313225323025374325323031332d6666326432303f7374796c653d666c61742d737175617265)](https://laravel.com)[![License](https://camo.githubusercontent.com/4649e4c0b0f331ebf1267d0f4cb38dd548def79b6440f1cc3f01c2425cf9e366/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6e697a61616d6f6d65722f6c61726176656c2d6669622e7376673f7374796c653d666c61742d73717561726526636f6c6f723d73756363657373)](LICENSE.md)

A modern Laravel SDK for [First Iraqi Bank (FIB)](https://fib.iq) — **payments**, **payouts**, and **refunds** in one package, with typed DTOs, enums, multi-account support, automatic status persistence, and a webhook-safe verification flow.

Built by [Nizaam Omer](https://nizaamomer.com) — [nizaamomer.com](https://nizaamomer.com)

Table of Contents
-----------------

[](#table-of-contents)

- [Why one package?](#why-one-package)
- [Requirements](#requirements)
- [Installation](#installation)
- [Payments](#payments)
    - [Creating a payment](#creating-a-payment)
    - [Checking payment status](#checking-payment-status)
    - [Cancelling a payment](#cancelling-a-payment)
    - [Refunding a payment](#refunding-a-payment)
    - [Handling the payment callback](#handling-the-payment-callback--never-trust-the-webhook-body)
    - [Missed webhooks: the sync command](#missed-webhooks-the-sync-command)
- [Payouts](#payouts)
- [Automatic persistence](#automatic-persistence)
- [Full example](#full-example)
- [Security](#security)
- [Testing](#testing)
- [FIB API Reference](#fib-api-reference)
- [Changelog](#changelog)
- [Author](#author)
- [License](#license)

Why one package?
----------------

[](#why-one-package)

Payments, payouts, and refunds all share the same OAuth2 client-credentials flow, the same `base_url`, and the same per-account credentials — splitting them into separate packages would mean authenticating multiple times and configuring everything twice. `FibPayment` handles money coming **in** (and refunds going back out), `FibPayout` handles money going **out**, both backed by one shared, cached OAuth token per account.

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

[](#requirements)

- PHP 8.2+
- Laravel 11.x, 12.x or 13.x

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

[](#installation)

```
composer require nizaamomer/laravel-fib
```

Publish the config file:

```
php artisan vendor:publish --tag="fib-config"
```

Run the migrations — creates `fib_payments`, `fib_payouts` and `fib_refunds`; every payment, payout, and refund is persisted automatically, no manual tracking code needed:

```
php artisan migrate
```

Add your FIB credentials to `.env`:

```
FIB_DEFAULT_ACCOUNT=default                    # which "accounts" entry in config/fib.php to use by default
FIB_BASE_URL=https://fib.stage.fib.iq          # stage (test). Production is https://fib.prod.fib.iq
FIB_CLIENT_ID=your-client-id                   # provided by FIB
FIB_CLIENT_SECRET=your-client-secret           # provided by FIB — keep this out of version control
FIB_CURRENCY=IQD                               # default currency for payments and payouts
FIB_CALLBACK_URL=https://your-app.test/fib/callback  # FIB POSTs payment status changes here
FIB_REFUNDABLE_FOR=P7D                         # optional, ISO-8601 duration — how long a payment stays refundable, defaults to P7D
```

Payments
--------

[](#payments)

### Creating a payment

[](#creating-a-payment)

```
use Nizaamomer\LaravelFib\Facades\FibPayment;

$payment = FibPayment::create(
    amount: 500.00,
    description: 'Order #1042',
    // callbackUrl and currency are optional — they fall back to
    // FIB_CALLBACK_URL and FIB_CURRENCY automatically when omitted
);

$payment->paymentId;      // string
$payment->readableCode;   // e.g. "S3LE-NZ2S-ZNGF"
$payment->qrCode;         // base64 data URL
$payment->businessAppLink;
$payment->validUntil;     // CarbonImmutable
```

A few more optional arguments are available when you need them:

```
use Nizaamomer\LaravelFib\Enums\Payments\PaymentCategory;

FibPayment::create(
    amount: 500.00,
    redirectUri: 'https://your-app.test/orders/1042', // where FIB redirects the user after paying/cancelling
    expiresIn: 'PT8H6M12.345S',                        // ISO-8601 duration — expire the payment after this long
    category: PaymentCategory::Ecommerce,              // defaults to UNKNOWN on FIB's side if omitted
);
```

Or resolve the contract instead of using the facade:

```
use Nizaamomer\LaravelFib\Contracts\Payments\FibPaymentServiceContract;

public function __construct(private FibPaymentServiceContract $payments) {}
```

### Checking payment status

[](#checking-payment-status)

```
$status = FibPayment::status($payment->paymentId);

$status->status;         // PaymentStatus::Paid | Unpaid | Declined | RefundRequested | Refunded
$status->isPaid();       // bool
$status->isRefundable(); // bool, based on FIB_REFUNDABLE_FOR
$status->amount;         // float
$status->paidAt;         // ?CarbonImmutable
```

### Cancelling a payment

[](#cancelling-a-payment)

```
FibPayment::cancel($payment->paymentId);
```

### Refunding a payment

[](#refunding-a-payment)

Only payments with `PAID` status, within their refundable window, can be refunded.

```
$refund = FibPayment::refund($payment->paymentId);

$refund->isSuccessful(); // bool — true only on HTTP 202
$refund->status;         // RefundStatus::Success | Failed
$refund->traceId;        // ?string, present on failure — quote this to FIB support
$refund->errorCodes;     // ?array, present on failure
```

After accepting a refund (202), FIB moves the payment through `REFUND_REQUESTED` and then `REFUNDED` — call `FibPayment::status()` again (or let `fib:sync-statuses` do it) to see the final state. A `PaymentRefundRequested` event fires either way and is persisted to `fib_refunds`, linked back to the `fib_payments` row.

### Handling the payment callback — never trust the webhook body

[](#handling-the-payment-callback--never-trust-the-webhook-body)

FIB's webhook payload is **not signed**. Anyone who learns or guesses your callback URL could POST a fake `"status": "PAID"` body. Always treat the callback as a *trigger to re-check*, never as the source of truth. FIB expects your handler to respond with `202`, and will retry up to 5 times if it doesn't get a response:

```
Route::post('/fib/callback', function (\Illuminate\Http\Request $request) {
    // Re-fetch the authoritative status from FIB — do not trust $request->status
    $status = FibPayment::status($request->input('id'));

    if ($status->isPaid()) {
        // fulfil the order
    }

    return response()->noContent(202);
})->name('fib.callback')->withoutMiddleware([\Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class])
  ->middleware('throttle:60,1');
```

The route needs CSRF exempted (FIB won't send a CSRF token) and should be throttled, since it's a public unauthenticated endpoint.

### Missed webhooks: the sync command

[](#missed-webhooks-the-sync-command)

Payments can be marked `UNPAID` in your database if a webhook is delayed, dropped, or never configured. Run:

```
php artisan fib:sync-statuses
```

to re-check every pending payment (and pending payout — see below) directly against the FIB API. Schedule it in `bootstrap/app.php`:

```
->withSchedule(function (Illuminate\Console\Scheduling\Schedule $schedule) {
    $schedule->command('fib:sync-statuses')->everyFiveMinutes();
})
```

Payouts
-------

[](#payouts)

Payouts move money **out** of your FIB account to a recipient's IBAN. This is a two-step flow: create, then authorize.

```
use Nizaamomer\LaravelFib\Facades\FibPayout;

$payout = FibPayout::create(
    amount: 1000,
    targetAccountIban: 'IQ23FIQB004073628710001',
    description: 'Vendor payment',
    // currency is optional — falls back to FIB_CURRENCY automatically
);

$payout->payoutId;
```

> **⚠️ Authorizing a payout releases real funds immediately.** Gate calls to `authorize()` behind your own application-level approval step (e.g. a second admin confirmation) — this SDK does not add artificial friction here, that responsibility belongs to your app's authorization policy.

```
FibPayout::authorize($payout->payoutId);

$details = FibPayout::details($payout->payoutId);
$details->status;         // PayoutStatus::Created | Authorized | Failed
$details->isAuthorized();
$details->isFailed();
$details->failureReason;
```

Payouts have **no webhook** — the only way to learn a payout's final status is to call `details()` again, or rely on `php artisan fib:sync-statuses`.

Automatic persistence
---------------------

[](#automatic-persistence)

Every `create()`, status/details, and refund call fires an event (`PaymentCreated`, `PaymentStatusUpdated`, `PaymentRefundRequested`, `PayoutCreated`, `PayoutStatusUpdated`) that this package listens to and upserts into `fib_payments`, `fib_payouts`, and `fib_refunds` automatically — no manual tracking code required.

### Linking a payment to your own model

[](#linking-a-payment-to-your-own-model)

The `payable` relation is optional and one-directional from our side: `fib_payments`/`fib_payouts` have nullable `payable_type`/`payable_id` columns with no real foreign-key constraint, so your app's tables never need to know this package exists.

Add the reverse relation on your own model (no migration needed on your side — the columns already live on our tables):

```
// app/Models/Order.php
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Nizaamomer\LaravelFib\Models\FibPayment;

class Order extends Model
{
    public function fibPayment(): MorphOne
    {
        return $this->morphOne(FibPayment::class, 'payable');
    }
}
```

Then associate it right after creating the payment — our `PaymentCreated` event fires synchronously, so the `fib_payments` row already exists by the time `create()` returns:

```
use Nizaamomer\LaravelFib\Models\FibPayment;

$payment = FibPayment::create(amount: $order->total, description: "Order #{$order->id}");

FibPayment::where('payment_id', $payment->paymentId)->first()
    ?->payable()->associate($order)->save();
```

From then on, read it back from either side:

```
$order->fibPayment->status;   // straight off your own model
$fibPayment->payable;          // the Order instance, straight off ours
```

### Listening to events yourself

[](#listening-to-events-yourself)

```
Event::listen(\Nizaamomer\LaravelFib\Events\Payments\PaymentStatusUpdated::class, function ($event) {
    // $event->status, $event->account
});
```

Full example
------------

[](#full-example)

[`docs/examples/PaymentController.php`](docs/examples/PaymentController.php) is a complete, heavily-commented controller covering every public method in the package — creating and checking a payment, cancelling, refunding, the webhook callback, and the payout create → authorize → details flow. It's illustrative (not autoloaded), so copy what you need into your own app.

Security
--------

[](#security)

- **TLS verification is never disabled.** This SDK does not expose a way to set Guzzle's `verify => false` — if you're migrating from an older internal integration that disabled certificate verification, remove that, it defeats HTTPS's protection against man-in-the-middle attacks.
- **Webhook payloads are not trusted.** `FibPayment::status()` always calls FIB directly; use it inside your callback handler instead of reading `status` off the request body (see above).
- **IDs are URL-encoded** before being interpolated into request paths, and **IBANs are format-validated** before a payout is created, so malformed or malicious input from your own callers can't corrupt the outgoing request.
- **Amounts must be greater than zero** — validated before any request is sent.
- **Credentials live in `.env`,** never in version control. Rotate `FIB_CLIENT_SECRET` immediately if it's ever exposed.
- **Tokens are cached, never logged.** The auth token is stored in your configured cache store, scoped per account, and is never written to logs or exceptions.

If you discover a security issue, please email  instead of using the public issue tracker.

Testing
-------

[](#testing)

```
composer test        # Pest
composer analyse      # Larastan / PHPStan (level 8)
composer format        # Laravel Pint
```

FIB API Reference
-----------------

[](#fib-api-reference)

See the [FIB Online Payments documentation](https://fib.iq/all-integrations/) for the underlying REST API this SDK wraps.

Changelog
---------

[](#changelog)

See [CHANGELOG.md](CHANGELOG.md) for what's changed in each release.

Author
------

[](#author)

**Nizaam Omer** — [nizaamomer.com](https://nizaamomer.com) ·

License
-------

[](#license)

MIT. See [LICENSE.md](LICENSE.md).

###  Health Score

39

—

LowBetter than 85% of packages

Maintenance100

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity45

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

Unknown

Total

1

Last Release

0d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/98692798?v=4)[Nizam Omer](/maintainers/nizaamomer)[@nizaamomer](https://github.com/nizaamomer)

---

Top Contributors

[![nizaamomer](https://avatars.githubusercontent.com/u/98692798?v=4)](https://github.com/nizaamomer "nizaamomer (7 commits)")

---

Tags

fibfib-paymentfib-payoutfirst-iraqi-bankiraqlaravellaravel-packagelaravel-paymentlaravel-payoutpaymentpayoutphprefundsdklaravelsdkpaymentpayoutFIBiraqfirst-iraqi-bank

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/nizaamomer-laravel-fib/health.svg)

```
[![Health](https://phpackages.com/badges/nizaamomer-laravel-fib/health.svg)](https://phpackages.com/packages/nizaamomer-laravel-fib)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3355.3M345](/packages/psalm-plugin-laravel)[larastan/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

6.5k55.4M8.1k](/packages/larastan-larastan)[laravel/ai

The official AI SDK for Laravel.

1.0k3.2M184](/packages/laravel-ai)[laravel/pulse

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

1.7k15.1M129](/packages/laravel-pulse)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

77022.3M143](/packages/laravel-mcp)[spatie/laravel-health

Monitor the health of a Laravel application

87512.0M159](/packages/spatie-laravel-health)

PHPackages © 2026

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