thejano/fib-payment-laravel
Composer 安装命令:
composer require thejano/fib-payment-laravel
包简介
A Laravel package for FIB online payments using OAuth2 authentication.
README 文档
README
A Laravel package for integrating with the First Iraqi Bank (FIB) online-shop payment API. Supports payment creation, status checks, cancellation, refunds, and typed error handling via OAuth2 client-credentials authentication.
Features
- OAuth2 authentication (client-credentials grant, automatic per-request)
- Create a payment with configurable amount, description, and options
- Check payment status (PAID / UNPAID / DECLINED)
- Cancel a payment
- Refund a paid payment
- Receive status-change callbacks (webhooks) from FIB
- Retrieve a raw access token
- Typed exceptions (
FibPaymentException) on any 4xx/5xx response - First-class testability — facade or dependency-injected services, all driven by Laravel's
Httpclient
Feature ↔ API coverage
| Feature | Method | FIB endpoint |
|---|---|---|
| Authenticate | FibPayment::getToken() |
POST /auth/realms/fib-online-shop/protocol/openid-connect/token |
| Create payment | FibPayment::create() |
POST /protected/v1/payments |
| Check status | FibPayment::status() |
GET /protected/v1/payments/{id}/status |
| Cancel payment | FibPayment::cancel() |
POST /protected/v1/payments/{id}/cancel |
| Refund payment | FibPayment::refund() |
POST /protected/v1/payments/{id}/refund |
| Status callback | (your route) | FIB POSTs to FIB_PAYMENT_CALLBACK_URL |
Requirements
- PHP
>= 8.1(with thembstringextension) - Laravel
>= 9.0(illuminate/supportandilluminate/http) - FIB online-shop credentials (client ID + secret)
The CI suite runs against Laravel 11, 12, and 13 on PHP 8.3 and 8.4 (see
.github/workflows/tests.yml).
Installation
Install the package via Composer:
composer require thejano/fib-payment-laravel
Publish the config file:
php artisan vendor:publish --tag="fib-payment"
Configuration
Add the following variables to your .env file:
FIB_PAYMENT_ENV=stage FIB_PAYMENT_DEV_URL=https://fib.dev.fib.iq FIB_PAYMENT_STAGE_URL=https://fib.stage.fib.iq FIB_PAYMENT_PROD_URL=https://fib.prod.fib.iq FIB_PAYMENT_CLIENT_ID=your-client-id FIB_PAYMENT_CLIENT_SECRET=your-client-secret FIB_PAYMENT_CALLBACK_URL=https://your-app.com/fib/callback FIB_PAYMENT_CURRENCY=IQD
| Variable | Required | Default | Description |
|---|---|---|---|
FIB_PAYMENT_ENV |
Yes | stage |
Active environment: dev, stage, or production |
FIB_PAYMENT_DEV_URL |
No | https://fib.dev.fib.iq |
Base URL for the dev environment |
FIB_PAYMENT_STAGE_URL |
No | https://fib.stage.fib.iq |
Base URL for the stage environment |
FIB_PAYMENT_PROD_URL |
No | https://fib.prod.fib.iq |
Base URL for the production environment |
FIB_PAYMENT_CLIENT_ID |
Yes | — | OAuth2 client ID issued by FIB |
FIB_PAYMENT_CLIENT_SECRET |
Yes | — | OAuth2 client secret issued by FIB |
FIB_PAYMENT_CALLBACK_URL |
Yes | — | URL FIB will POST to when a payment status changes |
FIB_PAYMENT_CURRENCY |
No | IQD |
Default currency for payments |
Usage
All operations are accessed through the FibPayment facade.
use TheJano\FibPayment\Facades\FibPayment;
Lifecycle: you create() a payment and present its QR code / app links to the customer.
The payment starts as UNPAID; once the customer pays it becomes PAID, and FIB calls your
callback URL. A payment can be cancel()ed while unpaid, or refund()ed after it is paid
(within the refundable window). At any point you can poll status() for the authoritative state.
Create a Payment
use TheJano\FibPayment\Facades\FibPayment; $payment = FibPayment::create(1000, 'Order #5', [ 'expiresIn' => 'PT12H', 'refundableFor' => 'P7D', 'category' => 'ECOMMERCE', 'redirectUri' => 'https://your-app.com/order/5', ]);
Response structure:
{
"paymentId": "string",
"readableCode": "string",
"qrCode": "base64 string",
"validUntil": "2024-01-01T12:00:00Z",
"personalAppLink": "https://...",
"businessAppLink": "https://...",
"corporateAppLink": "https://..."
}
| Field | Description |
|---|---|
paymentId |
Unique payment identifier — use this for status checks, cancellation, and refunds |
readableCode |
Human-readable code the customer can enter manually if they cannot scan the QR code |
qrCode |
Base64-encoded data URL of the QR code image for scanning with the FIB mobile app |
validUntil |
ISO-8601 datetime when the payment expires |
personalAppLink |
Deep link to the payment screen in the FIB Personal app |
businessAppLink |
Deep link to the payment screen in the FIB Business app |
corporateAppLink |
Deep link to the payment screen in the FIB Corporate app |
Supported $options keys
| Key | Type | Description |
|---|---|---|
currency |
string | Currency code (overrides FIB_PAYMENT_CURRENCY), e.g. IQD |
callbackUrl |
string | Per-payment callback URL (overrides FIB_PAYMENT_CALLBACK_URL) |
expiresIn |
string | ISO-8601 duration for payment expiry, e.g. PT12H (12 hours) |
refundableFor |
string | ISO-8601 duration during which the payment can be refunded, e.g. P7D (7 days) |
category |
string | Payment category, e.g. ECOMMERCE |
redirectUri |
string | URI the FIB app redirects to after the customer completes payment |
Description is automatically trimmed to 50 characters (multibyte-safe).
Check Payment Status
use TheJano\FibPayment\Facades\FibPayment; $status = FibPayment::status('payment-uuid');
Response structure:
{
"paymentId": "string",
"status": "PAID",
"validUntil": "2024-01-01T12:00:00Z",
"amount": {
"amount": 1000,
"currency": "IQD"
},
"decliningReason": null,
"declinedAt": null,
"paidBy": {
"name": "string",
"iban": "string"
}
}
| Field | Description |
|---|---|
paymentId |
The payment's unique identifier |
status |
One of PAID, UNPAID, or DECLINED |
validUntil |
ISO-8601 datetime when the payment expires |
amount |
Object with amount (number) and currency (string) |
decliningReason |
null, or one of SERVER_FAILURE, PAYMENT_EXPIRATION, PAYMENT_CANCELLATION |
declinedAt |
ISO-8601 datetime of decline, or null |
paidBy |
null while unpaid; object with name and iban of the payer once paid |
Cancel a Payment
use TheJano\FibPayment\Facades\FibPayment; $result = FibPayment::cancel('payment-uuid'); // Returns [] on success
Refund a Payment
Only PAID payments within the configured refundable window can be refunded.
use TheJano\FibPayment\Facades\FibPayment; $result = FibPayment::refund('payment-uuid'); // Returns [] on success
Retrieve an Access Token
use TheJano\FibPayment\Facades\FibPayment; $token = FibPayment::getToken(); // Returns the raw OAuth2 access token string
Handling Status-Change Callbacks (Webhooks)
When a payment's status changes, FIB sends a POST request to your FIB_PAYMENT_CALLBACK_URL
(or the per-payment callbackUrl option). The request body contains two fields:
{
"id": "payment-uuid",
"status": "PAID"
}
This package does not register a route for you — you own the endpoint so you can apply your own middleware, validation, and queueing. The recommended pattern is to treat the callback as a trigger and re-fetch the authoritative status from FIB rather than trusting the payload:
// routes/web.php (exclude from CSRF protection — it is a server-to-server POST) use Illuminate\Support\Facades\Route; use App\Http\Controllers\FibCallbackController; Route::post('/fib/callback', FibCallbackController::class);
namespace App\Http\Controllers; use Illuminate\Http\Request; use TheJano\FibPayment\Facades\FibPayment; use TheJano\FibPayment\Exceptions\FibPaymentException; class FibCallbackController extends Controller { public function __invoke(Request $request) { $paymentId = $request->input('id'); try { // Re-verify against FIB instead of trusting the callback body. $payment = FibPayment::status($paymentId); } catch (FibPaymentException $e) { report($e); return response()->noContent(500); } if (($payment['status'] ?? null) === 'PAID') { // Fulfil the order, mark it paid, etc. } return response()->noContent(); } }
Remember to add the callback path to the CSRF exception list (VerifyCsrfToken::$except
in Laravel 9–10, or $middleware->validateCsrfTokens(except: [...]) in Laravel 11+).
Using the Services Directly (Dependency Injection)
The facade is a thin wrapper over two container-resolvable services. If you prefer constructor
injection — for example, to keep classes explicit and easy to test in isolation — resolve
PaymentService and AuthService directly:
use TheJano\FibPayment\Services\PaymentService; class CheckoutService { public function __construct(private PaymentService $payments) {} public function startPayment(int $amount, string $description): array { return $this->payments->create($amount, $description); } }
PaymentService exposes the same create(), status(), cancel(), and refund() methods as
the facade; AuthService::getAccessToken() returns the raw token. Both are bound through the
container, so Laravel autowires them anywhere dependency injection is available.
Error Handling
All methods throw TheJano\FibPayment\Exceptions\FibPaymentException on any 4xx or 5xx response. Use getStatusCode() to retrieve the HTTP status and getResponseBody() to inspect the API error payload.
use TheJano\FibPayment\Facades\FibPayment; use TheJano\FibPayment\Exceptions\FibPaymentException; try { $payment = FibPayment::create(1000, 'Order #5'); } catch (FibPaymentException $e) { $httpStatus = $e->getStatusCode(); // int, e.g. 400 or 422 $body = $e->getResponseBody(); // mixed — the decoded JSON response body $message = $e->getMessage(); // string — 'FIB payment request failed.' // Log or handle accordingly \Log::error('FIB payment failed', [ 'status' => $httpStatus, 'body' => $body, ]); }
Error-handling caveats
Network failures: FibPaymentException is thrown when the FIB API returns a response (4xx/5xx). A low-level network failure (DNS error, connection timeout, etc.) surfaces instead as Laravel's Illuminate\Http\Client\ConnectionException, which callers should also catch if needed.
No token caching: The OAuth2 access token is never cached or stored. Every
create, status, cancel, refund, or getToken call performs a fresh
client-credentials authentication against FIB.
Instance caching: The FibPayment facade resolves a singleton whose AuthService and PaymentService objects are constructed once per process (config is read at construction time). This caches the service instances only — not the token. It is not designed to pick up runtime config changes within a long-lived worker such as Laravel Octane. Re-deploy or restart the worker to apply config changes.
Testing
Run the package's own suite:
composer test
The test suite uses Pest and Orchestra Testbench.
Testing your integration
Because every request goes through Laravel's Http client, you can fake FIB entirely in your
own application's tests — no network calls, no credentials:
use Illuminate\Support\Facades\Http; use TheJano\FibPayment\Facades\FibPayment; Http::fake([ '*/auth/realms/fib-online-shop/protocol/openid-connect/token' => Http::response([ 'access_token' => 'fake-token', ]), '*/protected/v1/payments' => Http::response([ 'paymentId' => 'pay_123', 'readableCode' => 'ABC123', ]), ]); $payment = FibPayment::create(1000, 'Test order'); expect($payment['paymentId'])->toBe('pay_123');
License
The thejano/fib-payment-laravel package is open-source software licensed under the MIT License.
fib-payment-laravel
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 0
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-27