# Ayarlar, Vault ve Metafield'lar

> Kurulum ayarları, şifreli Vault sırları, yerel ayar formu ve kaynaklara bağlı metafield’lar.
>
> Kaynak: https://tecof-app-dev.vercel.app/app-data · Bölüm: Uygulama geliştirme · Son güncelleme: 2018-10-20

Tecof, uygulamanızın kurulum başına veri saklamasını sağlayan üç katman sunar. Bu katmanlarla birçok uygulama **kendi veritabanına ihtiyaç duymadan**, örneğin Vercel üzerinde durumsuz (stateless) çalışabilir.

| Katman | Ne için | Boyut | Kaldırmada |
| :--- | :--- | :--- | :--- |
| Ayarlar | Sır olmayan yapılandırma (JSON) | ≤ 64 KB | Silinir |
| Vault (sırlar) | API anahtarı, parola, token | Değer ≤ 8 KB, en fazla 50 anahtar | Silinir |
| Metafield'lar | Sipariş, ürün, müşteri ya da mağazaya bağlı uygulama verisi | Değer ≤ 16 KB | Panelde gizlenir, 30 gün sonra temizlenir |

Ayarlar ve sırlar kapsam istemez; kurulumun kendisi yetkidir.

---

## Ayarlar

Kurulum başına tek bir JSON nesnesi.

| Metot | Uç | Açıklama |
| :--- | :--- | :--- |
| `GET` | `/api/v1/app/settings` | `{ settings, updatedAt }` |
| `PUT` | `/api/v1/app/settings` | `{ settings, merge?: boolean }`; ≤ 64 KB → `{ settings, updatedAt }` |

```ts
const api = tecof.client(installationId);

const { settings } = await api.settings.get();
await api.settings.set({ invoiceMode: "issued", seriesPrefix: "ABC" }, { merge: true });
```

`merge: true` gönderilen anahtarları mevcut nesneyle birleştirir; `merge` olmadan nesne tamamen değiştirilir. Panelin yerel ayar formu da **aynı ayar nesnesine** yazar (birleştirerek; formda olmayan anahtarlar korunur). API'den yapılan yazmalar `app.settings.updated` olayı üretmez.

---

## Vault (Sırlar)

Üçüncü taraf API anahtarları ve parolalar için. Değerler Tecof'ta şifreli tutulur ve yalnız uygulamanızın kurulum token'ıyla okunabilir.

| Metot | Uç | Yanıt |
| :--- | :--- | :--- |
| `GET` | `/api/v1/app/secrets` | `[{ key, updatedAt, lastReadAt, length }]` — **değer yok** |
| `GET` | `/api/v1/app/secrets/:key` | `{ key, value, updatedAt }` |
| `PUT` | `/api/v1/app/secrets/:key` | Gövde `{ value }` → `{ key, updatedAt }` |
| `DELETE` | `/api/v1/app/secrets/:key` | Siler |

```ts
await api.secrets.set("isbasiPassword", passwordFromForm);
const { value: password } = await api.secrets.get("isbasiPassword"); // okuma lastReadAt'i damgalar
const list = await api.secrets.list();                     // yalnız anahtar adları ve meta
await api.secrets.delete("isbasiPassword");
```

**Kurallar**

| Kural | Değer |
| :--- | :--- |
| Anahtar biçimi | `^[a-zA-Z][a-zA-Z0-9_.-]{0,63}$` |
| Anahtar sayısı | En fazla 50 |
| Değer | UTF-8 metin, en fazla 8 KB |

### Şifreleme tasarımı

Vault, sızıntı durumunda bile şifreli metnin başka bir yerde kullanılamamasını hedefler:

| Katman | Tasarım |
| :--- | :--- |
| Algoritma | **AES-256-GCM** (kimlik doğrulamalı şifreleme; değiştirilmiş şifreli metin çözülemez) |
| Kurulum başına anahtar | Veri anahtarı her kurulum için ayrı türetilir: **HKDF-SHA256**(ana anahtar, salt = kurulum kimliği, info = `tecof-app-vault/v1`) |
| AAD bağlama | Ek doğrulanmış veri = `uygulama kimliği \| kurulum kimliği \| anahtar adı`. Şifreli metin başka bir kuruluma, başka bir uygulamaya ya da başka bir anahtar adına kopyalanırsa çözülmez |
| Anahtar sürümü | Her kayıt hangi ana anahtarla şifrelendiğini (`keyId`) saklar; Tecof ana anahtarları döndürürken kayıtları yeniden şifreler |
| İstemci sırrı | Uygulamanızın istemci sırrı (`tcfs_…`) da aynı ana anahtarlarla, kendi bağlamına bağlanarak saklanır |

**Garantiler**

- Değerler panel API'sine, istek günlüklerine, webhook yüklerine ve hata mesajlarına **hiçbir zaman** çıkmaz. Panel yalnız "ayarlı / ayarlı değil" bilgisini ve güncelleme zamanını görür.
- Okuma `lastReadAt` alanını damgalar; liste ucuyla hangi sırrın ne zaman okunduğunu izleyebilirsiniz.
- Uygulama kaldırıldığında kurulumun sırları **anında** silinir.

Kendi tarafınızda: sırrı yalnız ihtiyaç anında okuyun, günlüğe yazmayın, istemci (tarayıcı) koduna göndermeyin.

---

## Yerel Ayar Formu

`tecof.app.json` içine `settings` şeması eklerseniz panel, uygulama sayfasında bir **"Ayarlar"** sekmesi gösterir. Böylece ayar ekranı için arayüz yazmanız gerekmez; arka plan (headless) uygulamalar için de idealdir.

```json
{
  "settings": [
    { "key": "username", "label": { "tr": "Kullanıcı adı", "en": "Username" }, "type": "text", "required": true },
    { "key": "password", "label": { "tr": "Parola", "en": "Password" }, "type": "secret", "required": true },
    { "key": "autoInvoice", "label": { "tr": "Ödenen siparişleri otomatik faturala", "en": "Auto-invoice paid orders" }, "type": "boolean" },
    { "key": "invoiceMode", "label": { "tr": "Fatura tipi", "en": "Invoice mode" }, "type": "select",
      "help": { "tr": "Kesilmiş fatura doğrudan alıcıya gider", "en": "Issued invoices are sent to the buyer" },
      "options": [
        { "value": "draft", "label": { "tr": "Taslak", "en": "Draft" } },
        { "value": "issued", "label": { "tr": "Kesilmiş", "en": "Issued" } }
      ] },
    { "key": "note", "label": { "tr": "Fatura notu", "en": "Invoice note" }, "type": "textarea" }
  ]
}
```

| Alan | Açıklama |
| :--- | :--- |
| `key` | Ayar anahtarı |
| `label` | `{ tr, en }` |
| `type` | `text`, `number`, `boolean`, `select`, `textarea`, `secret` |
| `required` | İsteğe bağlı |
| `options` | `select` için zorunlu: `[{ value, label: { tr, en } }]` |
| `help` | İsteğe bağlı yardım metni, `{ tr, en }` |

En fazla 30 alan tanımlanabilir.

### Panel tarafı

| Metot | Uç | Davranış |
| :--- | :--- | :--- |
| `GET` | `/api/merchant/apps/installations/:id/settings` | `{ schema, values }`; `secret` alanlar değer yerine `{ set: boolean, updatedAt }` döner |
| `PUT` | `/api/merchant/apps/installations/:id/settings` | `{ values }`; sır olmayanlar ayar nesnesine **birleştirilerek** (formda olmayan anahtarlar korunur), `secret` olanlar Vault'a yazılır |

- `secret` alan için boş string = **değiştirme**; `null` = **sil**.
- Kayıttan sonra uygulamanıza `app.settings.updated` webhook'u gelir: `data = { keys: [...], secretKeys: [...] }`. Değer taşımaz. Olay yalnız bu panel kaydında üretilir.
- Uygulamanın kendi yazdığı anahtarlar (ör. `billing`) form kaydında silinmez; yalnız formdaki anahtarla aynı adı taşıyorsa üzerine yazılır.

### Uygulama tarafı

Formdaki sır olmayan değerler ayarlarda, `secret` değerler Vault'ta aynı `key` ile durur:

```ts
const api = tecof.client(installationId);
const { settings } = await api.settings.get();
const username = settings.username as string;
const { value: password } = await api.secrets.get("password");
```

`app.settings.updated` geldiğinde önbelleğinizdeki değerleri geçersiz kılın ya da bağlantıyı test edin (ör. üçüncü taraf API'ye giriş deneyip sonucu metafield veya ayarlarla bildirin).

---

## Metafield'lar

Metafield, bir kaynağa (sipariş, ürün, müşteri, mağaza) bağlı uygulama verisidir. Örneğin fatura uygulaması siparişe fatura numarasını, kargo uygulaması takip bağlantısını yazar. Metafield'lar panelde gösterilebilir ve başka uygulamalarla paylaşılabilir.

| Metot | Uç | Açıklama |
| :--- | :--- | :--- |
| `GET` | `/api/v1/metafields?ownerType=order&ownerId=…[&namespace=isbasi]` | Listeleme |
| `PUT` | `/api/v1/metafields` | Yazma (owner + namespace + key tekildir; aynı anahtar güncellenir) → `{ id, namespace, ownerType, ownerId, key, type, value, visibility, panel, own, updatedAt }` |
| `DELETE` | `/api/v1/metafields/:id` | Silme |

### Alanlar

| Alan | Değerler |
| :--- | :--- |
| `ownerType` | `order`, `product`, `customer`, `merchant` |
| `ownerId` | Kaynağın kimliği. `ownerType: "merchant"` için yok sayılır (kayıt kurulumun mağazasına bağlanır) |
| `namespace` | Uygulamanızın `slug`'ı — **sunucu atar**, gövdede gönderilmez |
| `key` | Anahtar; `owner + namespace + key` tekildir |
| `type` | `string`, `number`, `boolean`, `json`, `url` |
| `value` | En fazla 16 KB |
| `visibility` | `private` (yalnız uygulamanız) ya da `shared` (aynı mağazadaki diğer uygulamalar okuyabilir) |
| `panel` | `{ label: { tr, en }, show: true }` → panelde gösterilir |

### Kapsam

Okuma ve yazma, kaynağın okuma kapsamını ister:

| `ownerType` | Gerekli kapsam |
| :--- | :--- |
| `order` | `orders:read` |
| `product` | `products:read` |
| `customer` | `customers:read` |
| `merchant` | Kapsam gerekmez |

### Panelde gösterim

`panel.show: true` olan metafield'lar ilgili detay sayfasındaki **"Uygulamalar"** kartında `label` ile listelenir; `url` tipi tıklanabilir bağlantı olarak gösterilir. Panel bu kartı `GET /api/merchant/apps/metafields?ownerType=order&ownerId=…` ile doldurur.

```ts
await api.metafields.set({
  ownerType: "order",
  ownerId: orderId,
  key: "invoiceNumber",
  type: "string",
  value: "ABC2026000000123",
  visibility: "shared",
  panel: { label: { tr: "İşbaşı fatura no", en: "İşbaşı invoice no" }, show: true },
});

await api.metafields.set({
  ownerType: "order",
  ownerId: orderId,
  key: "invoiceUrl",
  type: "url",
  value: "https://ornek-fatura-portali.example/fatura/ABC2026000000123",
  visibility: "private",
  panel: { label: { tr: "Faturayı görüntüle", en: "View invoice" }, show: true },
});

const mine = await api.metafields.list({ ownerType: "order", ownerId: orderId, namespace: "isbasi" });
```

### Kaldırmada

Uygulama kaldırıldığında panelde görünen metafield'lar gizlenir; veri 30 gün sonra temizlik işinde silinir.

Paylaşılan metafield'ların uygulamalar arası kullanımı için bkz. [Uygulamalar Arası İletişim](/app-interop).
