İadeler
Bir Refund, Order üzerindeki bir bayrak değil, birinci sınıf bir varlıktır. Kısmi iadeler verebilir, aynı Order’a karşı birden fazla iade yapabilir veya aynı akışta iade + yeniden tahsilat gerçekleştirebilirsiniz.
İade kaydının var olabileceği iki yol:
| Akış | Formu kim doldurur | Auth | Şu durumda başlar |
|---|---|---|---|
| Satıcı tarafından başlatılan | Paneliniz / backend’iniz | HMAC (sk_…) | Hemen APPROVED |
| Müşteri tarafından başlatılan | Alıcı, barındırılan sayfamızda | Tek kullanımlık token (kimlik bilgisi yok) | PENDING — onaylarsınız veya yapılandırmanız otomatik onaylıyorsa kısa devre yapar |
Müşteri tarafından başlatılan akış, kısa ömürlü bir iade-talep token
kullanır. Bir token üretirsiniz (B2B veya panel), URL’yi alıcıya nasıl
isterseniz iletirsiniz ve alıcı iade detaylarını
checkout.infraio.xyz/refund-request/:token üzerinde tamamlar. Alıcı asla
API’nize dokunmaz ve satıcı anahtarınızı asla görmez.
İade yaşam döngüsü
| Durum | Anlamı |
|---|---|
PENDING | İade kaydedildi, onay bekleniyor. Müşteri tarafından başlatılan iadeler her zaman buradan başlar. |
APPROVED | Yürütme için onaylı. Satıcı tarafından başlatılan iadeler doğrudan buraya atlar. |
REJECTED | İade reddedildi. Sipariş durumu değişmez. |
EXECUTED | Zincir üstü transfer onaylandı. Order PARTIALLY_REFUNDED / REFUNDED durumuna geçer. |
Satıcı tarafından başlatılan
İade etmeye karar verirsiniz (örn. alıcı sohbet üzerinden şikayet etti).
Satıcı-tarafından-başlatılan uç noktayı çağırın — incelemeyi atlar ve
hemen APPROVED durumuna geçer.
POST /b2b/v1/merchants/{merchant_id}/refunds
{
"order_id": "ord_01J5K…",
"amount": "49.00", // kısmi veya tam, siparişin gösterim para biriminde
"reason": "customer complaint #4521",
"refund_to_address": "0xBUYER…", // kripto raylar için gereklidir
"refund_network": "polygon", // network slug; bkz. Kavramlar → Zincirler
"refund_token_address":"0xUSDC_CONTRACT" // geri ödenen ERC-20 sözleşmesi; genellikle orijinal token
}İade isteğinde currency alanı yoktur — iadeler her zaman siparişin
gösterim para birimini miras alır (bugün USD). (refund_to_address, refund_network, refund_token_address) üçlüsü zincir üstü hedeftir;
payment-service kripto saga’sını yürütmek için bunları kullanır. Fiat
raylar için yok sayılırlar (provider tarafından otomatik yönlendirilir).
Sipariş, zincir üstü transferi yürütene kadar mevcut durumunu korur (bkz. Bir kripto iadesini yürütme).
Müşteri tarafından başlatılan — iade-talep token’ları
Alıcı iade formunu bizim barındırılan sayfamızda doldurur, sizinkinde değil. Tek işiniz bir token üretmek ve URL’yi iletmek.
Token yaşam döngüsü
| Durum | Anlamı | Müşteri URL’si neyi render eder |
|---|---|---|
ACTIVE | Token aktif, now < expires_at | İade formu (refund_to_address, reason, amount, isteğe bağlı not → metadata.note) |
SUBMITTED | Alıcı formu tamamladı; bir Refund satırı var | /r/:linkToken’ı yansıtan durum kartı |
EXPIRED_UNUSED | Alıcı göndermeden TTL geçti | Uyarı: “Bu bağlantının süresi doldu. Yeni bir tane talep edin” |
RENEWAL_REQUESTED | Alıcı yeni bir bağlantı istedi | Bekleme bildirimi: “Satıcınıza bildirildi” |
RENEWED | Satıcı yenilemeyi onayladı ve bir yedek üretti | ”Bu bağlantı değiştirildi — yeni bağlantı için e-postanıza bakın” (yeni token, yönlendirilmiş bağlantı saldırılarını engellemek için burada gösterilmez) |
CANCELED | Satıcı token’ı panelden iptal etti | Düz “Bu iade talebi iptal edildi” |
Token’lar tek kullanımlıktır. SUBMITTED olduktan sonra URL, alıcının
durumu kontrol etmesi için geçerli kalır ama tekrar göndermek için
kullanılamaz. Aynı siparişe karşı ikinci bir iade vermek için yeni bir
token üretin.
TTL varsayılanları
| Üretim kaynağı | Varsayılan TTL | Neden |
|---|---|---|
POST /b2b/v1/merchants/{merchant_id}/refund-requests (HMAC) | 30 dakika | Programatik — alıcıya hemen verildiği varsayılır. |
POST /payment/v1/merchants/{merchant_id}/refund-requests (panel JWT’si) | 24 saat | Manuel — satıcı URL’yi bir e-posta / SMS’e yapıştırır. |
Geçersiz kılmak isterseniz her iki uç nokta da gövde alanı olarak
ttl_seconds kabul eder. Sunucu tarafında uygulanan sert bir min/max
sınırı bugün yoktur — yaygın değerler 1 dakika ile 7 gün arasındadır.
Alıcıları şaşırtmamak veya iptal edilen token’lar üzerinde kapasite
tutmamak için bu aralık içinde kalın.
B2B API üzerinden üretim
Bir destek konuşmasından hemen sonra, sipariş iptal akışından vb. programatik olarak bir iade bağlantısı üretmek isteyen backend’ler için.
POST /b2b/v1/merchants/{merchant_id}/refund-requests
Content-Type: application/json
X-Client-ID: pk_live_…
X-Timestamp: 1729536000
X-Signature: 4f2a1b9c8d3e2f1a0b9c8d7e6f5a4b3c…
{
"ref_type": "order_id", // gerekli: order_id | order_number | session_id | session_key
"ref_value": "ord_01J5K…", // gerekli: ref_type ile eşleşir
"amount": "49.00", // gerekli — alıcının gönderebileceği maksimumu kilitler
"ttl_seconds": 1800, // opsiyonel — varsayılan 1800 (30 dk)
"metadata": { "support_ticket": "4521" }, // opsiyonel — Stripe stili anahtar/değer
"hide_summary": false, // barındırılan form için opsiyonel UI bayrakları
"hide_header": false
}B2B istek imzası sha256= öneki olmadan ham küçük harf hex’tir — bu
önek yalnızca gelen webhook imzalarında görünür (Infraio → sunucunuz).
Giden B2B imzalama dizesi METHOD\nPATH\nTIMESTAMP\nBODY şeklindedir;
kanonik algoritma için bkz.
Kimlik doğrulama.
Tutar üretim gövdesinde vardır ve gereklidir. Alıcının form üzerinde gönderebileceği tavanı kilitler — daha azı için gönderebilirler ama asla daha fazlası için değil. (Kısmi iadeler için kısmi tutarla bir token üretin; tam iadeler için sipariş toplamı ile üretin.)
Geriye dönük uyumluluk için eski { "order_id": "..." } şekli hâlâ kabul
edilir — dahili olarak (ref_type=order_id, ref_value=...)’a eşlenir — ama
yeni entegrasyonlar açık ref_type + ref_value çiftini kullanmalıdır.
Yanıt:
{
"token": "rfqt_01J7P3Q9R…",
"refund_url": "https://checkout.infraio.xyz/refund-request/rfqt_01J7P3Q9R…",
"expires_at": "2026-05-28T10:32:00Z"
}Webhook uç noktalarınıza refund_request.created tetikler (böylece bir
sipariş için hangi token’ın şu anda aktif olduğunu loglayabilir / denetim
yapabilirsiniz).
Panel üzerinden üretim
Satıcı panelindeki Issue Refund modal’ı
bir geçiş anahtarı sunar: Şimdi yürüt vs Müşteriye bağlantı gönder.
İkincisini seçmek arka planda POST /payment/v1/merchants/{merchant_id}/refund-requests
çağırır (JWT ile doğrulanır, yukarıdaki B2B ile aynı gövde şekli), ardından
size URL’yi bir kopyalama butonu ve QR kodu ile gösterir. Hangi kanal
mantıklıysa yapıştırın — e-posta, destek sohbeti, SMS.
JavaScript SDK üzerinden — openRefundRequest
Yığınınızda zaten @lartech/infraio-checkout-js varsa ve alıcının iadeyi
harici bir URL aracılığıyla değil, kendi sayfa akışınızın içinde
tamamlamasını istiyorsanız, B2B üretimini sdk.openRefundRequest() ile
eşleyin:
// Sunucu tarafı: token'ı üret
const { token } = await fetch("/api/mint-refund-token", { method: "POST" }).then(r => r.json());
// İstemci tarafı: barındırılan formu aç
const sdk = await loadInfraIo("pk_live_yourkeyhere");
const close = sdk.openRefundRequest({
token,
mode: "popup", // veya "redirect" | "embed"
onSuccess: ({ linkToken, refundId }) => {
// linkToken → /r/:linkToken alıcı durum sayfası.
// refundId → approve / reject için B2B API referansı.
window.location.href = `/r/${linkToken}`;
},
onCancel: () => { /* alıcı popup'ı kapattı */ },
onError: (err) => { /* bkz. SDK referansı */ },
});Tam seçenek tablosu için
SDK referansı → sdk.openRefundRequest()
sayfasına bakın.
Müşteri yenilemesi — alıcı tarafından yönlendirilen yeniden-üretme
Alıcı URL’yi token süresi dolduktan sonra açarsa, sayfa formun yerine bir Yeni bağlantı talep et butonu sunar. Tıklamak:
/pub/v1/refund-requests/:token/request-renewaladresine POST eder (kimlik bilgisi yok — token’ın kendisi bearer-kanıtıdır)- Opsiyonel olarak alıcının satıcı için bırakabileceği bir serbest metin
not (
customer_note) yakalar - Token’ı
RENEWAL_REQUESTED’a geçirir ve webhook’unuzarefund_request.renewal_requestedtetikler
Paneliniz yenileme-talepleri widget’ında bir rozet gösterir. Onaylayın
(tek tık) ve sistem yeni bir ACTIVE token üretir, refund_request.renewed
tetikler ve yeniden göndermek için yeni URL’yi kopyalamanıza izin verir.
Eski URL erişilebilir kalır ama “Değiştirildi — e-postanıza bakın” gösterir,
böylece eski URL’nin yönlendirilmiş bir kopyası yeniyi çekmek için
kullanılamaz.
Bir kripto iadesini yürütme
API niyeti kaydeder — fonları hareket ettirmez. Siz satıcı cüzdanınızdan zincir üstü transferi imzalar ve yayar, ardından tx hash’i iade kaydına damgalarsınız:
POST /b2b/v1/refunds/:refund_id/submit-tx
Content-Type: application/json
{
"tx_hash": "0xabcd…",
"network": "ethereum",
"token_address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
}Üç gövde alanının da olması gereklidir: aynı tx hash farklı zincirlerde var olabilir ve orijinal ödemenin tahsil edildiği stablecoin’den farklı bir stablecoin’de iade yapabilirsiniz.
Zincir izleyici, bu tx’in yapılandırılmış onay sayısını temizlediğini
gördüğünde (bkz. Zincirler ve varlıklar), iade
EXECUTED durumuna geçer ve Order’ın iade-toplamı güncellenir.
Satıcı fonlarının saklamasını kasıtlı olarak yapmıyoruz, bu da iadeleri
sizin adınıza yürütemeyeceğimiz anlamına gelir. Zincir üstü gönderimi
admin araçlarınıza entegre edin — bir multisig veya hot cüzdandan
eth_sendRawTransaction, tx hash’ini iade API’sine gönderme ile biten
bir iş akışı.
Webhook event’leri
İade alt sistemi iki event ailesini tetikler:
Token yaşam döngüsü (refund_request.*)
| Event | Şu durumda tetiklenir |
|---|---|
refund_request.created | Bir token üretildi — data.source b2b / dashboard / renewal |
refund_request.renewal_requested | Bir alıcı token süresi dolduktan sonra “Yeni bağlantı talep et” butonuna tıkladı. Bu event’e abone olun — satıcının harekete geçme zamanıdır. |
refund_request.renewed | Bir yenilemeyi onayladınız ve yeni bir token eskisini değiştirdi. data.old_token / data.new_token denetim zincirini oluşturur. |
refund_request.canceled | Bir token’ı panelden CANCELED’a geçirdiniz. Idempotent — yalnızca ilk geçiş bir event yayar. data.reason opsiyonel satıcı notudur. |
İade yaşam döngüsü (payment.refund.*)
| Event | Şu durumda tetiklenir |
|---|---|
payment.refund.requested | Yeni bir Refund satırı var — herhangi bir kaynaktan (form gönderimi, satıcı tarafından başlatılan API, panel). |
payment.refund.approved | İade onaylandı — ya otomatik olarak (satıcı tarafından başlatılan) ya da bekleyen bir iadeye /approve çağırdıktan sonra. |
payment.refund.rejected | Bekleyen bir iadeye /reject çağırdınız. |
payment.refund.executed | Fonlar hareket etti (kripto tx hash’iniz gerekli onaylara ulaştı). |
payment.failed bir iade için tetiklenmez — iadeler kendi event
serisine sahiptir, payment.refund.* öneki altında.
Sırada ne var
- SDK referansı →
sdk.openRefundRequest()— barındırılan iade formunu popup / yönlendirme / gömme olarak açın. - API referansı → İadeler — uç nokta kataloğu (üret, gönder, yenile, durum).
- Kavramlar → Siparişler — Refund durumunun Order yaşam döngüsüne nasıl bağlandığı.
- Webhook’lar → Genel bakış — tam event kataloğu.