Search by

rhaima / laravel-flouci

Rhaima96

Laravel package for integrating Flouci payments in Tunisia.

v1.5.0 2026-09-30 12:37 UTC

This package is auto-updated.

Last update: 2026-09-30 12:38:13 UTC


README

Tests Latest Stable Version Total Downloads License

Accept Flouci payments (Tunisia) in Laravel: create payments, verify them, handle the webhook with events, refund, and read the transaction history.

Requirements

  • PHP 8.2+
  • Laravel 12 or 13

Installation

composer require rhaima/laravel-flouci

The service provider and the Flouci facade are auto-discovered.

Configuration

Get your keys from the Flouci dashboard (every developer account has a TEST APP for the sandbox), then set:

FLOUCI_PUBLIC_KEY=
FLOUCI_PRIVATE_KEY=
FLOUCI_SUCCESS_LINK="${APP_URL}/payment/success"
FLOUCI_FAIL_LINK="${APP_URL}/payment/fail"
FLOUCI_WEBHOOK_URL="${APP_URL}/flouci/webhook"

Optionally publish the config file:

php artisan vendor:publish --tag=flouci-config
Option Env Default Description
base_url FLOUCI_BASE_URL https://developers.flouci.com/api API base URL
public_key FLOUCI_PUBLIC_KEY Public key
private_key FLOUCI_PRIVATE_KEY Private key
success_link FLOUCI_SUCCESS_LINK Default redirect after a successful payment
fail_link FLOUCI_FAIL_LINK Default redirect after a failed payment
webhook FLOUCI_WEBHOOK_URL Default webhook sent with each payment
card_payment FLOUCI_CARD_PAYMENT true Default accept_card (Flouci's own default is false)
image_url FLOUCI_IMAGE_URL Default image shown on the payment page
session_timeout FLOUCI_SESSION_TIMEOUT Flouci: 1200 Payment session duration in seconds (session_timeout_secs)
merchant_id FLOUCI_MERCHANT_ID Default merchant_id for transactionHistory()
timeout FLOUCI_TIMEOUT 15 HTTP timeout in seconds

Usage

Create a payment

use Flouci\Laravel\Facades\Flouci;

$payment = Flouci::generatePayment([
    'amount' => 10000,                  // in millimes: 10000 = 10 TND
    'developer_tracking_id' => 'order_1001',
]);

return redirect()->away($payment['result']['link']);

Any Flouci field can be passed per call and overrides the config defaults (success_link, fail_link, webhook, accept_card, image_url, session_timeout_secs).

Verify a payment

Flouci appends payment_id to your success and fail links. Always verify it server-side:

use Flouci\Laravel\Enums\PaymentStatus;

$verification = Flouci::verifyPayment($request->query('payment_id'));
$status = PaymentStatus::fromVerification($verification);

if ($status?->isPaid()) {
    // mark the order as paid
}

PaymentStatus cases: Success, Pending, Expired, Failure, PreauthSuccess, SystemFailure. isFinal() returns false for Pending and PreauthSuccess.

Webhook

Register the route (CSRF protection is removed automatically, so routes/web.php works):

Route::flouciWebhook();                       // GET|POST /flouci/webhook, named flouci.webhook
Route::flouciWebhook('payments/flouci/hook'); // custom URI
Route::flouciWebhook()->middleware('throttle:60,1');

Flouci calls it with GET ?payment_id=...&success=True|False and does not sign the request. The package only trusts the payment_id, reads the real status from verifyPayment(), then dispatches an event:

Flouci status Event
SUCCESS Flouci\Laravel\Events\PaymentSucceeded
FAILURE, SYSTEM_FAILURE Flouci\Laravel\Events\PaymentFailed
EXPIRED Flouci\Laravel\Events\PaymentExpired
PENDING, PREAUTH_SUCCESS none
use Flouci\Laravel\Events\PaymentSucceeded;

Event::listen(function (PaymentSucceeded $event) {
    $order = Order::where('reference', $event->trackingId())->firstOrFail();

    if ($order->amount_millimes !== $event->amount()) {
        return; // unexpected amount: do not mark the order as paid
    }

    $order->markAsPaid($event->paymentId);
});

Each event exposes paymentId, status (PaymentStatus), verification (raw response), trackingId() and amount(). To handle every case in one listener, type-hint the FlouciPaymentEvent interface.

Good to know:

  • Flouci retries the webhook until it gets a 2xx response. A replayed webhook dispatches its event only once (24h cache key): use a shared cache store (Redis, database) in production and keep an idempotency check on your order.
  • If the Flouci API is unreachable during verification, the route answers 5xx so Flouci retries.
  • Without a payment_id, the route answers 422.

Refund

$refund = Flouci::refund($paymentId); // full refund

Only completed, not yet refunded payments can be refunded. A refused refund always throws a FlouciException, even when Flouci answers HTTP 200 with "status": "error".

Transaction history

$history = Flouci::transactionHistory([
    'start_date' => '2026-09-01T00:00:00Z', // ISO-8601 strings
    'end_date' => '2026-09-30T23:59:59Z',
    'type' => 'online',                     // or pos
]);

Parameters are sent as-is to GET /api/developers/history (docs). merchant_id is required: pass it in the query or set FLOUCI_MERCHANT_ID.

Errors

Every failure (HTTP error, timeout, unreadable response) throws a FlouciException:

use Flouci\Laravel\Exceptions\FlouciException;

try {
    $payment = Flouci::generatePayment(['amount' => 10000]);
} catch (FlouciException $e) {
    $e->getCode();                         // HTTP status, 0 on network errors
    $e->response?->json('result.message'); // Flouci response body
}

Testing your app

Fake the Flouci API with Laravel's HTTP client:

Http::fake([
    'developers.flouci.com/api/v2/generate_payment' => Http::response([
        'result' => ['success' => true, 'payment_id' => 'abc', 'link' => 'https://checkout.flouci.com/abc'],
    ]),
]);

Test cards for the Flouci sandbox are listed in the Flouci docs.

AI agents

The package ships a Laravel Boost guideline and a flouci-payments skill, so coding agents (Claude Code, Cursor, Codex...) learn how to integrate Flouci correctly. Run php artisan boost:install (or boost:update --discover if Boost is already installed) after requiring the package.

Contributing

composer test     # Pest
composer lint     # Pint
composer analyse  # Larastan

To try the package against the real Flouci sandbox:

cp workbench/.env.example workbench/.env        # add your TEST APP keys
cloudflared tunnel --url http://localhost:8000  # then set the https URL as APP_URL
vendor/bin/testbench serve --port=8000

Open <APP_URL>/flouci/sandbox and pay with a test card. Webhook calls and dispatched events are logged to vendor/orchestra/testbench-core/laravel/storage/logs/laravel.log.

Security

See SECURITY.md.

License

MIT. See LICENSE.