PHPackages                             gigoshkin/bog-payments-php - 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. gigoshkin/bog-payments-php

ActiveLibrary

gigoshkin/bog-payments-php
==========================

Bank of Georgia Payments API client library for PHP 8.2+

v1.0.0(yesterday)00MITPHPPHP ^8.2CI passing

Since Apr 6Pushed yesterdayCompare

[ Source](https://github.com/gigoshkin/bog-payments-php)[ Packagist](https://packagist.org/packages/gigoshkin/bog-payments-php)[ RSS](/packages/gigoshkin-bog-payments-php/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (8)Versions (2)Used By (0)

BOG Payments
============

[](#bog-payments)

[![CI](https://github.com/gigoshkin/bog-payments-php/actions/workflows/ci.yml/badge.svg)](https://github.com/gigoshkin/bog-payments-php/actions/workflows/ci.yml)[![PHP](https://camo.githubusercontent.com/d939ed2787af03e76ef8d8b6cd0ffa17e5da11b7772994ae6ac95f50cb146d5f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e322532422d626c75653f6c6f676f3d706870)](https://www.php.net)[![License: MIT](https://camo.githubusercontent.com/784362b26e4b3546254f1893e778ba64616e362bd6ac791991d2c9e880a3a64e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e2e737667)](LICENSE)[![PHPUnit](https://camo.githubusercontent.com/b49dbf0775bd1e9515626b301e4d6fc41da14dfa9f22d4fa0c70dddc557fb8e3/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f746573746564253230776974682d504850556e697425323031312d627269676874677265656e)](https://phpunit.de)[![PSR-18](https://camo.githubusercontent.com/09987a09b8b673564474c7a19a84649887cddd537ec9481cc435308e77326c4f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5053522d3138253230636f6d70617469626c652d626c7565)](https://www.php-fig.org/psr/psr-18/)[![codecov](https://camo.githubusercontent.com/5eb8c27d36d7ae03d8ab453b2928a9c821a9cf156c078f8ddad9c5c0eb0e99ff/68747470733a2f2f636f6465636f762e696f2f6769746875622f6769676f73686b696e2f626f672d7061796d656e74732d7068702f67726170682f62616467652e7376673f746f6b656e3d51565a384e374a32334a)](https://codecov.io/github/gigoshkin/bog-payments-php)[![Mutation testing badge](https://camo.githubusercontent.com/d07300d3d300bcc6d325bde4f28d8a2311c6c847436df7d01d30dcab1878259d/68747470733a2f2f696d672e736869656c64732e696f2f656e64706f696e743f7374796c653d666c61742675726c3d687474707325334125324625324662616467652d6170692e737472796b65722d6d757461746f722e696f2532466769746875622e636f6d2532466769676f73686b696e253246626f672d7061796d656e74732d7068702532466d61696e)](https://dashboard.stryker-mutator.io/reports/github.com/gigoshkin/bog-payments-php/main)

A standalone, framework-agnostic PHP 8.2+ client for the [Bank of Georgia Payments API](https://api.bog.ge/docs/en/payments/introduction). Zero framework coupling in production — drop it into Symfony, Laravel, or any PSR-compatible project by injecting standard interfaces.

---

Requirements
------------

[](#requirements)

- PHP 8.2+
- `ext-openssl`, `ext-json`
- A PSR-18 HTTP client (e.g. Guzzle, Symfony HttpClient)
- PSR-17 request/stream factories (e.g. Nyholm PSR-7, Guzzle PSR-7)

Installation
------------

[](#installation)

```
composer require gigoshkin/bog-payments-php
```

You also need a concrete HTTP client (not included to keep the library decoupled):

```
# Option A — Guzzle
composer require guzzlehttp/guzzle guzzlehttp/psr7

# Option B — Symfony HttpClient
composer require symfony/http-client nyholm/psr7
```

---

Quick Start
-----------

[](#quick-start)

```
use Bog\Payments\BogClient;
use Bog\Payments\BogConfig;
use Bog\Payments\Dto\Request\BasketItem;
use Bog\Payments\Dto\Request\CreateOrderRequest;
use Bog\Payments\Enum\Currency;

// 1. Configure
$config = new BogConfig(
    clientId:         'your-client-id',
    clientSecret:     'your-client-secret',
    webhookPublicKey: file_get_contents('/path/to/bog-public-key.pem'), // optional, for webhook verification
);

// 2. Wire up (example with Guzzle)
$httpClient     = new \GuzzleHttp\Client();
$psr18Client    = new \GuzzleHttp\Psr7\HttpFactory(); // implements RequestFactoryInterface & StreamFactoryInterface

$client = BogClient::create($config, $httpClient, $psr18Client, $psr18Client);

// 3. Create an order
$order = $client->createOrder(new CreateOrderRequest(
    callbackUrl: 'https://your-shop.com/webhook/bog',
    totalAmount: 150.00,
    basket:      [
        new BasketItem(productId: 'SKU-001', quantity: 2, unitPrice: 50.00, description: 'Widget'),
        new BasketItem(productId: 'SKU-002', quantity: 1, unitPrice: 50.00),
    ],
    currency:        Currency::GEL,
    externalOrderId: 'ORDER-12345',
));

// 4. Redirect the customer
header('Location: ' . $order->redirectUrl);
```

---

Configuration
-------------

[](#configuration)

```
$config = new BogConfig(
    clientId:         'your-client-id',      // required
    clientSecret:     'your-client-secret',  // required
    baseUrl:          'https://api.bog.ge',  // default
    tokenUrl:         'https://oauth2.bog.ge/auth/realms/bog/protocol/openid-connect/token', // default
    ttlBufferSeconds: 30,                    // shave off from token TTL to avoid mid-request expiry
    webhookPublicKey: '-----BEGIN PUBLIC KEY-----...', // PEM string; required for webhook verification
);
```

---

API Reference
-------------

[](#api-reference)

### Orders

[](#orders)

```
// Create an order — returns redirect URL for the payment portal
$response = $client->createOrder(CreateOrderRequest $request, ?string $idempotencyKey = null): CreateOrderResponse;
// $response->orderId      — BOG order ID
// $response->redirectUrl  — send the customer here
// $response->detailsUrl   — polling endpoint

// Fetch full receipt / status
$details = $client->getOrderDetails(string $orderId): OrderDetails;
// $details->status         — OrderStatus enum
// $details->processedAmount
// $details->buyerEmail
// $details->paymentMethod  — PaymentMethod enum
// $details->actions        — array of OrderAction
```

### Refunds

[](#refunds)

```
// Full refund (omit amount)
$refund = $client->refund('order-id', new RefundRequest());

// Partial refund (cards, Apple Pay, Google Pay only)
$refund = $client->refund('order-id', new RefundRequest(amount: 25.00));

// $refund->key, $refund->message, $refund->actionId
```

### Recurring Payments (Saved Cards)

[](#recurring-payments-saved-cards)

```
// Step 1 — create an order with saveCard: true
$order = $client->createOrder(new CreateOrderRequest(
    callbackUrl: '...',
    totalAmount: 50.00,
    basket:      [...],
    saveCard:    true,
));

// Step 2 — after the customer pays, save the card token
$client->saveCard($order->orderId);

// Step 3 — charge later without customer interaction
// IMPORTANT: store the idempotency key before calling — use it on retry to avoid double-charges
$idempotencyKey = $myKeyGenerator->generate();
$newOrder = $client->chargeCard($order->orderId, new SubscribeRequest(
    callbackUrl:     'https://your-shop.com/webhook/bog',
    externalOrderId: 'SUBSCRIPTION-CYCLE-42',
), $idempotencyKey);

// Step 4 — remove a saved card
$client->deleteCard($order->orderId);
```

### Webhooks

[](#webhooks)

```
// In your webhook controller:
$rawBody        = file_get_contents('php://input');
$signatureHeader = $_SERVER['HTTP_CALLBACK_SIGNATURE'] ?? '';

try {
    $payload = $client->verifyAndParseWebhook($rawBody, $signatureHeader);
    // $payload->event       — e.g. 'order_payment'
    // $payload->order       — full OrderDetails DTO
    // $payload->requestTime — DateTimeImmutable

    match ($payload->order->status) {
        \Bog\Payments\Enum\OrderStatus::Completed => handleSuccess($payload->order),
        \Bog\Payments\Enum\OrderStatus::Refunded  => handleRefund($payload->order),
        default                                   => null,
    };

    http_response_code(200);
} catch (\Bog\Payments\Exception\WebhookVerificationException $e) {
    http_response_code(400);
}
```

---

Advanced: Pre-Authorization (Manual Capture)
--------------------------------------------

[](#advanced-pre-authorization-manual-capture)

```
use Bog\Payments\Enum\CaptureMode;

// Hold funds without charging (valid for 30 days)
$order = $client->createOrder(new CreateOrderRequest(
    callbackUrl: '...',
    totalAmount: 200.00,
    basket:      [...],
    capture:     CaptureMode::Manual,
));
// When the order status becomes 'pre_authorization_blocked', funds are held.
// Capture or cancel via the BOG merchant dashboard or future API calls.
```

Advanced: Split Payments
------------------------

[](#advanced-split-payments)

```
use Bog\Payments\Dto\Request\SplitTransfer;

$order = $client->createOrder(new CreateOrderRequest(
    callbackUrl:    '...',
    totalAmount:    100.00,
    basket:         [...],
    currency:       Currency::GEL,   // GEL only
    splitTransfers: [
        new SplitTransfer(iban: 'GE00000000000000000001', amount: 70.00),
        new SplitTransfer(iban: 'GE00000000000000000002', amount: 30.00),
    ],
));
```

---

Caching
-------

[](#caching)

Token caching is controlled via `CacheInterface`. The default `InMemoryCache` is single-process only. For production multi-process environments (PHP-FPM, RoadRunner) supply a shared cache:

```
use Bog\Payments\Cache\CacheInterface;

class RedisCacheAdapter implements CacheInterface
{
    public function __construct(private \Redis $redis) {}

    public function get(string $key): mixed
    {
        $value = $this->redis->get($key);
        return $value === false ? null : unserialize($value);
    }

    public function set(string $key, mixed $value, int $ttl): void
    {
        $this->redis->setex($key, $ttl, serialize($value));
    }

    public function delete(string $key): void
    {
        $this->redis->del($key);
    }
}

$client = BogClient::create($config, $httpClient, $requestFactory, $streamFactory, new RedisCacheAdapter($redis));
```

---

Framework Integration
---------------------

[](#framework-integration)

### Symfony (`services.yaml`)

[](#symfony-servicesyaml)

```
services:
    Bog\Payments\BogConfig:
        arguments:
            $clientId:         '%env(BOG_CLIENT_ID)%'
            $clientSecret:     '%env(BOG_CLIENT_SECRET)%'
            $webhookPublicKey: '%env(BOG_WEBHOOK_PUBLIC_KEY)%'

    Bog\Payments\BogClient:
        factory: ['Bog\Payments\BogClient', 'create']
        arguments:
            $config:          '@Bog\Payments\BogConfig'
            $httpClient:      '@psr18.client'
            $requestFactory:  '@Psr\Http\Message\RequestFactoryInterface'
            $streamFactory:   '@Psr\Http\Message\StreamFactoryInterface'
            $cache:           '@Bog\Payments\Cache\CacheInterface'
```

### Laravel (Service Provider)

[](#laravel-service-provider)

```
use Bog\Payments\BogClient;
use Bog\Payments\BogConfig;

$this->app->singleton(BogClient::class, function ($app) {
    $config = new BogConfig(
        clientId:         config('bog.client_id'),
        clientSecret:     config('bog.client_secret'),
        webhookPublicKey: config('bog.webhook_public_key'),
    );

    return BogClient::create(
        $config,
        $app->make(\GuzzleHttp\Client::class),
        new \GuzzleHttp\Psr7\HttpFactory(),
        new \GuzzleHttp\Psr7\HttpFactory(),
        $app->make(\Bog\Payments\Cache\CacheInterface::class),
    );
});
```

---

Error Handling
--------------

[](#error-handling)

All exceptions extend `Bog\Payments\Exception\BogException`.

ExceptionWhen`AuthenticationException`Invalid credentials or persistent 401 after token refresh`OrderNotFoundException`404 — order ID does not exist`ApiException`Any other non-2xx response; exposes `$statusCode` and `$responseBody``NetworkException`PSR-18 transport failure (timeout, DNS, etc.)`WebhookVerificationException`Signature mismatch or missing public key```
use Bog\Payments\Exception\ApiException;
use Bog\Payments\Exception\NetworkException;
use Bog\Payments\Exception\OrderNotFoundException;

try {
    $details = $client->getOrderDetails($orderId);
} catch (OrderNotFoundException $e) {
    // 404
} catch (ApiException $e) {
    // $e->statusCode, $e->responseBody
} catch (NetworkException $e) {
    // retry or alert
}
```

---

Running Tests
-------------

[](#running-tests)

```
composer install

# Unit and integration tests
php vendor/bin/phpunit --testdox

# Static analysis (PHPStan level 8)
php vendor/bin/phpstan analyse src --level=8

# Mutation testing (generates infection.log)
php -d pcov.enabled=1 -d pcov.directory=. vendor/bin/phpunit \
    --coverage-xml=build/coverage --log-junit=build/junit.xml
php vendor/bin/infection --coverage=build --threads=4
```

---

License
-------

[](#license)

MIT

###  Health Score

39

—

LowBetter than 85% of packages

Maintenance100

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% 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

1d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/64779512?v=4)[Giorgi Kiknadze](/maintainers/gigoshkin)[@gigoshkin](https://github.com/gigoshkin)

---

Top Contributors

[![gigoshkin](https://avatars.githubusercontent.com/u/64779512?v=4)](https://github.com/gigoshkin "gigoshkin (8 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/gigoshkin-bog-payments-php/health.svg)

```
[![Health](https://phpackages.com/badges/gigoshkin-bog-payments-php/health.svg)](https://phpackages.com/packages/gigoshkin-bog-payments-php)
```

###  Alternatives

[cakephp/cakephp

The CakePHP framework

8.8k18.5M1.6k](/packages/cakephp-cakephp)[tempest/framework

The PHP framework that gets out of your way.

2.1k23.1k9](/packages/tempest-framework)[swisnl/json-api-client

A PHP package for mapping remote JSON:API resources to Eloquent like models and collections.

211473.2k12](/packages/swisnl-json-api-client)[laudis/neo4j-php-client

Neo4j-PHP-Client is the most advanced PHP Client for Neo4j

184616.9k31](/packages/laudis-neo4j-php-client)[neos/flow

Flow Application Framework

862.0M447](/packages/neos-flow)[neos/flow-development-collection

Flow packages in a joined repository for pull requests.

144179.3k3](/packages/neos-flow-development-collection)

PHPackages © 2026

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