PHPackages                             aftandilmmd/odero-payment-for-laravel - 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. aftandilmmd/odero-payment-for-laravel

ActiveLibrary[Payment Processing](/categories/payments)

aftandilmmd/odero-payment-for-laravel
=====================================

OderoPay payment gateway integration for Laravel. A thin wrapper around aftandilmmd/odero-payment-for-php.

10PHP

Since Feb 9Pushed 3mo agoCompare

[ Source](https://github.com/aftandilmmd/odero-payment-for-laravel)[ Packagist](https://packagist.org/packages/aftandilmmd/odero-payment-for-laravel)[ RSS](/packages/aftandilmmd-odero-payment-for-laravel/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependenciesVersions (1)Used By (0)

**English** | [Türkçe](README.tr.md) | [Azərbaycanca](README.az.md)

OderoPay for Laravel
====================

[](#oderopay-for-laravel)

Laravel wrapper for the [OderoPay](https://developer.oderopay.com.tr/az) payment gateway. Supports both Azerbaijan (`odero.az`) and Turkey (`oderopay.com.tr`) regions with sandbox and live environments.

This package is a thin wrapper around [odero-payment-for-php](https://github.com/aftandilmmd/odero-payment-for-php) that adds Laravel-specific features: ServiceProvider auto-discovery, the `Odero` facade, `config/odero.php` with env variable support, and automatic Laravel Log channel integration.

> **Not using Laravel?** See [odero-payment-for-php](https://github.com/aftandilmmd/odero-payment-for-php) -- the framework-agnostic core package that works with any PHP 8.2+ application. All DTOs, Enums, Exceptions, and API logic live there.

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

[](#requirements)

- PHP 8.2+
- Laravel 11 or 12

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

[](#installation)

```
composer require aftandilmmd/odero-payment-for-laravel
```

The service provider and facade are auto-discovered.

To publish the config file:

```
php artisan vendor:publish --tag=odero-config
```

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

[](#configuration)

Add the following to your `.env` file:

```
ODERO_API_KEY=your-api-key
ODERO_SECRET_KEY=your-secret-key
ODERO_SANDBOX=true
ODERO_REGION=az
```

Optional logging:

```
ODERO_LOG_ENABLED=true
ODERO_LOG_CHANNEL=stack
```

Full config options in `config/odero.php`:

KeyDescriptionDefault`api_key`Merchant API key`''``secret_key`Merchant secret key`''``sandbox`Use sandbox environment`true``region`Region: `az` or `tr``az``auth_version`API auth version`V1``timeout`HTTP timeout in seconds`30``retry.times`Retry attempts`3``retry.sleep`Retry delay in ms`100``logging.enabled`Enable request/response logging`false``logging.channel`Log channel`stack`Usage
-----

[](#usage)

Use the `Odero` facade or inject `OderoServiceInterface`:

```
use Aftandilmmd\OderoPaymentLaravel\Facades\Odero;
use Aftandilmmd\OderoPayment\Contracts\OderoServiceInterface;

// Facade
Odero::initCheckout([...]);

// Dependency injection
public function __construct(private OderoServiceInterface $odero) {}
$this->odero->initCheckout([...]);
```

---

Checkout Payments
-----------------

[](#checkout-payments)

Initialize a checkout session and redirect the user to OderoPay's hosted payment page.

```
$response = Odero::initCheckout([
    'price' => 100.0,
    'paidPrice' => 100.0,
    'currency' => 'AZN',
    'paymentGroup' => 'PRODUCT',
    'conversationId' => 'order-123',
    'callbackUrl' => 'https://example.com/payment/callback',
    'items' => [
        ['name' => 'Monthly subscription', 'price' => 100.0],
    ],
]);

$response->token;          // "ckm-token-abc"
$response->pageUrl;        // "https://payment.odero.az/ckm-token-abc"
$response->tokenExpireDate; // "2026-03-01T12:00:00"

return redirect($response->pageUrl);
```

Retrieve the payment result after the callback:

```
$payment = Odero::retrieveCheckout($token);

$payment->id;             // 12345
$payment->paymentStatus;  // "SUCCESS"
$payment->price;          // 100.0
$payment->cardType;       // "CREDIT_CARD"
```

---

Card Payments (Non-3DS)
-----------------------

[](#card-payments-non-3ds)

Charge a card directly without 3D Secure:

```
$payment = Odero::createPayment([
    'price' => 50.0,
    'paidPrice' => 50.0,
    'currency' => 'AZN',
    'paymentGroup' => 'PRODUCT',
    'conversationId' => 'order-456',
    'card' => [
        'cardHolderName' => 'John Doe',
        'cardNumber' => '4508034508034509',
        'expireYear' => '2030',
        'expireMonth' => '12',
        'cvc' => '000',
    ],
    'items' => [
        ['name' => 'Product A', 'price' => 30.0],
        ['name' => 'Product B', 'price' => 20.0],
    ],
]);

if ($payment->paymentStatus === 'SUCCESS') {
    // Payment completed
    $payment->id;              // Payment ID
    $payment->binNumber;       // "450803"
    $payment->lastFourDigits;  // "4509"
    $payment->cardAssociation; // "VISA"
}
```

### Using the Card DTO

[](#using-the-card-dto)

```
use Aftandilmmd\OderoPayment\DTOs\Card;
use Aftandilmmd\OderoPayment\DTOs\PaymentItem;

$card = new Card(
    cardHolderName: 'John Doe',
    cardNumber: '4508034508034509',
    expireYear: '2030',
    expireMonth: '12',
    cvc: '000',
    storeCardAfterSuccessPayment: true,
    cardAlias: 'My Visa',
);

$items = [
    new PaymentItem(name: 'Product A', price: 30.0),
    new PaymentItem(name: 'Product B', price: 20.0, externalId: 'SKU-001'),
];

$payment = Odero::createPayment([
    'price' => 50.0,
    'paidPrice' => 50.0,
    'currency' => 'AZN',
    'card' => $card->toArray(),
    'items' => array_map(fn ($item) => $item->toArray(), $items),
]);
```

### Storing the Card

[](#storing-the-card)

To save the card for future payments, set `storeCardAfterSuccessPayment` to `true` in the card data. The response will include `cardUserKey` and `cardToken` for subsequent payments:

```
$payment = Odero::createPayment([
    'price' => 50.0,
    'paidPrice' => 50.0,
    'currency' => 'AZN',
    'card' => [
        'cardHolderName' => 'John Doe',
        'cardNumber' => '4508034508034509',
        'expireYear' => '2030',
        'expireMonth' => '12',
        'cvc' => '000',
        'storeCardAfterSuccessPayment' => true,
        'cardAlias' => 'My Visa Card',
    ],
    'items' => [['name' => 'Product', 'price' => 50.0]],
]);

// Save these for future payments
$cardUserKey = $payment->cardUserKey;
$cardToken = $payment->cardToken;
```

### Paying with a Stored Card

[](#paying-with-a-stored-card)

```
$payment = Odero::createPayment([
    'price' => 50.0,
    'paidPrice' => 50.0,
    'currency' => 'AZN',
    'card' => [
        'cardUserKey' => $savedCardUserKey,
        'cardToken' => $savedCardToken,
    ],
    'items' => [['name' => 'Product', 'price' => 50.0]],
]);
```

---

3D Secure Payments
------------------

[](#3d-secure-payments)

### Step 1: Initialize

[](#step-1-initialize)

```
$response = Odero::init3dsPayment([
    'price' => 100.0,
    'paidPrice' => 100.0,
    'currency' => 'AZN',
    'paymentGroup' => 'PRODUCT',
    'conversationId' => 'order-789',
    'callbackUrl' => 'https://example.com/payment/3ds-callback',
    'card' => [
        'cardHolderName' => 'John Doe',
        'cardNumber' => '4508034508034509',
        'expireYear' => '2030',
        'expireMonth' => '12',
        'cvc' => '000',
    ],
    'items' => [['name' => 'Product', 'price' => 100.0]],
]);

// Render the 3DS form in the browser
return response($response->decodedHtml());
```

### Step 2: Complete (in callback handler)

[](#step-2-complete-in-callback-handler)

```
$payment = Odero::complete3dsPayment($paymentId);

if ($payment->paymentStatus === 'SUCCESS') {
    // 3DS payment completed
}
```

---

Pre-Authorization
-----------------

[](#pre-authorization)

Create a pre-authorized payment (hold funds) and capture later:

```
// Create payment with PRE_AUTH phase
$payment = Odero::createPayment([
    'price' => 200.0,
    'paidPrice' => 200.0,
    'currency' => 'AZN',
    'paymentGroup' => 'PRODUCT',
    'paymentPhase' => 'PRE_AUTH',
    'card' => [...],
    'items' => [['name' => 'Hotel booking', 'price' => 200.0]],
]);

// Later, capture the funds (full or partial amount)
$captured = Odero::postAuthPayment($payment->id, 200.0);
```

---

Refunds
-------

[](#refunds)

### Full Refund

[](#full-refund)

```
$refund = Odero::refund(paymentId: 12345);

$refund->id;                    // Refund ID
$refund->status;                // "SUCCESS"
$refund->refundPrice;           // 100.0
$refund->refundType;            // "REFUND"
$refund->refundDestinationType; // "CARD"
```

With optional parameters:

```
$refund = Odero::refund(
    paymentId: 12345,
    conversationId: 'refund-001',
    destinationType: 'CARD',  // or 'WALLET'
);
```

### Partial Refund

[](#partial-refund)

Refund a specific transaction within a payment:

```
$refund = Odero::partialRefund(
    paymentTransactionId: 99,
    refundPrice: 30.0,
    conversationId: 'partial-refund-001',
);

$refund->id;                   // Partial refund ID
$refund->refundPrice;          // 30.0
$refund->isAfterSettlement;    // false
$refund->paymentTransactionId; // 99
```

### Retrieve Refund Status

[](#retrieve-refund-status)

```
$refund = Odero::retrieveRefund(refundId: 5001);
$partialRefund = Odero::retrievePartialRefund(id: 7001);
```

---

Payment Reporting
-----------------

[](#payment-reporting)

### Retrieve a Single Payment

[](#retrieve-a-single-payment)

```
$report = Odero::retrievePayment(paymentId: 12345);

$report->id;
$report->orderId;
$report->paymentStatus;
$report->paymentCard;         // Card details array
$report->paymentRefunds;      // Refunds array
$report->paymentTransactions; // Transactions array
```

### Search Payments

[](#search-payments)

```
$results = Odero::searchPayments([
    'paymentStatus' => 'SUCCESS',
    'currency' => 'AZN',
    'minPrice' => 10.0,
    'maxPrice' => 500.0,
    'page' => 0,
    'size' => 20,
]);

$results->items;     // Array of payments
$results->totalSize; // Total matching payments
$results->size;      // Page size

foreach ($results->items as $payment) {
    echo "{$payment['orderId']}: {$payment['price']} AZN";
}
```

---

Subscriptions
-------------

[](#subscriptions)

### Retrieve a Subscription

[](#retrieve-a-subscription)

```
$sub = Odero::retrieveSubscription(8001);

$sub->id;              // 8001
$sub->price;           // "29.99"
$sub->periodType;      // "30" (days)
$sub->status;          // 0 (Active)
$sub->nextPaymentDate; // "2026-02-15"
$sub->retryCount;      // 0
```

### Search Subscriptions

[](#search-subscriptions)

```
$results = Odero::searchSubscriptions([
    'page' => 0,
    'size' => 10,
]);
```

### Update Subscription Status

[](#update-subscription-status)

```
use Aftandilmmd\OderoPayment\Enums\SubscriptionStatus;

// Deactivate
Odero::updateSubscriptionStatus(8001, SubscriptionStatus::Passive->value);

// Reactivate
Odero::updateSubscriptionStatus(8001, SubscriptionStatus::Active->value);
```

### Subscription Plan DTO

[](#subscription-plan-dto)

```
use Aftandilmmd\OderoPayment\DTOs\SubscriptionPlan;

$plan = new SubscriptionPlan(
    price: 29.99,
    periodType: 30,       // 7 = weekly, 30 = monthly, 360 = yearly
    nochargeDayCount: 7,  // Free trial days
    description: 'Monthly Plan',
);

// Use $plan->toArray() when creating subscriptions
```

---

Pay by Link
-----------

[](#pay-by-link)

Create shareable payment links with optional QR codes.

### Create

[](#create)

```
$link = Odero::createPayByLink([
    'name' => 'Premium subscription',
    'price' => 49.99,
    'currency' => 'AZN',
    'stock' => 100,
    'enabledInstallments' => '1,2,3,6',
    'expireDate' => '2026-12-31T23:59:59',
]);

$link->id;        // 9001
$link->status;    // "ACTIVE"
$link->token;     // "pbl-token-abc"
$link->url;       // Payment URL
$link->qrCodeUrl; // QR code image URL
$link->soldCount; // 0
```

### Update

[](#update)

```
$link = Odero::updatePayByLink(9001, [
    'name' => 'Updated product name',
    'price' => 59.99,
    'stock' => 50,
]);
```

### Retrieve

[](#retrieve)

```
$link = Odero::retrievePayByLink(9001);
```

---

Card Storage
------------

[](#card-storage)

### Search Stored Cards

[](#search-stored-cards)

```
$cards = Odero::searchStoredCards([
    'cardUserKey' => 'card-user-key-abc',
]);

foreach ($cards->items as $card) {
    echo "{$card['cardAlias']}: {$card['binNumber']}****{$card['lastFourDigits']}";
    // "My Visa Card: 450803****4509"
}
```

### Delete a Stored Card

[](#delete-a-stored-card)

```
Odero::deleteStoredCard([
    'cardUserKey' => 'card-user-key-abc',
    'cardToken' => 'card-token-xyz',
]);
```

---

BIN Lookup &amp; Installments
-----------------------------

[](#bin-lookup--installments)

### BIN Lookup

[](#bin-lookup)

Get card and bank info from the first 6 digits:

```
$bin = Odero::lookupBin('450803');

$bin->binNumber;       // "450803"
$bin->cardType;        // "CREDIT_CARD"
$bin->cardAssociation; // "VISA"
$bin->cardBrand;       // "Bonus"
$bin->bankName;        // "Kapital Bank"
$bin->bankCode;        // 101
$bin->commercial;      // false
```

### Get Installment Options

[](#get-installment-options)

```
// All banks
$installments = Odero::getInstallments(100.0, 'AZN');

// Specific bank (by BIN)
$installments = Odero::getInstallments(100.0, 'AZN', '450803');

$installments->binNumber;       // "450803"
$installments->bankName;        // "Kapital Bank"
$installments->installmentPrices; // Array of installment options

foreach ($installments->installmentPrices as $option) {
    $count = $option['installmentNumber'];
    $total = $option['totalPrice'];
    $monthly = $option['installmentPrice'];
    echo "{$count}x {$monthly} AZN (total: {$total} AZN)";
}
// "1x 100.0 AZN (total: 100.0 AZN)"
// "3x 34.5 AZN (total: 103.5 AZN)"
```

---

Buyers
------

[](#buyers)

Manage buyer records for saved customer data.

### Create

[](#create-1)

```
$buyer = Odero::createBuyer([
    'name' => 'John',
    'surname' => 'Doe',
    'email' => 'john@example.com',
    'gsmNumber' => '+994501234567',
    'identityNumber' => 'ABC123',
    'registrationAddress' => 'Baku, Azerbaijan',
    'city' => 'Baku',
    'country' => 'Azerbaijan',
]);

$buyer->id;     // 2001
$buyer->status; // "ACTIVE"
```

### Update

[](#update-1)

```
$buyer = Odero::updateBuyer(2001, [
    'name' => 'Jane',
    'email' => 'jane@example.com',
]);
```

### Retrieve

[](#retrieve-1)

```
$buyer = Odero::retrieveBuyer(2001);
```

---

Payment Transactions
--------------------

[](#payment-transactions)

Approve or reject payment transactions for settlement.

### Approve

[](#approve)

```
Odero::approveTransactions([99, 100, 101]);

// Transactional mode: all-or-nothing
Odero::approveTransactions([99, 100], isTransactional: true);
```

### Disapprove

[](#disapprove)

```
Odero::disapproveTransactions([99, 100]);
```

---

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

[](#error-handling)

All API errors throw typed exceptions:

```
use Aftandilmmd\OderoPayment\Exceptions\OderoException;
use Aftandilmmd\OderoPayment\Exceptions\OderoAuthenticationException;

try {
    $payment = Odero::createPayment([...]);
} catch (OderoAuthenticationException $e) {
    // 401 - Invalid API credentials
    // $e->getMessage()  => "OderoPay authentication failed."
    // $e->getCode()     => 401
} catch (OderoException $e) {
    // 4xx/5xx - API error
    // $e->getMessage()  => "Invalid card number"
    // $e->getCode()     => 400
    // $e->errors        => ['errorCode' => '10051', 'errorDescription' => '...', 'errorGroup' => 'VALIDATION']
}
```

### Response Helper Methods

[](#response-helper-methods)

All response DTOs extend `OderoResponse` and include:

```
$response->isSuccessful(); // true if no errors
$response->hasErrors();    // true if errors present
$response->rawData;        // Full raw API response array
$response->errors;         // Errors array (if any)
```

---

Enums
-----

[](#enums)

The package provides typed enums for all API constants:

```
use Aftandilmmd\OderoPayment\Enums\Currency;
use Aftandilmmd\OderoPayment\Enums\PaymentStatus;
use Aftandilmmd\OderoPayment\Enums\SubscriptionStatus;

// Values
Currency::Azn->value;              // "AZN"
PaymentStatus::Success->value;     // "SUCCESS"
SubscriptionStatus::Active->value; // 0

// Labels
Currency::Azn->label();            // "Azerbaycan Manati"
PaymentStatus::Success->label();   // "Basarili"

// Select options (value => label)
Currency::options();
// ["AZN" => "Azerbaycan Manati", "TRY" => "Turk Lirasi", ...]
```

Available enums: `Currency`, `PaymentGroup`, `PaymentPhase`, `PaymentStatus`, `CardType`, `CardAssociation`, `RefundType`, `RefundDestinationType`, `SubscriptionStatus`, `TransactionStatus`.

---

Architecture
------------

[](#architecture)

This package is a thin wrapper around [odero-payment-for-php](https://github.com/aftandilmmd/odero-payment-for-php). It only contains:

- **`OderoServiceProvider`** -- Binds `OderoServiceInterface` as a singleton using `OderoFactory::create()`, merges config, and bridges Laravel's Log channel as a PSR-3 logger.
- **`Odero` Facade** -- Proxies to `OderoServiceInterface` for static method calls.
- **`config/odero.php`** -- Laravel config file with env variable support.

All DTOs, Enums, Exceptions, Contracts, Services, and business logic come from the core PHP package.

---

Testing
-------

[](#testing)

```
php artisan test --testsuite=Odero
```

22 tests, 76 assertions. Uses Guzzle `MockHandler` with JSON fixtures via the `fakeOdero()` test helper.

Related Packages
----------------

[](#related-packages)

- **[odero-payment-for-php](https://github.com/aftandilmmd/odero-payment-for-php)** -- The framework-agnostic core package. Contains all DTOs, Enums, Exceptions, Services, and API logic. Use this directly in non-Laravel PHP applications, or when you need full control over the HTTP client and logger.

License
-------

[](#license)

MIT

###  Health Score

19

—

LowBetter than 10% of packages

Maintenance55

Moderate activity, may be stable

Popularity2

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity12

Early-stage or recently created project

 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.

### Community

Maintainers

![](https://www.gravatar.com/avatar/630ea45c41a7b5c54294a46920d0036cf0693865c7d3254c445b6cc08670edd0?d=identicon)[aftandilmmd](/maintainers/aftandilmmd)

---

Top Contributors

[![aftandilmmd](https://avatars.githubusercontent.com/u/26919900?v=4)](https://github.com/aftandilmmd "aftandilmmd (2 commits)")

### Embed Badge

![Health badge](/badges/aftandilmmd-odero-payment-for-laravel/health.svg)

```
[![Health](https://phpackages.com/badges/aftandilmmd-odero-payment-for-laravel/health.svg)](https://phpackages.com/packages/aftandilmmd-odero-payment-for-laravel)
```

###  Alternatives

[omnipay/paypal

PayPal gateway for Omnipay payment processing library

3156.8M53](/packages/omnipay-paypal)[eduardokum/laravel-boleto

Biblioteca com boletos para o laravel

626351.9k2](/packages/eduardokum-laravel-boleto)[tbbc/money-bundle

This is a Symfony bundle that integrates moneyphp/money library (Fowler pattern): https://github.com/moneyphp/money.

1961.9M](/packages/tbbc-money-bundle)[2checkout/2checkout-php

2Checkout PHP Library

83740.3k2](/packages/2checkout-2checkout-php)[smhg/sepa-qr-data

Generate QR code data for SEPA payments

61717.2k5](/packages/smhg-sepa-qr-data)[omnipay/dummy

Dummy driver for the Omnipay payment processing library

271.2M33](/packages/omnipay-dummy)

PHPackages © 2026

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