# Hizmet İptal Kancaları

https://dev.wisecp.com/tr/hizmet-iptal-kancalari

Müşteri "bu hizmeti istemiyorum" dediğinde çalışan dokuz kanca: talep, onay, geri alma ve asıl iptal.

## Genel Bakış

İptalin **iki katmanı** vardır ve karıştırılırsa yanlış yere kod yazılır. Üstteki katman **talep**tir: müşteri ister, operatör onaylar ya da müşteri vazgeçer. Alttaki katman **işin kendisidir**: hizmet gerçekten kapatılır.

Talep katmanı bir kayıt tutar ve müşteriyle konuşur. İş katmanı ise zamanlanmış görevden çalışır ve sağlayıcıya gider. Bir hizmet **talep olmadan da** iptal edilebilir: ödenmemiş fatura bunu kendiliğinden yapar.

## Referans

### İptal talebini durdurma

gateservice.cancellation_request

`ClientServices` müşteri isteği

Müşteri iptal talebi oluşturmadan önce çalışır. Durdurursanız **talep kaydı yazılmaz**.

Parametreler 3

$servicearrayMüşterinin kendi hizmeti. Alan adları bu akıştan geçmez.

$urgencystringZamanlama: `now` hemen, `period-ending` dönem sonunda. İkisi çok farklı iştir: biri hizmeti bugün kapatır, diğeri ödenmiş dönemin sonunu bekler.

$reasonKeystringNeden anahtarı: `not-needed`, `too-expensive`, `switching`, `missing-features`, `other`.

Dönüş 1

stringBoş olmayan bir string talebi **durdurur**; müşteriye hata olarak döner.

Dinleyici PHP

```php
Hook::add('gate:service.cancellation_request', 10,
    function ($service, $urgency, $reasonKey) {
        // Taahhutlu hizmet donem sonundan once kapanmaz.
        if ($urgency === 'now' && Acme::underContract($service))
            return 'Taahhut suresi dolmadan hemen iptal yapilamaz.';

        return null;
    });
```

### İptal talebini izleme

actionservice.cancellation_requested

`ClientServices` kayıt kimliği verir

Talep kaydedildikten sonra çalışır. Hizmet bu noktada **hâlâ çalışıyor**.

Parametreler 5

$servicearrayHizmet kaydı.

$urgencystringZamanlama: `now` hemen, `period-ending` dönem sonunda. İkisi çok farklı iştir: biri hizmeti bugün kapatır, diğeri ödenmiş dönemin sonunu bekler.

$reasonKeystringNeden anahtarı: `not-needed`, `too-expensive`, `switching`, `missing-features`, `other`.

$reasonstringSaklanan neden metni: müşterinin dilindeki etiket ve varsa serbest açıklama. `other` seçiminde yalnız serbest metin gelir.

$eventIdintOluşan talep kaydının kimliği. Onay ve geri alma kancalarında aynı kayda döner.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:service.cancellation_requested', 10,
    function ($service, $urgency, $reasonKey, $reason, $eventId) {
        // Hizmet HALA calisiyor: bu bir niyet bildirimi, kapanis degil.
        Retention::opened($eventId, $reasonKey, $urgency);
    });
```

### İptal onayını durdurma

gateservice.cancellation_accept

`AdminServices` operatör onayı

Operatör iptal talebini onaylamadan önce çalışır.

Parametreler 2

$service_idintİptal edilecek hizmetin kimliği. Hizmet kaydı değil **kimlik** gelir.

$requestarrayTalep kaydı: `id`, `owner_id` ve çözülmüş `data` (zamanlama, neden).

Dönüş 1

stringBoş olmayan bir string onayı **durdurur**; operatöre hata olarak döner.

Dinleyici PHP

```php
Hook::add('gate:service.cancellation_accept', 10, function ($service_id, $request) {
    // Odenmemis faturasi olan hizmet iptal edilirse alacak tahsil edilemez.
    if (Acme::hasUnpaid($service_id)) return 'Once odenmemis faturayi kapatin.';

    return null;
});
```

### İptal onayını izleme

actionservice.cancellation.accepted

`AdminServices` onaylandı

Operatör talebi onayladıktan sonra çalışır.

Parametreler 3

$serviceIdintHizmet kimliği.

$cancellationTypestringİptal türü: hemen ya da dönem sonunda. Dönem sonu seçildiyse hizmet **bugün kapanmaz**.

$requestDataarrayTalep verisi: neden ve onaylayan bilgisi.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:service.cancellation.accepted', 10,
    function ($serviceId, $cancellationType, $requestData) {
        // Donem sonu iptalinde hizmet bugun kapanmaz; sayaci simdiden dusurmeyin.
        if ($cancellationType === 'now') Capacity::freed($serviceId);
    });
```

### Talebin geri alınmasını izleme

actionservice.cancellation_revoked

`ClientServices` müşteri vazgeçti

Müşteri iptal talebinden vazgeçtiğinde çalışır.

Parametreler 3

$servicearrayHizmet kaydı.

$eventarrayTalebin **silinmeden önceki** kaydı; içinde zamanlama, neden anahtarı, neden metni ve not bulunur. Kayıt artık yok, veriye buradan bakın.

$uidintHizmet sahibinin hesap kimliği.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:service.cancellation_revoked', 10,
    function ($service, $event, $uid) {
        // Vazgecme, elde tutma calismasinin basarisidir: kaydini kapatin.
        Retention::closed((int) ($event['id'] ?? 0), 'revoked');
    });
```

### İptal işini durdurma

gateservice.cancel

`cronjobs/ServiceCancel` veto yine iptal eder

Hizmet ya da ek hizmet gerçekten iptal edilmeden önce çalışır. Bu, talep katmanının **altındaki** kapıdır.

Parametreler 4

$target_typestring`service` ya da `addon`.

$target_idintİptal adayının kimliği.

$servicearrayCanlı satır: `status`, `type`, `module`, `duedate`.

$user_idintHizmet sahibi.

Dönüş 1

stringBoş olmayan bir string iptali **veto eder** — ama iş yine de **iptal sinyaline** çevrilir ve gerekçeniz kayda geçer. Askı kapısıyla aynı davranış: veto "hiçbir şey olmasın" değildir.

Dinleyici PHP

```php
Hook::add('gate:service.cancel', 10,
    function ($target_type, $target_id, $service, $user_id) {
        // Veri hala tasiniyorsa iptali bekletmek isteyebilirsiniz.
        if (Acme::migrationRunning($target_id)) return 'Tasima suruyor.';

        return null;
    });
```

### Eklenti iptal talebini izleme

actionservice.addon.cancellation_requested

`ClientServices` dönem sonunda

Müşteri bir eklentinin dönem sonunda iptalini istediğinde çalışır. Talep kaydedilmiştir ama **eklenti hâlâ etkindir**: vade gelene kadar çalışmaya devam eder.

Parametreler 5

$servicearrayEklentinin bağlı olduğu hizmet.

$addonarrayİptali istenen eklenti. Bu anda satır hâlâ etkin durumdadır; iptal vadede uygulanır.

$reasonstringMüşterinin yazdığı gerekçe. Zorunlu alan değil, **boş gelebilir**. 300 karaktere kırpılmıştır.

$eventIdintOluşan iptal talebi kaydının kimliği.

$uidintEklentinin sahibi hesap. İşlemi yapan alt kullanıcı olabilir; o başka bir kimliktir.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:service.addon.cancellation_requested', 10,
    function ($service, $addon, $reason, $eventId, $uid) {
        // Elde tutma teklifini baslatin: eklenti hala calisiyor, vakit var.
        Acme::offerRetention($uid, $addon['addon_name'] ?? '', $reason);
    });
```

### Eklenti iptalinin geri alınmasını izleme

actionservice.addon.cancellation_revoked

`ClientServices` vazgeçme

Müşteri iptal talebinden vazgeçtiğinde çalışır. Başlattığınız elde tutma akışını durdurmanın yeri burasıdır.

Parametreler 4

$servicearrayEklentinin bağlı olduğu hizmet.

$addonarrayİptali geri alınan eklenti.

$eventarray**Silinmiş** iptal talebi kaydı; gerekçe ve onay bilgisi içindedir. Kanca çalıştığında bu satır veritabanında artık yoktur, elinizdeki son kopyadır.

$uidintHizmetin sahibi hesap.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:service.addon.cancellation_revoked', 10,
    function ($service, $addon, $event, $uid) {
        Acme::stopRetention($uid, $addon['addon_name'] ?? '');
    });
```

### Planlı paket düşürmenin iptalini izleme

actionservice.scheduled_downgrade_cancelled

`ClientServices` vazgeçme

Vade sonunda uygulanacak paket düşürme iptal edildiğinde çalışır. Hizmet mevcut paketiyle devam eder.

Parametreler 3

$servicearrayHizmet kaydı. Paket **değişmedi**: düşürme zaten uygulanmamıştı, planlanmıştı.

$scheduledarrayİptal edilen planlı değişiklik, **güncelleme öncesi** hâliyle. Durum alanı hâlâ eski değerini taşır.

$uidintHizmetin sahibi hesap.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:service.scheduled_downgrade_cancelled', 10,
    function ($service, $scheduled, $uid) {
        // Kapasite planindan dusurmeyi geri alin.
        Acme::releaseForecast((int) ($service['id'] ?? 0));
    });
```

## Tuzaklar

> **Talep ile iptal aynı şey değildir**
> 
> Talep kancaları müşteri **istediğinde** çalışır; hizmet hâlâ ayaktadır. Asıl kapanış alt katmandaki iptal kancalarında olur. Kapasite düşmek, veri silmek ya da son faturayı kesmek gibi işleri talep kancasına yazmak, **müşteri vazgeçerse** geri alınamayan sonuç doğurur.

> **Dönem sonu iptali bugün kapatmaz**
> 
> Onay kancasındaki tür değeri **hemen** ya da **dönem sonu** olabilir. İkincisinde hizmet ödenmiş dönemin sonuna kadar çalışmaya devam eder. Türü okumadan "iptal edildi" varsayan bir dinleyici, müşterinin hâlâ kullandığı hizmeti kapalı sayar.

> **İptal vetosu iptali engellemez**
> 
> İptal kapısında metin döndürmek işi durdurmaz; sinyal **yine iptale** çevrilir, yalnız gerekçeniz kayda geçer. Askı kapısıyla aynı tuzak. Gerçekten engellemek istiyorsanız bunu **talep katmanında** yapın.

> **İptal her zaman talepten gelmez**
> 
> Ödenmemiş fatura, hizmeti **talep olmadan** iptale götürür. Yalnız talep kancalarını dinleyen bir sistem bu iptalleri hiç görmez. Bütün iptalleri görmek istiyorsanız alt katmandaki durum kancasını dinleyin.

## İlgili Makaleler

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