API sənədləşməsi
E-Xezine vasitəsilə ödəniş qəbulu üçün developerə lazım olan hər şey: ödənişlərin yaradılması, vebhuklar, geri qaytarmalar və ləğv, saxlanmış kartlar və avtomatik silinmələr, quraşdırılmış forma.
03.10.2026 tarixli redaksiya, API versiyası 2026-09-22
2026-09-22 versiyalı API üçün sənədləşmə. Baza ünvanı —
https://exezine.az/api/v2, maşın üçün təsvir —
openapi.json. Bütün metodlar üzrə interaktiv
arayış: açmaq.
03.10.2026 yenilikləri: appearance sahəsi ilə ödəniş formasının tərtibatı, quraşdırılmış forma dərhal açılır və çərçivənin hündürlüyünü özü seçir (skript ?v=2), «Kartı yadda saxla» qeydi — yalnız sizin istəyinizlə. Ləğv edilmiş və ya müddəti bitmiş ödənişi artıq açıq bank səhifəsində ödəmək olmur (ətraflı); vebhukların emal qaydaları dəqiqləşdirilib.
01.10.2026 yenilikləri: saxlanmış kartlar və avtomatik silinmələr,
ödənişin ləğvi, öz ödəniş müddətiniz və tez gələn payment.expired,
order_id üzrə ikiqat ödənişdən qorunma, sınaq vebhukları üçün ayrıca ünvan.
Bütün dəyişikliklər əlavədir: mövcud inteqrasiya düzəlişsiz işləyir.
Necə işləyir
- Serveriniz ödəniş yaradır:
POST /payments. - Cavabda
checkout_urlgəlir — bu bizim ödəniş səhifəmizdir. - Müştərini həmin ünvana yönləndirirsiniz (və ya öz saytınızda çərçivədə göstərirsiniz).
- Ödəniş bitəndə sizə vebhuk göndəririk:
payment.succeeded. - Müştərini sizin
return_urlünvanınıza qaytarırıq.
Siz yalnız E-Xezine ilə inteqrasiya edirsiniz. Ödənişi hansı ekvayrinq bankın emal etməsi bizim daxili məsələmizdir və istənilən an dəyişə bilər: ünvanlar, cavabın formatı və vebhuklar dəyişmir, kodunuz bunu hiss etmir.
Açarlar və avtorizasiya
Açarlar şəxsi kabinetdə, «API və vebhuklar» bölməsində verilir.
| Açar | Görünüşü | Nə edir |
|---|---|---|
| Canlı | exz_live_… | Real pul |
| Test | exz_test_… | Qum qutusu, pul yoxdur |
Açar yaradılarkən bir dəfə göstərilir. İtiribsinizsə — yenisini buraxın, köhnəsini ləğv edin. Hər sorğuda göndərilir:
Authorization: Bearer exz_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxTest və canlı mühitlər tam ayrıdır: test açarı ilə canlı ödəniş tapılmayacaq
(404 qayıdacaq) və əksinə.
Açar — pula girişdir. O, yalnız sizin serverinizdə yaşayır. Heç vaxt onu sayt kodunda, mobil tətbiqdə və ya repozitoriyada saxlamayın.
Ümumi qaydalar
Məbləğlər — valyutanın minimal vahidlərində tam ədədlərdir.
10 manat = 1000, 25.50 = 2550. API-də ümumiyyətlə
onluq kəsrlər yoxdur — yuvarlaqlaşdırma səbəbindən bir qəpiklik səhv mümkün deyil.
Vaxt — RFC 3339 sətirləri: 2026-09-22T18:30:00+04:00.
Kodlaşdırma — UTF-8, sorğunun gövdəsi JSON,
Content-Type: application/json başlığı ilə. Hər cavabda
request_id var — dəstəyə yazanda onu göstərin.
Xətalar
Həmişə eyni zərf:
{
"error": {
"type": "invalid_request_error",
"code": "amount_invalid",
"message": "Məbləğ sıfırdan böyük tam ədəd olmalıdır",
"param": "amount"
},
"request_id": "req_4f2c9a1b8e0d3a71"
}message deyil, code əsas götürün: o sabitdir, mətn isə
dəyişə bilər və Accept-Language: ru | az | en başlığından asılıdır.
| HTTP | type | Nə vaxt |
|---|---|---|
| 400 | invalid_request_error | Yanlış JSON |
| 401 | authentication_error | Açar yoxdur, yanlışdır və ya ləğv edilib |
| 403 | authentication_error | Mağaza üçün API söndürülüb |
| 404 | invalid_request_error | Obyekt tapılmadı |
| 409 | invalid_request_error | Konflikt: idempotentlik, uyğun olmayan status |
| 422 | invalid_request_error | Sahənin dəyəri uyğun deyil |
| 429 | rate_limit_error | Həddindən çox sorğu |
| 5xx | api_error | Bizim tərəf və ya şlüz |
Sorğu tezliyinin limiti
Susmaya görə hər açar üçün dəqiqədə 120 sorğu. Cavabda həmişə:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118Aşıldıqda — 429 və saniyələrlə Retry-After.
Göstərilən qədər gözləyin və təkrarlayın; dövrə ilə döyəcləməyin.
İdempotentlik
Ödəniş, geri qaytarma və ya link yaradarkən başlıq göndərin:
Idempotency-Key: sizin istənilən unikal sətriniz- Eyni açar və eyni gövdə → birinci nəticəni və
Idempotent-Replayed: truebaşlığını qaytarırıq. İkiqat silinmə olmayacaq. - Eyni açar, başqa gövdə →
409 idempotency_conflict. - Açarlar bir sutka yaşayır.
Beləliklə şəbəkə qırılandan sonra sorğunu təkrarlamaq təhlükəsizdir: heç vaxt bir ödəniş əvəzinə iki ödəniş yaranmayacaq.
Ödəniş yaratmaq
POST /api/v2/payments{
"amount": 4990,
"currency": "AZN",
"order_id": "1024",
"description": "Sifariş №1024",
"method": "card",
"return_url": "https://shop.az/thanks",
"customer": {
"email": "buyer@example.com",
"name": "Aysel Məmmədova",
"phone": "+994501234567",
"ip": "85.132.1.1",
"country": "AZ",
"city": "Baku",
"address": "Nizami 10",
"zip": "AZ1000"
},
"metadata": { "cart": "17", "source": "mobile" },
"locale": "az"
}| Sahə | Vacib | Təsvir |
|---|---|---|
amount | bəli | Tam ədəd, minimal vahidlər |
currency | xeyr | Susmaya görə AZN; siyahını biz qoşuruq |
order_id | xeyr | Sizin sifariş nömrəniz. Sonra ona görə axtarmaq olar |
description | xeyr | 200 simvola qədər, müştəriyə görünür |
method | xeyr | card (susmaya görə) və ya sbp |
return_url | xeyr | Müştərini hara qaytarmalı. İctimai https:// |
customer.* | xeyr | Nə qədər dolğun olsa, 3-D Secure imtinaları bir o qədər az olar |
metadata | xeyr | 20 sahəyə və 4 KB-a qədər. Olduğu kimi qaytarırıq |
locale | xeyr | az / ru / en — ödəniş səhifəsinin dili |
customer.id | xeyr | Sizin sisteminizdə alıcının id-si (64 simvola qədər: latın hərfləri, rəqəmlər, _ - . : @). Saxlanmış kartlar ona bağlanır |
expires_in_minutes | xeyr | Öz ödəniş müddətiniz: 5–1440 dəqiqə. Verilməsə — mağazanızın müddəti (standart olaraq 24 saat) |
save_payment_method | xeyr | true — kartı növbəti silinmələr üçün saxlamaq, "optional" — alıcıdan qeyd ilə soruşmaq. Bax: «Saxlanmış kartlar» |
payment_method | xeyr | pm_… — ödəniş səhifəsi olmadan saxlanmış kartdan silmək |
allow_duplicate_order | xeyr | true — artıq ödənilmiş order_id üzrə ikinci ödənişə icazə vermək |
appearance | xeyr | Kart formasının tərtibatı: rənglər, şriftlər, başlığı və altlığı gizlətmək. Bax: «Ödəniş formasının tərtibatı» |
customer haqqında. Emitent banklar 3-D Secure 2-də riski
məhz bu məlumatlara görə qiymətləndirir. Heç olmasa email,
ip və country göndərin — bu, imtinaların payını nəzərəçarpacaq
dərəcədə azaldır.
SBP haqqında. method: "sbp" yalnız
currency: "RUB" ilə işləyir. Başqa valyutada
422 method_not_available qayıdacaq.
İkiqat ödənişdən qorunma. Bu order_id üzrə artıq ödənilmiş
ödəniş varsa, ikincisi yaradılmır: 409 order_already_paid qayıdır,
error.payment_id sahəsində isə həmin ödənişin id-si olur. Beləliklə
«Ödə» düyməsinin təkrar basılması və ya sizin tərəfdə tapşırığın təkrarı pulu iki
dəfə silməyəcək. İkinci ödəniş qəsdən lazımdırsa (məsələn, əlavə ödəniş),
"allow_duplicate_order": true göndərin. Alıcı sifarişi yenə də iki dəfə
ödəyibsə (ilk ödənişdən əvvəl iki ödəniş səhifəsi açıb), ikinci ödənişdə
duplicate_of sahəsi doldurulacaq, biz isə Telegram-a bildiriş göndərəcəyik.
Ödəniş müddəti
Hər ödənişin müddəti var — expires_at sahəsi. Onu
expires_in_minutes təyin edir, göndərilməyibsə — mağazanızın tənzimləməsi
(standart olaraq 24 saat). Müddət bitəndən sonra ödəniş səhifəsi kartı qəbul etmir
və bir neçə dəqiqə ərzində payment.expired vebhuku gəlir. Bank son anda
hələ 3-D Secure aparırsa, müddətdən sonra 15 dəqiqəyə qədər gözləyirik və yalnız
bundan sonra ödənişi bağlayırıq.
Cavab 201
{
"id": "EXZ-20260922-7FD54BA1D8",
"object": "payment",
"livemode": true,
"status": "pending",
"amount": 4990,
"currency": "AZN",
"amount_refunded": 0,
"fee": 125,
"net": 4865,
"charged": null,
"order_id": "1024",
"description": "Sifariş №1024",
"metadata": { "cart": "17" },
"method": "card",
"customer": { "id": "user-77", "email": "buyer@example.com", "name": "Aysel", "phone": "" },
"card": null,
"failure": null,
"checkout_url": "https://exezine.az/checkout/EXZ-20260922-7FD54BA1D8",
"return_url": "https://shop.az/thanks",
"source": "api",
"created_at": "2026-09-22T18:30:00+04:00",
"next_action": null,
"initiator": "customer",
"save_payment_method": false,
"payment_method": null,
"duplicate_of": null,
"expires_at": "2026-09-23T18:30:00+04:00",
"paid_at": null,
"canceled_at": null,
"request_id": "req_c0ad2cf33ed31637"
}id — bizdəki ödənişin identifikatorudur. Onu sifarişin yanında saxlayın.
| Sahə | Mənası |
|---|---|
livemode | true — canlı ödəniş, false — qum qutusu |
fee | Bu ödəniş üzrə bizim komissiyamız |
net | Komissiyadan sonra sizə nə qədər çatacaq |
charged | Silinmə başqa valyutadadırsa — {"amount":…, "currency":"…"}, əks halda null |
amount_refunded | Artıq nə qədər geri qaytarılıb |
source | Ödəniş haradan: api, link, form, insales |
card | Ödənişdən sonra: {"brand":"visa","last4":"4242","country":"AZ"} |
paid_at | Ödəniş anı; ondan əvvəl — null |
customer.id | Göndərmisinizsə, alıcının id-si |
expires_at | Ödənişi hansı ana qədər ödəmək olar |
initiator | customer — alıcı ödəyir, merchant — alıcısız saxlanmış kartdan silinmə |
save_payment_method | Nə istənilib: true, "optional" və ya false |
payment_method | Saxlanmış kart (pm_…): bu ödənişin nəticəsində saxlanılan və ya silinmənin aparıldığı kart. Əks halda null |
next_action | requires_action zamanı: {"type":"redirect","url":"…"} — alıcını hara göndərmək lazımdır |
duplicate_of | Sifariş iki dəfə ödənilibsə, eyni order_id üzrə ilk ödənişin id-si |
canceled_at | Ödənişi nə vaxt ləğv etmisiniz |
Statuslar
| Status | Mənası |
|---|---|
pending | Yaradılıb, müştəri hələ ödəməyib |
succeeded | Ödənilib |
failed | İmtina, bax failure |
expired | Ödəniş müddəti bitib |
refunded | Tam geri qaytarılıb |
partially_refunded | Qismən geri qaytarılıb |
review | Bizdə yoxlanışdadır |
canceled | Sizin tərəfinizdən ləğv edilib (POST /payments/{id}/cancel) |
requires_action | Saxlanmış kartdan silinmə alıcının təsdiqini gözləyir (3-D Secure), bax next_action |
Yekun statuslar: succeeded, failed, expired,
canceled, refunded, partially_refunded.
İmtinanın səbəbi
"failure": { "code": "insufficient_funds", "message": "Kartda kifayət qədər vəsait yoxdur" }| code | Müştəriyə nə demək |
|---|---|
authentication_failed | Bank 3-D Secure təsdiqləmədi — yenidən cəhd edin |
insufficient_funds | Kifayət qədər vəsait yoxdur |
card_expired | Kartın etibarlılıq müddəti bitib |
limit_exceeded | Kart limiti aşılıb |
card_declined | Bank əməliyyatı rədd etdi |
expired | Ödəniş müddəti bitib, yeni ödəniş yaradın |
Statusu öyrənmək
GET /api/v2/payments/{id}curl -H "Authorization: Bearer $KEY" \
https://exezine.az/api/v2/payments/EXZ-20260922-7FD54BA1D8{id} — bizim EXZ-… və ya sizin
order_id (onda həmin nömrəli sonuncu ödəniş qayıdacaq).
Statusu dövrə ilə sorğulamaq nə lazımdır, nə də düzgündür — bunun üçün vebhuklar var. Sorğulamanın məqbul istifadəsi: müştəri saytınıza qayıdanda birdəfəlik yoxlama və «asılı qalmış» sifarişlər üzrə saatda bir tutuşdurma.
Ödənişlərin siyahısı
GET /api/v2/paymentsGET /api/v2/payments?limit=25&status=succeeded&order_id=1024| Parametr | Təsvir |
|---|---|
limit | 1–100, susmaya görə 25 |
status | Yuxarıdakı statuslardan istənilən biri |
order_id | Sizin sifariş nömrəniz |
from, to | Tarixlər YYYY-MM-DD və ya YYYY-MM-DD HH:MM |
starting_after | Növbəti səhifənin kursoru |
customer_id | Bir alıcının ödənişləri (sizin customer.id) |
{
"object": "list",
"data": [ { "…ödəniş…" } ],
"has_more": true,
"next_cursor": "EXZ-20260922-11E5866313",
"request_id": "req_…"
}has_more true olduqca next_cursor dəyərini
starting_after kimi göndərin.
Geri qaytarma
POST /api/v2/payments/{id}/refunds{ "amount": 3000, "reason": "Müştəri maldan imtina etdi" }amount göstərilmədikdə qalığın tam məbləği qaytarılır.
{
"object": "refund_result",
"refund": {
"id": "ref_12",
"object": "refund",
"payment_id": "EXZ-20260922-7FD54BA1D8",
"amount": 3000,
"currency": "AZN",
"status": "succeeded",
"reason": "Müştəri maldan imtina etdi",
"created_at": "2026-09-22T19:05:00+04:00"
},
"payment": { "…yenilənmiş ödəniş…" },
"request_id": "req_…"
}Ödəniş üzrə geri qaytarmaların siyahısı: GET /api/v2/payments/{id}/refunds.
| Kod | Nə vaxt |
|---|---|
payment_not_payable | Ödəniş ödənilməyib — qaytarmağa bir şey yoxdur |
refund_amount_invalid | Məbləğ qalıqdan çoxdur |
refund_failed | Şlüz geri qaytarmanı qəbul etmədi |
Geri qaytarma da Idempotency-Key dəstəkləyir — mütləq istifadə edin.
Ödənişin ləğvi
Alıcı fikrini dəyişibsə və ya sifariş artıq aktual deyilsə, ödənilməmiş ödənişi müddəti gözləmədən özünüz bağlayın.
POST /api/v2/payments/{id}/cancelcurl -X POST -H "Authorization: Bearer $KEY" -H "Idempotency-Key: cancel-1024" \
https://exezine.az/api/v2/payments/EXZ-20260922-7FD54BA1D8/cancel- Uğur —
200vəcanceledstatusunda ödəniş, üstəlikpayment.canceledvebhuku. - Yalnız
pendingvə yarequires_actionstatusundakı ödənişi ləğv etmək olar. Əks halda —409 payment_not_cancelable,error.payment_statussahəsində isə onun cari statusu (məsələn,succeeded: alıcı ödəməyə çatıb). - Ləğvdən əvvəl bankdan bir daha soruşuruq: ödənilmiş ödənişi ləğv etmək olmaz.
- Ləğv edilmiş ödənişin
checkout_urllinki artıq ödənişə aparmır.
Bank səhifəsi artıq açıqdırsa. Alıcı ödəniş səhifəsini
ləğvdən əvvəl aça bilərdi. Adətən onun üzərindən ödənişi keçirmək artıq mümkün olmayacaq:
silinmədən əvvəl ödəniş şlüzü bizdən soruşur və ləğv edilmiş ödənişi rədd edirik —
alıcı «Ödəniş mağaza tərəfindən ləğv edilib» görəcək. Lakin bunu 100% zəmanət etmək
olmur (məsələn, şlüzlə əlaqə xətası zamanı). Ona görə ləğvdən sonra gələn
payment.succeeded hadisəsini adi ödəniş kimi emal edin (və ya geri qaytarma edin).
Saxlanmış kartlar və avtomatik silinmələr
Abunəliklər, avtomatik ödənişlər və «bir kliklə» ödəniş üçün: alıcı kartı bir dəfə
bizim səhifədə daxil edir, sonra isə ödəniş səhifəsi olmadan ondan özünüz silirsiniz.
Kart nömrəsini nə siz, nə də biz görürük — sizdə yalnız pm_…
identifikatoru və kartın maskası var.
Funksiya sorğu ilə aktivləşdirilir: dəstəyə yazın, biz onu
bankla razılaşdıracağıq. Aktiv olub-olmadığı GET /ping cavabında
(features.saved_cards) və kabinetdə görünür.
1. Ödəniş zamanı kartı saxlamaq
POST /api/v2/payments{
"amount": 4990,
"order_id": "sub-77-1",
"save_payment_method": true,
"customer": { "id": "user-77", "email": "buyer@example.com" },
"return_url": "https://shop.az/thanks"
}| Dəyər | Necə işləyir |
|---|---|
true | Kart həmişə saxlanılır. Alıcının gələcək silinmələrə razılığını siz öz tərəfinizdə alırsınız (abunəlik zamanı qeyd, ofertanın şərtləri) |
"optional" | Ödəniş səhifəsində alıcı «Kartı növbəti ödənişlər üçün yadda saxla» qeydini görür. Qeyd qoymayıbsa — kart saxlanılmayacaq |
Alıcı kartı daxil edir və adi qaydada 3-D Secure-dan keçir. Ödəniş
succeeded olanda onda payment_method görünəcək:
"payment_method": {
"id": "pm_3f9a1c7e5b2d4f6a8c0e1b3d",
"object": "payment_method",
"type": "card",
"livemode": true,
"customer_id": "user-77",
"card": { "brand": "visa", "last4": "4242", "exp_month": 7, "exp_year": 2030, "country": "AZ" },
"status": "active",
"source": "checkout",
"metadata": {},
"created_at": "2026-10-01T12:00:00+04:00",
"last_used_at": null
}Eyni obyekt ayrıca payment_method.saved vebhuku ilə gələcək.
pm_…-ni alıcının yanında saxlayın. Alıcı yenidən eyni kartla ödəyərsə,
yeni kart yaranmayacaq — əvvəlki yenilənəcək.
2. Saxlanmış kartdan silmək
POST /api/v2/payments{
"amount": 4990,
"order_id": "sub-77-2026-11",
"description": "Abunəlik, noyabr",
"payment_method": "pm_3f9a1c7e5b2d4f6a8c0e1b3d",
"customer": { "id": "user-77" }
}Ödəniş səhifəsi yoxdur: bank dərhal cavab verir və 201 cavabı
nəticəni artıq status-da ehtiva edir:
| status | Nə etməli |
|---|---|
succeeded | Pul silinib. payment.succeeded vebhuku da gələcək |
failed | Bank imtina edib, səbəb failure-dadır. payment.failed gələcək |
requires_action | Bank alıcının təsdiqini tələb edir (3-D Secure). Alıcıya next_action.url linkini göndərin; nəticə vebhukla gələcək |
pending | Bank hələ cavab verməyib. Nəticə vebhukla gələcək; silinməni yeni idempotentlik açarı ilə təkrarlamayın |
| Sahə | Təsvir |
|---|---|
payment_method | Silinmə üçün mütləqdir: pm_… |
initiator | merchant (standart) — alıcısız silinmə (abunəlik, avtomatik ödəniş);
customer — alıcı hazırda sizdədir və saxlanmış kartla «Ödə» düyməsini özü basıb |
mit_type | Yalnız bizimlə razılaşdırılaraq: alıcısız silinmənin səbəbi —
delayed_charge, increment, resubmission, reauthorization, no_show |
customer.id | Göndərilibsə, kartın alıcısı ilə üst-üstə düşməlidir — əks halda 422 customer_mismatch |
Mütləq Idempotency-Key göndərin — məsələn
sub-77-2026-11. Şəbəkə qırılandan sonra sorğunun təkrarı eyni ödənişi
qaytaracaq, pulu ikinci dəfə silməyəcək. Bank imtina etdikdə (failed)
isə yeni cəhdi yeni açarla və gündə bir dəfədən tez olmayaraq edin: imtina
edilmiş kart üzrə tez-tez təkrarları banklar fırıldaqçılıq kimi qəbul edir.
3. Kartların idarə edilməsi
| Sorğu | Nə edir |
|---|---|
GET /api/v2/payment_methods?customer_id=user-77 | Alıcının kartları (customer_id olmadan — hamısı). Parametrlər: limit, starting_after, include_revoked=1 |
GET /api/v2/payment_methods/{pm_id} | Bir kart |
DELETE /api/v2/payment_methods/{pm_id} | Kartı silmək (alıcı onu sizdə ayırıb). Onunla artıq ödəmək olmaz |
Kartın statusu: active, expired (kartın müddəti bitib)
və ya revoked (silinib). Sınaq və real kartlar ödənişlər kimi ayrılıb.
Əvvəl saxlanmış kartların köçürülməsi
Əvvəllər ekvayer bankla birbaşa işləmisinizsə və sizdə eyni mağazanın alıcılarının kart tokenləri artıq varsa, onları köçürmək olar — alıcılar kartları yenidən daxil etməli olmayacaq. Köçürməni sorğu ilə aktivləşdiririk.
POST /api/v2/payment_methods{
"customer_id": "user-77",
"token": "bankdan əvvəllər alınmış kart tokeni",
"card": { "brand": "visa", "last4": "4242", "exp_month": 7, "exp_year": 2030 },
"metadata": { "migrated": "2026-10" }
}Cavab — 201 və kart obyekti (pm_…). Eyni tokenin eyni
alıcıya təkrar köçürülməsi həmin kartı qaytaracaq (200). Tokeni
əvvəlcədən yoxlamaq olmur: etibarsızdırsa, ilk silinmə imtina qaytaracaq.
Saxlanmış kart xətaları
| Kod | Nə vaxt |
|---|---|
saved_cards_disabled | Funksiya mağazanız üçün aktiv deyil |
payment_method_invalid | Kart tapılmadı (yazı xətası, açarın başqa rejimi) |
payment_method_revoked | Kart silinib |
payment_method_expired | Kartın müddəti bitib — alıcıdan yeni kartla saxlamaqla ödəməsini xahiş edin |
payment_method_unusable | Kartdan istifadə etmək olmur — alıcıdan kartı saxlamaqla yenidən ödəməsini xahiş edin |
customer_mismatch | Kart başqa alıcıya məxsusdur |
parameters_conflict | Bir sorğuda həm payment_method, həm də save_payment_method |
save_requires_card | Yalnız kartı saxlamaq olar, SBP-ni yox |
Ödəniş linkləri
Sayt olmadıqda və ya hesab əl ilə verildikdə.
POST /api/v2/links{ "amount": 12000, "currency": "AZN", "description": "Hesab №77", "max_uses": 3, "expires_at": "2026-10-01" }| Sahə | Təsvir |
|---|---|
amount | Minimal vahidlərdə tam ədəd. Göstərməsəniz — məbləği ödəyən daxil edəcək |
currency | Susmaya görə mağazanın valyutası |
description | 200 simvola qədər |
max_uses | Link üzrə neçə dəfə ödəmək olar. Göstərməsəniz — məhdudiyyətsiz |
expires_at | Bu andan sonra link işləmir |
type | dynamic (susmaya görə) və ya static |
{
"id": "idP62kFt",
"object": "payment_link",
"url": "https://exezine.az/l/idP62kFt",
"amount": 12000,
"currency": "AZN",
"max_uses": 3,
"uses": 0,
"expires_at": null,
"active": true,
"created_at": "2026-09-22T19:10:00+04:00"
}Oxumaq: GET /api/v2/links/{id}.
Vebhuklar — nəticəni öyrənməyin əsas yolu
Ünvan və hadisələr kabinetdə qurulur: «API və vebhuklar».
| Hadisə | Nə vaxt |
|---|---|
payment.succeeded | Ödəniş keçdi |
payment.failed | İmtina |
payment.expired | Ödəniş müddəti bitdi |
refund.succeeded | Geri qaytarma keçdi |
payment.canceled | Siz ödənişi ləğv etmisiniz |
payment_method.saved | Alıcının kartı saxlanılıb (data.object-də — kart, data.payment_id-də — ödəniş) |
Sizin ünvana POST{
"id": "evt_5f0f0c55264b546ecb1a3045",
"type": "payment.succeeded",
"api_version": "2026-09-22",
"livemode": true,
"created_at": "2026-09-22T18:45:00+04:00",
"data": { "object": { "…ödəniş…" } }
}refund.succeeded üçün data daxilində əlavə olaraq
refund da olur.
X-Exezine-Event: payment.succeeded
X-Exezine-Event-Id: evt_5f0f0c55264b546ecb1a3045
X-Exezine-Attempt: 1
X-Exezine-Signature: t=1790095200,v1=6c1f…a93Sınaq hadisələri üçün ayrıca ünvan
Kabinetdə ikinci ünvanı — «Sınaq hadisələri üçün URL» göstərmək olar.
Onda sınaq açarı ilə ödənişlər üzrə hadisələr ("livemode": false) ora,
real hadisələr isə əsas ünvana gedir. Sınaq ünvanının öz imza sirri var:
sınaq serverinin real sirri bilməsinə ehtiyac yoxdur. Göstərilməyibsə — bütün
hadisələr əvvəlki kimi əsas ünvana gedir; onları livemode sahəsinə görə fərqləndirin.
İmzanın yoxlanması — mütləqdir
İmza "<t>.<sorğunun xam gövdəsi>" sətrindən HMAC-SHA256
alqoritmi ilə, kabinetdəki vebhuk sirriniz açar kimi istifadə olunmaqla hesablanır.
<?php
$secret = 'kabinetdən_sizin_sirriniz';
$body = file_get_contents('php://input'); // XAM gövdə, json_decode-dan əvvəl
$sig = $_SERVER['HTTP_X_EXEZINE_SIGNATURE'] ?? '';
if (!preg_match('~t=(\d+),v1=([a-f0-9]{64})~', $sig, $m)) {
http_response_code(400); exit;
}
$expected = hash_hmac('sha256', $m[1] . '.' . $body, $secret);
if (!hash_equals($expected, $m[2]) || abs(time() - (int) $m[1]) > 300) {
http_response_code(400); exit; // imza uyğun gəlmədi və ya gecikib
}
$event = json_decode($body, true);
// … sizin emalınız …
http_response_code(200);
echo 'OK';const crypto = require('crypto');
function verify(rawBody, header, secret) {
const m = /t=(\d+),v1=([a-f0-9]{64})/.exec(header || '');
if (!m) return false;
const expected = crypto.createHmac('sha256', secret)
.update(`${m[1]}.${rawBody}`).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(m[2]));
return ok && Math.abs(Date.now() / 1000 - Number(m[1])) < 300;
}İmza xam gövdədən hesablanır. Freymvorkunuz JSON-u artıq
parçalayıbsa, məhz ilkin sətri götürün: yenidən yığılmış
JSON.stringify başqa baytlar verəcək və imza uyğun gəlməyəcək.
«İmza yoxlanmır» şikayətinin ən çox rast gəlinən səbəbi budur.
Emal qaydaları
- Tez
200cavab verin — amma yalnız hadisə saxlanılandan sonra. Cavabı 15 saniyə gözləyirik; ağır işi fon növbəsinə salın. 2xx-dən fərqli istənilən cavab uğursuzluq sayılır və çatdırılmanı təkrarlayırıq. Əvvəlcədən200cavab verməyin: emal sonra uğursuz olsa, təkrar artıq olmayacaq. - Hadisə iki dəfə gələ bilər. Hadisənin
iddəyərini (evt_…) yadda saxlayın və təkrarları nəzərə almayın. «Emal edildi» qeydini emalın özü ilə eyni baza tranzaksiyasında (və ya ondan sonra) qoyun, daha tez yox: əks halda ortada xəta olarsa, bizim təkrar dublikat kimi atılacaq. - Təkrarlar. Cəmi 8-ə qədər çatdırılma cəhdi: birincisi dərhal, sonra 1 dəq, 5 dəq, 30 dəq, 2 saat, 6 saat, 12 saat və 24 saat sonra təkrarlar. Sonra hadisə çatdırılmamış kimi işarələnir və kabinetdən düymə ilə yenidən göndərilə bilər.
- Ardıcıllığa zəmanət yoxdur. Gəliş sırasına deyil, gövdədəki
statusdəyərinə əsaslanın. - Pulu vebhuka görə hesablayın, müştərinin sayta qayıtmasına görə yox: müştəri vərəqi bağlaya bilər, ödəniş isə keçər.
Kabinetdə «Test hadisəsi göndər» düyməsi var — real imza ilə
test.ping gələcək. Canlı başlanğıcdan əvvəl qəbuledicini yoxlamaq üçün rahatdır.
Müştərinin sayta qaytarılması
Ödənişdən sonra müştərini sizin return_url ünvanınıza yönləndirir
və əlavə edirik:
https://shop.az/thanks?payment_id=EXZ-20260922-7FD54BA1D8&status=succeededBu parametrlər yalnız «Təşəkkür» səhifəsini göstərmək üçündür.
Sifarişi ödənilmiş saymaq yalnız vebhukdan və ya
GET /payments/{id} sorğusundan sonra olar: müştəri ünvan sətrini
redaktə edə bilər.
Quraşdırılmış forma
Müştəri sizin səhifənizdə qalır, kart forması isə bizim verdiyimiz çərçivədə açılır. Kart məlumatları sizin saytdan keçmir.
- Saytınızın domenlərini bizə bildirin — onları ağ siyahıya salacağıq, əks halda brauzer quraşdırmaya icazə verməyəcək.
- Səhifədə:
HTML + JS<div id="pay"></div>
<script src="https://exezine.az/assets/js/exezine-checkout.js?v=2"></script>
<script>
// checkout_url POST /api/v2/payments cavabında gəlib
ExezineCheckout.mount('#pay', {
url: payment.checkout_url,
onSuccess: function (p) { location.href = '/thanks?id=' + p.payment_id; },
onFailure: function (p) { alert('Ödəniş keçmədi'); }
});
</script>Çərçivə əvəzinə sadə keçid:
ExezineCheckout.redirect(payment.checkout_url);Bu skriptin verdiyi nəticə interfeys üçündür. Malı göndərmək və sifarişin statusunu dəyişmək — yalnız vebhuka görə.
Çərçivə necə işləyir
Kart forması aralıq ekran olmadan dərhal açılır. Alıcı onu çarpazla bağlasa, çərçivədə «Kartla ödə» düyməsi görünəcək — forma yenidən açılacaq. Forma uğurlu ödənişdən sonra bağlanıbsa, çərçivə bankın təsdiqinə qədər «Ödəniş yoxlanılır…» göstərir — bu anda ikinci dəfə ödəmək mümkün deyil.
Çərçivənin hündürlüyünü skript özü seçir: kart forması açıq olanda —
formHeight (defolt 640 px), ödənişdən sonra — nəticə mesajına görə.
mount parametri | Təsvir |
|---|---|
url | Mütləqdir. API cavabındakı checkout_url |
autoOpen | false — əvvəlcə məbləğ və düymə ilə bizim ekran (əvvəlki kimi). Defolt true |
formHeight | Kart forması açıq olanda çərçivənin hündürlüyü, px (360–1400). Defolt 640 |
height | Çərçivənin sabit hündürlüyü, px. Onda avto-hündürlük söndürülür |
onSuccess, onFailure | Ödənişin nəticəsi (interfeys üçün) |
onReady, onResize, onMessage | Çərçivə yükləndi / hündürlüyü dəyişdi / bizim istənilən mesaj |
Skripti yuxarıdakı nümunədəki kimi ?v=2 ilə qoşun:
brauzerlər faylların köhnə nüsxələrini uzun müddət saxlayır, yeni ünvan isə yeni versiyaya zəmanət verir.
Bizim skript olmadan öz çərçivəniz. <iframe src="{checkout_url}?embed=1">.
Ünvan parametrləri: auto=0 — formanı dərhal açmamaq, fh=640 —
formanın hündürlüyü. Hündürlüyü message hadisəsinə görə dəyişin: əgər
event.origin === "https://exezine.az", data.source === "exezine"
və data.type === "resize"-dirsə, data.height-də lazımi hündürlük pikseldədir.
Ödəniş formasının tərtibatı
Kart formasını saytınızın üslubunda tərtib etmək olar: rənglər, şriftlər,
yuvarlaqlaşdırma, başlığı və altlığı gizlətmək. Tərtibat hər ödənişdə
appearance sahəsi ilə ötürülür — kabinetdə heç nə qurmaq lazım deyil, onu
istənilən an öz kodunuzda dəyişmək olar. Həm quraşdırılmış formada, həm də ayrıca
ödəniş səhifəsində işləyir.
POST /api/v2/payments{
"amount": 3500,
"order_id": "fee-77-2026-11",
"appearance": {
"widget": { "backgroundColor": "#0E1114", "buttonsColor": "#C6FF00", "backgroundType": 0 },
"header": { "display": "none" },
"price": { "color": "#C6FF00", "fontSize": "20px", "fontWeight": "700" },
"button": { "pay": { "backgroundColor": "#C6FF00", "color": "#0E1114", "borderRadius": "12px" } },
"inputs": { "backgroundColor": "#1A2027", "color": "#F4F6F8", "cvc": { "type": "password" } },
"footer": { "display": "none" }
}
}| Bölmə | Nəyi tərtib edir |
|---|---|
widget | Bütün forma: backgroundColor — fon, buttonsColor — düymələrin rəngi, backgroundType — fon naxışı (0 — naxışsız), şrift |
header | Başlıq (mağazanın adı, «təhlükəsiz ödəniş»). "display": "none" — gizlətmək |
price, description | Məbləğ və ödənişin təyinatı |
button | Düymələr: card — «Kartla ödə», brands — Apple Pay / Google Pay, pay — «Ödə» |
inputs | Daxiletmə sahələri; inputs.cvc.type: "password" — CVC-ni nöqtələrlə gizlətmək |
cardFace | Kartın təsviri |
footer | Loqotipli altlıq. "display": "none" — gizlətmək |
Xassə adları CSS-dəki kimidir, amma bitişik: backgroundColor, fontSize,
fontFamily, fontWeight, borderRadius, letterSpacing,
margin. Dəyərlər — sətirlər ("#0E1114", "14px"), rəqəmlər,
true/false.
Məhdudiyyətlər. İç-içəlik — 4 səviyyəyə qədər, obyektin ölçüsü — 64 KB-a qədər.
Sətirlərdə < > { } simvolları, həmçinin javascript:,
expression( və @import olmaz. base64 şəkillər buraya sığmır —
tərtibat rənglər və şriftlərlə verilir. Xəta olduqda 422 appearance_invalid
qayıdacaq, error.param-da isə dəqiq yer, məsələn appearance.header.display.
Biz formatı yoxlayırıq, xassələrin mənasını yox: xassənin adında hərf səhvi xəta yaratmayacaq — xassə sadəcə işləməyəcək. Hamı üçün açmazdan əvvəl görünüşü bir ödənişdə yoxlayın. Ödəniş tərtibatı dəstəkləməyən provayder vasitəsilə keçirilərsə, ödəniş adi qaydada keçəcək, sadəcə forma standart olacaq.
Formada «Kartı yadda saxla» qeydi yalnız
save_payment_method: "optional" olduqda görünür. API vasitəsilə digər ödənişlərdə o yoxdur.
Qoşulmanın ardıcıllığı
- Test açarı alın,
GET /api/v2/pingçağırın — əlaqəyə əmin olun. - Vebhuk ünvanını qurun, «Test hadisəsi göndər» düyməsini basın, imzanın uyğun gəlməsinə nail olun.
- Yaradılmadan
payment.succeededhadisəsinə qədər test ödənişi keçirin. - Eyni
Idempotency-Keyilə təkrar sorğunu yoxlayın. - Geri qaytarmanı yoxlayın — tam və qismən.
- Canlı açar alın və 3–5-ci bəndləri kiçik məbləğlə təkrarlayın.
- Test açarını canlı serverin tənzimləmələrindən silin.
- Saxlanmış kartlardan istifadə edirsinizsə —
save_payment_methodilə ilk ödənişi,pm_…üzrə silinməni, imtinanı və kartın silinməsini yoxlayın.
Nə etmək lazım deyil
GET /payments/{id}sorğusunu dövrə ilə göndərmək — bunun üçün vebhuklar var.- Müştəri qayıdanda ünvan sətrindəki parametrlərə inanmaq.
- Açarı brauzerdə və ya mobil tətbiqdə saxlamaq.
Idempotency-Keyolmadan ödəniş göndərmək — şəbəkə qırılanda dublikat alacaqsınız.- Xətanın
messagemətnini təhlil etmək —codedəyərinə baxın.
| Əlamət | Səbəb |
|---|---|
| İmza heç cür uyğun gəlmir | Gövdə yoxlamadan əvvəl freymvork tərəfindən parçalanıb. Xam gövdə lazımdır |
| Məbləğlər 100 dəfə böyükdür | 4990 əvəzinə 49.90 göndərilib |
422 amount_invalid | Onluq kəsr göndərilib. API-də yalnız tam ədədlər var |
| Bir sifarişə iki ödəniş | Idempotency-Key yoxdur |
Mövcud ödənişə 404 | Canlı mühitdə test açarı və ya əksinə |
409 order_already_paid | Bu order_id ilə sifariş artıq ödənilib. Bu, ikiqat ödənişdən qorunmadır |
Kartdan silinmə həmişə failed | Kart bloklanıb və ya boşdur. Gündə bir dəfədən tez təkrarlamayın, 3–4 imtinadan sonra alıcıdan yenidən ödəməsini xahiş edin |
Dəstək
support@exezine.az ünvanına yazın.
Cavabdakı request_id dəyərini və vaxtı dəqiqəyə qədər göstərin —
belə olduqda sorğunu jurnalda dərhal tapırıq.
Cavab tapmadınız və ya nəsə yazıldığı kimi işləmir? Bizə yazın — konkret cavab veririk.
support@exezine.az