PHPackages                             froshly/parakit - 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. froshly/parakit

ActiveLibrary[Payment Processing](/categories/payments)

froshly/parakit
===============

The payment kit for Kurdistan and Iraq — Laravel-native.

v0.9.3(2w ago)138↓100%MITPHPPHP ^8.2CI passing

Since May 15Pushed 2w agoCompare

[ Source](https://github.com/ShahramMebashar/parakit)[ Packagist](https://packagist.org/packages/froshly/parakit)[ RSS](/packages/froshly-parakit/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (4)Dependencies (11)Versions (15)Used By (0)

Parakit
=======

[](#parakit)

> پارەکیت — The payment kit for Kurdistan and Iraq, Laravel-native.

[![CI](https://github.com/ShahramMebashar/parakit/actions/workflows/ci.yml/badge.svg)](https://github.com/ShahramMebashar/parakit/actions/workflows/ci.yml)[![License: MIT](https://camo.githubusercontent.com/fdf2982b9f5d7489dcf44570e714e3a15fce6253e0cc6b5aa61a075aac2ff71b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d79656c6c6f772e737667)](LICENSE)

Take payments from Iraqi and Kurdish customers in a Laravel app — **FIB**, **ZainCash**, **Nass Pay**, **Nass Wallet**, **FastPay**, **QiCard** — with idempotent webhooks, retry &amp; circuit-breaker, redacted logging, a sweeper for lost webhooks, and three localised UIs (en/ar/ckb) out of the box.

`composer require` → first sandbox charge in under 15 minutes. That's the goal.

---

Contents
--------

[](#contents)

- [Install](#install)
- [Charge a customer](#charge-a-customer)
- [Receive a webhook](#receive-a-webhook)
- [What you get for free](#what-you-get-for-free)
- [Operations](#operations)
- [Receipts](#receipts)
- [Multi-tenant / multiple merchants](#multi-tenant--multiple-merchants)
    - [Credentials in a database — `resolveMerchantUsing()`](#credentials-in-a-database--resolvemerchantusing)
- [Laravel Octane](#laravel-octane)
- [Security](#security)
- [Versioning](#versioning)
- [License](#license)

---

Install
-------

[](#install)

```
composer require froshly/parakit
php artisan parakit:install
```

Add credentials to your `.env`:

```
PARAKIT_DEFAULT=fib

FIB_BASE_URL=https://fib.stage.fib.iq
FIB_CLIENT_ID=your-fib-client-id
FIB_CLIENT_SECRET=your-fib-client-secret
FIB_CALLBACK_URL=https://yourapp.com/payments/webhooks/fib

ZAINCASH_BASE_URL=https://pg-api-uat.zaincash.iq
ZAINCASH_CLIENT_ID=your-client-id
ZAINCASH_CLIENT_SECRET=your-client-secret
ZAINCASH_API_KEY=your-api-key
ZAINCASH_SUCCESS_URL=https://yourapp.com/payments/zaincash/return
ZAINCASH_FAILURE_URL=https://yourapp.com/payments/zaincash/failed

NASS_BASE_URL=https://uat-gateway.nass.iq:9746
NASS_USERNAME=your-nass-username
NASS_PASSWORD=your-nass-password
NASS_CALLBACK_URL=https://yourapp.com/payments/webhooks/nass
NASS_RETURN_URL=https://yourapp.com/payments/nass/return

NASSWALLET_BASE_URL=https://uatgw1.nasswallet.com/payment/transaction
NASSWALLET_PORTAL_URL=https://uatcheckout1.nasswallet.com
NASSWALLET_BASIC_TOKEN=your-nasswallet-basic-token
NASSWALLET_USERNAME=your-nasswallet-username
NASSWALLET_PASSWORD=your-nasswallet-password
NASSWALLET_TRANSACTION_PIN=your-transaction-pin
NASSWALLET_CALLBACK_URL=https://yourapp.com/payments/webhooks/nasswallet

FASTPAY_BASE_URL=https://staging-pgw.fast-pay.iq
FASTPAY_STORE_ID=your-store-id
FASTPAY_STORE_PASSWORD=your-store-password
FASTPAY_REFUND_SECRET_KEY=your-refund-secret-key
FASTPAY_SUCCESS_URL=https://yourapp.com/payments/fastpay/return
FASTPAY_CANCEL_URL=https://yourapp.com/payments/fastpay/cancel
FASTPAY_CALLBACK_URL=https://yourapp.com/payments/webhooks/fastpay

QICARD_BASE_URL=https://uat-sandbox-3ds-api.qi.iq
QICARD_USERNAME=your-qicard-username
QICARD_PASSWORD=your-qicard-password
QICARD_TERMINAL_ID=your-terminal-id
QICARD_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
QICARD_FINISH_PAYMENT_URL=https://yourapp.com/payments/qicard/finish
QICARD_NOTIFICATION_URL=https://yourapp.com/payments/webhooks/qicard
```

Verify everything is wired:

```
php artisan parakit:doctor --gateway=fib
php artisan parakit:transactions:test-charge fib --amount=1000
```

---

Charge a customer
-----------------

[](#charge-a-customer)

### Fluent builder (recommended)

[](#fluent-builder-recommended)

```
use Froshly\Parakit\Facades\Payment;
use Froshly\Parakit\Enums\Currency;

$response = Payment::for($order)
    ->driver('fib')
    ->amount(5000, Currency::IQD)
    ->description("Order #{$order->id}")
    ->idempotencyKey($order->id)
    ->charge();

if ($response->failed()) {
    return back()->with('error', $response->error->message(app()->getLocale()));
}

// FIB returns a QR + readable code + deep link.
return view('checkout.fib', [
    'qrCode'       => $response->qrCode,
    'readableCode' => $response->readableCode,
    'deepLink'     => $response->deepLink,
]);
```

### Explicit DTO

[](#explicit-dto)

```
use Froshly\Parakit\DTOs\PaymentRequest;

$response = Payment::driver('zaincash')->charge(new PaymentRequest(
    reference: $order->id,
    amount: 5000,
    currency: Currency::IQD,
    description: 'Order #' . $order->id,
));

return redirect()->away($response->redirectUrl);
```

---

Receive a webhook
-----------------

[](#receive-a-webhook)

Parakit registers `POST /payments/webhooks/{gateway}` automatically. Listen for lifecycle events anywhere in your app:

```
use Froshly\Parakit\Events\PaymentSucceeded;

Event::listen(PaymentSucceeded::class, function ($event) {
    Order::find($event->transaction->reference)->markPaid();
});
```

Other events: `PaymentInitiated`, `PaymentFailed`, `PaymentCancelled`, `PaymentRefunded`, `WebhookReceived`, `WebhookVerificationFailed`, `GatewayTimeout`, `CircuitOpened`.

---

What you get for free
---------------------

[](#what-you-get-for-free)

ConcernMechanism**Idempotency**`charge()` with the same `idempotencyKey` returns a cached response for 24h. DB unique index on `(gateway, event_id)` makes webhook delivery race-safe.**Retries**Exponential backoff + jitter on transient gateway failures. Never on 4xx, never without an idempotency key.**Circuit breaker**Per-gateway. Fails fast after N failures, auto-recovers after cooldown.**State machine**Illegal status transitions (e.g. `Paid → Pending`) throw and are logged — no double-fulfillment.**Webhook replay protection**Rejects webhooks older than `parakit.webhooks.tolerance_seconds`.**Lost-webhook recovery**`parakit:transactions:sweep-pending` polls status for stale pending transactions every 5 minutes.**Redacted logging**Request/response written to `payment_logs` with secrets redacted by configured key names + Luhn-gated PAN regex.**Correlation IDs**One ULID traces a payment across `charge → webhook → status → refund`.**Translations**en / ar / ckb shipped; publish with `php artisan vendor:publish --tag=parakit-lang`.---

Operations
----------

[](#operations)

CommandPurpose`parakit:install`Publishes config + migrations, runs `migrate`.`parakit:doctor [--gateway=fib]`Verifies config + connectivity. Non-zero exit on failure.`parakit:transactions:test-charge fib --amount=1000`Sandbox roundtrip. Proves end-to-end works.`parakit:transactions:sweep-pending`Polls status for pending transactions to recover lost webhooks. Auto-scheduled every 5 min.`parakit:webhooks:simulate fib --transaction-id=pid_1 --status=paid`POSTs a correctly-formed test webhook to your local app.`parakit:logs:prune --days=90`Trims `payment_logs` per retention policy. Auto-scheduled daily.`parakit:receipts:preview --all`Renders sample receipts (every template/type/locale) to disk for previewing designs. `--format=html|pdf`.---

Receipts
--------

[](#receipts)

Turn any transaction into a branded PDF receipt — no gateway involved, it reads the stored `payment_transactions` row.

```
use Froshly\Parakit\Facades\Receipt;

$transaction = PaymentTransaction::where('reference', $order->id)->firstOrFail();

return Receipt::for($transaction)
    ->template('modern')                 // modern (default) · classic · minimal
    ->customer([
        'name'  => $order->customer_name, // gateways rarely return these —
        'email' => $order->customer_email,// pass what your app knows
    ])
    ->download();                         // → PDF download response
```

`generate()` returns a receipt document you deliver however you like:

```
$pdf = Receipt::for($transaction)->generate();

$pdf->stream();              // inline PDF response
$pdf->download();            // attachment response
$pdf->save('s3');            // persist to a disk, returns the path
$bytes = $pdf->raw();        // raw PDF bytes
```

- **Refund receipts** — `Receipt::for($transaction)->asRefund()->generate()`.
- **Templates** — three production-grade designs, selectable per call or via `parakit.receipts.template`. Publish them to customise: `php artisan vendor:publish --tag=parakit-views`.
- **Localised** — renders in en / ar / ckb, RTL-aware, money formatted per currency. Locale resolves from `->locale()`, transaction metadata, then config.
- **Customer details** — pass a `CustomerDetails` DTO or an array; anything you omit falls back to the transaction's `metadata`.

Iterating on a design? Render samples to disk with no real payment needed:

```
php artisan parakit:receipts:preview --template=modern --locale=ckb   # one
php artisan parakit:receipts:preview --all --format=html              # the lot
```

`--format=html` is fastest for layout work (open in a browser); `--format=pdf`shows true dompdf fidelity.

Emailing a receipt is intentionally left to your application — Parakit produces the PDF, your app decides how it reaches the customer.

---

Multi-tenant / multiple merchants
---------------------------------

[](#multi-tenant--multiple-merchants)

Need FIB credentials for merchant A and a different set for merchant B? Add as many named gateway configs as you need — same driver, different keys:

```
// config/parakit.php
'gateways' => [
    'fib_main' => [
        'driver'        => 'fib',
        'base_url'      => env('FIB_MAIN_BASE_URL'),
        'client_id'     => env('FIB_MAIN_CLIENT_ID'),
        'client_secret' => env('FIB_MAIN_CLIENT_SECRET'),
        'callback_url'  => env('FIB_MAIN_CALLBACK_URL'),
    ],
    'fib_branch' => [
        'driver'        => 'fib',
        'base_url'      => env('FIB_BRANCH_BASE_URL'),
        'client_id'     => env('FIB_BRANCH_CLIENT_ID'),
        'client_secret' => env('FIB_BRANCH_CLIENT_SECRET'),
        'callback_url'  => env('FIB_BRANCH_CALLBACK_URL'),
    ],
    'zaincash_merchant_b' => [
        'driver'      => 'zaincash',
        'base_url'    => env('ZC_B_BASE_URL'),
        'merchant_id' => env('ZC_B_MERCHANT_ID'),
        'msisdn'      => env('ZC_B_MSISDN'),
        'secret'      => env('ZC_B_SECRET'),
        'redirect_url'=> env('ZC_B_REDIRECT_URL'),
    ],
],
```

Then select the right one per request:

```
// Resolve at runtime — driven by your tenant lookup, user attribute, etc.
$gateway = $merchant->gateway_key; // e.g. "fib_branch"

$response = Payment::for($order)
    ->driver($gateway)
    ->amount(5000, Currency::IQD)
    ->description("Order #{$order->id}")
    ->idempotencyKey($order->id)
    ->charge();
```

Each named config is fully isolated: its own circuit-breaker state, idempotency cache namespace, webhook route (`POST /payments/webhooks/fib_branch`), and `gateway` column in `payment_transactions`. Two configs sharing the same driver never bleed into each other.

### Credentials in a database — `resolveMerchantUsing()`

[](#credentials-in-a-database--resolvemerchantusing)

When merchants self-onboard and their credentials live in your database — not in `config/parakit.php` — register a resolver once in a service provider. Parakit calls it with the gateway name and expects the same config array shape as a static entry:

```
// app/Providers/AppServiceProvider.php — boot()
use Froshly\Parakit\Facades\Payment;

Payment::resolveMerchantUsing(function (string $gateway): array {
    $merchant = app(TenantManager::class)->current();

    return $merchant->gatewayConfig($gateway); // ['driver' => 'fib', 'base_url' => ..., ...]
});
```

After that, nothing else changes — `Payment::for($order)->driver('fib')->charge()` routes through your resolver. The resolver receives the gateway name, so one tenant can have any number of gateways (`fib`, `zaincash`, `fib_vip`, …), each resolved independently.

> Store credentials **encrypted** (or as secret-manager references) and decrypt inside the resolver — don't keep raw secrets in plain DB columns.

---

Laravel Octane
--------------

[](#laravel-octane)

Parakit is Octane-safe out of the box — no extra setup, just run `octane:start`:

- **Correlation IDs** — `AssignCorrelationId` runs `CorrelationId::reset()` in its `terminate()` hook, so the per-request ULID is scrubbed from the container before the next request lands on the same worker.
- **Resolved gateways** — `PaymentManager` memoises instantiated drivers for the life of a request. Parakit flushes that cache automatically after every request (on `RequestHandled`, plus Octane's `RequestTerminated`), so a resolver returning per-tenant credentials never leaks one tenant's gateway into the next request on a reused worker.

If you reconfigure gateways manually mid-request, you can still call `app('parakit.manager')->flushResolved()` yourself.

---

Security
--------

[](#security)

See [SECURITY.md](SECURITY.md). TL;DR: webhook signatures are mandatory, comparisons are constant-time, TLS verification is always on, secrets are redacted before they touch the DB.

Report vulnerabilities privately via **GitHub Security Advisories** on this repository (*Security* tab → *Report a vulnerability*).

---

Versioning
----------

[](#versioning)

Semantic versioning. v0.1 is alpha — public API may still shift before v1.0. Subsequent releases will lock the API and follow `^11.0 || ^12.0` for Laravel.

See [CHANGELOG.md](CHANGELOG.md) for the full history.

---

License
-------

[](#license)

MIT. See [LICENSE](LICENSE).

###  Health Score

42

—

FairBetter than 88% of packages

Maintenance97

Actively maintained with recent releases

Popularity13

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity43

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 ~1 days

Total

12

Last Release

16d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/24a06f23ed335cd9eaeb5005340f4ab448b594d2678c835f3bbd716e97f747fa?d=identicon)[ShahramMebashar](/maintainers/ShahramMebashar)

---

Top Contributors

[![ShahramMebashar](https://avatars.githubusercontent.com/u/25268287?v=4)](https://github.com/ShahramMebashar "ShahramMebashar (96 commits)")

###  Code Quality

TestsPest

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/froshly-parakit/health.svg)

```
[![Health](https://phpackages.com/badges/froshly-parakit/health.svg)](https://phpackages.com/packages/froshly-parakit)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3325.1M337](/packages/psalm-plugin-laravel)[larastan/larastan

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

6.4k51.0M7.4k](/packages/larastan-larastan)[laravel/passport

Laravel Passport provides OAuth2 server support to Laravel.

3.4k89.4M569](/packages/laravel-passport)[pressbooks/pressbooks

Pressbooks is an open source book publishing tool built on a WordPress multisite platform. Pressbooks outputs books in multiple formats, including PDF, EPUB, web, and a variety of XML flavours, using a theming/templating system, driven by CSS.

45344.0k1](/packages/pressbooks-pressbooks)[api-platform/laravel

API Platform support for Laravel

59156.3k10](/packages/api-platform-laravel)[simplestats-io/laravel-client

Analytics for Laravel. Track visitors, registrations, and payments. Discover which channels actually drive revenue, not just traffic. Server-side, GDPR compliant, ad-blocker proof.

5019.3k](/packages/simplestats-io-laravel-client)

PHPackages © 2026

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