achetibi/laravel-satim

Laravel package for integrating SATIM online payments.

Maintainers

Package info

github.com/achetibi/laravel-satim

pkg:composer/achetibi/laravel-satim

Transparency log

Statistics

Installs: 89

Dependents: 0

Suggesters: 0

Stars: 15

Open Issues: 0

v2.1.0 2026-07-08 05:50 UTC

This package is auto-updated.

Last update: 2026-07-08 05:55:04 UTC


README

Latest Version on Packagist Total Downloads Tests PHPStan License

Laravel Satim is a clean, strongly-typed Laravel package for SATIM online payments in Algeria — the interbank e-payment gateway behind CIB and Edahabia card payments (paiement en ligne / الدفع الإلكتروني). It covers the full transaction lifecycle — registration, confirmation, status and refund — behind pure request DTOs, immutable response objects, typed value objects and a real exception hierarchy.

Keywords: SATIM Laravel, CIB Laravel, e-paiement Algérie, Algerian payment gateway, carte CIB / Edahabia, DZ.

🚀 Features

  • Simple configuration via .env
  • Register / Confirm / Status / Refund operations
  • Immutable, typed responses — each response owns its own data
  • Typed value objects for nested response structures (card auth info, merchant params…)
  • Real exception hierarchy driven by the official SATIM error codes
  • Config-driven HTTP method — POST by default, as recommended by SATIM
  • Automatic retries for transport failures and 5xx responses
  • Credentials injected into the HTTP client (requests stay pure data-transfer objects)
  • Translations in Arabic, English and French with automatic fallback
  • Analysed at PHPStan level 10 and formatted with Pint (PSR-12)

📦 Requirements

  • PHP 8.3+
  • Laravel 11, 12 or 13

📦 Installation

composer require achetibi/laravel-satim

⚙️ Configuration

Publish the config file:

php artisan vendor:publish --provider="LaravelSatim\SatimServiceProvider" --tag="satim-config"

Optionally publish the translations:

php artisan vendor:publish --provider="LaravelSatim\SatimServiceProvider" --tag="satim-lang"

Add the following variables to your .env:

SATIM_ENV=test
SATIM_USERNAME=your_username
SATIM_PASSWORD=your_password
SATIM_TERMINAL=your_terminal
SATIM_CURRENCY=DZD
SATIM_LANGUAGE=fr

Optional HTTP client settings (defaults shown):

# SATIM strongly recommends POST so credentials are never exposed in URLs/logs.
SATIM_HTTP_METHOD=POST
SATIM_TIMEOUT=30
SATIM_CONNECT_TIMEOUT=10
SATIM_RETRIES=2
SATIM_RETRY_DELAY=300
SATIM_SSL_VERIFY=true

# Optional request logging (endpoint/method/status only, never credentials)
SATIM_LOG=false
SATIM_LOG_CHANNEL=stack

Disable SSL verification in local development only. SATIM_RETRIES / SATIM_RETRY_DELAY control the number of automatic retries and the linear back-off delay (in milliseconds) for transport failures and 5xx responses.

Every configuration option is documented inline in config/satim.php.

🧠 Usage

Resolve the gateway from the container via SatimGatewayInterface, or use the Satim facade — both share the same singleton instance.

1. Register a transaction

use LaravelSatim\Contracts\SatimGatewayInterface;
use LaravelSatim\Http\Requests\SatimRegisterRequest;

$response = app(SatimGatewayInterface::class)->register(new SatimRegisterRequest(
    orderNumber: 'ORD123456',    // alphanumeric, max 10 chars, unique per transaction
    amount: 1500.00,             // amount in DZD (dinars); converted to centimes internally
    returnUrl: route('payment.success'),
    udf1: 'ORD123456',           // required user-defined field
));

// Redirect the customer to the hosted payment page:
return redirect()->away($response->formUrl());

// Keep $response->orderId() to confirm the order later.

For merchants enabled for bill payment, pass the funding type indicator (sent by SATIM inside jsonParams as fundingTypeIndicator):

use LaravelSatim\Enums\FundingType;

new SatimRegisterRequest(
    orderNumber: 'ORD123456',
    amount: 1500.00,
    returnUrl: route('payment.success'),
    udf1: 'ORD123456',
    fundingType: FundingType::BILL_PAYMENT, // "CP" (or FundingType::BILL_PAYMENT_698 for "698")
);

SatimRegisterRequest also accepts the optional udf2udf5, failUrl, description, currency and language arguments. SatimRegisterResponse exposes successful(), errorCode(), errorMessage(), orderId(), formUrl() and raw().

2. Confirm a transaction

After the customer pays and is redirected back to your returnUrl, confirm the order. mdOrder is the gateway order identifier returned by register() as $response->orderId():

use LaravelSatim\Contracts\SatimGatewayInterface;
use LaravelSatim\Http\Requests\SatimConfirmRequest;

$response = app(SatimGatewayInterface::class)->confirm(new SatimConfirmRequest(
    mdOrder: 'BnTjnFDzZSP97QXu8FXq',
));

if ($response->successful()) {
    // Payment captured (OrderStatus::DEPOSITED).
} else {
    // Inspect the outcome — $response->orderStatus(), $response->message().
}

SatimConfirmResponse exposes rich accessors: successful(), orderStatus() (a LaravelSatim\Enums\OrderStatus enum), message(), amount(), depositAmount(), currency(), approvalCode(), authorizationResponseId(), pan(), errorCode(), errorMessage(), raw(), plus params() — a typed LaravelSatim\ValueObjects\ConfirmResponse\Params value object (respCode, respCodeDesc, udf1udf5, extra).

3. Refund a transaction

use LaravelSatim\Contracts\SatimGatewayInterface;
use LaravelSatim\Http\Requests\SatimRefundRequest;

$response = app(SatimGatewayInterface::class)->refund(new SatimRefundRequest(
    orderId: 'BnTjnFDzZSP97QXu8FXq',
    amount: 1500.00,
));

4. Check a transaction status

Fetch the full, extended status of an order at any time (reconciliation, webhooks, retries…) using its gateway orderId (the value returned by register() as $response->orderId()):

use LaravelSatim\Contracts\SatimGatewayInterface;
use LaravelSatim\Http\Requests\SatimStatusRequest;

$response = app(SatimGatewayInterface::class)->status(new SatimStatusRequest(
    orderId: 'ehf9z2yvvThwQ4AACW2G',
));

if ($response->successful()) {
    // Payment captured (OrderStatus::DEPOSITED).
}

SatimStatusResponse exposes successful(), orderStatus(), message(), amount(), currency(), date() and authDateTime() (both ?DateTimeImmutable), ip(), fraudLevel(), terminalId(), authRefNum(), orderNumber(), orderDescription(), errorCode(), errorMessage(), raw(), plus three typed value objects:

$response->merchantOrderParams()->disablePhone;      // ?bool
$response->merchantOrderParams()->transmissionDate;  // ?DateTimeImmutable
$response->merchantOrderParams()->udf1;              // ?string  (also udf2–udf5, forceTerminalId, extra)

$response->cardAuthInfo()->pan;                      // '628058**7215'
$response->cardAuthInfo()->expiration;               // '202701'  (also cardholderName, approvalCode, …)

$response->attributes()->mdOrder;                    // gateway order id  (+ ->extra)

They live under LaravelSatim\ValueObjects\StatusResponse\{MerchantOrderParams, CardAuthInfo, Attributes}. Each keeps any unmodelled field in an extra array, so nothing is ever lost.

Using the facade

use LaravelSatim\Facades\Satim;
use LaravelSatim\Http\Requests\SatimRegisterRequest;

// register(), confirm(), status() and refund() are all available on the facade.
$response = Satim::register(new SatimRegisterRequest(/* ... */));

Overriding language and currency

Language and currency default to your configuration. Override them per request directly on the request DTO:

use LaravelSatim\Enums\Currency;
use LaravelSatim\Enums\Language;
use LaravelSatim\Http\Requests\SatimRegisterRequest;

new SatimRegisterRequest(
    orderNumber: 'ORD123456',
    amount: 1500.00,
    returnUrl: route('payment.success'),
    udf1: 'ORD123456',
    currency: Currency::DZD,
    language: Language::ARABIC,
);

🌍 Translations

The package ships translations for Arabic (ar), English (en) and French (fr). Package texts (error messages and validation messages) follow the active Laravel locale. When the active locale is not one of the three supported locales, the package falls back to the locale configured in satim.defaults.language (and ultimately to English), so end users never see raw translation keys.

⚠️ Error handling

API-level errors (invalid credentials, unknown order, system errors…) raise typed exceptions. A declined card during confirmation is a business outcome, not an exception: confirm() returns a SatimConfirmResponse you inspect with successful() / orderStatus() / message().

use LaravelSatim\Exceptions\SatimAbstractException;
use LaravelSatim\Exceptions\SatimConnectionException;
use LaravelSatim\Exceptions\SatimResponseException;
use LaravelSatim\Exceptions\SatimValidationException;

try {
    $response = app(SatimGatewayInterface::class)->register($request);
} catch (SatimValidationException $e) {
    // Invalid request data (thrown before the request is sent) — $e->errors(), $e->first()
} catch (SatimResponseException $e) {
    // SATIM returned an error code — $e->errorCode, $e->errorMessage
} catch (SatimConnectionException $e) {
    // Network / transport failure — $e->getPrevious()
} catch (SatimAbstractException $e) {
    // Base type: catch-all for any SATIM error
}

Exception hierarchy:

SatimAbstractException (base)
├── SatimConfigurationException   // missing credentials / invalid config
├── SatimValidationException      // request DTO validation (->errors(), ->first(), ->messages())
├── SatimConnectionException      // transport / HTTP failure
├── SatimEncodingException        // failed to JSON-encode jsonParams
└── SatimResponseException        // SATIM returned an error code (->errorCode, ->errorMessage)

✅ Testing

composer test

Runs Pint (PSR-12), PHPStan (level 10) and the Pest unit suite. The suite mirrors the src directory structure under tests/Unit. Continuous integration runs the full matrix of PHP 8.3/8.4 against Laravel 11, 12 and 13, on both the lowest and the latest resolvable dependencies.

📌 Roadmap

  • Register / Confirm / Refund operations
  • Native request validation layer
  • Typed exception mapping from SATIM error codes
  • Config-driven HTTP method and automatic retries
  • Full unit test suite mirroring src
  • Status operation (extended order status)
  • Webhook support

🔒 Security

If you discover any security-related issues, please email chetibi.abderrahim@gmail.com instead of using the issue tracker.

🙏 Credits

📄 License

The MIT License (MIT). See LICENSE.md for full license text.