# Sipariş Takibi

Satın alma ve ciro takibi
Kaynak: https://selwise.com/docs/integration/order-tracking

Sipariş takibi, Selwise'in gelir ilişkilendirmesinin (attribution) temelidir: panelde göreceğiniz her satın alma ve ciro rakamı bildirdiğiniz siparişlerden doğar. Kampanya, öneri, arama ve zenginleştirmenin gerçek gelire katkısını ölçebilmek için tamamlanan siparişleri Selwise'e bildirmeniz gerekir.

En güvenilir yöntem, **teşekkür / sipariş onay sayfasında** `window.Selwise.trackOrder(...)` çağırmaktır. Sunucudan ya da arka ofisten göndermek için [Sipariş API'si](/docs/api-reference/orders-api) sayfasına bakın.

> **Veri katmanınız varsa sipariş zaten gidiyor olabilir**
>
> Sitenizin veri katmanı bir `purchase` olayı yayınlıyorsa (GA4 `purchase`, Enhanced Ecommerce) ve panelde **Data Layer** entegrasyonu açıksa, widget bu olayı otomatik olarak `trackOrder`'a çevirir ve siparişi kendisi kaydeder. Bu durumda aşağıdaki çağrıyı **ayrıca eklemek gerekmez**. Hangi yolun açık olduğunu panelde **Kanallar → Kanal Özeti** (Channels → Channel Overview) ekranındaki sipariş takibi kurulumundan ve [Veri Katmanı](/docs/integration/data-layer) sayfasından kontrol edin.

## Temel kullanım

Sipariş onay sayfasına, gerçek sipariş verilerini doldurarak aşağıdaki bloğu ekleyin:

```html
<script>
  window.Selwise?.trackOrder({
    orderId: 'ORDER_12345',
    currency: 'TRY', // ISO-4217 (örn. TRY / EUR / USD)
    total: 129.90,
    items: [
      {
        productItemCode: 'SKU-RED-XL',
        name: 'Tişört Kırmızı XL',
        quantity: 1,
        unitPrice: 129.90,
      },
    ],
  });
</script>
```

`window.Selwise?.` (opsiyonel zincir) ve [kurulum snippet'indeki komut kuyruğu](/docs/integration/widget-installation) birlikte çalışır: widget henüz yüklenmemişse çağrı kuyruğa girer ve widget hazır olunca gönderilir. Kuyruk satırı olmadan `async` yükleme siparişi sessizce kaybettirir.

> **Alan adları birebir olmalı**
>
> `trackOrder` sık kullanılan adları göndermeden önce çevirir: `id` → `orderId`, kalemde `productId` / `sku` / `itemCode` → `productItemCode`, `price` → `unitPrice`, `title` → `name`. Doğru adlar (`orderId`, `productItemCode`, `unitPrice`) verilmişse onlar kazanır. Sunucudan doğrudan `POST /orders` çağırıyorsanız bu çeviri yoktur; orada doğru adları kullanın. Widget ayrıca oturumdaki **son temas** bilgisini (son tıklanan kampanya, öneri, arama ürünü, zenginleştirme bloğu) kendisi ekler; kampanya detayındaki *direct/assisted revenue* buradan gelir.

## Tüm alanlar

```html
<script>
  window.Selwise?.trackOrder({
    // Zorunlu
    orderId: 'ORDER_12345',

    // Tutarlar
    currency: 'TRY',
    total: 129.90,
    subtotal: 119.90,
    discountTotal: 10.00,
    shippingTotal: 20.00,
    taxTotal: 0.00,

    // Durum (iptal ve iade ciroya sayılmaz, aşağıya bakın)
    status: 'paid',

    // Bağlam (verilmezse otomatik doldurulur)
    placedAt: new Date().toISOString(),

    // Sipariş kalemleri
    items: [
      {
        productItemCode: 'SKU-001', // besleme item_code ile birebir aynı olmalı
        name: 'Ürün Adı',
        quantity: 2,
        unitPrice: 49.95,
      },
    ],

    // Opsiyonel meta veri
    metadata: {
      paymentMethod: 'credit_card',
      couponCode: 'SAVE10',
    },
  });
</script>
```

> **productItemCode besleme ile aynı olmalı**
>
> `items[].productItemCode`, ürün beslemenizdeki ürün kodunu (`item_code`) birebir içermelidir. Aksi hâlde satırlar ürünlerle eşleşmez ve ürün bazlı gelir ve öneri raporları boş kalır.

## Alanların karşılıkları

| `trackOrder` alanı | Tür | Zorunlu | Açıklama |
| --- | --- | --- | --- |
| `orderId` | `string` | Evet | Mağazanızdaki sipariş kimliği; en çok 255 karakter. Boşsa çağrı gönderilmez ve konsola `trackOrder ignored` uyarısı düşer. Tekilleştirme anahtarıdır |
| `currency` | `string` | Hayır | ISO-4217, 3 harf. Verilmezse kanalın para birimi |
| `total` | `number` | Hayır | Müşterinin ödediği genel toplam: KDV ve kargo dahil, indirimler düşülmüş. Selwise ciroyu bu alandan okur, üzerine vergi ya da kargo eklemez. Verilmezse kalem toplamı kullanılır |
| `subtotal`, `discountTotal`, `shippingTotal`, `taxTotal` | `number` | Hayır | Tutar dökümü |
| `status` | `string` | Hayır | Serbest metin, en çok 20 karakter. Aşağıdaki durumlar ciroya sayılmaz |
| `placedAt` | `string` | Hayır | ISO tarih. Geçersizse istek `400` ile reddedilir |
| `items[]` | `array` | Hayır | Sipariş kalemleri |
| `metadata` | `object` | Hayır | Serbest anahtar-değer verisi (`couponCode` okunur) |
| `visitorId`, `siteUserId` | `string` | Hayır | Verilmezse widget kendi bildiği değerleri yazar |

Widget `sessionId`, `pageUrl`, `referrer`, `attribution`, `experimentAssignments` ve `visitorId`/`siteUserId` alanlarını kendiliğinden ekler. Kalem alanları: `productItemCode`, `name`, `quantity`, `unitPrice` (ya da `price`), `totalPrice`, `metadata`. İstek gövdesinin tamamı, sınırlar ve hata durumları için [Sipariş API'si](/docs/api-reference/orders-api) sayfasına bakın.

### Gelire sayılmayan durumlar

`status` aşağıdakilerden biriyse sipariş kaydedilir ama **ciro ve dönüşüm rakamlarına girmez**: `cancelled`, `canceled`, `refunded`, `partially_refunded`, `partial_refund`, `failed`, `failure`, `voided`, `void`, `declined`, `chargeback`, `returned`, `return`. Durum verilmezse sipariş gelir sayılır. Sipariş sonradan iptal, iade ya da ödendiyse bunu sipariş sisteminizden `POST /public/sites/:siteKey/orders/:orderId/status` ile bildirin (`orders_write` anahtarı; bkz. [Sipariş API'si](/docs/api-reference/orders-api)).

Ödeme bekleyen bir durumla (`pending`, `awaiting_payment` gibi) gönderilen sipariş gelir sayılır; ödeme hiç gelmezse siparişi `cancelled` olarak bildirin. Aynı müşteri aynı ürünleri aynı adetlerle 3 saat içinde yeniden sipariş ederse ilk sipariş `superseded` olur; sonradan gönderdiğiniz bir durum bunu değiştirir.

## Tekilleştirme ve çift sayım

- **Aynı `orderId` ikinci kez gönderilirse** Selwise ikinci gönderimi yok sayar: sunucu `200` döner, `created: false` ve ilk kaydın kimliğini verir. Sayfa yenilenmesi kaynaklı çift kayıt böylece engellenir. Bu `409` **değildir**; çağrı hata vermez.
- **Hem veri katmanı `purchase` hem `trackOrder` kullanıyorsanız** sipariş yine bir kez kaydedilir (`orderId` ile tekilleştirilir) ve ciro iki katına çıkmaz. Ama olay akışında `purchase` olayı iki kez görünebilir. **Tek kaynak** seçin: veri katmanı köprüsünü kullanıyorsanız elle çağrıya gerek yoktur; kullanmıyorsanız `trackOrder`'ı esas alın.
- `trackOrder`'ın döndürdüğü söz, istek başarısız olsa bile çözülür; hata yutulur ve yeniden denenmez. Kaçırılması gerekmeyen siparişleri sunucu tarafından da gönderin.

> **Nereye koymalı**
>
> `trackOrder`'ı yalnızca ödeme başarıyla tamamlandığında render edilen sipariş onay sayfasına koyun. Ara adımlara ya da her sayfaya koymayın. Sayfa yenilenirse aynı `orderId` ile tekrar çağrılması zararsızdır.

## Platform örnekleri

### Shopify

Sipariş durumu sayfası script'i alanına Liquid ile:

```html
<script>
  var orderId = '{{ order.id }}';
  var total = {{ order.total_price | divided_by: 100.0 }};
  var currency = '{{ shop.currency }}';

  window.Selwise?.trackOrder({
    orderId: String(orderId),
    currency: currency,
    total: total,
    placedAt: new Date().toISOString(),
    items: [
      {% for line_item in order.line_items %}
      {
        productItemCode: '{{ line_item.sku }}',
        name: '{{ line_item.title | escape }}',
        quantity: {{ line_item.quantity }},
        unitPrice: {{ line_item.price | divided_by: 100.0 }}
      }{% unless forloop.last %},{% endunless %}
      {% endfor %}
    ]
  });
</script>
```

### Google Tag Manager (GA4 purchase üzerinden)

Veri katmanı köprüsü kapalıysa GTM'de bir "Custom HTML" etiketi oluşturup GA4 `purchase` olayında tetikleyin:

```html
<script>
  var ecommerce = window.dataLayer?.find(function (x) {
    return x && x.ecommerce;
  })?.ecommerce;

  if (ecommerce && ecommerce.transaction_id) {
    window.Selwise?.trackOrder({
      orderId: String(ecommerce.transaction_id),
      currency: ecommerce.currency || 'TRY',
      total: Number(ecommerce.value || 0),
      items: (ecommerce.items || []).map(function (item) {
        return {
          productItemCode: item.item_id || item.item_sku,
          name: item.item_name,
          quantity: Number(item.quantity || 1),
          unitPrice: Number(item.price || 0),
        };
      }),
    });
  }
</script>
```

Platforma özel sipariş takibi adımları: [Platformlar](/docs/platforms/overview).

## Sunucu tarafı (headless) gönderim

Tarayıcıya güvenemediğiniz ya da sipariş sonradan onaylandığında arka ofisinizden göndermek istediğiniz durumlarda siparişi doğrudan sunucudan gönderebilirsiniz. **`Origin` göndermeyin**; başlıksız istek ve `x-selwise-api-key` (kapsam `mobile_write`) kullanın:

```bash
curl -X POST https://api.selwise.com/api/v1/public/sites/SITE_KEY/orders \
  -H "Content-Type: application/json" \
  -H "x-selwise-api-key: swpk_live_..." \
  -d '{
    "orderId": "ORDER_12345",
    "currency": "TRY",
    "total": 129.90,
    "visitorId": "VISITOR_ID",
    "sessionId": "SESSION_ID",
    "items": [
      { "productItemCode": "SKU-RED-XL", "name": "Tişört", "quantity": 1, "unitPrice": 129.90 }
    ]
  }'
```

Atıf için `visitorId` ve `sessionId`'yi tarayıcıdan (`Selwise.getVisitorId()`, `getSessionId()`) sipariş sırasında taşıyın; yoksa sunucudan gelen sipariş hiçbir kampanyaya ya da öneriye bağlanamaz. Hız sınırı ve hata kodları: [Sipariş API'si](/docs/api-reference/orders-api), [Hatalar ve Sınırlar](/docs/api-reference/errors-and-limits).

## Doğrulama

1. Bir test siparişi verin. Ağ sekmesinde `POST .../orders` isteğini bulun: yanıt `{ "success": true, "created": true, "id": "..." }` olmalı.
2. Sayfayı yenileyin: bu kez `created: false` gelmeli (tekilleştirme çalışıyor).
3. Panelde siparişi [Ciro](/docs/analytics/revenue) ekranında görün (birkaç dakika sürebilir). Ürün satırları boşsa `productItemCode` besleme ile eşleşmiyordur.
4. İstek hiç gitmiyorsa: [Takip Sorunları](/docs/troubleshooting/tracking-issues).
