Exploring Infinite Possibilities on FrontEnd 🚀
Burak Sağlık
Search...

Sat Sep 12 2026

HMRC MTD API Entegrasyonu: OAuth2, VAT Beyanı ve Test Rehberi

HMRC MTD API Entegrasyonu: OAuth2, VAT Beyanı ve Test Rehberi

🎯 Giriş ve Hedefler

Makaleyi okumaya hazırsan başlayalım! Bu yazı, HMRC MTD (Making Tax Digital) API'sine bağlanma sürecini adım adım anlatıyor. Konuyla ilgileniyorsun, çünkü veri kontrolü, otomatik raportlama veya yeni bir entegrasyon mu halletmen gerekiyor? İşte tam da bunun için buradasın.

Neden HMRC MTD API'sine bağlanmak önemli?

  • Kanuni uyumluluk – İngiltere'deki şirketler vergi süreçlerini dijitalleştirmek zorunda. API, bu süreçte sana destek oluyor.
  • Gerçek zamanlı veri – Envanter, satış veya personel verilerini vergi kuruluşuna anında iletebiliyorsun.
  • Otomasyon avantajı – Manuel giriş hatalarını azaltıyor ve zaman tasarrufu sağlıyor.

Yazının sonunda ne kazanacaksın?

  • HMRC MTD API'sinin temel bilgileri ✅
  • Sağlayıcıdan aldığın verileri nasıl işleyeceğin 🎯
  • Bir bağlantı kurup test etmen için pratik örnekler 🛠
  • Hata yönetimi ve test etme konusunda ipuçları 🔍

Hadi pratik bir örnek üzerinden anlayalım. Sende bir Python sahibi olman gerekmez; sadece temel bir isteğe Başlatmak için oluşturduğun komutu biliyorsan yeterli. 🔁

Hızlı plan:

  • Giriş – Amaç ve bağlam
  • Bağlantı adımları – Kimlik doğrulama, endpoint tanımlama, veri talebi
  • İşleme – Yanıtleri ayrıştırma, doğrulama ve depolama
  • Test – Hata durumlarını gözden geçirme ve olası sorunları giderme
  • Sonuç – Özet ve bir sonraki adımlar

Hazırsan başlayalım ve bu API'sine bağlanmayı birlikte öğrenelim! 🚀


🔁 HMRC Making Tax Digital (MTD) Nedir?

Hazırsan bu konuyu beraber çözelim. Making Tax Digital (MTD), HMRC'nin (İngiltere Gelir İdaresi) "vergiyi dijitalleştir" sloganıyla başlattığı bir dönüşüm programı. Kısaca: kağıt işlerini bitirip, her şeyi yazılım aracılığıyla yapmaya zorluyorlar 🎯

Ne Değişiyor Aslında?

Eski usül: Yıllık bir kez Excel tablosu ya da kağıt beyanname doldurup gönderiyordun.
Yeni usül (MTD): Her çeyrekte (3 ayda bir) dijital kayıt tutup, uyumlu yazılım üzerinden HMRC'ye gönderiyorsun.

Kapsam: Kim Etkilenen? 🎯

Vergiler Durum
VAT (KDV) Zaten zorunlu — £85.000 üzeri herkes dahil 📌
Gelir Vergisi (Income Tax) MTD for ITSA — 2026 Nisan'dan itibaren £50.000+ gelirli bireysel mükellefler için başlıyor
Kurumlar Vergisi (Corporation Tax) MTD for Corporation Tax — henuz tarihi belli değil, pilot aşamasında 🛠

Not: "Uyumlu yazılım" dememizin sebebi — HMRC kendi portalından değil, API üzerinden veri alıyor. Yani senin muhasebe yazılımın (Xero, QuickBooks, FreeAgent, Sage vs.) HMRC ile doğrudan konuşuyor 🤝

Neden API Tabanlı? Avantajları Nelere? ✅

Hadi pratik bir örnek üzerinden anlayalım:

Eski yöntem:
Sen → Excel → HMRC portalına gir → Elle kopyala-yapıştır → Hata riski ❗️

MTD (API) yöntemi:
Sen → Muhasebe yazılımın → "Gönder" butonu → HMRC API'si alır, doğrular, onaylar ✅

Kazanımlar:

  • Hata azalır — elle veri girişi yok, kopyalama hatası yok 🛑
  • Gerçek zamanlı — HMRC verini anında görür, sen de ceza/faiz riskini minimize edersin ⚡
  • Kayıt tutma zorunluluğu — Dijital kayıt (digital records) tutmak şart, yani "yıl sonu toparlarım" diyemezsin 📋
  • Entegrasyon kolaylığı — Kendi yazılımını geliştirenler için REST API endpoint'leri açık, sandbox ortamı var 🧪

Nasıl Çalışır Teknik Tarafı? 🔧

HMRC OAuth 2.0 ile kimlik doğrulama yapıyor. Senin yazılımın:

  1. Kullanıcıyı HMRC onay sayfasına yönlendirir
  2. Access token alır (genelde 1-2 saat geçerli)
  3. Her çeyrek dönem için /obligations endpoint'inden hangi dönemlerin due olduğunu çeker
  4. Veriyi /returns endpoint'ine POST eder
  5. HMRC 200 OK + correlationId döner → işlem tamam ✅
// Örnek VAT beyanı payload'ı (basitleştirilmiş)
{
  "periodKey": "24AA",
  "vatDueSales": 12500.00,
  "vatDueAcquisitions": 0.00,
  "totalVatDue": 12500.00,
  "vatReclaimedCurrPeriod": 3200.00,
  "netVatDue": 9300.00,
  "totalValueSalesExVAT": 62500.00,
  "totalValuePurchasesExVAT": 16000.00,
  "totalValueGoodsSuppliedExVAT": 0.00,
  "totalAcquisitionsExVAT": 0.00,
  "finalised": true
}

Bu sayede ne oluyor?
HMRC verini anında alır, doğrular, sana submission receipt (beyanname kabul belgesi) döner. Sen de bu correlationId'yi loglarsın, müşterine "gönderildi, onaylandı" diyorsun 🎉

Özetle 🎯

Maddeler Durum
Amaç Vergi sistemini gerçek zamanlı, dijital, hatasız hale getirmek
Kimin için? Önce VAT, şimdi ITSA (gelir vergisi), sonra Corporation Tax
Nasıl? HMRC onaylı yazılım + REST API + OAuth 2.0
Senin işin ne? Müşterilerini uyumlu yazılımla tanıştırmak, API entegrasyonunu test etmek, sandbox'ta denemek 🧪

Sonraki bölümde MTD için HMRC sandbox ortamını nasıl kurarız, ilk API çağrısını nasıl yaparız — onu beraber yapacağız. Hazır mısın? 🚀


❗️ Ön Koşullar ve Gerekli Hesaplar

HMRC API’leriyle çalışmaya başlamadan önce şu adımları tamamlamanız gerekiyor. Her madde neden önemli kısaca açıklanmış 👇

  • HMRC Developer Hub hesabı açın 🎯

    • Neden? Tüm API abonelikleri, dokümantasyon ve test ortamlarına erişim bu hesap üzerinden sağlanır.
  • Uygulama (Application) kaydını oluşturun 🛠

    • Neden? Her API çağrısında gönderilecek client_id ve client_secret bu kayıttan alınır; uygulamayı tanımlar ve yetkilendirme akışını başlatır.
  • Test kullanıcısı (Test User) oluşturun 👤

    • Neden? Canlı verilerle çalışmadan önce sandbox ortamında istek atabilmeniz için sanal mükellef hesapları gereklidir.
  • Gerekli izinleri (Scopes) ekleyin

    • read:vat – KDV beyanlarını okumak için
    • write:vat – KDV beyanı göndermek için
    • read:self-assessment – Gelir vergisi beyanlarını okumak için
    • write:self-assessment – Gelir vergisi beyanı göndermek için
    • Neden? İlgili scope’lar olmadan API isteğiniz 403 Forbidden döner; her işlev için doğru yetkiyi tanımayı unutmayın.
  • Redirect URI’yi kaydedin 🔁

    • Neden? OAuth2 akışında kullanıcı yetkilendirildikten sonra dönüş adresi bu URI olmalıdır; aksi takdirde yetkilendirme başarısız olur.
  • IP allowlist (gerekliyse) yapılandırın 🌐

    • Neden? Kurumsal güvenlik politikaları dışarıdan gelen istekleri kısıtlarsa, HMRC’nin sandbox/prod IP aralıklarını beyaz listeye almanız gerekir.

Bu adımları sırayla tamamladığınızda, kod tarafında hemen client_id, client_secret ve test kullanıcı bilgileriyle OAuth2 token almaya başlayabilirsiniz. 🚀


🛠 Geliştirme Ortamını Kurma

Hazırsan başlayalım! 🚀 Önce makinemizde Node.js ve npm'in kurulu olduğundan emin olalım. Sonra proje klasörünü açıp gerekli paketleri kuracağız. Adım adım gidersek karmaşık bir şey yok.

Node.js ve npm Kurulumu

Terminali aç (macOS/Linux'ta Terminal, Windows'ta PowerShell veya Git Bash işini görür) ve şu komutları sırayla çalıştır:

# Node.js ve npm sürümlerini kontrol et
node --version
npm --version

Eğer sürüm numaraları gelirse zaten kurulu demektir ✅. Gelmezse veya hata alırsan:

# macOS (Homebrew ile)
brew install node

# Windows (Chocolatey ile)
choco install nodejs

# Ubuntu/Debian
sudo apt update && sudo apt install nodejs npm

Ne oluyor burada? Node.js, JavaScript'i tarayıcı dışında çalıştıran runtime. npm ise paket yöneticimiz — kütüphaneleri indirmek için lazım.


Proje Klasörünü Oluşturma

Şimdi temiz bir klasör açalım ve içine girelim:

# Proje klasörü oluştur
mkdir email-sender-api
cd email-sender-api

package.json Oluşturma (npm init)

Projeyi npm paketi haline getirelim. İki yöntem var:

Hızlı yöntem (varsayılanlarla kabul et):

npm init -y

İnteraktif yöntem (isim, versiyon, entry point vs. sorar):

npm init

İpucu: -y flag'i "yes to all" demek — hızlıca geçmek istiyorsan birebir 🎯


Gerekli Paketleri Kurma

Şimdi ihtiyacımız olacak iki paketi kuralım:

# HTTP istemcisi (axios) ve environment variable desteği (dotenv)
npm install axios dotenv

Ne oluyor burada?

  • axios → Promise tabanlı, hem Node hem tarayıcıda çalışan popüler HTTP client. fetch de var (Node 18+ built-in), ama axios hata yönetimi ve interceptor'lar için daha pratik 🛠
  • dotenv.env dosyasındaki KEY=VALUE çiftlerini process.env'e yükler. Hassas verileri (API key, şifre) koddan ayırmak için şart ❗️

Kurulum bittiyse package.json içinde dependencies kısmını kontrol et:

"dependencies": {
  "axios": "^1.x.x",
  "dotenv": "^16.x.x"
}

.env Dosyası Oluşturma

Proje kökünde .env dosyasını yarat ve API anahtarlarını buraya koy:

touch .env

İçine şu şekilde doldur (kendi key'lerini yaz tabii):

# .env örneği
BREVO_API_KEY=xsmtpsib-xxxxxxxxxxxxxxxxxxxxxxxx
SENDER_EMAIL=senin@email.com
SENDER_NAME=Uygulama Adın

⚠️ Önemli: .env dosyasını asla git'e commit etme! .gitignore içine .env satırını ekle.


Test İçin Postman / Insomnia Kurulumu

API'yi test ederken tarayıcı yeterli olmaz (POST body, header vs. lazım). İki popüler seçenek:

Araç Kurulum Neden?
Postman https://postman.com/downloads En yaygın, koleksiyon paylaşımı kolay, otomatik test yazılabilir
Insomnia https://insomnia.rest/download Daha hafif, açık kaynak, GraphQL desteği güçlü

İkisini de dene, hangisi hoşuna giderse onu kullan ✅. Ben Insomnia'yı tercih ediyorum — arayüzü daha sade.


Hadi Özetleyelim 📋

  1. ✅ Node.js + npm kuruldu
  2. ✅ Proje klasörü açıldı (email-sender-api)
  3. npm init -y ile package.json oluşturuldu
  4. axios ve dotenv kuruldu
  5. .env dosyası hazırlandı (API key'ler buraya)
  6. ✅ Postman/Insomnia indirildi

Artık kod yazmaya hazırız! Sonraki bölümde Brevo API'sine istek atan fonksiyonu yazacağız 🎯


🔁 OAuth2 Akışını Anlama ve Uygulama Kaydı

HMRC’nin Authorization Code Grant akışı, kullanıcı onayı aldıktan sonra güvenli bir access token almanızı sağlar.
Aşağıda basit bir metin tabanlı diyagram ve adım adım ne olduğunu görelim 👇

Akış Diyagramı (metin tabanlı)

[👤 Kullanıcı] 
      ▼ (1) Tarayıcıyı HMRC yetkilendirme sayfasına yönlendir
[🌐 HMRC Auth Server] ──► Kullanıcı giriş yapar & izin verir
      ▼ (2) Onay sonrası Redirect URI’ye geri dönüş (code parametresi ile)
[🛠 Uygulamanız (Redirect URI)]
      ▼ (3) Authorization Code + Client ID/Secret ile Token Endpoint’e istek at
[🔐 HMRC Token Endpoint]
      ▼ (4) Access Token (+ Refresh Token) döner
[✅ Uygulamanız artık API çağrıları yapabilir]

Ne oluyor burada?

  1. Kullanıcıyı HMRC’nin onay sayfasına gönderirsiniz.
  2. Kullanıcı izin verdikten sonra HMRC, Redirect URI’nize bir authorization code ekleyerek yönlendirir.
  3. Bu code’ı, Client ID ve Client Secret ile birlikte token endpoint’ine POST edersiniz.
  4. Karşılığında access token (ve genelde refresh token) alırsınız.

Parametreler nereden alınır? 📋

Parametre Nereden alınır? Nasıl saklanır?
Client ID HMRC Developer Hub → My Applications .env dosyasında CLIENT_ID
Client Secret Aynı sayfada Show secret butonuyla .env dosyasında CLIENT_SECRET
Redirect URI Uygulama kayıt sırasında tanımladığınız URL (ör. https://app.example.com/callback) .env dosyasında REDIRECT_URI
Scope Hangi API’lere erişim istiyorsunuz? (ör. read:vat write:vat) .env dosyasında SCOPE

🔑 Güvenlik ipucu: CLIENT_SECRET asla front‑end kodunda veya public repo’larda olmamalı. Sadece sunucu tarafında (backend) .env üzerinden okunmalı.


.env örneği (ini formatında)

CLIENT_ID=abc123def456
CLIENT_SECRET=super‑secret‑value‑xyz789
REDIRECT_URI=https://app.example.com/callback
SCOPE=read:vat write:vat

Nasıl çalışıyor?
Uygulamanız başladığında bu değişkenleri process.env.CLIENT_ID gibi okur, yetkilendirme URL’sini oluşturur ve token isteğinde gönderir. Böylece hassas bilgiler kodunuzdan tamamen ayrılmış olur ✅.


Hazırsan bir sonraki adımda yetkilendirme URL’sini oluşturup kullanıcıyı nasıl yönlendireceğimizi görelim 🚀


🛠 Backend Servisi İçin Proje Yapısı

Hadi pratik bir örnek üzerinden anlayalım – bir backend projesi ne kadar düzenli olursa, yeni bir geliştirici için o kadar kolay olur, değil mi? 🎯

Temel klasör yapısı

src/
├── routes/          # HTTP rotaları (e sunucuya gelen isteklerin yönlendirilmesi)
│   └── userRoutes.js
├── controllers/     # Gelen istekleri işleyen ve veriyi view’e ileten katman
│   └── userController.js
├── services/        # İş mantığı – veri tabanı, dış API'ler, iş kuraları
│   └── userService.js
├── utils/           # Ortak yardımcı fonksiyonlar, loglama, hata kontrolü, vs.
│   └── logger.js
├── config/          # Ortam değişkenleri, veritabanı ayarları, API anahtarları
│   └── index.js
├── app.js           # Express/Uv/REST sunucusu oluşturulur
└── server.js        # Güncel dinleyici ve başlangıç kodu

Her klasörün sorumluluğu

  • routes 🎯 – Belirli bir endpoint’i, method’u (GET, POST, vs.) ve ilgili controller’i bağlar.
  • controllers 🛠 – Gelen isteği alır, parametreleri doğrular, ardından servisi çağırır ve cevabı döndürür.
  • services 🔁 – Temiz iş mantığı: veritabanı sorguları, dış API çağrıları, veri işleme, validasyon.
  • utils ✅ – Yeniden kullanılabilir yardımcı fonksiyonlar (loglama, hata sınıflandırması, şifreleme, vs.).
  • config 📁 – Ortam değişkenleri (.env), veritabanı bağlantı dizileri, API seçenekleri, vs. için merkezi yer.

Kod organizasyonu için ipuçları

  • Her dosyayı tek bir amaca adarla – büyük veya çoklu sorumluluğu olan dosyalar yerine küçük modüller oluşturun.
  • Dışa aktarmayı düzenli tutun:
    // services/userService.js
    export default class UserService {
      async getUser(id) { ... }
    }
    // ve buna bağlı controller’da:
    import UserService from '../services/userService';
    const service = new UserService();
    
  • İlgili sınıfları veya fonksiyonları bir arada tutun; boşta bekleyen veya ayrı bir konumda gereksiz olan kodları silin.
  • Git’te anlaşılabilir commit mesajları oluşturun – bir mesaj, bir değişikliğin neden olduğu sorunu özetlemelidir (örn. “✅ Kullanıcı girişi için rotayı ekle”, “🔁 Profil güncelleme mantığını service katmanına taşı”).
  • Kod kalitesini kontrol edin – ESLint, Prettier veya TypeScript ile type‑checking gibi araçlar entegrasyon, okunabilirlik ve hata azaltma için yardımcı olur.
  • Ahşap döngüden kaçının – bir klasöre veya dosyaya “her şeyi atıp” sonra kaldırmayın; bunun yerine modüler ve test edilebilir parçalar oluşturun.

Bu sayede ne oluyor? ✅

  • Bakım kolaylığı: Bir özelliği bulmak, geliştirmek veya yeniden düzenlemek hızlı ve zahmetsiz olur.
  • Ekibin verimliliği: Herkes, projeyi yeniden düzenlemeden ilgili modüle doğrudan dalış yapabilir.
  • Hataya karşı dayanıklılık: Tekrarlayan veya belirsiz kod, iş birimlerinin geliştirilmesine ve hataların en aza indirilmesine yardımcı olur.

Hadi bu yapıdan yararlanarak projeyi kurmaya başlayalım – eğer sorularınız olursa, sormaktan çekinmeyin! 🚀


🔁 Access Token Alma ve Yenileme Mantığı

OAuth2 akışında authorization code elinize geçtiğinde, asıl işlem POST /oauth/token endpoint’ine bir istek atarak access token ve refresh token almak olur.
İşte bu adım adım nasıl yönetebileceğiniz 👇

1️⃣ Token isteği nasıl atılır?

  • grant_type=authorization_code ile ilk token alınır
  • grant_type=refresh_token ile süresi dolan access token yenilenir
  • Her yanıtta expires_in (saniye) gelir → bu süreyle tokenCache’e kaydedip, süresi dolmadan önce yenileme yapabilirsiniz

2️⃣ Basit bir tokenCache (memory) örneği

// tokenCache.js
const tokenCache = new Map(); // key: clientId, value: { accessToken, refreshToken, expiresAt }

function setTokens(clientId, { access_token, refresh_token, expires_in }) {
  const expiresAt = Date.now() + expires_in * 1000; // ms cinsinden
  tokenCache.set(clientId, { accessToken: access_token, refreshToken: refresh_token, expiresAt });
}

function getTokens(clientId) {
  return tokenCache.get(clientId);
}

function clearTokens(clientId) {
  tokenCache.delete(clientId);
}

module.exports = { tokenCache, setTokens, getTokens, clearTokens };

Not: Production’da Redis veya benzeri bir distributed store tercih edin; memory map tek instance’da kalır ve restart sonrası kaybolur 🛑

3️⃣ tokenRequest – İlk token alma

// auth.js
const axios = require('axios');
const { setTokens } = require('./tokenCache');

async function tokenRequest({ clientId, clientSecret, code, redirectUri, tokenUrl }) {
  const params = new URLSearchParams({
    grant_type: 'authorization_code',
    code,
    redirect_uri: redirectUri,
    client_id: clientId,
    client_secret: clientSecret,
  });

  try {
    const response = await axios.post(tokenUrl, params, {
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    });

    const { access_token, refresh_token, expires_in } = response.data;
    setTokens(clientId, { access_token, refresh_token, expires_in });
    return { accessToken: access_token, refreshToken: refresh_token };
  } catch (err) {
    // Hata detaylarını loglayıp üst katmana fırlatıyoruz
    console.error('❗️ Token alma hatası:', err.response?.data || err.message);
    throw new Error('Access token alınamadı');
  }
}

Ne oluyor burada?

  • axios.post ile form-urlencoded body gönderiyoruz
  • Başarılıysa tokenları cache’e atıyoruz, hata olursa try-catch ile yakalayıp anlamlı bir mesaj fırlatıyoruz ✅

4️⃣ refreshToken – Access token yenileme

// auth.js (devamı)
const { getTokens, setTokens, clearTokens } = require('./tokenCache');

async function refreshAccessToken({ clientId, clientSecret, tokenUrl }) {
  const stored = getTokens(clientId);
  if (!stored?.refreshToken) {
    throw new Error('Refresh token bulunamadı, tekrar login gerekli');
  }

  const params = new URLSearchParams({
    grant_type: 'refresh_token',
    refresh_token: stored.refreshToken,
    client_id: clientId,
    client_secret: clientSecret,
  });

  try {
    const response = await axios.post(tokenUrl, params, {
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    });

    const { access_token, refresh_token, expires_in } = response.data;
    // Yeni refresh_token dönüyorsa güncelle, yoksa eskisi geçerli kalır
    setTokens(clientId, {
      access_token,
      refresh_token: refresh_token || stored.refreshToken,
      expires_in,
    });
    return access_token;
  } catch (err) {
    // Refresh token geçersizse cache’i temizle
    if (err.response?.status === 400 || err.response?.status === 401) {
      clearTokens(clientId);
    }
    console.error('❗️ Token yenileme hatası:', err.response?.data || err.message);
    throw new Error('Access token yenilenemedi');
  }
}

Nasıl çalışıyor?

  1. Cache’den refreshToken alınır
  2. grant_type=refresh_token ile yeni istek atılır
  3. Yanıttaki yeni tokenlar cache’e yazılır
  4. 400/401 gelirse refresh token geçersiz demektir → cache temizlenir, kullanıcıyı login sayfasına yönlendirebilirsiniz 🔁

5️⃣ Token süresi dolumunu takip etme (basit polling)

// scheduler.js
const { tokenCache } = require('./tokenCache');
const { refreshAccessToken } = require('./auth');

setInterval(async () => {
  const now = Date.now();
  for (const [clientId, data] of tokenCache.entries()) {
    // Expire olmadan 30 sn önce yenile
    if (data.expiresAt - now < 30_000) {
      try {
        await refreshAccessToken({ clientId, clientSecret: process.env.CLIENT_SECRET, tokenUrl: process.env.TOKEN_URL });
        console.log(`${clientId} için token yenilendi`);
      } catch (e) {
        console.warn(`⚠️ ${clientId} token yenilenemedi:`, e.message);
      }
    }
  }
}, 60_000); // her dakika kontrol
  • Memory map kullanıyorsanız bu timer tek process içinde çalışır
  • Redis kullanıyorsanız, TTL ile otomatik silinme ve pub/sub ile çoklu instance senkronizasyonu yapabilirsiniz 🛠

Özet

Adım Ne Yapılır? Hata Durumu
Authorization codePOST /oauth/token tokenRequest ile access + refresh token alınır 400/401 → kullanıcıyı login’e yönlendir
Access token süresi doluyor refreshAccessToken ile yenilenir Refresh token geçersizse cache temizlenir
Depolama Geliştirme: Map (memory) Production: Redis Memory restart sonrası kaybolur ❌

Bu yapıyı projenize entegre edip, try-catch blokları sayesinde hataları merkezi loglayabilir, kullanıcı deneyimini kesintisiz tutabilirsiniz 🚀


🛠 MTD API İstekleri Gönderme (Örnek: VAT Bildirimi)

Access token'ımız hazır, şimdi asıl işe geçelim: VAT beyannamesini çekmek için MTD'nin GET /organisations/{vrn}/returns endpoint'ini çağıracağız 🎯

Ne gönderiyoruz?

  • Authorization headerBearer <access_token>
  • Accept headerapplication/vnd.hmrc.1.0+json (HMRC sürümleme şart)
  • Query parametreleristartDate, endDate (opsiyonel ama önerilir)

Kod örneği: getVatReturns(vrn, accessToken)

const axios = require('axios');

async function getVatReturns(vrn, accessToken) {
  const url = `https://api.service.hmrc.gov.uk/organisations/${vrn}/returns`;

  const headers = {
    Authorization: `Bearer ${accessToken}`,
    Accept: 'application/vnd.hmrc.1.0+json',
  };

  // İstersen tarih aralığı da ekle
  const params = {
    startDate: '2024-01-01',
    endDate: '2024-03-31',
  };

  try {
    const response = await axios.get(url, { headers, params });

    // HMRC 200 döndüyse response.data içinde `returns` dizisi gelir
    const returns = response.data.returns || [];

    console.log(`${returns.length} VAT dönemi bulundu`);
    return returns;
  } catch (error) {
    // Hata detayını yakala
    if (error.response) {
      const status = error.response.status;
      const message = error.response.data?.message || 'Bilinmeyen hata';
      console.error(`⛔ HMRC hatası (${status}): ${message}`);
    } else {
      console.error('⛔ Ağ/bağlantı hatası:', error.message);
    }
    throw error; // Üst katman karar versin
  }
}

Nasıl çalışıyor? 🔍

  1. URL → VRN'yi path'e ekleyerek dinamik oluşturuyoruz
  2. HeadersAuthorization ve Accept zorunlu; eksikse 401/406 alırsınız
  3. ParamsstartDate/endDate göndermezseniz son 12 ay gelir (bazı hesaplarda çok veri = yavaşlık)
  4. Responseresponse.data.returns dizisi; her eleman periodKey, vatDueSales, vatDueAcquisitions, totalVatDue, totalValueSalesExVAT, totalValuePurchasesExVAT, finalised alanlarını taşır
  5. Hata yakalamaerror.response varsa HMRC'den gelen JSON hata mesajını, yoksa ağ hatasını logluyoruz

Küçük ipucu 💡

Sandbox vs Production: Sandbox'ta vrn test verisiyle çalışır. Production'da gerçek VRN + gerçek token lazım. İkisi de aynı fonksiyon — sadece base URL değişir (api.service.hmrc.gov.uk vs test-api.service.hmrc.gov.uk).

Hazırsan bir sonraki adımda yanıt verisini nasıl dönüştüreceğimizi (ve olası sayfalama/pagination'i) anlatırım 🚀


❗️ Hata Yönetimi, Loglama ve Güvenlik İpuçları

Hazırsan hataları düzenli, güvenli ve izlenebilir hale getirelim 🚀

1️⃣ Yaygın HTTP hata kodları için özel hata sınıfları

  • 400 Bad RequestBadRequestError
  • 401 UnauthorizedUnauthorizedError
  • 403 ForbiddenForbiddenError
  • 429 Too Many RequestsTooManyRequestsError
  • 500 Internal Server ErrorInternalServerError

Bu sınıflar Error’dan türetir ve statusCode özelliği taşır. Böylece middleware tek bir yerde hepsini yakalar.

// errors/httpErrors.js
class HttpError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.name = this.constructor.name;
    this.statusCode = statusCode;
    Error.captureStackTrace(this, this.constructor);
  }
}

class BadRequestError extends HttpError {
  constructor(message = 'Geçersiz istek') {
    super(message, 400);
  }
}
class UnauthorizedError extends HttpError {
  constructor(message = 'Yetkisiz erişim') {
    super(message, 401);
  }
}
class ForbiddenError extends HttpError {
  constructor(message = 'Yasaklı erişim') {
    super(message, 403);
  }
}
class TooManyRequestsError extends HttpError {
  constructor(message = 'Çok fazla istek, lütfen bekleyin') {
    super(message, 429);
  }
}
class InternalServerError extends HttpError {
  constructor(message = 'Sunucu hatası') {
    super(message, 500);
  }
}

module.exports = {
  BadRequestError,
  UnauthorizedError,
  ForbiddenError,
  TooManyRequestsError,
  InternalServerError,
};

Ne kazandık?

  • Her hata tipinin kendi status code’u var.
  • Controller’larda throw new BadRequestError('Eksik alan') yeterli.

2️⃣ Merkezi error handler middleware

Express’in son middleware’i olarak ekleriz. Tüm hatalar buraya düşer, loglanır ve istemciye tutarlı JSON döner.

// middlewares/errorHandler.js
const { logger } = require('../utils/logger');
const {
  BadRequestError,
  UnauthorizedError,
  ForbiddenError,
  TooManyRequestsError,
  InternalServerError,
} = require('../errors/httpErrors');

function errorHandler(err, req, res, next) {
  // Bilinen HTTP hataları için statusCode kullan
  const status = err.statusCode ?? 500;
  const message = err.message ?? 'Beklenmeyen bir hata oluştu';

  // Log seviyesi: 4xx → warn, 5xx → error
  const logLevel = status >= 500 ? 'error' : 'warn';
  logger[logLevel]({
    msg: message,
    path: req.originalUrl,
    method: req.method,
    status,
    stack: err.stack,
  });

  // Güvenlik: 500’lerde stack trace gönderme
  const response = {
    success: false,
    error: {
      code: err.name ?? 'InternalServerError',
      message,
    },
  };

  if (status >= 500) {
    response.error.details = 'Sunucu hatası, lütfen daha sonra tekrar deneyin';
  }

  res.status(status).json(response);
}

module.exports = errorHandler;

Kullanım (app.js içinde en sonda):

const errorHandler = require('./middlewares/errorHandler');
app.use(errorHandler);

3️⃣ Loglama – Winston örneği

Winston esnek, çoklu transport destekler. Production’da JSON formatında, dosya + console yazdırırız.

// utils/logger.js
const winston = require('winston');
const path = require('path');

const logFormat = winston.format.combine(
  winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
  winston.format.errors({ stack: true }),
  winston.format.json()
);

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL ?? 'info',
  format: logFormat,
  transports: [
    new winston.transports.Console({
      format: winston.format.combine(
        winston.format.colorize(),
        winston.format.simple()
      ),
    }),
    new winston.transports.File({
      filename: path.join(__dirname, '..', 'logs', 'error.log'),
      level: 'error',
    }),
    new winston.transports.File({
      filename: path.join(__dirname, '..', 'logs', 'combined.log'),
    }),
  ],
});

module.exports = { logger };

Pino tercih edersen yapı aynı mantıkta, sadece pino() ile başlarsın. İkisi de structured logging sağlar → ELK/Datadog entegrasyonu kolay.


4️⃣ Secret’ları koruma – .gitignore + Secret Manager

Ne yapmalı? Nasıl?
.env dosyasını git’e ekleme .gitignore içine .env satırı ekle
Üretim secret’larını kodda tutma AWS Secrets Manager / HashiCorp Vault / GCP Secret Manager kullan
CI/CD’de secret enjeksiyonu Pipeline değişkenleri (GitHub Actions secrets, GitLab CI variables)

.gitignore örneği

# .gitignore
node_modules/
dist/
.env
.env.*
logs/
*.log

Küçük bir hatırlatma 🔔

  • dotenv sadece development için yeterli.
  • Production’da runtime secret manager’dan çek, process.env’e bas.

5️⃣ Özet kontrol listesi ✅

  • Özel hata sınıfları tanımlı mı?
  • errorHandler middleware en sonda mı?
  • Logger structured JSON üretiyor mu?
  • .env .gitignore’da mı?
  • Production secret’ları secret manager’dan alınıyor mu?

Bu adımları uyguladığında:

  • Hatalar tek noktadan yönetilir 🎯
  • Loglar arama/uyarı sistemlerine hazır gelir 🔎
  • Hassas veriler repo’da kalmaz 🔐

Hadi bir sonraki konuya geçelim! 🚀


🎯 Test Etme, Canlıya Alma ve Sonraki Adımlar

Hazırsan başlayalım, uçtan uca bir test senaryosunu sandbox ortamında nasıl çalıştıracağımızı pratik bir örnek üzerinden inceleyelim.

Sandbox'ta test senaryosu (test-user)

  • 📥 Token al → Client, API’ye bir bearer token isteği gönderir.
  • 📊 VAT bildirimi çek → Alınan token ile gümrükleme endpointine istek atılır ve vergi raporu alınıyorda sonraki işleme hazırlanır.
  • Yanıtı doğrula → Beklenen format ve yanıt koduyla karşılaştırarak hatalı durumları yakalarız.

Bu üç adımdan oluşan bir orkestrasyon, production’a gitmeden önce kritik yol haritasını gösterir.

CI/CD pipeline (GitHub Actions)

Her push’ta aşağıdaki işlemler otomatik olarak gerçekleştirilir:

  • Sıfır kurulum – Docker tabanlı ortam, npm ci ile bağımlılıkları temiz kurar.
  • Test çalıştırmanode test-runner.js ile yukarıda anlattığımız test senaryosunu çalıştırırız.
  • ContainerlaşmaDockerfile ile uygulama, taşınabilir bir görüntüye paketlenir.
  • Yayın – Daha sonra canlı ortama teslimat için birim paketleri yiyecek registry’ye gönderilir.

Daha fazla senaryo ya da ortam eklemek istediğimizde, sadece .github/workflows/ci.yml dosyasını güncellemek yeterli.

# Dockerfile
FROM node:18-alpine AS base

WORKDIR /app
COPY package*.json ./
RUN npm ci

COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

Bu Dockerfile ile uygulamamızı Dockerfile tabanlı bir görüntüye dönüştürüyoruz. Temel adımdaki container, npm ci ile bağımlılıkları yükler, server.js dosyasını çalıştırır ve dinlenmeyi bekler.

Sonraki adımlar

  • Webhook entegrasyonu – Şimdiye kadar manuel çalıştırdığımız testleri, real‑time olaylara bağlayarak otomatik denetim döngüsü oluşturabiliriz.
  • Çoklu şirket desteği – Tek endpoint, birden fazla kuruluş için ayrı sanbox ortamları ile çalışabilecek şekilde genişletilebilir.
  • Performans göstergeleri – Test çıktılarından anlık metrikler toplayarak monitörleme stack’ine data gönderme.

🛠️ Bunlar, pipeline’ı bir adım ileri taşıyacak yeni özellikler. Yakında gönderilerini takipte kalabilir, stack’i denemeye başlayabilirsin!


🎁 Bonus Tavsiye / Son Söz

Merhaba arkadaş 👋, bugün OAuth2 ile kimlik doğrulamasını nasıl sorunsuz kullanabileceğinizi öğrendik ve bu harika bir başlangıç oldu! 🔑

  • Özet: • API'lere güvenli erişim için erişim jetonlarını alma ve yenileme işlemini anladın. • Kapsamlı izinler (örnek: scope) ile verilerinizi korudunuz. • Anahtar/metin tabanlı akış ile hem yeni hem de eski istemcilerle çalışan bir yapı geliştirdiniz. • Bir anlık durum kontrolü olarak Docker ile yapılandırmayı da denedik. 🏎

  • Aklında bulunsun: • Daha fazla mikro servis veya küçültülebilir uygulamalar geliştirmek istiyorsan, OAuth2 zaten sağlam bir temel sağlar. • Aşağıdaki kaynaklar, ileri düzey senaryolarda ("özel politikalar", "özel belirteç yapıları" gibi) işine yarayabilir:

  • Bir sonraki adıma geçiş: 👀 Bir sonraki yazımızda, kapsamlı otomatik test stratejilerini inceleyebilir ve her akış türü için yapılandırmalar geliştirirken sorunları nasıl bulabileceğimizi öğrenebiliriz. 🎯

Umarım bu seyahat bize pratik ve güvenli bir yapı verdi ve yeni bölümler keşfetmeye devam etmek için motive oldun! 🚀
Tekrar beklerim — mutlu kodlama! 💪


Bu içerik tamamen yapay zeka destekli otomasyon sistemi ile üretilmiştir.

Burak Sağlık

Burak Saglik

©2024 Desing and Developed by @Burak Sağlık

All rights reserved