Tecof'ta fatura kesen her uygulama aynı sipariş verisini okur, aynı para kurallarını uygular ve faturanın durumunu siparişe aynı biçimde yazar. Böylece mağaza hangi entegratörü seçerse seçsin fatura tutarı aynı çıkar; kargo, muhasebe ya da raporlama uygulamaları da faturanın kesilip kesilmediğini sağlayıcıyı bilmeden öğrenir.
Bu sayfa üç şeyi anlatır: Tecof'un kendi fatura uygulamaları, fatura yazan bir uygulamanın uyması gereken ortak kurallar ve bu kuralları hazır veren @tecof/invoice-kit paketi.
Tecof'un Fatura Uygulamaları#
Hepsi platform uygulamasıdır (integration: "platform"): kimlik bilgileri Vault'ta şifreli durur, siparişler webhook ile gelir, sipariş ekranlarında aksiyon sunar. Her birinin deposunda kurulum, ayarlar ve sınırları anlatan kendi README'si bulunur.
| Uygulama | Slug | Belge | Modlar | Fatura numarası | Sipariş iptal/iade edilince |
|---|---|---|---|---|---|
| Logo İşbaşı | isbasi | İşbaşı satış faturası | kapalı · taslak · kes | İşbaşı | İşaretle ya da yalnız taslağı sil |
| GİB e-Arşiv Portalı | earsiv | e-Arşiv (portal) | kapalı · taslak (imza portalda) | GİB | İşaretle ya da imzasız taslağı sil |
| Paraşüt | parasut | Satış faturası, isteğe bağlı e-Arşiv / e-Fatura | kapalı · fatura · e-belge | Paraşüt | İşaretle ya da e-belgesi olmayan faturayı iptal et |
| EDM Bilişim | edm | e-Fatura / e-Arşiv (UBL-TR) | kapalı · taslak · gönder | EDM | İşaretle ya da e-Arşiv'i koşullu iptal et |
| QNB eSolutions | qnb | e-Fatura / e-Arşiv (UBL-TR) | kapalı · gönder | QNB | İşaretle (iptal QNB portalında) |
| KolayBi | kolaybi | Satış faturası, isteğe bağlı e-Arşiv / e-Fatura | kapalı · fatura · e-belge | KolayBi | İşaretle ya da resmileşmemiş ve tahsilatı olmayan faturayı sil |
Ortak davranışlar:
- Tetikleyici:
order.paid,order.fulfilledya da yalnız elle (manual). - Aksiyonlar: sipariş detayında önizleme, oluşturma, yeniden deneme ve PDF çekmecesi (
order.detail, iframe); sipariş listesinde "Seçilenleri faturala" (order.list, api, en fazla 50 sipariş). - e-Fatura mı e-Arşiv mi: e-belge kesen uygulamalar alıcının e-Fatura mükellefiyetini sağlayıcıya sorar; mükellefe e-Fatura, diğerlerine e-Arşiv keser. Anonim ve yurt dışı alıcı sorgulanmadan e-Arşiv alır. Hangi alıcıların sorgulandığı sağlayıcıya göre değişir: İşbaşı ve Paraşüt yalnız kurumsal alıcıyı, EDM ve QNB kimlik numarası olan her yurt içi alıcıyı sorgular. KolayBi API'si mükellef sorgusu sunmadığı için belge türünü KolayBi belirler. Sorgu hata verirse belge kesilmez; mükellefe yanlışlıkla e-Arşiv gitmesin diye e-Arşiv'e düşülmez.
- Çift fatura koruması: uygulama, sağlayıcıyı çağırmadan önce siparişi
creatingolarak işaretler. Zaman aşımı ya da yarım kalan denemeden sonra önce sağlayıcıda sipariş numarası veya belge UUID'siyle arar; belge varsa onu benimser, yoksa yeniden dener.
e-Fatura bu uygulamaların hiçbirinde otomatik iptal edilmez; düzeltme iade faturasıyla yapılır. e-Arşiv'i otomatik iptal eden tek uygulama EDM'dir: tam iptal ya da iade olması, fatura tarihinden itibaren 7 gün içinde kalınması ve EDM'deki belge durumunun iptale izin vermesi gerekir.
Kurumsal ve Bireysel Alıcı#
Checkout'ta müşteri Bireysel ya da Kurumsal fatura seçer. Seçim sipariş nesnesinin billingAddress alanına yazılır (Siparişler):
| Alan | Bireysel | Kurumsal (Türkiye) | Kurumsal (yurt dışı) |
|---|---|---|---|
invoiceType | "individual" | "corporate" | "corporate" |
company | boş olabilir | Unvan, zorunlu | Unvan, zorunlu |
taxNumber | boş | 10 haneli VKN ya da şahıs şirketi için 11 haneli TCKN; sağlama hanesi doğrulanır | Serbest biçim (harf, rakam, -; en fazla 30) |
taxOffice | boş | Zorunlu | Zorunlu değil |
identityNumber | TCKN; yoksa anonim 11111111111 | — | — |
Checkout bu kurallara uymayan siparişi 400 invalid-invoice-identity ile reddeder. Yine de eski siparişlerde ya da elle oluşturulan kayıtlarda tutarsız veri bulunabileceği için fatura uygulamaları alıcıyı kendileri sınıflandırmalıdır. classifyBuyer bunu tek adımda yapar (aşağıda).
Ortak Fatura Durumu#
Her fatura uygulaması sipariş başına tek bir paylaşılan metafield yazar. Panel bu alanı sipariş detayındaki "Uygulamalar" kartında gösterir. Aynı mağazadaki diğer uygulamalar orders:read kapsamıyla GET /api/v1/metafields?ownerType=order&ownerId=… üzerinden okuyabilir; yanıtta kendi alanlarının yanında öteki uygulamaların paylaşılan alanları da gelir.
| Özellik | Değer |
|---|---|
ownerType | "order" |
namespace | Uygulamanın slug'ı (isbasi, parasut, …) |
key | "invoice" |
type | "json" |
visibility | "shared" |
panel | { label: { tr, en }, show: true } |
{
"status": "issued",
"documentType": "e-arsiv",
"invoiceNumber": "ABC2026000000123",
"uuid": "0b6c6f0e-3a7d-4a35-9a4e-5f2f6b1c9d10",
"updatedAt": "2026-10-10T09:17:02.000Z"
}status sözlüğü bütün fatura uygulamalarında aynıdır:
| Durum | Anlamı |
|---|---|
creating | Talep alındı, sağlayıcıya gönderiliyor |
draft | Sağlayıcıda taslak olarak duruyor |
issued | Kesildi; e-belgeyse GİB'e gönderildi |
signed | İmzalandı (portal akışlarında) |
failed | Başarısız; error alanında temizlenmiş neden |
skipped | Bilerek atlandı; neden reason alanında (örn. total-mismatch, non-try-currency, ayar kapalı) |
needs-cancellation | Sipariş iptal ya da iade edildi; belgenin iptali elle yapılmalı |
deleted | Taslak silindi |
cancelled | Sağlayıcıda iptal edildi |
Değerde kişisel veri tutulmaz: alıcı adı, adresi ya da tam vergi numarası metafield'a ve olay yüküne yazılmaz.
Uygulamalar ayrıca şu olayları yayınlar; dinleyen uygulama app.<slug>. önekiyle abone olur:
| Olay | Ne zaman |
|---|---|
app.<slug>.invoice.created | Fatura ya da taslak oluştu |
app.<slug>.invoice.failed | Deneme başarısız oldu |
app.<slug>.invoice.cancelled | Belge silindi ya da sağlayıcıda iptal edildi |
// Kargo uygulaması: faturası kesilmemiş siparişi kargoya vermeden önce uyarır
const fields = await api.metafields.list({ ownerType: "order", ownerId: orderId });
const issued = fields.some((m) => m.key === "invoice" && ["issued", "signed"].includes(m.value?.status));Ortak Para Kuralları#
Fatura yazan bir uygulama, sipariş toplamıyla birebir aynı faturayı üretmek zorundadır. Kurallar @tecof/invoice-kit içinde uygulanmıştır; kendi hesaplamanızı yazıyorsanız aynılarını izleyin.
- Satır KDV modu önce satırdan (
taxIncluded), sonra siparişten (totals.pricesIncludeTax) okunur. Oranınullolan eski satırlarda mağaza varsayılanı kullanılır. - KDV matrahı indirim sonrası tutardır. Sipariş, her satıra düşen indirimi
lineItems[].discountAmountalanında taşır; faturayı bu paylarla kurun. Kapsamlı kupon yalnız kendi satırlarına düşer. Eski siparişlerde pay yoksa indirimi ürün satırlarına tutarla orantılı dağıtın. İndirim kargo ve ödeme ücretine düşmez. - Kargo ve ödeme ek ücreti ayrı hizmet satırıdır. Oranı bilinmeyen kargo KDV dahil sayılır.
- Hediye çeki ödeme aracıdır, indirim değildir: fatura tam tutardan kesilir.
- Mutabakat: küçük kuruş farkları (varsayılan
max(0,05; 0,01 × satır sayısı)) en büyük satırda düzeltilir. Fark bu toleransı aşarsatotal-mismatchuyarısı oluşur. total-mismatcholan sipariş otomatik ve toplu faturalamada atlanır (status: "skipped",reason: "total-mismatch"). Mağaza sipariş çekmecesinde farkı görüp onaylayarak "Farka rağmen oluştur" diyebilir.- TRY dışı sipariş: Paraşüt ve KolayBi uygulamaları ayar açıksa döviz faturasını TCMB kuruyla keser; diğer uygulamalar bu sürümde yalnız TRY faturalar ve döviz siparişini
skippedolarak işaretler.toInvoiceDraftdöviz siparişinde kur ister (exchange-rate-missing).
@tecof/invoice-kit#
Siparişi (GET /api/v1/orders/:id) doğru bir Türk faturasına çeviren, bağımlılıksız TypeScript paketidir. Tecof'un bütün fatura uygulamaları bunu kullanır.
npm install @tecof/invoice-kitimport { toInvoiceDraft, buildUblInvoice } from "@tecof/invoice-kit";
const order = await api.orders.get(orderId);
const draft = toInvoiceDraft(order, { defaultVatRate: 20 });
if (draft.warnings.some((w) => w.code === "total-mismatch")) {
// Otomatik akışta atla; kararı mağazaya bırak
}
// e-Fatura / e-Arşiv entegratörü UBL bekliyorsa
const xml = buildUblInvoice(draft, {
profile: "EARSIVFATURA",
typeCode: "SATIS",
uuid: crypto.randomUUID(),
supplier,
});| Dışa aktarım | Ne yapar |
|---|---|
toInvoiceDraft(order, options?) | Alıcı, satırlar, KDV dökümü, toplamlar, düzenleme tarihi (Europe/Istanbul) ve uyarılar. Hata fırlatmaz; faturayı kesip kesmemek çağıranın kararıdır |
classifyBuyer(order) | corporate, individual, anonymous ya da foreign; kimlik numarası, unvan, vergi dairesi ve uyarılar |
buildUblInvoice(draft, options) | İmzasız UBL-TR 1.2 fatura XML'i (TEMELFATURA, TICARIFATURA, EARSIVFATURA) |
isValidVkn, isValidTckn | Sağlama hanesi doğrulaması; checkout ile aynı algoritma |
amountInWords(tutar) | "Yalnız Bin İki Yüz Otuz Dört Türk Lirası Elli Kuruş" |
suggestEArchivePaymentMethod(provider) | Tecof ödeme sağlayıcısını GİB ödeme şekline çevirir |
Alıcı sınıflandırması:
- Fatura adresi yurt dışıysa
foreignolur. Kurumsalda kimlik2222222222kullanılır, alıcının kendi vergi numarası nota ve UBL'e ayrıca yazılır. invoiceType: "corporate"ve geçerli VKN ya da TCKN varsacorporateolur. Numara geçersizse bireysel kurala düşer vetax-number-invaliduyarısı oluşur.- Geçerli TCKN varsa
individual, yoksaanonymous(11111111111) olur.
Bilinen sınırlar: ihracat, tevkifat, ÖTV ve özel matrah faturaları desteklenmez. Paket belgeyi imzalamaz ve zarf üretmez; bunlar entegratörün işidir. Alıcının e-Fatura mükellefi olup olmadığını da sorgulamaz; bunu entegratörden öğrenmeniz gerekir.
Kendi Fatura Uygulamanızı Yazarken#
- Kimlik bilgilerini yalnız Vault'ta tutun; arayüze ve günlüklere maskeli yazın.
- Sağlayıcı adresi ayarlanabiliyorsa yalnız
httpsve sağlayıcının kendi alan adlarına izin verin. Aksi hâlde saklanan kimlik bilgileri başka bir sunucuya gönderilebilir. - Siparişi her zaman kurulum token'ıyla okuyun. Başka mağazanın siparişi
404döner; panelden gelen sipariş kimliğine bu kontrol olmadan güvenmeyin. - Webhook'u 8 saniye içinde yanıtlayın, sağlayıcı çağrısını arka plana alın. Aynı olay birden fazla gelebilir;
creatingişaretiyle çift faturayı önleyin. - Durumu yukarıdaki sözleşmeyle yazın, olayları yayınlayın. Sağlayıcı hata mesajlarını göstermeden önce oturum kimliği, parola ve tam vergi numarasından arındırın.
- GİB'e gitmiş bir belgeyi kural dışında iptal etmeyin; emin değilseniz
needs-cancellationişaretleyip kararı mağazaya bırakın.
Uçtan uca örnek için Örnek Uygulama: Logo İşbaşı sayfasına bakın.