Ortamlar (sandbox / production)

Her kurulum bir ortama aittir: sandbox (test mağazaları) veya production (gerçek mağazalar). Ortam yedi yüzeyde birden gelir ve üç şeyi belirler: hangi API kökünü çağıracağın, hangi kurulumun credential'ını kullanacağın ve teslimleri nasıl ayrı tutacağın. Aynı tenant iki ortamda birden kurulu olabilir.

İki ortam

environmentNe zamanAPI kökü
sandboxTest mağazaları — yayınlanmamış eklentini denerkenhttps://sandbox.plugins.restomenum.app
productionGerçek mağazalar — marketplace’ten kurulumhttps://plugins.restomenum.app

Geçerli değerler yalnız bunlardır: sandboxproduction

Ortam nereden gelir? (7 yüzey)

environment’ı tahmin etme — her zaman sana verilir:

YüzeyNerede
Event webhook’larıİmzalı gövde — envelope.environment
Yaşam döngüsü (app.* / subscription.* / delivery.*)İmzalı gövde
Gate hook’ları (type:"hook")İmzalı gövde
Buton action’ları (type:"action")İmzalı gövde
Capability relay (type:"capability")İmzalı gövde
OAuth ConnectQuery parametresi — ?environment=
iframe session tokenJWT claim — claims.environment
X-Restomenum-Environment header’ı imzasız kolaylık kopyasıdır: imzaya dahil değildir ve gövdeyi kuyruğa atıp sonra işleyen tasarımlarda kaybolur. Kararlarını daima imzalı gövdedeki alana (veya token claim’ine) dayandır.

1) Depolama anahtarı — tenantId + environment

Aynı tenantId iki ortamda birden kurulu olabilir ve her kurulumun apiKey + webhookSecret’ı ayrıdır. Kurulumu yalnız tenantId ile saklarsan ikinci kurulum birincinin üzerine yazar; sonra imza doğrulaması yanlış secret’la yapılır ve geçerli teslimler 401’e düşer.

// Kurulum kaydının anahtarı: tenantId + environment (yalnız tenantId DEĞİL).
// Aynı tenant hem sandbox hem production'da kurulu olabilir; apiKey/webhookSecret AYRIDIR.
const key = (tenantId, environment) => `${environment}:${tenantId}`;

save({ tenantId, environment, apiKey, webhookSecret, scopes });

// Gelen her istekte ortamı GÖVDEDEN/CLAIM'den al, tahmin etme:
const install = find(envelope.tenantId, envelope.environment);   // webhook / action / hook / capability
const install = find(claims.tenantId, claims.environment);       // iframe session token
tenant "tnt_42"
   ├─ sandbox    → apiKey_A · webhookSecret_A   ◄── ayrı kurulum
   └─ production → apiKey_B · webhookSecret_B   ◄── ayrı kurulum

imza anahtarı = find(tenantId, environment).webhookSecret

Resmi SDK bunu destekler: getSecret(tenantId, environment) — verifyAndParseWebhook, verifyAndParseCapability ve verifySessionToken ortamı ikinci argümanla verir.

2) API kökü seçimi

Token takası ve tüm Callback API çağrıları kurulumun ortamındaki köke gitmelidir. Tek bir köke sabitlenmiş kod, production kurulumunda yanlış kökle konuşur.

// API kökünü kurulumun ortamından çöz — tek bir köke SABİTLEME.
const BASES = {
  sandbox: 'https://sandbox.plugins.restomenum.app',
  production: 'https://plugins.restomenum.app',
};

// /connect?code=…&environment=production&state=…
const env = environment === 'production' ? 'production' : 'sandbox';
await fetch(`${BASES[env]}/plugin-api/oauth/token`, { /* … */ });

// Resmi SDK: ortamı geçmen yeterli (kökü kendisi çözer)
import { exchangeCode, RestomenumClient } from '@restomenum/plugin-sdk';
const cred = await exchangeCode({ code, clientId, clientSecret }, { environment: env });
const client = new RestomenumClient({ apiKey: cred.apiKey, environment: env });
Bu, en sık yapılan hatadır: sandbox’ta geliştirip kökü sabit bırakmak. Kurulum production’dan geldiğinde token exchange yanlış köke gider ve kurulum tamamlanmaz.

3) Dedup ve durum kapsamı

  • Event dedup (envelope.id) ve idempotency anahtarlarını ortamla kapsa: `${environment}:${id}`. İki ortamın teslimlerini aynı kovada tutma — sandbox’ta işlenen bir kayıt production teslimini “zaten gördüm” diye düşürmesin.
  • İş verisi (sipariş, rezervasyon, fatura) ortam başına ayrı tutulmalı; sandbox verisi gerçek raporlara/faturaya karışmamalı.
  • Sağlayıcı capability çağrılarında da requestId dedup’ını ortamla kapsa (capability relay).

4) Uç nokta adresleri — eklenti seviyesi + sürüm mührü

webhook, connect, action adreslerin ve UI sayfalarının ortak origin’i eklentinin tamamı için tanımlanır — her sürümde yeniden girmezsin. Sürüm editöründeki “Uç noktalar” kartı bu tek konfigürasyonu düzenler.

Bir sürümü kaydettiğinde o anki adres sürüme mühürlenir(snapshot). Kurulumlar mühürlenmiş adrese teslim alır:

eklenti konfigürasyonu          sürüm mührü (kurulumun gördüğü)
  origin  https://acme.com    →   v1.0  https://acme.com/webhook      ← yayında
  webhook /webhook                 v1.1  https://acme.com/webhook      ← taslak

origin'i https://new.com yap  →   v1.0  https://acme.com/webhook      ← DEĞİŞMEZ
                                   v1.1  kaydet → https://new.com/webhook
Adresi değiştirdin ama canlıda değişmedi mi? Beklenen davranış budur. Yayındaki sürüm kendi mührünü kullanmaya devam eder; yeni adres ancak yeni bir sürüm kaydedip yayınlayınca geçerli olur. Bu bilinçli bir güvenlik kararıdır: onaylanmış bir sürümün teslim adresi, yeniden incelemeden değiştirilemez.
Pratik sonuç: domain taşırken eski adresi hemen kapatma. Kurulu tenant’lar yeni sürüme yükselene kadar eski adrese teslim almaya devam eder — iki adresi bir süre birlikte çalışır tut.

5) Sandbox origin’i — yereli tünelle

Bir sürümün webhookUrl, connectUrl, actionUrl ve sayfa origin’leri tek bir kayıtlı domainden türer (tek-apex kuralı). Bu yüzden test mağazandaki kurulum da varsayılan olarak canlı sunucuna teslim eder — yerelde denemek için yayınlı sürümün adresini tünele çevirmek gerekirdi ve bu canlıyı kırardı.

Bunun yerine eklenti sayfasındaki (ya da sürüm editöründeki, uç noktaların üstündeki) Sandbox origin’i alanına tünel adresini yaz (ngrok, cloudflared…). Eklenti sayfasında alanın kendi Kaydet’i vardır; sürüm editöründe sayfanın Kaydet’iyle birlikte yazılır. Yalnız sandbox teslimleri oraya gider; path’ler sürümden gelir, yalnız origin değişir:

sürüm (değişmez)                  sandbox origin'i: https://acme.ngrok.app
  https://acme.com/webhook           →  https://acme.ngrok.app/webhook
  https://acme.com/oauth/connect     →  https://acme.ngrok.app/oauth/connect
  https://acme.com/api/action        →  https://acme.ngrok.app/api/action
  pages[].customUiOrigin             →  https://acme.ngrok.app

production kurulumları  →  https://acme.com/...   (değişmez)
  • Sürümü düzenlemez, incelemeye takılmaz. Eklenti düzeyinde bir ayardır; tünel adresin değişince yeniden review’a girmezsin.
  • Yalın https origin gir: path, query ya da sondaki / olmadan. Sandbox runtime bu adrese internet üzerinden ulaşır — localhost erişilemez, tünel gerekir.
  • Kaydettiğinde test aynası hemen tazelenir ve kurulu test mağazalarının adresleri de otomatik hizalanır — eklentiyi kaldırıp yeniden kurmana gerek yok. Teslim, bir sonraki event’te yeni tünele gider. (Yalnız origin değişir; path’ler ve sayfa listesi sürümden gelmeye devam eder.)
  • Alanı boşaltmak override’ı kaldırır — sandbox da yayın adresine döner.
  • Eklenti başına tek tünel. Origin eklentinin tüm sandbox kurulumlarını birden taşır — iki test mağazasında iki farklı sürümü ayrı tünellerle deneyemez, iki geliştirici aynı eklenti için ayrı tünel kullanamazsınız (biri diğerini ezer).
  • Test event ve teslim logları production tarafına bakar. Portaldaki “Test event gönder” imzalı POST’u yayın adresine yollar (tünele değil); tünele düşen sandbox teslimleri de portalın teslim loglarında / teslim sağlığı ekranlarında görünmez — tüneli debug ederken kendi sunucu loglarına bak.
Sandbox origin’i production’a asla sızmaz: gerçek mağaza kurulumları sürümde yazan adresi kullanmaya devam eder. Yine de imza doğrulamayı tünelde de yap — tünel adresi tahmin edilebilir ve herkese açıktır.

Kontrol listesi

  1. /connect’te environment’ı oku (varsayma).
  2. Token takasını o ortamın köküne gönder.
  3. Kurulumu tenantId + environment ile sakla.
  4. Gelen her imzalı gövdede/claim’de ortamı oku → doğru kurulumun secret’ıyla doğrula.
  5. Dedup ve iş verisini ortamla kapsa.
Yayınlanmamış eklentini gerçek bir akışla denemek için Test Mağazaları kullan — oradan gelen kurulumlar sandbox ortamındadır.