thejano/fib-payment-laravel 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

thejano/fib-payment-laravel

Composer 安装命令:

composer require thejano/fib-payment-laravel

包简介

A Laravel package for FIB online payments using OAuth2 authentication.

README 文档

README

Latest Version on Packagist Tests Total Downloads PHP Version Support License

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 Http client

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 the mbstring extension)
  • Laravel >= 9.0 (illuminate/support and illuminate/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

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-06-27