# Ödeme Kancaları

https://dev.wisecp.com/tr/odeme-kancalari

Paranın gerçekten girdiği on üç kanca: ödeme kapıları, kaydedilen ödeme satırı ve otomatik tahsilat.

## Genel Bakış

Bir fatura **tek seferde** ödenmek zorunda değildir: kısmi ödemeler birikir ve bakiye sıfırlandığında fatura kendiliğinden ödenmişe geçer. Bu yüzden ödeme kancaları fatura durum kancasından **daha sık** çalışır.

İkinci nokta kimin ödediğidir. Paylaşım bağlantısıyla yapılan ödemede **ödeyen kişi kimliksizdir**; kancanın verdiği hesap kimliği faturanın **sahibini** gösterir, ödeyeni değil.

## Referans

### Müşteri ödemesini durdurma

gateinvoice.client_pay

`ClientInvoicePay` para hareket etmeden

Müşteri fatura öderken, **hiçbir para hareket etmeden** önce çalışır.

Parametreler 4

$invoicearrayFatura kaydı. Para birimi kaydın kendi alanındadır; ayrı bir parametre gelmez.

$uidintFaturanın **sahibi**. Paylaşım ödemesinde bile sahiptir; ödeyen kişiyi göstermez.

$methodarrayÇözülmüş ödeme yöntemi: akış tipi, komisyon oranı, ham bakiye, banka hesapları, ödeme kuruluşu ayrıntıları.

$shareboolPaylaşım bağlantısıyla mı ödeniyor. `true` iken oturum **yoktur** ve ödeyenin kim olduğu bilinmez.

Dönüş 1

stringBoş olmayan bir string ödemeyi **durdurur**: komisyon kitaplanmaz, bakiye düşmez, ödeme kaydı hiç açılmaz.

Dinleyici PHP

```php
Hook::add('gate:invoice.client_pay', 10, function ($invoice, $uid, $method, $share) {
    // Paylasim odemesinde odeyen kimliksizdir: risk kurallarini ona gore kurun.
    if ($share && (float) ($invoice['total'] ?? 0) > 5000)
        return 'Bu tutar paylasim linkiyle odenemez.';

    return null;
});
```

### Toplu ödemeyi durdurma

gateinvoice.bulk_pay

`ClientInvoicePay` para birimi ayrı gelir

Müşteri birden çok faturayı birlikte öderken çalışır.

Parametreler 3

$invoicesarrayÖdenecek faturalar.

$cidintSeçimin para birimi. Tekil ödeme kapısının aksine burada **ayrı bir parametre** olarak gelir, çünkü seçim tek para biriminde toplanır.

$methodarrayÇözülmüş ödeme yöntemi.

Dönüş 1

stringBoş olmayan bir string toplu ödemeyi **durdurur**.

Dinleyici PHP

```php
Hook::add('gate:invoice.bulk_pay', 10, function ($invoices, $cid, $method) {
    if (count($invoices) > 50) return 'Tek seferde en fazla 50 fatura odenir.';

    return null;
});
```

### Ödeme satırını yazılmadan değiştirme

filterinvoice.payment_data

`Hook::runRefs` referansla

Ödeme satırı yazılmadan önce çalışır. **Bu satırdan sonra** fatura kendiliğinden ödenmişe geçebilir.

Parametreler 2

$payment_rowarrayrefYazılacak ödeme: `owner_id`, `amount_in`, `currency`, `rate`, `fees`, `pmethod`, `transaction_id`, `paid_at`.

$invoicearrayÖdemenin uygulandığı fatura.

Dönüş 1

voidDeğer **referansla** değişir; dönüş kullanılmaz. Tutarı değiştirmek faturanın ödenmiş sayılıp sayılmayacağını belirler.

Dinleyici PHP

```php
Hook::add('filter:invoice.payment_data', 10, function (&$payment_row, $invoice) {
    // Kendi referansinizi tasiyin; tutari degistirmek odendi kararini etkiler.
    $payment_row['transaction_id'] = Acme::ref($payment_row['transaction_id'] ?? '');
});
```

### Ödemenin kaydedildiğini öğrenme

actioninvoice.payment_recorded

`Invoices` kısmi olabilir

Ödeme satırı yazıldıktan sonra çalışır. Fatura bu noktada **hâlâ ödenmemiş** olabilir.

Parametreler 3

$payment_idintYeni ödeme kaydının kimliği.

$invoice_idintÖdemenin uygulandığı fatura.

$paymentarrayKaydedilen ödeme: tutar, para birimi, kur, yöntem, işlem numarası, ödeme zamanı, komisyon, kaydeden. Tutar faturanın **tamamı olmayabilir**.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:invoice.payment_recorded', 10,
    function ($payment_id, $invoice_id, $payment) {
        // Kismi odeme olabilir: 'fatura kapandi' demeden once bakiyeye bakin.
        Accounting::received($invoice_id, (float) ($payment['amount_in'] ?? 0));
    });
```

### Ödemenin eklendiğini izleme

actioninvoice.payment_added

`AdminInvoices` düz parametreler

Faturaya ödeme eklendikten sonra çalışır. Bir önceki kancanın aksine değerler **ayrı ayrı** gelir.

Parametreler 5

$invoice_idintFatura kimliği.

$amountfloatÖdeme tutarı, **faturanın** para biriminde.

$currencyIdintFatura para biriminin kimliği.

$pmethodstringÖdeme yöntemi.

$txn_idstringDış işlem numarası.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:invoice.payment_added', 10,
    function ($invoice_id, $amount, $currencyId, $pmethod, $txn_id) {
        Accounting::line($invoice_id, (float) $amount, (int) $currencyId, $pmethod);
    });
```

### Ödemenin silindiğini izleme

actioninvoice.payment_deleted

`AdminInvoices` fatura kimliği 0 olabilir

Bir ödeme kaydı silindikten sonra çalışır.

Parametreler 2

$payment_idintSilinen ödemenin kimliği.

$invoice_idintBağlı olduğu fatura. Ödeme bir faturaya bağlı değilse `0` gelir.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:invoice.payment_deleted', 10, function ($payment_id, $invoice_id) {
    if ($invoice_id === 0) return;                 // faturaya bagli degil

    Accounting::reversed($invoice_id, (int) $payment_id);
});
```

### Otomatik tahsilatı izleme

actioninvoice.auto_payment_attempted

`cronjobs/InvoiceAutoPayment` deneme sonucu

Otomatik ödeme denendikten sonra çalışır — **başarılı olsun ya da olmasın**.

Parametreler 3

$invoice_idintDenenen fatura.

$final_statusstringSonuç: `paid` ya da `unpaid`. Denemenin başarısını buradan okuyun.

$resultarrayAdım adım sonuç: bakiye adımı ve kart adımı ayrı ayrı (`outcome`, `amount`, `error`), ayrıca başlangıç ve bitiş bakiyesi. Kart neden düştü sorusunun cevabı buradadır.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:invoice.auto_payment_attempted', 10,
    function ($invoice_id, $final_status, $result) {
        // Kart adiminin hatasi ayri gelir: musteriye dogru sebebi soyleyin.
        if ($final_status !== 'paid')
            Dunning::failed($invoice_id, $result['card_step']['error'] ?? '');
    });
```

### Havale bildirimini izleme

actioninvoice.bank_transfer_notified

`ClientInvoicePay` müşteri beyanı

Müşteri havale ettiğini bildirdiğinde çalışır. **Para gelmiş değildir**: bu yalnız bir beyandır, fatura ödenmemiş kalır.

Parametreler 3

$idintHavale bildirilen faturanın kimliği.

$uidintFaturanın **sahibi**. Paylaşım bağlantısıyla ödemede bildirimi başkası yapmış olabilir; bu değer yine sahibi gösterir.

$transferarrayBildirim yükü: banka kimliği ve adı, gönderen adı ve **havale referansı**. Referans, ekstre eşleştirmesinin anahtarıdır: tekli ödemede fatura numarası, toplu ödemede seçimin tamamı için ortak bir değer.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:invoice.bank_transfer_notified', 10, function ($id, $uid, $transfer) {
    // Para gelmedi, yalnizca beyan var: ekstre eslesmesini referanstan kurun.
    Acme::watchStatement($transfer['rce'] ?? '', $id);
});
```

### Kasa kaydını izleme

actioninvoice.cash_recorded

`AdminMoney` kasa kaydı

Kasaya elle bir gelir ya da gider kaydı girildiğinde çalışır.

Parametreler 4

$inex_idintOluşan kaydın kimliği.

$typestring`income` ya da `expense`.

$amountfloatTutar, biçimden arındırılmış hâliyle.

$currencyintPara biriminin kimliği.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:invoice.cash_recorded', 10,
    function ($inex_id, $type, $amount, $currency) {
        Acme::postToLedger($inex_id, $type, $amount, $currency);
    });
```

### Kuruluş üzerinden iadeyi izleme

actioninvoice.refunded_via_module

`AdminInvoices` kuruluş üzerinden

Bir fatura ödeme kuruluşu üzerinden iade edildiğinde çalışır. Para gerçekten geri gönderilmiştir.

Parametreler 2

$invoicearrayİade edilen fatura.

$pmethodstringİadeyi işleyen ödeme modülü.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:invoice.refunded_via_module', 10, function ($invoice, $pmethod) {
    Acme::recordRefund((int) ($invoice['id'] ?? 0), $pmethod);
});
```

### Ödeme yönteminin değişmesini izleme

actioninvoice.gateway_changed

`AdminInvoices` yöntem değişimi

Bir faturanın ödeme yöntemi değiştikten sonra çalışır.

Parametreler 4

$idintFatura kimliği.

$oldPmethodstringÖnceki yöntem; hiç seçilmemişse boş gelir.

$newPmethodstringYeni yöntem.

$invoicearrayFatura kaydı, **değişimden önceki** hâliyle.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:invoice.gateway_changed', 10,
    function ($id, $oldPmethod, $newPmethod, $invoice) {
        // Yontem degistiyse eski kurulustaki bekleyen oturumu kapatin.
        if ($oldPmethod !== '') Acme::dropPendingSession($id, $oldPmethod);
    });
```

### Kartın otomatik ödeme sırasını izleme

actionpayment.card_autopay_changed

`AccountCards` zincir değişimi

Bir kartın otomatik ödeme zincirindeki yeri değiştiğinde çalışır. Zincir, yenileme sırasında hangi kartın önce denendiğini belirler.

Parametreler 3

$uidintKartın sahibi.

$cardIdintKaydın kimliği.

$actionstringNe oldu: `on` zincire eklendi, `off` çıkarıldı, `promote` ilk sıraya alındı.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:payment.card_autopay_changed', 10, function ($uid, $cardId, $action) {
    // Zincir bosaldiysa musteri yenilemede odemesiz kalir.
    if ($action === 'off') Acme::warnIfChainEmpty($uid);
});
```

### Abonelik tahsilatını izleme

actionsubscription.payment_recorded

`SubscriptionPoll` anlaşmadan

Ödeme kuruluşundan gelen abonelik tahsilatı işlendikten sonra çalışır. Sonucun başarılı olması şart değildir: reddedilen ve tekrarlanan bildirimler de buraya düşer.

Parametreler 2

$identifierstringKuruluştan gelen abonelik tanımlayıcısı.

$resultarraySonuç: durumu, ilgili fatura ve ödeme kimliği, gerekirse gerekçe. Durum `paid` olabileceği gibi kısmi, tekrarlanmış ya da reddedilmiş de olabilir. Başarı varsaymayın.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:subscription.payment_recorded', 10, function ($identifier, $result) {
    // Basari varsaymayin: durum reddedilmis de olabilir.
    if (($result['status'] ?? '') !== 'paid') return;

    Acme::confirmCharge($identifier, (int) ($result['invoice_id'] ?? 0));
});
```

## Tuzaklar

> **Ödeme kaydı faturanın kapandığı anlamına gelmez**
> 
> Kısmi ödemeler ayrı satırlar olarak birikir; kanca **her satırda** çalışır. Fatura ancak bakiye sıfırlandığında ödenmişe geçer ve o an **durum kancasında** görünür. "Ödeme geldi, hizmeti aç" mantığını buraya yazmak, **eksik ödemede** hizmeti açar.

> **Paylaşım ödemesinde ödeyen kimliksizdir**
> 
> Ödeme kapısındaki hesap kimliği **her zaman faturanın sahibidir**. Paylaşım bağlantısıyla ödeyen kişi bambaşka biri olabilir ve oturumu yoktur. "Bu ödemeyi kim yaptı" sorusunu bu kimlikten cevaplamak **yanlış kişiyi** gösterir.

> **İki ödeme olayının şekli farklıdır**
> 
> Biri ödemeyi **tek dizi** içinde verir, diğeri tutar, para birimi, yöntem ve işlem numarasını **ayrı parametreler** olarak. Yanlışını yazmak dinleyiciyi düşürür ve dışarıdan "çalışmıyor" gibi görünür.

> **Otomatik tahsilat iki adımlıdır**
> 
> Önce bakiye denenir, sonra kart. Sonuç dizisi **ikisini ayrı ayrı** raporlar. Müşteriye "kartınız reddedildi" demeden önce bakiye adımının ne yaptığına bakın: fatura bakiyeden kısmen kapanmış olabilir.

## İlgili Makaleler

- [Fatura Yaşam Döngüsü Kancaları](https://dev.wisecp.com/tr/fatura-yasam-dongusu-kancalari)
- Fatura ve Ödeme Kancaları
- Sipariş Akışı Kancaları
