# Sepet ve Ödeme

https://dev.wisecp.com/tr/sepet-ve-odeme

Satın alma hunisini on dört view kurar, hepsi tek kabuğu paylaşır. Sipariş özeti geniş ekranda belgeden dışarı taşınır.

## Genel Bakış

Yapılandırma, sepet ve ödeme genel header ile footer'ı bırakıp adım göstergeli sade bir kabuğa geçer. Satın almanın ortasındaki ziyaretçiye çıkış sunulmaz.

İki şey temadaki hiçbir şeye benzemez. Sipariş özeti masaüstünde kabından taşınır. Kenar çubuğu düzeni sayfa değil tema başına bir karardır.

## Yapı

`$checkout_step` ile numaralanan dört adres, artı siparişi izleyen ekranlar.

| Adım | View | Ziyaretçi ne yapar |
| --- | --- | --- |
| 1 | `checkout/configure` | Döngü, alan adı, gereksinim ve eklenti seçer |
| 1 | `checkout/configure-addon` | Sahip olduğu hizmet için eklenti yapılandırır |
| 1 | `checkout/configure-domain` | Sepetteki alan adı satırını düzenler |
| 2 | `checkout/cart` | Satırları gözden geçirir, kupon uygular, kalem siler |
| 3 | `checkout/checkout` | Hesap, fatura ve ödemeyi tek sayfada verir |
| 3 | `checkout/pay` | Sepet dışında bir faturayı öder |
| 4 | `checkout/order-complete` | Sipariş onayı |
| 4 | `checkout/invoice-complete` | Doğrudan ödenen faturanın onayı |
| 4 | `checkout/pay-result` | Yönlendiren geçitten dönüş |

Kalan beşi kendi layout'unu taşımaz.

- **section-account, section-billing, section-payment**: `checkout/checkout.tpl` dahil eder, kart başına biri.
- **section-rail-items**: Özetin kalem satırları; özet iki yerde göründüğü için ayrıdır.
- **checkout/pay-choices**: Dahil edilmez; üç operation AJAX HTML'i olarak döndürür.

## Adım Adım

### Huni Kabuğunu Kurun

1. `layouts/checkout.tpl`'i genişletin: adım göstergesi, ince footer, kabuk script'i, gezinme yok.
2. Adımı view'da hesaplamayın; `$checkout_step`'i controller kurar.
3. Sayfa script'lerini `{block name=scripts}`'e koyun: çekirdek önce, kabuk sonra yüklenir.
4. Modal'ları `{block name=body_end}`'e koyun: `<main>` içindeki yığın bağlamı sabit katmanı bozar.

### Sipariş Özetini Doğru Kurun

Burada iç içeliği sayın; girinti yanıltır.

1. Aside, split kabının **doğrudan çocuğu** ve ana kolonun **kardeşi** olmalıdır.
2. Forma bir `id` verin, dışarıdaki gönder butonlarını `form` özniteliğiyle bağlayın: stack eylem çubuğu ve taşınan aside dışarıda kalır.
3. Özeti operation'ların döndürdüğü şekilden kurun; iki şekil iki toplam demektir.

### Kenar Çubuğu Varyantlarını Sunun

1. `checkout_sidebar`'ı üç seçenekli select olarak bildirin: rail, card, stack.
2. Seçimi layout'tan kök elemana damgalayın. Ray varsayılandır ve sınıf taşımaz.
3. Kabuk script'i o sınıfı okur, card ve stack modunda taşımayı atlar.

### Ödeme Panelini Barındırın

1. Ödeme bölümüne, operation'ın dönen HTML ile dolduracağı boş bir panel verin.
2. Bir geçit gömülebilir hiçbir şey döndürmeyebilir: yanıt yedek bildirir, footer butonu ödeme sayfasına taşır.
3. Kendi geçit işaretlemenizi kurmayın; paylaşılan parça yönlendiren geçidi tek butona çevirir.

## Referans

### Huni Değişkenleri

Huninin paylaştığı iki ad var. Ödeme gövdesine `$cart_items` yazarsanız hiçbir şey görünmez ve bildirilmez.

- **$checkout_step**: Dördünde de: 1 yapılandırma, 2 sepet, 3 ödeme, 4 tamamlandı. Header partial'ı okur.
- **$checkout_legal**: Yapılandırma, sepet ve ödemede: satın alma anı sözleşmeleri, sayfa bazında işaretlenir. Footer bağlantıları değil.

| Sepet sayfası | Ödeme adımı | Taşıdığı |
| --- | --- | --- |
| `$cart_items` | `$checkout_items` | Satırlar, fiyatlanmış ve biçimlenmiş. Aynı şekil |
| `$cart_summary` | `$checkout_summary` | İndirim grupları, katmanlı vergiler ve kazanç; operation'ların döndürdüğü şekille |
| `$cart_subtotal`, `$cart_total` | özetten okunur | Özet bloğunun dışında yazılan rakamlar |
| `$cart_count` | `$cart_count` | Satırlardan yeniden hesaplanır; rozet değerini ezer |
| `$coupon_enabled`, `$cart_has_unconfigured` | kurulmaz | Yalnız sepet: kuponlar açık mı, yapılandırılmamış satır var mı |
| kurulmaz | `$payment_methods`, `$payment_default`, `$payment_locked` | Yalnız ödeme: açık geçitler, ön seçim, seçimsizlik işareti |
| kurulmaz | `$is_member`, `$billing_profiles`, `$countries` | Yalnız ödeme: hesap ve fatura kartları |

- **$domain_section**: Yalnız yapılandırmada. Her zaman `visible` ve `mode` taşır. Önce `visible`'a dallanın: gizlenmiş kart yok olan değişken olarak değil `["visible" => false, "mode" => ""]` olarak gelir. `license` modu `need_domain`, `need_ip`, `can_change`; `chooser` ise `tabs`, `tab_count`, `default_tab`, `subdomains`, `nameservers`, `check_url`, `free_json` ekler.

### Huninin İhtiyaç Duyduğu Fonksiyonlar

```php
// {csrf form='<anahtar>'} anahtara göre kapsamlanmış gizli bir jeton input'u basar.
// $input = false input elemanı yerine çıplak jetonu döndürür.
public static function get_csrf_token($form_index = '', $input = true);

// {money amount=$x currency=$cid} tek bir tutarı biçimler.
// $currency verilmezse ziyaretçinin seçili para birimine düşer.
public static function formatter_symbol($amount = 0, $currency = 0, $exchange = false, $info = false): string;

// {link route='cart'} ve {link route='configure' p1=$type p2=$id}
// p1..p5 sayı sırasına göre tek bir listeye TOPLANIR, listeye indekslenmez:
// p1'i atlamak boşluk bırakmaz, p2'yi ilk sıraya taşır; yani yalnız p2 ile
// kurulan bir bağlantı sessizce yanlış segmenti adresler.
public static function client($route = '', $params = [], $lang = '');

// {captcha area='<alan>' tray='<id>' class='mb-3' force=true} aktif sağlayıcıyı render
// eder; operatör o form alanı için captcha'yı açmadıysa '' döner.
// $opts tam olarak üç anahtar kabul eder, şablon fonksiyonu da yalnız bunları geçirir:
//   tray   bir collapse sarmalayıcısının id'si; slot onun içinde kapalı render edilir
//   class  sarmalayıcı sınıfları; tray varken 'mt-2', yokken 'mb-3' varsayılır
//   force  alan bazlı anahtarı yok sayarak koşulsuz ve görünür render et
public static function widget(string $area = '', array $opts = []): string;
```

### Geçit Paneli Modları

| Mod | Geçidin döndürdüğü | Panelinize ulaşan |
| --- | --- | --- |
| `html` | Kendi işaretlemesi, kart formu ya da barındırılan alanlar | O işaretleme, olduğu gibi gömülür |
| `choices` | Seçenekler, örneğin taksit ya da banka hesabı | Paylaşılan pay-choices parçası, o seçeneklerden kurulur |
| `redirect` | Tek bir hedef adres | Aynı parça tek seçenekle: tek buton |
| `none` | Gömülebilir hiçbir şey, miras tam sayfa formu | Hiçbir şey; yedek bildirilir, footer butonu devralır |

Şablonun gördüğü iki değer var: ilk üç durum `mode: "html"` ve bir `html` dizesi, dördüncüsü işaretlemesiz `mode: "fallback"`.

### pay-choices'a Ne Ulaşır

Sayfanın verisiyle değil, iki anahtarlı açık bir yükle kurulur: layout, sepet ve hesap değişkenleri kapsam dışıdır.

```php
$this->view->chose("website")->render("checkout/pay-choices", [

    // Buton başına bir kayıt. Yönlendiren bir geçit tam bir elemanlı liste olarak gelir.
    'pay_choices' => [
        [
            'url'   => 'https://gateway.example.com/session/abc',
            'label' => 'Pay now',   // modülün kendi buton etiketi
            'image' => '',          // doluysa etiket YERİNE görseli basın
        ],
    ],

    // Butonların üstündeki isteğe bağlı başlık. Boşken hiçbir şey basmayın.
    'pay_choices_note' => '',

], true);

// Ödeme sayfası (views/checkout/pay) aynı iki adı kendisi kurar, ayrıca
// pay_mode, pay_html, pay_total_fmt, pay_stored_cards, pay_can_store,
// pay_can_autopay, pay_has_installments, pay_capture_url ve pay_error.
```

## Örnek

```smarty
{extends file='layouts/checkout.tpl'}

{block name=scripts}
    <script src="{asset path='js/checkout.js'}" defer></script>
{/block}

{block name=content}
<section>
  <div class="container">

    {* Form split'i SARAR, aside dahil. Yine de bir id verin: aşağıdaki eylem
       çubuğu formun dışında durur ve açık bağa ihtiyaç duyar. *}
    <form method="post" novalidate data-checkout id="checkout-form">

      {* Derinlik önemli: aside, checkout-split'in ÇOCUĞU ve checkout-split-main'in
         KARDEŞİDİR. Bir seviye daha derinde, script'in taşıma yapmadığı card ve
         stack modlarında içeriğin altına düşer. *}
      <div class="checkout-split">

        <div class="checkout-split-main">
          <div class="checkout-split-main-inner">
            {csrf form='checkout'}
            {include file='views/checkout/section-account.tpl'}
            {include file='views/checkout/section-billing.tpl'}
            {include file='views/checkout/section-payment.tpl'}
          </div>
        </div>

        {* >=lg'de kabuk script'i bu düğümü <body>'ye TAŞIR. İçinde gönderme
           yapması gereken her şey o andan itibaren form="checkout-form" ister. *}
        <aside class="checkout-split-aside">
          <div class="checkout-split-aside-inner">
            {include file='views/checkout/section-rail-items.tpl'}
          </div>
        </aside>

      </div>
    </form>

    {* Stack modu eylem çubuğu: bilerek formun DIŞINDA, yani gönderme yapmasını
       sağlayan tek şey form="". Taşınan aside da aynı kurala tabidir. *}
    <div class="checkout-actionbar{if $payment_locked} d-none{/if}" data-checkout-actionbar>
      <span class="checkout-actionbar-value num-tabular" data-role="actionbar-total">{$checkout_summary.total_fmt}</span>
      <button type="submit" form="checkout-form" class="btn btn-primary">
        {lang key='website/checkout/place-order'}
      </button>
    </div>

  </div>
</section>
{/block}

{* Modal'lar main'in DIŞINDA yaşar: içerik bloğu, sabit konumlu bir arka perdeyi
   bozan bir yığın bağlamına sarılıdır. *}
{block name=body_end}
  <div class="modal" id="billingProfileModal" tabindex="-1"></div>
{/block}
```

```php
// templates/website/{Tema}/theme.php manifestin tamamını döndürür.

return [
    'meta'   => ['name' => 'Acme', 'version' => '1.0.0', 'author' => 'Acme'],
    'engine' => 'smarty',
    'status' => 'ready',

    'settings' => [
    'groups' => [
        'checkout' => ['label' => 'grp_checkout', 'icon' => 'bi-cart3'],
    ],
    'fields' => [
        // layouts/checkout.tpl okur ve kök elemana bir sınıf damgalar.
        // Bu bir TEMA ayarıdır, sayfa başına değil: yapılandırma, sepet ve ödeme
        // birlikte değişir; huniyi görsel olarak tutarlı tutan da budur.
        'checkout_sidebar' => [
            'type'    => 'select',
            'group'   => 'checkout',
            'label'   => 'set_checkout_sidebar',
            'desc'    => 'set_checkout_sidebar_desc',
            'options' => [
                'rail'  => 'opt_checkout_rail',    // taşınan tam yükseklikte ray (varsayılan)
                'card'  => 'opt_checkout_card',    // içeriğin yanında düz kart
                'stack' => 'opt_checkout_stack',   // içeriğin altına yığılmış
            ],
            'default' => 'rail',
        ],
    ],
    ],
];
```

## Tuzaklar

> **Yanlış yerleştirilmiş aside ray modunda görünmez**
> 
> Ray modunda script özeti body'ye taşır, konumu hiç görünmez. Card ya da stack'te aynı işaretleme özeti içeriğin altına bırakır.

> **Delege dinleyici modal butonunu görmez**
> 
> Modal'lar script'inizin kapsadığı kökün dışındadır; sayfa kabına bakan dinleyici modal butonlarını sessizce kaçırır. En yakın modal'a da izin verin.

> **Temanın kendi collapse'ını kullanın**
> 
> Çerçevenin collapse'ı bu kabukta zıplar; temalar kendi uygulamalarını kullanır, yapılandırmanın alan adı ve ad sunucusu panelleri dahil.

> **Alanı toplamak onu kalıcılaştırmak değildir**
> 
> Yapılandırma, şablonun değil toplama zincirinin okuduğu alan gruplarını doğrular. Makul adlı bir input hiçbir yere ulaşmaz; her yeni alanı operation'ına kadar izleyin.

> **Alan adı kartı genel arama ucunu yeniden kullanır**
> 
> Yapılandırmanın kendi uygunluk ucu yoktur: genel aramanın jeton form anahtarıyla alan adı controller'ının check operation'ına gönderir.

## İlgili Makaleler

- [Sayfa Ekranları](https://dev.wisecp.com/tr/sayfa-yuzeyleri)
- [Katalog ve Ürün Sayfaları](https://dev.wisecp.com/tr/katalog-ve-urun-sayfalari)
- [Tema Formlarını Güvenceye Alma](https://dev.wisecp.com/tr/tema-formlarini-guvenli-hale-getirme)
- [Tema Ayarları](https://dev.wisecp.com/tr/tema-ayarlari)
- [Ödeme Geçidi Yazma](https://dev.wisecp.com/tr/odeme-geciti-yazma)
