# Yükseltme ve Düşürme

https://dev.wisecp.com/tr/yukseltme-ve-dusurme

Bir hizmetin ürününü değiştiren, farkı faturalayan ve değişimi sonuna kadar takip eden dokuz uç.

## Genel Bakış

Bir müşteri ürününü değiştirdiğinde iki iş birden olur: para hesabı ve sunucudaki değişim. Bu dokuz uç ikisini de yürütür — önce hangi ürünlere geçilebileceğini ve farkı gösterir, sonra bir kayıt açar ve o kaydı sonuna kadar takip eder.

Değişim **anında olmaz**. Kayıt açıldığında yanıttaki `flow` alanı nasıl ilerleyeceğini söyler: ödeme bekleniyorsa fatura ödenene kadar durur, dönem sonuna bırakıldıysa o güne kadar bekler, işleme alındıysa arka plan görevi yürütür.

## Referans

### Seçenekleri Getirme

post/api/v1/admin/services/{id}/upgrade-options

`Services/GetUpgradeOptions` admin fiyat hesaplı

Hizmetin geçebileceği ürünleri, her biri için fiyat ve farkla birlikte döndürür.

Gövde 1

gradestringYön: `up` yükseltme, `down` düşürme. Varsayılan `up`.

Dönen alanlar data[] — 6

idintHedef ürünün kimliği.

titlestringÜrün adı.

typestringÜrün tipi.

modulestringÜrünün sunucu modülü.

categorystringÜrünün kategorisi.

pricesarray 9 alanBu ürüne geçmenin fiyat seçenekleri. Vergi, döviz ve döngü hesaplanmış hâlde gelir.

price_idintFiyatın kimliği. Değişimi oluştururken bu gönderilir.

periodstringDönem birimi.

period_timeintDönem çarpanı.

cyclestringFaturalama döngüsü.

amountfloatYeni dönem tutarı.

differencefloatMevcut hizmete göre fark.

tax_amountfloatVergi tutarı.

payablefloatVergi dahil ödenecek toplam.

currencyintPara birimi kimliği.

Hatalar 2

not_found404Hizmet bulunamadı.

insufficient_scope403Anahtar gerekli kapsamı taşımıyor.

İstek cURL JavaScript PHP (HTTP) PHP (Dahili)

```bash
curl -X POST 'https://panel.ornek.com/api/v1/admin/services/529/upgrade-options' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"grade":"up"}'
```

```javascript
const res = await fetch('https://panel.ornek.com/api/v1/admin/services/529/upgrade-options', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ grade: 'up' }),
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.ornek.com/api/v1/admin/services/529/upgrade-options');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['grade' => 'up']),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// Fiyat kimligi BURADAN gelir; katalogdaki fiyat kimligini varsaymayin.
$options = Api::Services()->GetUpgradeOptions(['id' => 529, 'grade' => 'up'])['data'];

$target = $options[0];
$price  = $target['prices'][0];

Api::Services()->CreateUpdowngrade([
    'id'         => 529,
    'product_id' => $target['id'],
    'price_id'   => $price['price_id'],
]);
```

Yanıt 200

```json
{
  "data": [
    {
      "id": 16,
      "title": "Pro SSD 2",
      "type": "hosting",
      "module": "cpanel",
      "category": "Hosting",
      "prices": [
        {
          "price_id": 12722,
          "period": "month",
          "period_time": 1,
          "cycle": "monthly",
          "amount": 185.82,
          "difference": 185.82,
          "tax_amount": 37.16,
          "payable": 222.99,
          "currency": 840
        }
      ]
    }
  ]
}
```

### Değişimi Oluşturma

post/api/v1/admin/services/{id}/updowngrade

`Services/CreateUpdowngrade` admin akışı sunucu seçer

Yükseltme ya da düşürme kaydı açar. Nasıl ilerleyeceğine gönderdiğiniz ayarlar karar verir.

Gövde 9

product_idintzorunluHedef ürünün kimliği.

price_idintSeçenek listesinden gelen fiyat kimliği. Vermezseniz varsayılan fiyat kullanılır.

typestring`up` ya da `down`. Varsayılan `up`.

invoice_generationstringFatura kipi: `none` fatura yok, `unpaid` ödenmemiş fatura, `paid` ödenmiş sayılan fatura.

refundstringDüşürmede iade: `none` ya da `credit` müşteri bakiyesine.

scheduleboolDüşürmeyi dönem sonuna erteler. Müşteri ödediği dönemi kullanmayı sürdürür.

notificationboolMüşteriye bildirim gönderir.

pmethodstringFaturanın ödeme yöntemi.

confirm_recreateboolModülün hesabı yeniden kurmasına onay verir. Gerekiyorsa ilk istek kayıt açmaz, uyarı döndürür.

Dönen alanlar data — 5

flowstringUygulanan akış: `invoice_unpaid` ödeme bekleniyor, `scheduled` dönem sonuna bırakıldı, `queued` işleme alındı.

updowngrade_idintAçılan kaydın kimliği.

invoice_idintÜretilen faturanın kimliği. Fatura yoksa sıfır.

warningstringOnay gerektiğinde `recreate` döner. Bu yanıtta kayıt **açılmamıştır**.

warning_keystringUyarının dil anahtarı.

Hatalar 5

not_found404Hizmet bulunamadı.

product_required422Hedef ürün verilmedi.

blocked_by_gate422`gate:service.upgrade` kancası işlemi veto etti.

subscription_cancel_failed500Eski ödeme aboneliği iptal edilemedi.

insufficient_scope403Anahtar gerekli kapsamı taşımıyor.

İstek cURL JavaScript PHP (HTTP) PHP (Dahili)

```bash
curl -X POST 'https://panel.ornek.com/api/v1/admin/services/529/updowngrade' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"product_id":16,"price_id":12722,"type":"up","invoice_generation":"unpaid","notification":true}'
```

```javascript
const res = await fetch('https://panel.ornek.com/api/v1/admin/services/529/updowngrade', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    product_id: 16,
    price_id: 12722,
    type: 'up',
    invoice_generation: 'unpaid',
    notification: true,
  }),
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.ornek.com/api/v1/admin/services/529/updowngrade');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'product_id'         => 16,
        'price_id'           => 12722,
        'type'               => 'up',
        'invoice_generation' => 'unpaid',
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
$response = Api::Services()->CreateUpdowngrade([
    'id'         => 529,
    'product_id' => 16,
    'price_id'   => 12722,
]);

// Uyari donduyse KAYIT ACILMAMISTIR: onaylayip istegi TEKRAR gonderin.
if (($response['data']['warning'] ?? '') === 'recreate') {
    Api::Services()->CreateUpdowngrade([
        'id'               => 529,
        'product_id'       => 16,
        'price_id'         => 12722,
        'confirm_recreate' => true,
    ]);
}
```

Yanıt 201 200

```json
{
  "data": {
    "flow": "invoice_unpaid",
    "updowngrade_id": 75,
    "invoice_id": 1222
  }
}
```

```json
{
  "data": {
    "warning": "recreate",
    "warning_key": "updown-recreate-warning"
  }
}
```

### Kayıtları Listeleme

get/api/v1/admin/services/updowngrades

`Services/GetUpdowngrades` admin sayfalı

Açılmış yükseltme ve düşürme kayıtlarını döndürür.

Sorgu parametreleri 5

searchstringFatura numarasında, ürün adında ve müşteride arar.

typestring`up` ya da `down`.

statusstringDuruma göre süzer.

pageintVarsayılan 1.

limitintVarsayılan 25, en çok 100.

Dönen alanlar data[] — 13

idintKaydın kimliği.

service_idintDeğişen hizmetin kimliği.

user_idintMüşterinin kimliği.

invoice_idintBağlı faturanın kimliği. Fatura üretilmediyse sıfır.

typestring`up` yükseltme, `down` düşürme.

statusstring`waiting`, `pending`, `inprocess`, `completed` ya da `cancelled`.

status_msgstringDuruma dair mesaj. Başarısız kayıtta hatanın kendisi burada yazar.

refundstringDüşürmede iade biçimi.

old_product_idintMevcut ürünün kimliği.

new_product_idintHedef ürünün kimliği.

created_atstringKaydın açıldığı zaman.

clientobject 3 alanHizmetin sahibi.

idintMüşteri kimliği.

full_namestringAd ve soyad.

company_namestringFirma adı.

detailsobject 8 alanEski ve yeni ürünün karşılaştırması.

old_namestringMevcut ürünün adı.

new_namestringHedef ürünün adı.

old_categorystringMevcut ürünün kategorisi.

new_categorystringHedef ürünün kategorisi.

old_amountfloatMevcut dönem tutarı.

new_amountfloatYeni dönem tutarı.

differencefloatİki tutar arasındaki fark.

currency_idintPara birimi kimliği.

Meta 4

totalintSüzgece uyan toplam kayıt.

pageintBulunduğunuz sayfa.

limitintSayfa boyutu.

next_pageintSonraki sayfa. Sıfır son sayfada olduğunuz anlamına gelir.

Hatalar 1

insufficient_scope403Anahtar gerekli kapsamı taşımıyor.

İstek cURL JavaScript PHP (HTTP) PHP (Dahili)

```bash
curl -G 'https://panel.ornek.com/api/v1/admin/services/updowngrades' \
  -H "Authorization: Bearer $API_KEY" \
  -d status=waiting
```

```javascript
const url = new URL('https://panel.ornek.com/api/v1/admin/services/updowngrades');
url.searchParams.set('status', 'waiting');

const res  = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } });
const body = await res.json();
```

```php
$url = 'https://panel.ornek.com/api/v1/admin/services/updowngrades?' . http_build_query(['status' => 'waiting']);

$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
$response = Api::Services()->GetUpdowngrades([], ['status' => 'waiting']);
```

### Kayıt Detayı

get/api/v1/admin/services/updowngrades/{uid}

`Services/GetUpdowngrade` admin

Tek bir değişim kaydını döndürür. Şema liste öğesiyle aynıdır.

Dönen alanlar data — 13

idintKaydın kimliği.

service_idintDeğişen hizmetin kimliği.

user_idintMüşterinin kimliği.

invoice_idintBağlı faturanın kimliği. Fatura üretilmediyse sıfır.

typestring`up` yükseltme, `down` düşürme.

statusstring`waiting`, `pending`, `inprocess`, `completed` ya da `cancelled`.

status_msgstringDuruma dair mesaj. Başarısız kayıtta hatanın kendisi burada yazar.

refundstringDüşürmede iade biçimi.

old_product_idintMevcut ürünün kimliği.

new_product_idintHedef ürünün kimliği.

created_atstringKaydın açıldığı zaman.

clientobject 3 alanHizmetin sahibi.

idintMüşteri kimliği.

full_namestringAd ve soyad.

company_namestringFirma adı.

detailsobject 8 alanEski ve yeni ürünün karşılaştırması.

old_namestringMevcut ürünün adı.

new_namestringHedef ürünün adı.

old_categorystringMevcut ürünün kategorisi.

new_categorystringHedef ürünün kategorisi.

old_amountfloatMevcut dönem tutarı.

new_amountfloatYeni dönem tutarı.

differencefloatİki tutar arasındaki fark.

currency_idintPara birimi kimliği.

Hatalar 2

not_found404Kayıt bulunamadı.

insufficient_scope403Anahtar gerekli kapsamı taşımıyor.

İstek cURL JavaScript PHP (HTTP) PHP (Dahili)

```bash
curl 'https://panel.ornek.com/api/v1/admin/services/updowngrades/74' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res  = await fetch('https://panel.ornek.com/api/v1/admin/services/updowngrades/74', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
```

```php
$ch = curl_init('https://panel.ornek.com/api/v1/admin/services/updowngrades/74');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
$record = Api::Services()->GetUpdowngrade(['uid' => 74])['data'];

// Takilan bir kaydin sebebi status_msg alanindadir.
$why = $record['status_msg'];
```

### Kaydı Onaylama

post/api/v1/admin/services/updowngrades/{uid}/approve

`Services/ApproveUpdowngrade` admin

Varsa iadeyi işler ve kaydı işleme alır. Değişimi arka plan görevi yürütür.

Gövde —

——Gövde gerekmez, boş gönderin. Kaydı adresteki kimlik belirler.

Dönen alanlar data — 2

statusstringİşlem sonrası durum.

idintKaydın kimliği.

Hatalar 3

not_found404Kayıt bulunamadı.

already_completed422Tamamlanmış kayıt onaylanamaz.

insufficient_scope403Anahtar gerekli kapsamı taşımıyor.

İstek cURL JavaScript PHP (HTTP) PHP (Dahili)

```bash
curl -X POST 'https://panel.ornek.com/api/v1/admin/services/updowngrades/74/approve' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res = await fetch('https://panel.ornek.com/api/v1/admin/services/updowngrades/74/approve', {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.ornek.com/api/v1/admin/services/updowngrades/74/approve');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// Onay iadeyi bir KEZ isler; ayni kaydi tekrar onaylamak ikinci iade uretmez.
$response = Api::Services()->ApproveUpdowngrade(['uid' => 74]);
```

### Kaydı Yeniden Deneme

post/api/v1/admin/services/updowngrades/{uid}/retry

`Services/RetryUpdowngrade` admin

Takılmış bir kaydı yeniden işleme alır.

Gövde —

——Gövde gerekmez, boş gönderin. Kaydı adresteki kimlik belirler.

Dönen alanlar data — 2

statusstringİşlem sonrası durum.

idintKaydın kimliği.

Hatalar 3

not_found404Kayıt bulunamadı.

already_completed422Tamamlanmış kayıt yeniden denenemez.

insufficient_scope403Anahtar gerekli kapsamı taşımıyor.

İstek cURL JavaScript PHP (HTTP) PHP (Dahili)

```bash
curl -X POST 'https://panel.ornek.com/api/v1/admin/services/updowngrades/74/retry' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res = await fetch('https://panel.ornek.com/api/v1/admin/services/updowngrades/74/retry', {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.ornek.com/api/v1/admin/services/updowngrades/74/retry');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// Yeniden denemeden once sebebe bakin: ayni kosul surerse ayni yerde takilir.
$record = Api::Services()->GetUpdowngrade(['uid' => 74])['data'];

if ($record['status_msg'] === '') {
    Api::Services()->RetryUpdowngrade(['uid' => 74]);
}
```

### Kaydı Tamamlama

post/api/v1/admin/services/updowngrades/{uid}/complete

`Services/CompleteUpdowngrade` admin modülsüz değişim

Modülü olmayan bir değişimi elle sonlandırır.

Gövde —

——Gövde gerekmez, boş gönderin. Kaydı adresteki kimlik belirler.

Dönen alanlar data — 2

statusstringİşlem sonrası durum.

idintKaydın kimliği.

Hatalar 3

not_found404Kayıt bulunamadı.

complete_failed500Tamamlama başarısız oldu.

insufficient_scope403Anahtar gerekli kapsamı taşımıyor.

İstek cURL JavaScript PHP (HTTP) PHP (Dahili)

```bash
curl -X POST 'https://panel.ornek.com/api/v1/admin/services/updowngrades/74/complete' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res = await fetch('https://panel.ornek.com/api/v1/admin/services/updowngrades/74/complete', {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.ornek.com/api/v1/admin/services/updowngrades/74/complete');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// Modulu olan bir hizmette bunu cagirmayin: degisimi arka plan gorevi yurutur.
$response = Api::Services()->CompleteUpdowngrade(['uid' => 74]);
```

### Kaydı Silme

delete/api/v1/admin/services/updowngrades/{uid}

`Services/DeleteUpdowngrade` admin fatura da iptal olur

Kaydı siler, bağlı ödenmemiş faturayı iptal eder ve bekleyen işleri durdurur.

Dönen alanlar data — 2

deletedboolSilme başarılı mı.

idintSilinen kaydın kimliği.

Hatalar 2

not_found404Kayıt bulunamadı.

insufficient_scope403Anahtar gerekli kapsamı taşımıyor.

İstek cURL JavaScript PHP (HTTP) PHP (Dahili)

```bash
curl -X DELETE 'https://panel.ornek.com/api/v1/admin/services/updowngrades/74' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res = await fetch('https://panel.ornek.com/api/v1/admin/services/updowngrades/74', {
  method: 'DELETE',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.ornek.com/api/v1/admin/services/updowngrades/74');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'DELETE',
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// Tamamlanmis bir kaydi silmek degisimi GERI ALMAZ: hizmet yeni urunde kalir.
$response = Api::Services()->DeleteUpdowngrade(['uid' => 74]);
```

### Planlı Düşürmeyi İptal Etme

post/api/v1/admin/services/{id}/cancel-scheduled-downgrade

`Services/CancelScheduledDowngrade` admin

Dönem sonuna bırakılmış bir düşürmeyi hizmete uygulanmadan geri alır.

Gövde —

——Gövde gerekmez, boş gönderin. Hizmeti adresteki kimlik belirler.

Dönen alanlar data — 2

cancelledboolİptal başarılı mı.

idintİptal edilen kaydın kimliği.

Hatalar 2

not_found404Planlı düşürme bulunamadı.

insufficient_scope403Anahtar gerekli kapsamı taşımıyor.

İstek cURL JavaScript PHP (HTTP) PHP (Dahili)

```bash
curl -X POST 'https://panel.ornek.com/api/v1/admin/services/529/cancel-scheduled-downgrade' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res = await fetch('https://panel.ornek.com/api/v1/admin/services/529/cancel-scheduled-downgrade', {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.ornek.com/api/v1/admin/services/529/cancel-scheduled-downgrade');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
// Adres HIZMET kimligini alir, kayit kimligini degil: bekleyen plan hizmetten bulunur.
$response = Api::Services()->CancelScheduledDowngrade(['id' => 529]);
```

## Tuzaklar

> **Uyarı yanıtında kayıt açılmamıştır**
> 
> Değişim modülün hesabı yeniden kurmasını gerektiriyorsa ilk istek `200` ve bir uyarı döndürür, ama **hiçbir kayıt açmaz**. İşlemin gerçekleşmesi için onayı ekleyip isteği yeniden göndermeniz gerekir. Yanıtta `updowngrade_id` yoksa hiçbir şey olmamıştır.

> **Fiyat kimliği seçenek listesinden gelir**
> 
> Değişim oluştururken gönderilen fiyat kimliği, seçenek ucundan dönen listedeki kimliktir. Katalogdaki fiyatı doğrudan kullanmak yanlış dönem ya da yanlış para birimiyle işlem açar; listede dönen tutarlar zaten vergi ve döviz hesaplanmış hâldedir.

> **Silmek tamamlanmış değişimi geri almaz**
> 
> Silme ucu bekleyen bir kaydı iptal etmek için vardır: ödenmemiş faturayı da iptal eder ve bekleyen işleri durdurur. Tamamlanmış bir kaydı silmek hizmeti eski ürüne **döndürmez**; geri almak için ters yönde yeni bir değişim açmanız gerekir.

> **Yeniden denemeden önce sebebi okuyun**
> 
> Takılan bir kaydın sebebi `status_msg` alanında yazar. Aynı koşul sürerken yeniden denemek kaydı aynı yerde takar; önce sebebi giderin. Modülü olmayan bir değişim zaten kendiliğinden ilerlemez, onun için tamamlama ucu vardır.

> **Planlı düşürme iptali hizmet kimliği ister**
> 
> Diğer sekiz uç kayıt kimliğiyle çalışırken planlı düşürme iptali **hizmet** kimliğini alır: bekleyen plan hizmetten bulunur. Kayıt kimliğini vermek `404` döndürür.

## İlgili Makaleler

- [Hizmet Uçları](https://dev.wisecp.com/tr/hizmet-uclari)
- [Yenileme ve İptal](https://dev.wisecp.com/tr/yenileme-ve-iptal)
- [Ürün Uçları](https://dev.wisecp.com/tr/urun-uclari)
