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ür | Ne olur | Ne zaman |
|---|---|---|
iframe | Panel, uygulamanızın bir sayfasını modal ya da çekmece içinde açar | Kullanıcıdan girdi almak, önizleme göstermek gerekiyorsa |
api | Panel 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österir | Tek tıkla biten işlemler, toplu işlemler |
Yerleşimler#
placement | Ekran | Bağlam (ids) | Gerekli kapsam |
|---|---|---|---|
order.detail | Sipariş detayı, başlıktaki "Uygulamalar" menüsü | [orderId] | orders:read |
order.list | Sipariş 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.detail | Müşteri detayı | [customerId] | customers:read |
customer.list | Müş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):
{
"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?"
}
}
]
}| Alan | Kural |
|---|---|
key | ^[a-z][a-z0-9-]{1,39}$; uygulama içinde tekil |
type | iframe ya da api |
placement | Yukarıdaki tablodan biri |
title | { tr, en }; panel diline göre gösterilir |
path | appUrl'e göre, / ile başlar |
presentation | Yalnız iframe: modal (varsayılan) ya da drawer |
confirm | Yalnı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
endHer ç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:
https://isbasi.apps.tecof.site/actions/create-invoice?installationId=…&merchantId=…&locale=tr&embedded=1&host=…&session=…&actionRunId=…&actionKey=create-invoice&placement=order.detail&ids=6720bb00cc11dd22ee33ff44ids 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.
// 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.
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{
"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:
{
"ok": true,
"message": "2 fatura oluşturuldu",
"kind": "success",
"redirectUrl": "https://isbasi.apps.tecof.site/invoices",
"refresh": true
}| Alan | Anlamı |
|---|---|
ok | İşlem sonucu |
message | Kullanıcıya gösterilecek mesaj |
kind | success, error, info, warning |
redirectUrl | İsteğe bağlı yönlendirme adresi |
refresh | true 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#
// 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:
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-binaryile gönderilen gövde bayt bayt aynı olmalıdır.installationIdgerç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#
| Belirti | Olası neden | Çözüm |
|---|---|---|
| Aksiyon panelde görünmüyor | Yapılandırma gönderilmedi; kurulum aktif değil; yerleşimin gerektirdiği kapsam verilmemiş; onaysız uygulama test mağazası dışında | tecof app config push; kapsamı ekleyip mağazaya onaylatın; testStores'u kontrol edin |
| Test mağazasında aksiyon yerel sunucuya ulaşmıyor | tecof app dev kapalı ya da devUrl'in 8 saatlik süresi doldu | tecof app dev'i yeniden başlatın |
Panelde app-action-failed | Uç 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üyor | Gövde JSON olarak okunup yeniden serileştirildi; yanlış sır; saat farkı 300 sn'yi aşıyor | await 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 ediliyor | Başlıktaki tüm v1 değerlerini deneyin (SDK yapar); yeni sırrı ortama yazın |
| Panelde beklediğiniz mesaj görünmüyor | Yanıt JSON değil ya da 16 KB sınırını aşıyor | Yanıtı en fazla 16 KB'lık JSON olarak dönün |
| iframe aksiyonu boş açılıyor | Sayfa X-Frame-Options: DENY ya da panel origin'ini içermeyen frame-ancestors gönderiyor | Panel origin'inin uygulamanızı çerçevelemesine izin verin |
close() çalışmıyor | App 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ıt | Panel seçim sınırı | Yerleşim başına en fazla 50 kimlik gelir |