Integrify EPoint (PHP)¶
EPoint ödəniş şlüzünün PHP inteqrasiyası — ödəniş, kart yaddaşı, payout, refund və bölünmüş ödənişlər.
Composer: integrify/epoint
Kod: https://github.com/integrify-sdk/integrify-php/tree/main/packages/epoint
Sənədlər: https://integrify-php.mmzeynalli.dev/integrations/epoint/
Python qarşılığı: integrify-epoint
[!IMPORTANT] EPoint uğursuz ödəniş üçün də HTTP 200 qaytarır. Yəni
RequestFailedatılmaması ödənişin alındığı demək deyil — hər cavabdaisSuccessful()var və ona baxmaq məcburidir.RequestFailedyalnız şəbəkə problemi üçün atılır.
Kitabxananın yüklənməsi¶
composer require integrify/epoint
Paket PSR-18 klienti və PSR-17 factory-si tələb edir (integrify/core onları virtual
paket kimi istəyir), ona görə tətbiqinizdə real implementasiya olmalıdır:
composer require guzzlehttp/guzzle nyholm/psr7
Konfiqurasiya¶
| Dəyişən | Məcburi | Default | Təsvir |
|---|---|---|---|
EPOINT_PUBLIC_KEY |
✅ | — | Hər payload-a düşən public key |
EPOINT_PRIVATE_KEY |
✅ | — | İmzalama açarı. Heç vaxt göndərilmir |
EPOINT_INTERFACE_LANG |
❌ | az |
Ödəniş səhifəsinin dili (az, en, ru) |
EPOINT_SUCCESS_REDIRECT_URL |
❌ | — | Uğurlu ödənişdən sonra yönləndirmə |
EPOINT_FAILED_REDIRECT_URL |
❌ | — | Uğursuz ödənişdən sonra yönləndirmə |
use Integrify\EPoint\EPointClient;
use Integrify\EPoint\EPointConfig;
// Environment-dən
$client = new EPointClient(EPointConfig::fromEnvironment());
// Və ya açıq şəkildə — konfiqurasiya dəyişməz value object-dir, qlobal state yoxdur
$client = new EPointClient(new EPointConfig(
publicKey: 'i000000001',
privateKey: 'secret',
successRedirectUrl: 'https://shop.az/ok',
errorRedirectUrl: 'https://shop.az/fail',
));
Diqqət: environment dəyişəni
EPOINT_FAILED_REDIRECT_URLadlanır, API-də gedən field isəerror_redirect_url. Adlar Python kitabxanası ilə eyni saxlanılıb ki, iki SDK bir.envfaylını paylaşa bilsin.
Ödəniş axını¶
Ödəniş iki mərhələlidir: pay() yalnız onu başladır.
$client->pay(...) ──▶ redirectUrl ──▶ müştəri kartı daxil edir
│
sizin callback URL-iniz ◀───┘
│
Callback::decode($body) ──▶ CallbackData
$result = $client->pay(amount: 100, currency: 'AZN', orderId: 'order-1');
if (!$result->isSuccessful()) {
// $result->status, $result->message
return;
}
// Müştərini bura yönləndirin
return redirect($result->redirectUrl);
Nəticə callback URL-inizə POST olunur:
use Integrify\EPoint\Callback;
use Integrify\EPoint\Exception\SignatureMismatch;
$callback = new Callback(EPointConfig::fromEnvironment());
try {
$data = $callback->decode($rawRequestBody);
} catch (SignatureMismatch) {
// Bu sorğu EPoint-dən gəlmir
return response('', 403);
}
if ($data->isSuccessful()) {
// $data->orderId sizin göndərdiyiniz IDdir — sifarişi bununla tapın
}
[!WARNING] Callback URL-i internetdən əlçatandır: ona istənilən kəs sorğu ata bilər. İmza yoxlaması "bu datanı həqiqətən EPoint göndərdi" sualının yeganə cavabıdır, ona görə uyğunsuzluq
nulldeyil,SignatureMismatchilə bildirilir — yoxlamağı unutmaq mümkün olmasın.
Sorğular¶
| Metod | Endpoint | Cavab |
|---|---|---|
pay() |
POST /api/1/request |
RedirectUrl |
getTransactionStatus() |
POST /api/1/get-status |
TransactionStatusResult |
saveCard() |
POST /api/1/card-registration |
RedirectUrlWithCardId |
payWithSavedCard() |
POST /api/1/execute-pay |
PaymentResult |
payAndSaveCard() |
POST /api/1/card-registration-with-pay |
RedirectUrlWithCardId |
payout() |
POST /api/1/refund-request |
PaymentResult |
refund() |
POST /api/1/reverse |
MinimalResult |
splitPay() |
POST /api/1/split-request |
RedirectUrl |
splitPayWithSavedCard() |
POST /api/1/split-execute-pay |
SplitPaymentResult |
splitPayAndSaveCard() |
POST /api/1/split-card-registration-with-pay |
RedirectUrlWithCardId |
[!CAUTION] EPoint iki endpoint-i əksinə adlandırıb:
/api/1/refund-requestpayout-dur (pul sizdən müştəriyə),/api/1/reverseisə refund-dur (ödənişin geri qaytarılması). Metod adları niyyəti bildirir —payout()vərefund().
Saxlanılmış kartla ödəniş¶
// 1. Kartı qeyd edin (ödəniş olmadan)
$registration = $client->saveCard();
// müştərini $registration->redirectUrl-ə yönləndirin; təsdiq callback ilə gəlir
// 2. Callback gəldikdən sonra kartla ödəniş edin — müştəri iştirakı olmadan
$result = $client->payWithSavedCard(
amount: 100,
currency: 'AZN',
orderId: 'order-2',
cardId: $cardId,
);
$result->isSuccessful();
$result->rrn; // yalnız uğurlu əməliyyatda
$result->cardMask; // 123456******1234
$result->codeMessage(); // bankın kodunun izahı
Bölünmüş ödəniş¶
$result = $client->splitPay(
amount: 100,
currency: 'AZN',
orderId: 'order-3',
splitUserId: 'epoint_user_id', // ikinci EPoint istifadəçisi
splitAmount: 50,
);
Geri qaytarma¶
$client->refund('texxxxxx', 'AZN'); // tam
$client->refund('texxxxxx', 'AZN', 50); // yarımçıq
Status sorğusu iki ayrı sual verir¶
$status = $client->getTransactionStatus('texxxxxx');
$status->isSuccessful(); // sorğu alındımı (yalnız `server_error` = false)
$status->isPaid(); // tranzaksiya uğurlu oldumu
$status->isReturned(); // geri qaytarılıbmı
Status sorğusunda error normal cavabdır — "soruşduğun tranzaksiya uğursuz olub",
yəni sorğunun özü uğurludur. İkisini bir-biri ilə dəyişmək uğursuz ödənişi uğurlu kimi
oxumaq deməkdir. Digər bütün cavablarda isSuccessful() sadəcə status === 'success'
yoxlayır.
Məbləğlər¶
Metodlar int|float|string qəbul edir, EPoint isə məbləği JSON sətri kimi gözləyir
("100", "10.50").
$client->pay(100, ...); // "100"
$client->pay('10.50', ...); // "10.50" — sonda sıfır qorunur
$client->pay(10.55, ...); // "10.55"
Pul üçün string və ya int tövsiyə olunur. float qəbul edilir, lakin:
0.1 + 0.2PHP-də0.30000000000000004-dür; bu paket onu"0.3"kimi göndərir (Python kitabxanası eyni girişdə tam"0.30000000000000004"göndərir);(string) 1e20PHP-də"1.0E+20"verir və EPoint eksponensial yazılışı qəbul etmir, ona görə çevirmə heç vaxt eksponensial nəticə vermir;'1e5'kimi sətirlərInvalidRequestilə rədd olunur, halbukiis_numeric()onları qəbul edir.
Status və kodlar¶
$result->status; // xam sətir — EPoint yeni dəyər əlavə etsə də sınmır
$result->status(); // TransactionStatus|null
$result->code; // bankın xam kodu, məs. '000'
$result->codeMessage(); // 'Təsdiq edildi', tanınmayan kod üçün null
Enum-lar DTO property tipi kimi istifadə olunmur: EPoint sabah yeni status əlavə
etsə, validasiya sınmamalıdır. BankCode::all() 102 tanınan bank kodunu qaytarır.
Xətalar¶
| Exception | Nə vaxt |
|---|---|
MissingConfiguration |
EPOINT_PUBLIC_KEY/EPOINT_PRIVATE_KEY yoxdur |
InvalidRequest |
Məbləğ ədəd deyil, sonlu deyil, və ya eksponensial yazılışdadır |
SignatureMismatch |
Callback-in imzası uyğun gəlmir, və ya body formatı yanlışdır |
ValidationFailed |
Cavab (və ya callback datası) DTO-ya uyğun gəlmir |
RequestFailed |
Şəbəkə xətası, və ya HTTP >= 400 |
Hamısı Integrify\Exception\IntegrifyException-i implement edir.
Testlər¶
Testlər RecordingTransport inject edir — HTTP klienti heç vaxt mock olunmur:
use Integrify\Http\RecordingTransport;
use Integrify\Response;
$transport = new RecordingTransport();
$client = new EPointClient($config, $transport);
$transport->queue(Response::json(['status' => 'success', 'redirect_url' => 'https://epoint.az/']));
$client->pay(100, 'AZN', 'order-1');
// Zərfi açıb payload-a baxın
$body = $transport->lastRequest()->body;
$payload = json_decode(base64_decode($body['data']), true);
Məftildə yalnız zərf gedir — {data, signature} — yəni payload base64-lənmiş JSON-dur.
Private key nə payload-da, nə header-də görünür.