Integrify API inteqrasiyalarını rahatlaşdıran PHP kitabxanasıdır. Bu repository bütün Integrify PHP paketlərini bir monorepo daxilində birləşdirir: paylaşılan core + ayrıca publish olunan inteqrasiya paketləri.
English below: English section
- Dokumentasiya portalı: https://integrify.mmzeynalli.dev
- Kod bazası: https://github.com/integrify-sdk/integrify-php
- Python versiyası: https://github.com/integrify-sdk/integrify-python
- Hər inteqrasiya ayrıca paketdir, yalnız lazım olanı yükləyib istifadə edirsiniz.
- Paylaşılan
integrify/coreilə kod təkrarının qarşısı alınır. - Paketlər birlikdə inkişaf etdirilir, amma ayrı-ayrı publish olunur.
- Composer monorepo sayəsində bütün paketlər eyni mühitdə test/lint edilir.
- Adi, tipli metodlar — magic dispatch yoxdur. IDE-də avtomatik tamamlama, "go to definition" və refactoring annotasiyasız işləyir.
- Konkret qaytarış tipləri —
listGoodsGroups(): list<GoodsGroup>kimi, generic wrapper açmağa ehtiyac yoxdur. - Kitabxanadakı bütün sinif və funksiyalar tamamilə dokumentləşdirilib.
- Kitabxanadakı bütün sinif və funksiyalar tipləndirilib; PHPStan
level: maxtəmizdir. - Sorğuların çoxunun məntiq axını (flowsu) izah edilib.
Note
PHP-də Python-dakına bənzər async dəstəyi olmadığı üçün bu kitabxana yalnız sinxron
klientlər verir. Python-dakı EPointAsyncRequest kimi qarşılığı yoxdur.
composer require integrify/coreLayihənizdə PSR-18 klienti yoxdursa, birini əlavə edin:
composer require guzzlehttp/guzzle nyholm/psr7Important
Paketlər hələ 0.x-dədir. Composer-in ^ operatoru sıfırdan fərqli ən soldakı
rəqəmi qoruduğu üçün ^0.1 = >=0.1.0 <0.2.0 deməkdir — yəni yalnız patch
yeniləmələri. API sabitləşənə qədər 0.2.0 kimi minor release-lər constraint-i
genişləndirməyi tələb edəcək. Detallar:
.github/workflows/publish.yml.
Hər inteqrasiya Client-dən törəyir və hər endpoint adi, tipli metoddur:
use Integrify\Client;
use Integrify\Dto\Attribute\Field;
use Integrify\Dto\Data;
final readonly class Payment extends Data
{
public function __construct(
#[Field(name: 'order_id', maxLength: 32)]
public string $orderId,
#[Field(min: 1)]
public int $amount,
public ?string $description = null,
) {
}
}
final class EPointClient extends Client
{
public function pay(Payment $payment): Payment
{
return $this->post('/api/1/request', $payment)->to(Payment::class);
}
public function status(string $orderId): Payment
{
return $this->get($this->uri('/api/1/get-status/{id}', ['id' => $orderId]))
->to(Payment::class);
}
}
$client = new EPointClient(new HttpTransport(), 'https://epoint.az');
$payment = $client->pay(new Payment(
orderId: '12345678',
amount: 100,
description: 'Ödəniş',
));Dəyişən path hissələri həmişə uri() şablonundan keçirilməlidir — sətir
birləşdirməsi ('/get-status/' . $orderId) kodlanmamış ?, # və / simvollarının
url-i dəyişməsinə imkan verir.
Bütün sorğuların cavab formatı Response class-ıdır:
final readonly class Response
{
public int $status;
/** Cavab sorğusunun status kodu */
public array $headers;
/** Cavab sorğusunun header-ləri */
public string $body;
/** Cavab sorğusunun xam body-si */
public function isSuccessful(): bool; // status < 400
public function header(string $name): ?string;
public function headerValues(string $name): array;
public function toArray(): array; // JSON deyilsə: []
public function to(string $dto): Data; // tək obyekt
public function toList(string $dto): array; // kök səviyyəli JSON array
}Python-dakı ApiResponse.ok burada isSuccessful(), status_code isə status-dur.
body xam mətndir; toArray()/to()/toList() onu oxuyur.
Metodlar konkret tip qaytardığı üçün uğursuzluq qaytarış dəyəri ilə bildirilə bilmir — HTTP səviyyəsindəki xətalar exception kimi qalxır:
| Exception | Nə vaxt |
|---|---|
ValidationFailed |
DTO-nun field-ləri qaydalara uyğun deyil (sorğu göndərilmir) |
InvalidRequest |
Sorğu qurularkən klient kodundakı səhv: path-də kodlanmamış ?/#, doldurulmamış {placeholder}, yad host |
RequestFailed |
Şəbəkə xətası, və ya 400-dən böyük status kodu (->request, ->response daşıyır) |
MissingConfiguration |
Məcburi environment dəyişəni yoxdur |
Hamısı IntegrifyException interfeysini implement edir:
try {
$client->pay($payment);
} catch (Integrify\Exception\IntegrifyException $exception) {
// kitabxanadan gələn hər şey
}Sorğu göndərmədən nəyin göndəriləcəyini yoxlamaq üçün RecordingTransport (Python-dakı
dry rejiminin qarşılığı, lakin qaytarış tiplərini pozmadan):
use Integrify\Http\RecordingTransport;
use Integrify\Response;
$transport = new RecordingTransport();
$transport->queue(Response::json(['status' => 'success']));
$client = new EPointClient($transport, 'https://epoint.az');
$client->pay($payment);
$transport->lastRequest()->uri; // göndərilən url
$transport->lastRequest()->headers; // göndərilən header-lər
$transport->lastRequest()->body; // göndərilən payload (massiv)Növbədən artıq sorğu gözlənilmirsə RecordingTransport exception qaldırır — "bir dəfə
çağırılır" testi dörd çağırışda keçməsin deyə. Bunu istəmirsinizsə, konstruktora
fallback cavab verin: new RecordingTransport(Response::json([])).
Caution
Bütün sorğular rəsmi dokumentasiyalara uyğun yazılsalar da, Integrify qeyri-rəsmi API klient-dir.
Even though all requests are written according to official documentation, Integrify is an unofficial library for these integrations.
| Paket / Package | Composer | Status | Python qarşılığı |
|---|---|---|---|
core |
integrify/core |
✅ | integrify-core |
lsim |
integrify/lsim |
✅ | integrify-lsim |
epoint |
integrify/epoint |
✅ | integrify-epoint |
kapitalbank |
integrify/kapitalbank |
✅ | integrify-kapitalbank |
postaguvercini |
integrify/postaguvercini |
✅ | integrify-postaguvercini |
azericard |
integrify/azericard |
✅ | integrify-azericard |
| Clopos | integrify/clopos |
integrify-clopos |
|
| ECustoms (məxfi) | — | integrify-ecustoms |
✅ = publish olunub · = planlaşdırılır
Yeni paket əlavə etmək üçün CLAUDE.md, publish prosesi üçün
.github/workflows/publish.yml faylına baxın.
Integrify is a PHP toolkit for API integrations with Azerbaijani services. This repository is the monorepo for the Integrify PHP package family: a shared core plus independently published integration packages.
It shares design goals and the exact wire protocol with
integrify-python, but it is not
a transliteration of it. The PHP API is written for PHP: real typed methods, readonly
DTOs, constructor injection, exceptions. Where the two disagree on shape, PHP idiom wins
— only the bytes on the wire have to match.
- Project docs portal: https://integrify.mmzeynalli.dev
- Code repository: https://github.com/integrify-sdk/integrify-php
| Document | Purpose |
|---|---|
PHP-PRIMER.md |
This codebase explained for developers who know Python but not PHP |
CLAUDE.md |
Internal conventions |
packages/core/README.md |
The core package in detail |
- Each integration is installed independently, so users install only what they need.
- A shared
integrify/coreavoids duplicated base logic. - Packages are developed together but released independently.
- The Composer monorepo keeps linting and tests unified across packages.
- Real, typed methods — no magic dispatch. Autocomplete, go-to-definition and refactoring work without annotations.
- Concrete return types rather than a generic response wrapper you have to unwrap.
readonlyDTOs built with named arguments; the API's inconsistent field names are hidden behind attributes.- Constructor injection — configuration is an immutable value object, so there is no global state to reset between tests.
- PSR-18 / PSR-17 HTTP layer, swappable via the
Transportinterface. - One exception hierarchy — everything the library throws is an
IntegrifyException. - Everything is documented and type hinted; PHPStan runs at
level: max.
PHP has no async story comparable to Python's, so this library ships sync clients only.
composer require integrify/core
composer require guzzlehttp/guzzle nyholm/psr7 # if you have no PSR-18 client yetImportant
The packages are still 0.x. Composer's caret preserves the left-most non-zero digit,
so ^0.1 means >=0.1.0 <0.2.0 — patch updates only. Until the API settles, a minor
release such as 0.2.0 will require consumers to widen their constraint.
$client = new EPointClient(new HttpTransport(), 'https://epoint.az');
$payment = $client->pay(new Payment(
orderId: '12345678',
amount: 100,
description: 'Payment',
));Dynamic path segments always go through the uri() template so they are encoded:
$this->get($this->uri('/api/1/get-status/{id}', ['id' => $orderId]));String concatenation is refused when the result would contain ? or #, because that
is almost always an unencoded user value smuggling query parameters into the URL.
final readonly class Response
{
public int $status; // HTTP status code
public array $headers; // response headers
public string $body; // raw body
public function isSuccessful(): bool; // status < 400
public function header(string $name): ?string;
public function headerValues(string $name): array;
public function toArray(): array; // [] when the body is not JSON
public function to(string $dto): Data;
public function toList(string $dto): array;
}Python's ApiResponse.ok is isSuccessful() here, and status_code is status.
| Exception | When |
|---|---|
ValidationFailed |
DTO validation failed; nothing was sent |
InvalidRequest |
The request could not be built: unencoded ?/# in a path, an unfilled {placeholder}, a foreign host |
RequestFailed |
Network failure, or HTTP >= 400 — carries ->request and ->response |
MissingConfiguration |
A required environment variable is absent |
All of them implement IntegrifyException.
$transport = new RecordingTransport();
$transport->queue(Response::json(['status' => 'success']));
$client = new EPointClient($transport, 'https://epoint.az');
$client->pay($payment);
$transport->lastRequest()->uri;
$transport->lastRequest()->body;An unqueued request raises, so a test asserting "we call the API once" cannot quietly pass when the code calls it four times. Pass a fallback response to the constructor to opt out.
See the table in the Azerbaijani section — it is the same list.
composer install # dependencies
composer packages # list the packages and their mirrors
composer sync-packages # rewrite the root autoload map after adding a package
composer format # fix code style (PHP-CS-Fixer)
composer lint # check code style
composer type-check # static analysis (PHPStan, level max)
composer test # run the test suite (PHPUnit)
composer coverage # tests + coverage report (needs Xdebug or PCOV)
composer all # check-packages + format + type-check + testThe same pre-commit setup as integrify-python, with
composer scripts in place of just tasks, so the hooks and CI can never disagree:
pip install pre-commit
pre-commit install
pre-commit install --hook-type pre-pushOn commit: markup checks (XML, YAML, JSON), check-packages, format on the staged
PHP files, and type-check. On push: test and secure, which are slower and hit
the network.
pre-commit run --all-files # run everything once, without committing'php-cs-fixer' is not recognized as an internal or external command (or the same for
phpunit / phpstan) — the dev tools are not installed. Composer puts vendor/bin on
the PATH when it runs a script, so this means that directory is empty or missing:
composer installNote that composer install --no-dev deliberately skips these tools, so composer lint,
composer test and composer type-check will not work after it. Use a plain
composer install for development.
To check what is actually installed:
dir vendor\bin :: Windows
ls vendor/bin # macOS / LinuxPHP CS Fixer ... does not support your PHP version — your PHP is newer than the
installed fixer. Run composer update friendsofphp/php-cs-fixer first; if you need to
run anyway, set PHP_CS_FIXER_IGNORE_ENV=1.
Caution
Integrify is an unofficial API client, even though it is based on official documentation.
