İçeriğe geç
Docs

Aksiyonlar (Actions)

Panel yerleşimleri, iframe ve API aksiyonları, imzalı istek/yanıt biçimi ve aksiyonları yerelde test etme.

Son güncelleme:

Aksiyonlar, uygulamanızın Tecof panelindeki sipariş, ürün ve müşteri ekranlarına kendi işlemlerini eklemesini sağlar. Örneğin bir muhasebe uygulaması sipariş detayına "Fatura kes" düğmesi, sipariş listesine "Seçilenleri faturala" toplu işlemi ekleyebilir.

İki tür aksiyon vardır:

TürNe olurNe zaman
iframePanel, uygulamanızın bir sayfasını modal ya da çekmece içinde açarKullanıcıdan girdi almak, önizleme göstermek gerekiyorsa
apiPanel Tecof'a çalıştırma isteği gönderir; Tecof uygulamanıza imzalı bir POST atar ve yanıtınızı kullanıcıya bildirim olarak gösterirTek tıkla biten işlemler, toplu işlemler

Yerleşimler#

placementEkranBağlam (ids)Gerekli kapsam
order.detailSipariş detayı, başlıktaki "Uygulamalar" menüsü[orderId]orders:read
order.listSipariş listesi, toplu işlem menüsüSeçili sipariş kimlikleri (≤ 50)orders:read
product.detailÜrün detayı[productId]products:read
product.listÜrün listesi, toplu işlem menüsüSeçili ürün kimlikleri (≤ 50)products:read
customer.detailMüşteri detayı[customerId]customers:read
customer.listMüşteri listesi, toplu işlem menüsüSeçili müşteri kimlikleri (≤ 50)customers:read

Aksiyon panelde yalnız aktif ve yerleşimin gerektirdiği kapsamı verilmiş kurulumlarda listelenir.


Tanımlama#

Aksiyonlar tecof.app.json içindeki actions dizisinde tanımlanır (en fazla 12):

JSON
{
  "actions": [
    {
      "key": "create-invoice",
      "type": "iframe",
      "placement": "order.detail",
      "title": { "tr": "İşbaşı'nda fatura kes", "en": "Create İşbaşı invoice" },
      "path": "/actions/create-invoice",
      "presentation": "modal"
    },
    {
      "key": "bulk-invoice",
      "type": "api",
      "placement": "order.list",
      "title": { "tr": "Seçilenleri faturala", "en": "Invoice selected" },
      "path": "/api/actions/bulk-invoice",
      "confirm": {
        "tr": "Seçili siparişler için fatura kesilsin mi?",
        "en": "Create invoices for selected orders?"
      }
    }
  ]
}
AlanKural
key^[a-z][a-z0-9-]{1,39}$; uygulama içinde tekil
typeiframe ya da api
placementYukarıdaki tablodan biri
title{ tr, en }; panel diline göre gösterilir
pathappUrl'e göre, / ile başlar
presentationYalnız iframe: modal (varsayılan) ya da drawer
confirmYalnız api, isteğe bağlı: { tr, en } metniyle panel çalıştırmadan önce onay sorar

Değişikliği tecof app config push ile gönderin.


Panel Tarafında Akış#

sequenceDiagram
    participant U as Kullanıcı (panel)
    participant P as Tecof Paneli / API
    participant A as Uygulamanız

    P->>P: GET /api/merchant/apps/actions?placement=order.list
    U->>P: Aksiyonu seçer (ve onaylar)
    P->>P: POST /api/merchant/apps/actions/run { installationId, actionKey, placement, ids }
    alt type = iframe
        P->>A: iframe: launch URL + actionRunId, actionKey, placement, ids
        A->>P: App Bridge close({ refresh, message, kind })
    else type = api
        P->>A: İmzalı POST (15 sn zaman aşımı)
        A-->>P: { ok, message, kind, redirectUrl, refresh }
        P->>U: Bildirim / yenileme / yönlendirme
    end
Diyagram

Her çalıştırma bir kayıt (runId) üretir. Geliştirici API'sinden geçmişi izleyebilirsiniz: GET /api/developer/apps/:id/action-runs.


iframe Aksiyonları#

Panel aksiyon penceresini, uygulamanızın açılış (launch) adresine şu parametreleri ekleyerek açar:

Metin
https://isbasi.apps.tecof.site/actions/create-invoice?installationId=…&merchantId=…&locale=tr&embedded=1&host=…&session=…&actionRunId=…&actionKey=create-invoice&placement=order.detail&ids=6720bb00cc11dd22ee33ff44

ids virgülle ayrılmış kimliklerdir. Aynı bilgi App Bridge bağlamında context.action = { runId, key, placement, ids } olarak da gelir. İşiniz bitince close ile pencereyi kapatın; refresh: true paneldeki ekranı yeniler.

TSX
// app/actions/create-invoice/page.tsx
"use client";

import { useState } from "react";
import { useAppBridge, useAppContext, useSessionFetch } from "@tecof/app-bridge/react";

export default function CreateInvoiceAction() {
  const bridge = useAppBridge();
  const context = useAppContext();
  const sessionFetch = useSessionFetch();
  const [busy, setBusy] = useState(false);

  const orderId = context.action?.ids[0];

  async function createInvoice() {
    if (!orderId) return;
    setBusy(true);
    bridge.setLoading(true);
    try {
      const res = await sessionFetch("/api/invoices", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ orderId }),
      });
      if (!res.ok) throw new Error("invoice-failed");
      bridge.close({ refresh: true, message: "Fatura oluşturuldu", kind: "success" });
    } catch {
      bridge.toast("Fatura oluşturulamadı", { kind: "error" });
    } finally {
      bridge.setLoading(false);
      setBusy(false);
    }
  }

  return (
    <main style={{ padding: 24 }}>
      <h1>Fatura kes</h1>
      <p>Sipariş: {orderId}</p>
      <button onClick={createInvoice} disabled={busy}>Faturayı oluştur</button>
      <button onClick={() => bridge.close()}>Vazgeç</button>
    </main>
  );
}

Sayfanın AppBridgeProvider içinde olması gerekir; bkz. App Bridge. /api/invoices sizin uçunuzdur ve isteği oturum token'ıyla doğrular (tecof.sessionFromRequest(req)).


API Aksiyonları#

İstek#

Tecof, appUrl + path adresine imzalı bir POST gönderir (test mağazası kurulumunda tecof app dev açıksa tünel adresine). Zaman aşımı 15 saniyedir; aksiyonlar yeniden denenmez.

HTTP
POST /api/actions/bulk-invoice HTTP/1.1
Content-Type: application/json
X-Tecof-Timestamp: 1760087770
X-Tecof-Signature: t=1760087770,v1=5f2c…e91a
X-Tecof-Action: bulk-invoice
X-Tecof-Installation-Id: 6720aa00bb11cc22dd33ee55
JSON
{
  "actionRunId": "6720cc00dd11ee22ff33aa55",
  "actionKey": "bulk-invoice",
  "placement": "order.list",
  "ids": ["6720bb00cc11dd22ee33ff44", "6720bb00cc11dd22ee33ff45"],
  "merchantId": "66f0c0ffee0000000000abcd",
  "installationId": "6720aa00bb11cc22dd33ee55",
  "userId": "66f0aa00bb11cc22dd33ee66",
  "locale": "tr",
  "timestamp": "2026-10-10T09:16:10.000Z"
}

Yanıt#

Yanıt JSON ve en fazla 16 KB olmalıdır:

JSON
{
  "ok": true,
  "message": "2 fatura oluşturuldu",
  "kind": "success",
  "redirectUrl": "https://isbasi.apps.tecof.site/invoices",
  "refresh": true
}
AlanAnlamı
okİşlem sonucu
messageKullanıcıya gösterilecek mesaj
kindsuccess, error, info, warning
redirectUrlİsteğe bağlı yönlendirme adresi
refreshtrue ise panel ekranı yeniler

Panel tarafına { runId, type: "api", ok, message, kind, redirectUrl, refresh } döner. 2xx olmayan yanıt ya da zaman aşımı ok: false, kind: "error" ve genel app-action-failed mesajına dönüşür. Kullanıcıya kendi hata mesajınızı göstermek istiyorsanız 200 ile { "ok": false, "kind": "error", "message": "…" } dönün.

İşleyici#

TypeScript
// app/api/actions/bulk-invoice/route.ts
import { tecof } from "@/lib/tecof";

export async function POST(req: Request) {
  const rawBody = await req.text();

  let action;
  try {
    action = tecof.verifyActionRequest(rawBody, req.headers);
  } catch {
    return Response.json({ ok: false, kind: "error", message: "Geçersiz imza" }, { status: 401 });
  }

  const api = tecof.client(action.installationId);
  let created = 0;
  const failed: string[] = [];

  for (const orderId of action.ids) {
    try {
      const order = await api.orders.get(orderId);
      // … faturayı oluştur, siparişe metafield yaz
      created++;
    } catch {
      failed.push(orderId);
    }
  }

  if (failed.length > 0) {
    return Response.json({
      ok: created > 0,
      kind: created > 0 ? "warning" : "error",
      message: `${created} fatura oluşturuldu, ${failed.length} sipariş atlandı`,
      refresh: true,
    });
  }

  return Response.json({ ok: true, kind: "success", message: `${created} fatura oluşturuldu`, refresh: true });
}

15 saniyelik sınırı aşabilecek toplu işlerde işi küçük parçalara bölün; aksiyon yeniden denenmediği için işleyicinin yarıda kalması durumunda kullanıcının aksiyonu tekrar çalıştırması güvenli olmalıdır (idempotent tasarım: işlenmiş siparişleri metafield ile atlayın).


Yerelde Test#

Uçtan uca (önerilen): tecof app dev çalışırken uygulamanız test mağazasında kuruluysa, panelden çalıştırdığınız hem iframe hem API aksiyonları tünel üzerinden yerel sunucunuza gelir (devUrl, test mağazası kurulumlarında appUrl'in yerine geçer). Bkz. Hızlı Başlangıç.

Yalnız imza ve yanıt biçimi: API aksiyonunu yerel sunucunuza imzalı curl ile de gönderebilirsiniz:

Terminal
SECRET="$TECOF_CLIENT_SECRET"
T=$(date +%s)
BODY='{"actionRunId":"local-run-1","actionKey":"bulk-invoice","placement":"order.list","ids":["6720bb00cc11dd22ee33ff44"],"merchantId":"66f0c0ffee0000000000abcd","installationId":"6720aa00bb11cc22dd33ee55","userId":"66f0aa00bb11cc22dd33ee66","locale":"tr","timestamp":"2026-10-10T09:16:10.000Z"}'
SIG=$(printf '%s.%s' "$T" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $NF}')

curl -X POST http://localhost:3000/api/actions/bulk-invoice \
  -H "Content-Type: application/json" \
  -H "X-Tecof-Timestamp: $T" \
  -H "X-Tecof-Signature: t=$T,v1=$SIG" \
  -H "X-Tecof-Action: bulk-invoice" \
  -H "X-Tecof-Installation-Id: 6720aa00bb11cc22dd33ee55" \
  --data-binary "$BODY"
  • printf '%s.%s' ile imzalanan metin ile --data-binary ile gönderilen gövde bayt bayt aynı olmalıdır.
  • installationId gerçek bir test kurulumuna ait değilse işleyicideki API çağrıları başarısız olur; uçtan uca akışı test mağazasında tünelle doğrulayın.
  • iframe aksiyonları için test mağazasında aksiyonu panelden çalıştırın; App Bridge yalnız panel içinde çalışır.

Sık Karşılaşılan Hatalar#

BelirtiOlası nedenÇözüm
Aksiyon panelde görünmüyorYapılandırma gönderilmedi; kurulum aktif değil; yerleşimin gerektirdiği kapsam verilmemiş; onaysız uygulama test mağazası dışındatecof app config push; kapsamı ekleyip mağazaya onaylatın; testStores'u kontrol edin
Test mağazasında aksiyon yerel sunucuya ulaşmıyortecof app dev kapalı ya da devUrl'in 8 saatlik süresi doldutecof app dev'i yeniden başlatın
Panelde app-action-failedUç 2xx dışı yanıt döndü ya da 15 saniyeyi aştıKendi hata mesajınız için 200 + ok: false dönün; işi bölün
İşleyici 401 dönüyorGövde JSON olarak okunup yeniden serileştirildi; yanlış sır; saat farkı 300 sn'yi aşıyorawait req.text() kullanın; TECOF_CLIENT_SECRET'ı kontrol edin; sunucu saatini eşitleyin
Sır döndürmeden sonra imza hatasıUygulama eski sırla çalışıyor ve yalnız tek v1 kontrol ediliyorBaşlıktaki tüm v1 değerlerini deneyin (SDK yapar); yeni sırrı ortama yazın
Panelde beklediğiniz mesaj görünmüyorYanıt JSON değil ya da 16 KB sınırını aşıyorYanıtı en fazla 16 KB'lık JSON olarak dönün
iframe aksiyonu boş açılıyorSayfa X-Frame-Options: DENY ya da panel origin'ini içermeyen frame-ancestors gönderiyorPanel origin'inin uygulamanızı çerçevelemesine izin verin
close() çalışmıyorApp Bridge hazır değil ya da sayfa panel dışında açıldıAppBridgeProvider / bridge.ready() sonrasında çağırın
Toplu işlemde 50'den fazla kayıtPanel seçim sınırıYerleşim başına en fazla 50 kimlik gelir