PHPackages                             tcgunel/omnipay-tami - 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. tcgunel/omnipay-tami

ActiveLibrary[Payment Processing](/categories/payments)

tcgunel/omnipay-tami
====================

Omnipay extension for Tami Payment Gateway

v2.2.1(1mo ago)010MITPHPPHP ^8.3CI passing

Since Mar 23Pushed 1mo agoCompare

[ Source](https://github.com/tcgunel/omnipay-tami)[ Packagist](https://packagist.org/packages/tcgunel/omnipay-tami)[ Docs](https://github.com/tcgunel/omnipay-tami)[ RSS](/packages/tcgunel-omnipay-tami/feed)WikiDiscussions main Synced 3w ago

READMEChangelogDependencies (14)Versions (7)Used By (0)

Omnipay: Tami
=============

[](#omnipay-tami)

**Tami payment gateway driver for the Omnipay PHP payment processing library.**

[Omnipay](https://github.com/thephpleague/omnipay) is a framework-agnostic, multi-gateway payment processing library for PHP. This package implements Tami support for Omnipay, including 3D Secure, partial / full cancellation and refund, BIN lookup, installment lookup, and transaction query.

The package is built against Tami's documentation at  — but it also absorbs the divergences between the published docs and the actual production payloads. See the [Doc-vs-prod quirks](#doc-vs-prod-quirks-the-package-absorbs) section.

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

[](#installation)

```
composer require tcgunel/omnipay-tami
```

Requires PHP 8.3+ and `omnipay/common ^3.0`.

Credentials
-----------

[](#credentials)

A merchant needs five values from Tami's portal under **İş Yerim → POS Yetkileri**:

Tami portal labelSetterNotesÜye İş Yeri Numarası`setMerchantId()`8-digit merchant numberTerminal Numarası`setMerchantUser()`8-digit terminal number, on a separate POS Yetkileri sub-screenGüvenlik Anahtarı`setMerchantStorekey()`UUID-style secret key, used for both `PG-Auth-Token` and 3DS callback `hashedData` HMACKid Değeri—JWT `kid` for body signing (see below)K Değeri—base64url-encoded HMAC-SHA512 key for body signing (JWK `k`)The two JWK fields are passed through `setMerchantPassword()` as a single `kid|k` string:

```
$gateway->setMerchantPassword('your-kid-here|your-base64url-encoded-k-here');
```

Each request to Tami carries:

- HTTP header `PG-Auth-Token: merchantId:merchantUser:base64(sha256(merchantId + merchantUser + merchantStorekey))`
- JSON body field `securityHash` — a JWS Compact Serialization (RFC 7515) of the rest of the request body, signed with HS512 using the `k` value, with `{"alg":"HS512","typ":"JWT","kid":""}` as the header.

Gateway setup
-------------

[](#gateway-setup)

```
use Omnipay\Omnipay;

$gateway = Omnipay::create('Tami');

$gateway->setMerchantId('your-merchant-number');
$gateway->setMerchantUser('your-terminal-number');
$gateway->setMerchantStorekey('your-secret-key');
$gateway->setMerchantPassword('your-kid-here|your-base64url-encoded-k-here');
$gateway->setTestMode(true); // sandbox base URL
```

Endpoints used:

SandboxProductionBase URL`https://sandbox-paymentapi.tami.com.tr``https://paymentapi.tami.com.tr`Methods
-------

[](#methods)

MethodEndpointReturns`purchase()``POST /payment/auth``PurchaseResponse` (3DS-aware)`completePurchase()``POST /payment/complete-3ds``CompletePurchaseResponse``cancel()``POST /payment/reverse``CancelResponse` (full or partial)`refund()``POST /payment/reverse``RefundResponse``bin()``POST /installment/bin-info``BinResponse` (BIN metadata only)`binInstallment()``POST /installment/installment-info``BinInstallmentResponse` (BIN + installment list)`query()``POST /payment/query``QueryResponse` (order status, transaction history)`acceptNotification()`n/a`Notification` (parses the 3DS callback POST)Direct (non-3D) payment
-----------------------

[](#direct-non-3d-payment)

```
$response = $gateway->purchase([
    'amount' => '100.00',
    'currency' => 'TRY',
    'transactionId' => 'ORDER-123',  // becomes Tami orderId, 2..36 chars, alnum + _-
    'installment' => 1,
    'card' => [
        'firstName' => 'Ada',
        'lastName' => 'Lovelace',
        'number' => '4155650100416111',
        'expiryMonth' => '01',
        'expiryYear' => '2030',
        'cvv' => '123',
    ],
    'clientIp' => '127.0.0.1',
])->send();

if ($response->isSuccessful()) {
    echo $response->getTransactionReference();  // bankReferenceNumber
} else {
    echo $response->getMessage();
}
```

3D Secure payment
-----------------

[](#3d-secure-payment)

### Step 1 — initiate

[](#step-1--initiate)

```
$response = $gateway->purchase([
    'amount' => '100.00',
    'currency' => 'TRY',
    'transactionId' => 'ORDER-123',
    'installment' => 1,
    'secure' => true,
    'returnUrl' => 'https://merchant.example/orders/ORDER-123/verify-payment',
    'card' => [ /* ... */ ],
    'clientIp' => '127.0.0.1',
])->send();

if ($response->isRedirect()) {
    // Tami returns base64-encoded HTML for the bank's 3D page.
    // PurchaseResponse::getRedirectResponse() decodes it and returns a
    // ready-to-emit Symfony HttpResponse — Omnipay's standard pattern works:
    return $response->getRedirectResponse();

    // Or pull the raw HTML if you need to embed it differently:
    // echo $response->getRedirectHtml();
}
```

### Step 2 — handle the callback

[](#step-2--handle-the-callback)

After the user authenticates with the bank, Tami POSTs to `returnUrl` with the 3DS verification payload. Use `acceptNotification()` to parse it — the `Notification` class hides the production wire-format quirks (see the [quirks section](#doc-vs-prod-quirks-the-package-absorbs)):

```
$notification = $gateway->acceptNotification($_POST);

if (! $notification->isSuccessful()) {
    return failure_view($notification->getMessage());
}

if (! $notification->verifyHash($merchantStorekey)) {
    return failure_view('3DS hash verification failed');
}

// 3DS challenge succeeded — finalize the charge.
$completion = $gateway->completePurchase([
    'transactionId' => $notification->getTransactionId(),
])->send();

if ($completion->isSuccessful()) {
    // Charge captured. $completion->getTransactionReference() = bank ref number.
}
```

`Notification` exposes:

MethodWhat it returns`isSuccessful()``true` only when `mdStatus === '1'` AND `success ∈ {1, "1", "true", true}``getTransactionStatus()`Omnipay constant: `STATUS_COMPLETED` or `STATUS_FAILED``getMessage()``mdErrorMessage` ?? `errorMessage``getTransactionId()`the `orderId` you sent`getTransactionReference()``bankReferenceNumber` (when present)`getMdStatus()`raw `mdStatus` string`verifyHash($secretKey)`HMAC-SHA256 check over the field list in `hashParams``getData()`raw callback arrayCancel (same-day reversal)
--------------------------

[](#cancel-same-day-reversal)

```
$gateway->cancel([
    'transactionId' => 'ORDER-123',
    // optional partial:
    'amount' => '12.50',
    // optional reason, capped at 150 chars:
    'description' => 'customer change of mind',
])->send();
```

Refund (after settlement)
-------------------------

[](#refund-after-settlement)

```
$gateway->refund([
    'transactionId' => 'ORDER-123',
    'amount' => '50.00',
    'description' => 'partial return',  // optional, 150 chars
])->send();
```

Both endpoints are `POST /payment/reverse` — Tami switches between same-day reversal and post-settlement refund automatically.

BIN lookup
----------

[](#bin-lookup)

Two endpoints, depending on what you need:

```
// Just card metadata (bank, brand, type, commercial flag)
$bin = $gateway->bin(['binNumber' => '45438877'])->send();

$bin->getBankName();   // "T. GARANTİ BANKASI A.Ş."
$bin->getCardOrg();    // "VISA"
$bin->getCardType();   // "CREDIT"
$bin->isCommercial();  // false

// BIN metadata + merchant-permitted installment list + force3ds/forceCvc flags
$info = $gateway->binInstallment(['binNumber' => '45438877'])->send();

$info->getInstallments();  // [1, 3, 5, ...]
$info->getData();          // full payload incl. force3ds, forceCvc
```

`isInstallment` on the response indicates whether the merchant is authorized for installments at all.

Transaction query
-----------------

[](#transaction-query)

```
$query = $gateway->query([
    'transactionId' => 'ORDER-123',
    'isTransactionDetail' => true,  // include the full transactions[] history
])->send();

$query->getOrderStatus();    // AUTH | REVERSE | REFUND | PARTIAL_REFUND | PRE_AUTH | POST_AUTH | CHARGEBACK
$query->getPaymentStatus();  // NOT_COMPLETE | SUCCESS | FAIL | TIME_OUT
$query->getTransactions();   // [{transactionType, transactionStatus, transactionDate, bankAuthCode, bankReferenceNumber}, ...]
```

Use this for reconciliation jobs and "did this order actually settle" checks.

Doc-vs-prod quirks the package absorbs
--------------------------------------

[](#doc-vs-prod-quirks-the-package-absorbs)

Tami's published docs at  are out of date in several places. The package handles all of these — you should not need to special-case them in your application code, but they're documented here so future-you knows what to expect when reading raw payloads.

**Request-side**

QuirkDoc saysProduction wants`securityHash` JWT encoding"JWS, base64url, no padding" (implicitly, via the `nimbus-jose-jwt` Java sample)base64url, no padding — but earlier package versions used standard base64 with padding, which Tami rejected. Fixed in v2.1.0.JWT header `kid` fieldRFC 7515 standard `kid`Same. Earlier package versions sent `kidValue` instead, which Tami ignored. Fixed in v2.1.0.`paymentGroup` default`PRODUCT``PRODUCT`. Earlier package defaulted to `OTHER`. Fixed in v2.1.0.`buyer.buyerId`requiredrequired. Auto-fallback now uses `transactionId` instead of an empty string.**3DS callback (POST to `callbackUrl`)**

FieldDocsProduction`success`string `"true"` / `"false"`string `"1"` / `"0"` on the wire — but Tami **signs the canonical "true"/"false" form** for `hashedData`, so the package canonicalises before HMAC`hashParams`not documentedsent on every callback; explicit list of fields used to compute `hashedData``mdErrorMessage`not documentedsent on every callback (e.g. "Y-status/Challenge authentication via ACS")Hash field list`cardOrg`, `currency`, `originalAmount`, `orderID`, `status``cardOrganization`, `currencyCode`, `txnAmount`, `orderId`, `success``callbackStatus`, `transactionDate`not documentedsometimes presentThe `Notification` class:

- Treats `success` as truthy when it's any of `1`, `"1"`, `"true"`, or boolean `true`.
- Reads `mdErrorMessage` ?? `errorMessage` for the human-readable failure reason.
- Reads `hashParams` from the callback and uses it verbatim as the field list/order for `hashedData` verification, with a sensible default if Tami ever stops sending `hashParams`.

**Purchase response**

`PurchaseResponse::getRedirectResponse()` is overridden to return a Symfony `HttpResponse` containing the **decoded** `threeDSHtmlContent`. Omnipay's default builds a self-submitting form posting to `getRedirectUrl()`, which doesn't exist for Tami — the bank-side HTML is delivered inline as base64.

Sandbox
-------

[](#sandbox)

Sandbox base URL: `https://sandbox-paymentapi.tami.com.tr`. Sandbox portal: `https://sandbox-portal.tami.com.tr`.

Tami publishes test merchant credentials on the docs site (the `/tami-satis-islemi-3dli` page). Test cards are listed at `/test-kartlari`. Error code reference at `/hata-kodlari`.

Testing
-------

[](#testing)

```
composer test
```

The suite runs against mocked HTTP responses; no network access required.

License
-------

[](#license)

MIT.

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance90

Actively maintained with recent releases

Popularity7

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity53

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

Every ~9 days

Total

6

Last Release

50d ago

Major Versions

v1.0.0 → v2.0.02026-03-23

PHP version history (2 changes)v1.0.0PHP ^8.0

v2.0.0PHP ^8.3

### Community

Maintainers

![](https://www.gravatar.com/avatar/36dffe883e88aeef07c26067af3d6a7eda1c2a81f1ae45fdd430b721665131da?d=identicon)[Mobius Studio](/maintainers/Mobius%20Studio)

---

Top Contributors

[![tcgunel](https://avatars.githubusercontent.com/u/3923425?v=4)](https://github.com/tcgunel "tcgunel (10 commits)")

---

Tags

paymentgatewayomnipaysanal-pos3d-securetami

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/tcgunel-omnipay-tami/health.svg)

```
[![Health](https://phpackages.com/badges/tcgunel-omnipay-tami/health.svg)](https://phpackages.com/packages/tcgunel-omnipay-tami)
```

###  Alternatives

[yasinkuyu/omnipay-iyzico

Iyzico gateway for Omnipay payment processing library

137.3k](/packages/yasinkuyu-omnipay-iyzico)

PHPackages © 2026

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