<!-- Source: https://docs.infraio.xyz/tr/concepts/refunds -->
<!-- Last updated: 2026-10-04 -->

# İ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.

> **Note:**
>
> İadeler [satıcı uygulamasından](https://docs.infraio.xyz/tr/get-started/merchant-app) da verilebilir.

İ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ü

```mermaid
stateDiagram-v2
    [*] --> PENDING:  refund created (customer submit or B2B customer-flow)
    PENDING --> APPROVED: passes review (auto for merchant-initiated)
    PENDING --> REJECTED: review denies
    APPROVED --> EXECUTED: on-chain tx confirmed
    APPROVED --> REJECTED: canceled before execution
    REJECTED --> [*]
    EXECUTED --> [*]
```

| 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.

```http
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.
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](#executing-a-crypto-refund)).

---

## 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ü

```mermaid
stateDiagram-v2
    [*] --> ACTIVE:           mint (B2B or dashboard)
    ACTIVE --> SUBMITTED:     buyer submits the form
    ACTIVE --> EXPIRED_UNUSED: now > expires_at
    ACTIVE --> CANCELED:      merchant cancels (dashboard)
    EXPIRED_UNUSED --> RENEWAL_REQUESTED: buyer clicks "Request new link"
    RENEWAL_REQUESTED --> RENEWED: merchant approves, new ACTIVE token issued
    SUBMITTED --> [*]
    CANCELED --> [*]
    RENEWED --> [*]
```

| 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 iade kaydı 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" |

> **Note:**
>
> 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. |
| Satıcı paneli | **24 saat** | Manuel — satıcı URL'yi bir e-posta / SMS'e yapıştırır. |

Varsayılanı `ttl_seconds` gövde alanıyla geçersiz kılabilirsiniz. Minimum
veya maksimum sınır uygulanmaz; yaygın değerler 1 dakika ile 7 gün
arasındadır.

### 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.

```http
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
}
```

> **Warning:**
>
> 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](https://docs.infraio.xyz/tr/api-reference/authentication).

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.)

Eski `{ "order_id": "..." }` şekli hâlâ kabul edilir ve
`ref_type=order_id` olarak değerlendirilir, ancak yeni entegrasyonlar açık
`ref_type` + `ref_value` çiftini kullanmalıdır.

Yanıt:

```json
{
  "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](https://app.infraio.xyz) Issue Refund modal'ı
bir geçiş anahtarı sunar: **Şimdi yürüt** vs **Müşteriye bağlantı gönder**.
İkincisini seçmek bir iade-talep token'ı oluşturur (yukarıdaki B2B
çağrısıyla aynı) ve 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:

```ts
// 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()`](https://docs.infraio.xyz/tr/sdks/javascript#sdkopenrefundrequest-)
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:

1. Yenileme talebini gönderir (kimlik bilgisi gerekmez; bağlantının
   kendisi yetkilendirir)
2. Opsiyonel olarak alıcının sizin için bırakabileceği bir serbest metin
   not (`customer_note`) yakalar
3. Token'ı `RENEWAL_REQUESTED`'a geçirir ve webhook'unuza
   `refund_request.renewal_requested` tetikler

Paneliniz yenileme-talepleri widget'ında bir rozet gösterir. Onaylayın
(tek tık) ve yeni bir `ACTIVE` token verilir, `refund_request.renewed`
tetiklenir 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:

```http
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.

InfraIO Pay, bu işlemin gerekli onay sayısına ulaştığını
gördüğünde (bkz. [Zincirler ve varlıklar](https://docs.infraio.xyz/tr/concepts/chains)), iade
`EXECUTED` durumuna geçer ve Order'ın iade-toplamı güncellenir.

> **Warning:**
>
> 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ışı.

### TRON, Solana ve TON'da iadeler

Akış aynıdır: iadeyi kendi cüzdanınızdan gönderir, ardından işlem hash'ini iletirsiniz. Ayrıntılar ağa göre değişir:

- Panelde iade ekranı hedefi, tutarı, ağı ve tokeni gösterir; ağ destekliyorsa bir QR kodu da sunar: Solana'da Solana Pay QR'ı, TON'da bir TON transfer bağlantısı. TRON'da kopyalanacak hedef adresi gösterir (hiçbir cüzdan bağlantısı tutarı taşımaz), bu yüzden tutarı kendiniz girin.
- `token_address`, o ağdaki tokenin adresidir: TRC-20 kontratı, SPL mint'i veya Jetton master adresi.
- İşlem hash biçimleri farklıdır: TRON'da öneksiz hex, Solana'da base58 imza, TON'da hex veya base64 hash.
- Platform tam olarak bu işlemi zincir üstünde doğrular, ardından [Zincirler ve varlıklar](https://docs.infraio.xyz/tr/concepts/chains) sayfasındaki onay sayılarını kullanarak iadeyi `EXECUTED` durumuna geçirir.

---

## 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()`](https://docs.infraio.xyz/tr/sdks/javascript#sdkopenrefundrequest-) — barındırılan iade formunu popup / yönlendirme / gömme olarak açın.
- [API referansı → İadeler](https://docs.infraio.xyz/tr/api-reference#refunds) — uç nokta kataloğu (üret, gönder, yenile, durum).
- [Kavramlar → Siparişler](https://docs.infraio.xyz/tr/concepts/orders) — Refund durumunun Order yaşam döngüsüne nasıl bağlandığı.
- [Webhook'lar → Genel bakış](https://docs.infraio.xyz/tr/webhooks/overview) — tam event kataloğu.
