PHPackages                             elgibor-solution/laravel-payment-bni - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Payment Processing](/categories/payments)
4. /
5. elgibor-solution/laravel-payment-bni

ActiveLibrary[Payment Processing](/categories/payments)

elgibor-solution/laravel-payment-bni
====================================

Laravel package for BNI eCollection (VA) &amp; QRIS with auditing, granular errors, tests &amp; Postman.

1.0.0(4mo ago)0109↓33.3%Apache-2.0PHPPHP ^8.2

Since Dec 23Pushed 4mo agoCompare

[ Source](https://github.com/elgiborsolution/laravel-payment-bni)[ Packagist](https://packagist.org/packages/elgibor-solution/laravel-payment-bni)[ RSS](/packages/elgibor-solution-laravel-payment-bni/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (6)Versions (3)Used By (0)

elgibor-solution/laravel-payment-bni
====================================

[](#elgibor-solutionlaravel-payment-bni)

Namespace: `ESolution\BNIPayment`

Laravel package to integrate **BNI Virtual Account / eCollection** and **BNI QRIS SNAP BI (MPM)** with:

- ✅ VA: create, update, inquiry
- ✅ QRIS SNAP BI (Dynamic QR MPM + Inquiry)
- ✅ Access Token B2B (SNAP)
- ✅ X-SIGNATURE (HMAC / RSA)
- ✅ Webhook payment notification
- ✅ Audit trail ke DB (request/response)
- ✅ Error handling granular (kode BNI)
- ✅ DB Config (override config file)
- ✅ Mirror billing + reconcile (scheduler)
- ✅ Events: `BniPaymentReceived`, `BniBillingPaid`, `BniBillingExpired`
- ✅ Unit tests (Orchestra), Postman collection

---

📦 Installation
--------------

[](#-installation)

```
composer require elgibor-solution/laravel-payment-bni
```

Publish config &amp; migration:

```
php artisan vendor:publish --provider="ESolution\BNIPayment\BNIPaymentServiceProvider"
php artisan vendor:publish --provider="ESolution\BNIPayment\BNIPaymentServiceProvider" --tag=bni-migrations
php artisan migrate
```

---

⚙️ Configuration
----------------

[](#️-configuration)

File utama konfigurasi: **`config/bni.php`**

### ENV minimal

[](#env-minimal)

```
# General BNI
BNI_HOSTNAME=api.bni-ecollection.com
BNI_HOSTNAME_STAGING=apibeta.bni-ecollection.com
BNI_PORT=443
BNI_ORIGIN=https://your-domain.com
BNI_CLIENT_ID=your_bni_client_id
BNI_TIMEOUT=15
BNI_VERIFY_SSL=true
BNI_DEBUG=true        # true = sandbox / staging

# ==== QRIS SNAP / SNAP BI ====
BNI_SNAP_BASE_URL=https://merchant-api.qris-bni.com/apisnap
BNI_SNAP_BASE_URL_STAGING=https://qris-merchant-api.spesandbox.com/apisnap
BNI_SNAP_CLIENT_ID=your_snap_client_id
BNI_SNAP_CLIENT_KEY=your_snap_client_key
BNI_SNAP_CLIENT_SECRET=your_snap_client_secret
BNI_SNAP_PARTNER_ID=your_partner_id
BNI_SNAP_VERSION=v1.0
BNI_SNAP_PRIVATE_KEY_PATH=/full/path/to/private_key.pem
BNI_SNAP_SIGNATURE_TYPE=1   # 1=HMAC+Token, 2=RSA no token

# ==== QRIS Merchant Info ====
BNI_QRIS_MERCHANT_ID=00007100010926
BNI_QRIS_TERMINAL_ID=213141251124
BNI_QRIS_PATH_GENERATE_QR=/v1.0/debit/payment-qr/qr-mpm
BNI_QRIS_PATH_QUERY_PAYMENT=/v1.0/debit/payment-qr/qr-mpm/status
BNI_QRIS_PATH_ACCESS_TOKEN=/access-token/b2b
```

### Potongan `config/bni.php` (versi baru)

[](#potongan-configbniphp-versi-baru)

```
return [
    'hostname' => env('BNI_HOSTNAME', 'api.bni-ecollection.com'),
    'hostname_staging' => env('BNI_HOSTNAME_STAGING', 'apibeta.bni-ecollection.com'),
    'port' => (int) env('BNI_PORT', 443),
    'origin' => env('BNI_ORIGIN', 'your-origin'),
    'client_id' => env('BNI_CLIENT_ID', '320'),
    'timeout' => (int) env('BNI_TIMEOUT', 15),
    'verify_ssl' => (bool) env('BNI_VERIFY_SSL', true),

    'debug' => (bool) env('BNI_DEBUG', false),

    'snap' => [
        'base_url' => env('BNI_SNAP_BASE_URL', 'https://merchant-api.qris-bni.com/apisnap'),
        'base_url_staging' => env('BNI_SNAP_BASE_URL_STAGING', 'https://qris-merchant-api.spesandbox.com/apisnap'),
        'version' => env('BNI_SNAP_VERSION', 'v1.0'),
        'client_id' => env('BNI_SNAP_CLIENT_ID', env('BNI_CLIENT_ID')),
        'client_key' => env('BNI_SNAP_CLIENT_KEY', env('BNI_CLIENT_ID')),
        'client_secret' => env('BNI_SNAP_CLIENT_SECRET', ''),
        'partner_id' => env('BNI_SNAP_PARTNER_ID', ''),
        'private_key_path' => env('BNI_SNAP_PRIVATE_KEY_PATH', storage_path('app/bni/snap_private_key.pem')),
        'public_key_path' => env('BNI_SNAP_PUBLIC_KEY_PATH', storage_path('app/bni/snap_public_key.pem')),
        'signature_type' => (int) env('BNI_SNAP_SIGNATURE_TYPE', 1),
        'timeout' => (int) env('BNI_SNAP_TIMEOUT', env('BNI_TIMEOUT', 15)),
        'verify_ssl' => (bool) env('BNI_SNAP_VERIFY_SSL', env('BNI_VERIFY_SSL', true)),
    ],

    'routes' => [
        'prefix' => env('BNI_ROUTE_PREFIX', ''),
        'middleware' => ['api'],
    ],

    'qris' => [
        'merchant_id' => env('BNI_QRIS_MERCHANT_ID', ''),
        'terminal_id' => env('BNI_QRIS_TERMINAL_ID', ''),
        'path_access_token'  => env('BNI_QRIS_PATH_ACCESS_TOKEN', '/access-token/b2b'),
        'path_generate_qr'   => env('BNI_QRIS_PATH_GENERATE_QR', '/v1.0/debit/payment-qr/qr-mpm'),
        'path_query_payment' => env('BNI_QRIS_PATH_QUERY_PAYMENT', '/v1.0/debit/payment-qr/qr-mpm/status'),
        // fallback lama
        'path_create_dynamic' => '/qris/create',
        'path_inquiry_status' => '/qris/inquiry',
    ],

    'schedule' => [
        'enabled' => env('BNI_SCHEDULE_ENABLED', false),
        'cron' => env('BNI_SCHEDULE_CRON', '*/5 * * * *'),
    ],
];
```

> ⚠️ **Catatan:** Untuk VA / eCollection masih memakai mekanisme lama (BniEnc). SNAP BI khusus untuk channel `qris`.

---

🧱 Arsitektur Singkat
--------------------

[](#-arsitektur-singkat)

ClassFungsi`BaseClient`HTTP client, logger, routing ke host BNI (VA / SNAP)`BniVaClient`Integrasi BNI Virtual Account / eCollection`BniQrisClient`Integrasi BNI QRIS SNAP BI (generate QR &amp; inquiry)`BniSnapAuth`Access Token B2B, X-SIGNATURE (HMAC / RSA)`BniEnc`Enkripsi / dekripsi payload BNI VA`BniPaymentLog`Audit trail request/response`BniBilling`Mirror data billing VA di DBEvent `BniPaymentReceived`Dikirim saat ada webhook paymentEvent `BniBillingPaid`Dikirim saat VA sukses dibayarEvent `BniBillingExpired`Dikirim saat VA kedaluwarsa---

🧾 Penggunaan BNI VA / eCollection
---------------------------------

[](#-penggunaan-bni-va--ecollection)

### 1. Persiapan data kredensial

[](#1-persiapan-data-kredensial)

Untuk VA, kamu akan menerima dari BNI:

- `client_id`
- `secret_key` (kadang disebut `client_secret` atau `api_key` di dokumen lama)
- Prefix billing (optional, tergantung setup)

**Saran:** simpan di `.env` kamu sendiri:

```
BNI_VA_CLIENT_ID=your_va_client_id
BNI_VA_SECRET=your_va_secret_key
BNI_VA_PREFIX=SAAS   # contoh prefix untuk nomor VA
```

Dan di `config/services.php` (opsional):

```
'bni_va' => [
    'client_id' => env('BNI_VA_CLIENT_ID', env('BNI_CLIENT_ID')),
    'secret' => env('BNI_VA_SECRET', ''),
    'prefix' => env('BNI_VA_PREFIX', ''),
],
```

### 2. Membuat VA (Create Billing)

[](#2-membuat-va-create-billing)

```
use ESolution\BNIPayment\Clients\BniVaClient;

$clientId = config('services.bni_va.client_id');   // atau config('bni.client_id')
$secret   = config('services.bni_va.secret');
$prefix   = config('services.bni_va.prefix', '');

$client = app(BniVaClient::class);

$response = $client->createVa([
    'type'           => 'createbilling',
    'client_id'      => $clientId,
    'trx_id'         => 'INV-2025-0001',   // unique per invoice
    'trx_amount'     => '150000',          // tanpa desimal
    'billing_type'   => 'c',               // 'c' = closed / fixed amount
    'customer_name'  => 'PT Pelanggan Makmur',
    'customer_email' => 'finance@pelanggan.com',
    'customer_phone' => '08123456789',
    'datetime_expired' => '2025-12-31 23:59:00',
    'description'    => 'Tagihan langganan SaaS bulan Desember 2025',
], $clientId, $prefix, $secret);
```

Paket ini akan otomatis:

- Meng-enkripsi payload
- Memanggil endpoint BNI eCollection
- Menyimpan / mengupdate data ke tabel `bni_billings` via model `BniBilling`:
    - `trx_id`
    - `virtual_account`
    - `trx_amount`
    - `customer_name` / `customer_email` / `customer_phone`
    - `billing_type`
    - `description`
    - `expired_at`

### 3. Update VA (Update Billing)

[](#3-update-va-update-billing)

```
$response = $client->updateVa([
    'type'           => 'updatebilling',
    'client_id'      => $clientId,
    'trx_id'         => 'INV-2025-0001',
    'trx_amount'     => '200000',
    'customer_name'  => 'PT Pelanggan Makmur',
    'customer_email' => 'finance@pelanggan.com',
    'customer_phone' => '08123456789',
    'billing_type'   => 'c',
    'description'    => 'Update nominal tagihan',
    'datetime_expired' => '2026-01-15 23:59:00',
], $clientId, $prefix, $secret);
```

### 4. Inquiry VA (Cek Status Billing)

[](#4-inquiry-va-cek-status-billing)

```
$response = $client->inquiryVa('INV-2025-0001', $clientId, $prefix, $secret);

// Contoh akses data:
$status   = $response['status'] ?? null;
$paidAmt  = $response['payment_amount'] ?? null;
```

---

🔔 Webhook Payment Notification (VA)
-----------------------------------

[](#-webhook-payment-notification-va)

Paket ini menyediakan route webhook default untuk notifikasi pembayaran BNI VA.

### 1. Route

[](#1-route)

Setelah publish config, route default:

```
POST /bni/va/payment-notification

```

Route ini:

- Memverifikasi dan mendekripsi payload
- Menyimpan log ke `bni_payment_logs`
- Mengupdate status `BniBilling`
- Mem-broadcast event `BniPaymentReceived`

### 2. Contoh Listener

[](#2-contoh-listener)

Daftarkan listener di `EventServiceProvider`:

```
protected $listen = [
    \ESolution\BNIPayment\Events\BniPaymentReceived::class => [
        \App\Listeners\HandleBniPaymentReceived::class,
    ],
];
```

Contoh listener sederhana:

```
namespace App\Listeners;

use ESolution\BNIPayment\Events\BniPaymentReceived;

class HandleBniPaymentReceived
{
    public function handle(BniPaymentReceived $event)
    {
        $payload = $event->payload;

        // contoh: tandai invoice sebagai paid
        $trxId = $payload['trx_id'] ?? null;

        if ($trxId) {
            // update invoice internal kamu di sini
        }
    }
}
```

---

🕒 Scheduler &amp; Reconcile
---------------------------

[](#-scheduler--reconcile)

Aktifkan scheduler di `.env`:

```
BNI_SCHEDULE_ENABLED=true
BNI_SCHEDULE_CRON=*/5 * * * *   # setiap 5 menit
```

Tambahkan ke `app/Console/Kernel.php`:

```
protected function schedule(Schedule $schedule)
{
    if (config('bni.schedule.enabled')) {
        $schedule->command('bni:reconcile')->cron(config('bni.schedule.cron'));
    }
}
```

Jalankan manual:

```
php artisan bni:reconcile
```

---

💳 Penggunaan BNI QRIS SNAP BI (MPM)
-----------------------------------

[](#-penggunaan-bni-qris-snap-bi-mpm)

### 1. Inisialisasi Client

[](#1-inisialisasi-client)

```
use ESolution\BNIPayment\Clients\BniQrisClient;

$qris = new BniQrisClient();
```

### 2. Generate Dynamic QR (MPM)

[](#2-generate-dynamic-qr-mpm)

```
$response = $qris->generateQr([
    'partnerReferenceNo' => 'INV-2025-0001',
    'amount' => [
        'value' => '15000.00',
        'currency' => 'IDR',
    ],
    'merchantId'    => config('bni.qris.merchant_id'),
    'terminalId'    => config('bni.qris.terminal_id'),
    'validityPeriod'=> '2025-12-31T23:59:00+07:00',
    'additionalInfo'=> [
        'additionalData' => 'Tagihan SaaS Desember 2025',
    ],
]);

$qrContent = $response['qrContent'] ?? null;
```

### 3. Inquiry Payment (MPM Query Payment)

[](#3-inquiry-payment-mpm-query-payment)

```
// cukup dengan partnerReferenceNo
$response = $qris->queryPayment('INV-2025-0001');

// atau dengan payload lengkap
$response = $qris->queryPayment([
    'partnerReferenceNo' => 'INV-2025-0001',
]);

$status = $response['responseCode'] ?? null;
```

> `BniQrisClient::createDynamic()` dan `BniQrisClient::inquiryStatus()` masih ada sebagai alias untuk kompatibilitas mundur, namun disarankan pindah ke `generateQr()` dan `queryPayment()`.

---

🔑 Access Token B2B SNAP
-----------------------

[](#-access-token-b2b-snap)

Access Token (`../{version}/access-token/b2b`) diambil otomatis oleh `BaseClient` ketika:

- Channel = `qris`
- `signature_type = 1` (Symmetric Signature with Get Token)

Token disimpan di Laravel Cache.

Jika ingin ambil manual:

```
use ESolution\BNIPayment\Services\BniSnapAuth;

$token = BniSnapAuth::getAccessToken();
```

---

🔐 X-SIGNATURE (HMAC / RSA)
--------------------------

[](#-x-signature-hmac--rsa)

Implementasi mengikuti dokumen **MPM – BNI QR ACQUIRING MERCHANT API SNAP BI v1.5.5**.

### Signature Type 1 – Symmetric (HMAC SHA512)

[](#signature-type-1--symmetric-hmac-sha512)

```
stringToSign =
  HTTPMethod + ":" +
  EndpointUrl + ":" +
  AccessToken + ":" +
  Lowercase(HexEncode(SHA-256(minify(RequestBody)))) + ":" +
  TimeStamp

```

Header:

- `X-SIGNATURE = HMAC_SHA512(clientSecret, stringToSign)`
- `Authorization: Bearer {accessToken}`

### Signature Type 2 – Asymmetric (RSA SHA256)

[](#signature-type-2--asymmetric-rsa-sha256)

```
stringToSign =
  HTTPMethod + ":" +
  EndpointUrl + ":" +
  Lowercase(HexEncode(SHA-256(minify(RequestBody)))) + ":" +
  TimeStamp

```

Header:

- `X-SIGNATURE = base64(RSA-SHA256(privateKey, stringToSign))`

Paket ini menangani detail tersebut secara otomatis lewat `BniSnapAuth::buildRequestSignature()` dan `BaseClient::snapRequest()`.

---

🧪 Testing
---------

[](#-testing)

```
php artisan test
```

Atau dari Tinker:

```
php artisan tinker

>>> app(ESolution\BNIPayment\Clients\BniVaClient::class)->createVa([...]);
>>> app(ESolution\BNIPayment\Clients\BniQrisClient::class)->generateQr([...]);
```

---

🤝 Contributing
--------------

[](#-contributing)

Pull request dan issue sangat diterima.

1. Fork repository
2. Buat branch feature: `git checkout -b feature/nama-feature`
3. Commit &amp; push
4. Buka Pull Request

---

📄 License
---------

[](#-license)

Apache 2.0

---

🧑‍💻 Maintainer
--------------

[](#‍-maintainer)

**PT Elgibor Solusi Digital**

###  Health Score

38

—

LowBetter than 85% of packages

Maintenance74

Regular maintenance activity

Popularity12

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 89.5% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

141d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/f5e7899407946e1ed48940ae3b5fea2477eacc7ec98e9f53122e2d97718b7087?d=identicon)[esolution](/maintainers/esolution)

---

Top Contributors

[![elgibor-solution](https://avatars.githubusercontent.com/u/783039?v=4)](https://github.com/elgibor-solution "elgibor-solution (17 commits)")[![bayuelgibor](https://avatars.githubusercontent.com/u/162445024?v=4)](https://github.com/bayuelgibor "bayuelgibor (2 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/elgibor-solution-laravel-payment-bni/health.svg)

```
[![Health](https://phpackages.com/badges/elgibor-solution-laravel-payment-bni/health.svg)](https://phpackages.com/packages/elgibor-solution-laravel-payment-bni)
```

###  Alternatives

[laravel/socialite

Laravel wrapper around OAuth 1 &amp; OAuth 2 libraries.

5.7k96.9M674](/packages/laravel-socialite)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9682.1M97](/packages/roots-acorn)[laravel/cashier-paddle

Cashier Paddle provides an expressive, fluent interface to Paddle's subscription billing services.

264778.4k3](/packages/laravel-cashier-paddle)[spatie/laravel-export

Create a static site bundle from a Laravel app

646127.9k5](/packages/spatie-laravel-export)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

245.2k](/packages/aedart-athenaeum)[musahmusah/laravel-multipayment-gateways

A Laravel Package that makes implementation of multiple payment Gateways endpoints and webhooks seamless

852.2k1](/packages/musahmusah-laravel-multipayment-gateways)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
