Webhook Olay Türleri: Fatura ve Çekim Bildirimleri Rehberi
MercanPay webhook olay türleri: invoice.paid, invoice.partial, withdrawal.completed ve diğerleri. Gövde yapısı, alanlar, teslimat geçmişi ve kod örneği.

Webhook olay türleri, MercanPay'in sunucunuza "şu anda ne oldu?" sorusunun cevabını gönderme biçimidir. Bir fatura ödendiğinde, eksik ödendiğinde, süresi dolduğunda ya da bir çekim tamamlandığında sunucunuza imzalı bir POST isteği gelir. Bu rehberde her olayın ne zaman gönderildiğini, gövdesinde hangi alanların bulunduğunu ve bunları kodda nasıl karşılayacağınızı tek bir yerde topluyoruz.
Webhook'lar nereye gönderilir?
Olaylar iki adrese gidebilir:
- Ayarlar → Webhook URL: Hesabınızdaki tüm olayların gönderildiği varsayılan adres.
- Faturadaki
callback_url: Fatura oluştururken bu alanı verirseniz, o faturanın olayları bu adrese ek olarak gönderilir.
İki adres de tanımlıysa aynı olay ikisine de gider ve her ikisinde de aynı olay kimliğini (id) taşır. Böylece tekrarları olay kimliğiyle kolayca eleyebilirsiniz.
Webhook'lar yalnızca ilgili durum değişikliği kalıcı olarak kaydedildikten sonra gönderilir. Yani size "ödendi" bildirimi geldiyse fatura sistemde gerçekten ödendi durumundadır.
Her olayın ortak yapısı
Tüm olaylar aynı zarf yapısını kullanır:
{
"id": "3f1c2b9a-…",
"type": "invoice.paid",
"created_at": "2026-11-05T10:34:12.000Z",
"data": { }
}
id: Olayın benzersiz kimliği. Tekrarları elemek için kullanın.type: Olay türü.created_at: Olayın oluştuğu zaman (UTC).data: Olaya göre değişen içerik.
İstek başlıkları da her zaman aynıdır: Content-Type: application/json, User-Agent: MercanPay-Webhook/1.0 ve imzayı taşıyan X-MercanPay-Signature: t=…,v1=…. İmzanın nasıl doğrulanacağını webhook imzası doğrulama yazımızda anlattık.
Fatura olayları
| Olay | Ne zaman gönderilir | Sipariş için anlamı |
|---|---|---|
invoice.paid |
Fatura tam ödendi | Siparişi onaylayın |
invoice.overpaid |
Fatura fazla ödendi | Siparişi onaylayın, fazla kısmı değerlendirin |
invoice.partial |
Eksik ödeme alındı | Bekleyin, teslim etmeyin |
invoice.expired |
Fatura ödenmeden süresi doldu | Siparişi kapatın ya da yeni fatura önerin |
invoice.cancelled |
Fatura iptal edildi | Siparişi kapatın |
Ödeme kaynaklı olaylarda (paid, overpaid, partial) data içinde iki nesne bulunur:
data.invoice: Faturanın güncel hali.id,order_id,status,token,amount,paid_amount,remaining_amount,overpaid_amount,price_amount,metadatagibi alanlar içerir.data.payment: Bu olayı tetikleyen ödeme.txid,from(gönderen adres),amountvelatealanlarını içerir.
late alanı true ise ödeme, faturanın süresi dolduktan sonra gelmiştir. Geç ödemenin geç sayılıp sayılmadığı, ödemenin yazıldığı bloğun zamanına göre belirlenir. Yani son saniyede ödeme yapan müşteri, onay süresi yüzünden geç sayılmaz. USD fiyatlı bir TRX faturası geç ödenirse tutar güncel kurla yeniden fiyatlanır ve bu bilgi data.payment.repriced nesnesinde (previous_amount, amount, rate_usd) gelir. Ayrıntılar için eksik, fazla ve geç ödemeler yazımıza bakın.
invoice.expired ve invoice.cancelled olaylarında yalnızca data.invoice bulunur, çünkü bu olayları bir ödeme tetiklemez.
Toleransla ödendi sayılan faturalar
Panelde eksik ödeme toleransını açtıysanız, belirlediğiniz oran içinde kalan küçük eksikler invoice.paid olarak bildirilir. Bu durumda faturadaki underpaid_amount alanı, eksik kalan ama kabul edilen tutarı gösterir. Kayıtlarınızda bu alanı da dikkate alın.
Çekim olayları
| Olay | Ne zaman gönderilir |
|---|---|
withdrawal.completed |
Çekim zincirde tamamlandı |
withdrawal.failed |
Çekim başarısız oldu, tutar bakiyenize iade edildi |
withdrawal.rejected |
Çekim reddedildi, tutar bakiyenize iade edildi |
Çekim olaylarında data.withdrawal nesnesi gelir: id, idempotency_key, status, token, amount, fee, to_address, txid, error, created_at ve completed_at. API ile çekim yapıyorsanız, çekimi oluştururken kullandığınız idempotency_key sayesinde olayı kendi kaydınızla eşleştirebilirsiniz. Çekim akışının tamamını kripto bakiye çekim süreci yazımızda anlattık.
Test olayı: test.ping
Panelde Webhooklar sayfasındaki "Test gönder" düğmesi, Webhook URL'inize hemen bir test.ping olayı gönderir. Bu olay gerçek bir işlemi temsil etmez. Amacı adresinizin erişilebilir olduğunu ve imza doğrulamanızın çalıştığını görmektir. Test olayı otomatik olarak yeniden denenmez.
Teslimat geçmişi ve tekrar gönderme
Webhooklar sayfasında gönderilen her olayın kaydı tutulur. Her teslimat için şunları görürsünüz:
- Olay türü ve gönderildiği URL
- Durum (iletildi, bekliyor, başarısız) ve deneme sayısı
- Sunucunuzun döndüğü HTTP kodu ve yanıt süresi
- Sunucunuzun yanıt gövdesinin ilk kısmı
Bir olay başarısız olduysa, sorunu düzelttikten sonra "Tekrar gönder" ile yeniden iletebilirsiniz. Teslimat geçmişini dilerseniz CSV, Excel ya da PDF olarak da dışa aktarabilirsiniz.
Başarısız teslimatların sık nedenleri
- 10 saniyede yanıt verilmemesi. Uzun işleri kuyruğa atın ve hemen 2xx dönün.
- Yönlendirme dönen adres. Webhook istekleri yönlendirmeyi takip etmez.
http'denhttps'e ya da sonunda/olan adrese yönlendirme yapan bir URL başarısız sayılır. Son adresi doğrudan girin. - Kimlik doğrulama duvarı. Webhook uç noktanız oturum ya da parola istememelidir. Güvenliği imza doğrulaması sağlar.
- Yanlış imza sırrı. Ayarlar'daki
whsec_…sırrını yenilediyseniz sunucunuzdaki değeri de güncelleyin.
2xx dönmeyen istekler artan aralıklarla yeniden denenir. Yeniden deneme takvimini ve bunun kodunuza etkisini webhook yeniden deneme ve idempotent işleme yazımızda ayrıntılı ele aldık.
Kod örneği: olay türüne göre yönlendirme
Aşağıdaki Python örneği, resmi kütüphanenin verify_webhook fonksiyonuyla imzayı doğrular ve her olay türünü ayrı bir işleve yönlendirir:
import json
import os
from flask import Flask, request, abort
from mercanpay import verify_webhook
app = Flask(__name__)
SECRET = os.environ["MERCANPAY_WEBHOOK_SECRET"]
@app.post("/mercanpay/webhook")
def webhook():
body = request.get_data()
if not verify_webhook(SECRET, body, request.headers.get("X-MercanPay-Signature", "")):
abort(401)
event = json.loads(body)
if already_processed(event["id"]):
return "", 200
kind, data = event["type"], event["data"]
if kind in ("invoice.paid", "invoice.overpaid"):
fulfil_order(data["invoice"]["order_id"], data["invoice"]["paid_amount"], data["payment"]["late"])
elif kind == "invoice.partial":
mark_partial(data["invoice"]["order_id"], data["invoice"]["remaining_amount"])
elif kind in ("invoice.expired", "invoice.cancelled"):
close_order(data["invoice"]["order_id"])
elif kind == "withdrawal.completed":
mark_payout_sent(data["withdrawal"]["idempotency_key"], data["withdrawal"]["txid"])
elif kind in ("withdrawal.failed", "withdrawal.rejected"):
mark_payout_failed(data["withdrawal"]["idempotency_key"], data["withdrawal"]["error"])
remember(event["id"])
return "", 200
Tanımadığınız bir olay türü gelirse de 2xx dönün. Bu sayede ileride yeni olay türleri eklense bile entegrasyonunuz gereksiz yeniden denemelere yol açmaz. PHP tarafı için PHP ile kripto ödeme entegrasyonu rehberine göz atabilirsiniz.
Canlıya almadan önce kısa kontrol listesi
Webhook uç noktanızı gerçek siparişlere bağlamadan önce şu adımları tamamlayın:
- HTTPS kullanın ve Ayarlar'a son adresi, yönlendirme olmadan girin.
- "Test gönder" ile deneyin. Teslimat geçmişinde 2xx yanıtı gördüğünüzden emin olun.
- İmza doğrulamasının reddettiğini de görün. Yanlış bir sırla gelen isteğin 401 aldığını test edin.
- Tekrarları eleyin. Aynı olay iki kez geldiğinde siparişin iki kez işlenmediğini kontrol edin.
- Her olay türünü düşünün. Yalnızca
invoice.paiddeğil,invoice.partial,invoice.overpaidveinvoice.expirediçin de ne yapılacağı belli olsun. - Küçük tutarlı gerçek bir ödemeyle akışı uçtan uca deneyin.
Sık sorulan sorular
Webhook yerine API sorgusu kullanabilir miyim?
Evet. GET /v1/invoices/{id} ya da GET /v1/invoices/by-order/{order_id} ile faturanın güncel durumunu her zaman sorgulayabilirsiniz. Yine de anlık bildirim için webhook, yedek kontrol için API sorgusu en sağlam yaklaşımdır.
Olaylar sırayla mı gelir?
Sıralamaya güvenmeyin. Örneğin bir yeniden deneme, daha sonraki bir olaydan sonra ulaşabilir. Kararlarınızı data.invoice.status alanına göre verin.
Webhook URL'im yoksa ne olur?
Olaylar gönderilmez. Ödemeler yine bakiyenize yazılır, panelden ve e-posta bildirimlerinden takip edebilirsiniz.
Çekim olayları API dışındaki çekimler için de gelir mi?
Evet. Panelden yaptığınız çekimler için de withdrawal.* olayları gönderilir.
MercanPay ile başlayın
Her durum değişikliğini imzalı olarak bildiren bir altyapıyla ödeme almak için satıcı hesabınızı açın. Tüm alanlar için dokümantasyonu inceleyin.


