codetay/bankapi-laravel

Laravel bridge for the BankAPI.VN PHP SDK

Maintainers

Package info

github.com/codetay/bankapi-laravel

pkg:composer/codetay/bankapi-laravel

Transparency log

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-08-27 15:29 UTC

This package is not auto-updated.

Last update: 2026-08-28 01:10:39 UTC


README

Laravel bridge for codetay/bankapi-php: auto-registered service provider, a BankApi facade, and a webhook-verifying middleware.

Install

composer require codetay/bankapi-laravel

The service provider (BankApi\Laravel\BankApiServiceProvider) is auto-discovered. Publish the config file:

php artisan vendor:publish --tag=bankapi-config

This creates config/bankapi.php. Set these environment variables:

BANKAPI_KEY=bk_live_...
BANKAPI_BASE_URL=https://<your-org-slug>.bankapi.vn
BANKAPI_WEBHOOK_SECRET=whsec_...

Set BANKAPI_BASE_URL to your organization host (https://<your-org-slug>.bankapi.vn): org-scoped operations (banking, webhook endpoints) are rejected with a 403 on a non-organization host. BANKAPI_WEBHOOK_SECRET is the secret shown once when you create a webhook endpoint (see the core SDK README) — required only if you use the webhook middleware.

The base URL must be https (plain http only for loopback hosts in local development) — the API key travels on every request, so anything else is refused at boot.

When Guzzle is installed, the package builds its HTTP client with bounded timeouts and redirect following turned off (the API key rides in the X-API-Key header, which Guzzle does not strip when a redirect changes host). Tune the timeouts if your network needs it:

BANKAPI_HTTP_TIMEOUT=10
BANKAPI_HTTP_CONNECT_TIMEOUT=5

Facade usage

use BankApi\Laravel\Facades\BankApi;

$page = BankApi::banking()->transactions(limit: 50);
foreach ($page->autoPaging() as $transaction) {
    // ...
}

$created = BankApi::webhookEndpoints()->create(
    url: 'https://myapp.test/hooks/bankapi',
    eventTypes: ['bank.credit'],
);
// $created->secret (whsec_...) is shown ONLY here — store it now.

The facade resolves the same singleton BankApi\BankApi instance the container builds from config/bankapi.php; you can also type-hint BankApi\BankApi for constructor injection instead of the facade.

Webhook route

Register a route and attach the bankapi.webhook middleware alias. On success it verifies the signature, puts the decoded event on the request as bankapi_event, and dispatches a BankApi\Laravel\Events\BankApiWebhookReceived event; on failure it aborts the request with 400 (bad signature) or 500 (webhook secret not configured).

use BankApi\Webhook\Event;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::post('/webhooks/bankapi', function (Request $request) {
    /** @var Event $event */
    $event = $request->attributes->get('bankapi_event');

    // handled asynchronously via the BankApiWebhookReceived listener below;
    // just acknowledge quickly here.
    return response()->noContent();
})->middleware('bankapi.webhook');

CSRF exclusion

Webhook requests come from BankAPI's servers, not a browser session, so the route must be excluded from CSRF verification:

Laravel 11+ (in bootstrap/app.php):

->withMiddleware(function (Middleware $middleware) {
    $middleware->validateCsrfTokens(except: [
        'webhooks/*',
    ]);
})

Laravel 10 (in app/Http/Middleware/VerifyCsrfToken.php):

protected $except = [
    'webhooks/*',
];

Handling the event

Prefer a queued listener over handling the webhook inline in the route, so BankAPI gets a fast 2xx and retries don't pile up. Dedupe on $event->event->deliveryId — BankAPI retries deliveries on 408/429/5xx responses, so the same delivery can arrive more than once.

namespace App\Listeners;

use BankApi\Laravel\Events\BankApiWebhookReceived;
use Illuminate\Contracts\Queue\ShouldQueue;

final class HandleBankApiWebhook implements ShouldQueue
{
    public function handle(BankApiWebhookReceived $listenerEvent): void
    {
        $event = $listenerEvent->event; // BankApi\Webhook\Event

        if (WebhookDelivery::query()->where('delivery_id', $event->deliveryId)->exists()) {
            return; // already processed
        }

        match ($event->type) {
            'bank.credit' => ProcessCredit::dispatch($event->data),
            default => null,
        };

        WebhookDelivery::create(['delivery_id' => $event->deliveryId]);
    }
}

Register it in EventServiceProvider (or via Event::listen()):

protected $listen = [
    \BankApi\Laravel\Events\BankApiWebhookReceived::class => [
        \App\Listeners\HandleBankApiWebhook::class,
    ],
];

See the core SDK README for the exception hierarchy, pagination, and the raw signature-verification contract.