Oyun (Gaming) Sektöründe KVKK Kabusu ve Veribenim Çözümü: Global Oyuncu Verisi, Çerezsiz Takip ve GDPR/KVKK Çakışması
19 dakika okuma

Oyun (Gaming) Sektöründe KVKK Kabusu ve Veribenim Çözümü: Global Oyuncu Verisi, Çerezsiz Takip ve GDPR/KVKK Çakışması

Oyun (Gaming) Sektöründe KVKK Kabusu ve Veribenim Çözümü: Global Oyuncu Verisi, Çerezsiz Takip ve GDPR/KVKK Çakışması

Giriş: Gaming'in Sessiz Veri Canavarı — Bir Mobil Oyun Kaç Veri Noktası Topluyor?

Birkaç saniye düşün: Kullanıcı app store'dan bir mobil oyun indirir. Oyunu açar. 5 dakikalık intro animasyonundan sonra login ekranına ulaşır.

O 5 dakika içinde oyun kaç veri noktası toplamıştır?

  1. Cihaz Kimliği: IDFA (iOS), GAID (Android)

  2. Coğrafi Konum: IP Adresi → Ülke/Şehir

  3. Cihaz Bilgisi: Model, OS versiyonu, ekran boyutu

  4. Ağ Bilgisi: WiFi/Cellular, hız

  5. App Store Verileri: Kurulum tarihi, güncelleme geçmişi

  6. Dokunuş Haritası: Ekrana hangi noktaları dokundu

  7. Harf Basma Süresi: Her seçeneğe ne kadar bakıştı

  8. Kütüphaneler: Cihazda hangi uygulamalar yüklü (fingerprinting)

Ve kullanıcı henüz bir tuşa basmadı.

Oyuncu giriş yaptığında (username + password) ve ilk satın alma yaptığında (ödeme kartı bilgileri):

  • Hesap başlama tarihi

  • Oyun başındaki harcadığı süreler (davranışsal meta-data)

  • Satın alınan item'ler (finansal veri)

  • Ödeme yöntemi (hassas)

Sonuç: Ortalama bir mobil oyun, ilk gün içinde 500+ veri noktası toplamıştır.

Peki, bu veriler nerede tutuluyor? Kimin erişimi var? Nasıl GDPR/KVKK uyumlu hale geliyor?

Cevap: Çoğu oyun stüdyosu bilmiyor.

Bu makale, Gaming Compliance Kabusu'nu açıyor ve Veribenim'in nasıl çözüm sunduğunu anlatıyor.


1. GDPR vs. KVKK vs. COPPA Üçgen Çakışması: Küresel Oyun Stüdyosu Ne Yapacak?

Türkiye'de bir oyun şirketi hayal edin. Ancak oyuncuları küreseldir:

  • 50% Avrupa (GDPR)

  • 30% Türkiye (KVKK)

  • 15% USA (COPPA + State Laws)

  • 5% Diğer (China, Japan, India)

Sorun: Üç yargı alanının farklı kuralları var.

📊 Üç Yargının Karşılaştırması

Konu

GDPR (EU)

KVKK (TR)

COPPA (USA)

Yaşı 13'ten Küçük Çocuklar

13+ (Madde 8)

Özel koşul yok (13+)

8-12 İzin Gerekli

Rıza Yönetimi

Explicit + Granular

Explicit + Açık

Parental Consent

Cookieless Tracking

Consent Zorunlu

Consent Zorunlu

Yok/Minimal

Veri Silme (DSAR)

30 gün

30 gün

30 gün

Cezalandırma

€20M veya %4 Turnover

₺1.5M + Kapatma

$42K + FTC

Aktarım (Data Transfer)

Adequacy Gerekli

Yurt Dışı Yasak

State Laws

🎮 GAMING NOTU: Oyuncu 12 yaşında Türk ve ABD'de IP alias varsa? COPPA + KVKK ikisi de geçerli. Hangisi daha katı? COPPA. Hangisini uygulamalısınız? İkisinin kesişimi (intersection compliance).

Senaryo: Cross-Border Gaming

Türkiye Oyun Şirketi "Dragon Legends" → Global Publishing
├─ Sunucu: AWS Dublin (EU)
├─ Ödeme: Stripe (USA)
├─ Analytics: Firebase (Google/USA)
├─ CDN: Cloudflare (USA/EU)
└─ Oyuncu: 12 yaşında Türkiye + ABD VPN

Sorun:
1. Veri Dublin'de (GDPR) ✓
2. Ödeme Stripe'e (USA) → Adequacy gerekli ✗
3. Analytics Firebase'e (USA) → GDPR Chapter 5 gerekli ✗
4. Oyuncu 12 yaşında (COPPA) → Ebeveyn Consent ✗
5. Ebeveyn Türkiye'de → KVKK Madde 8 (Veri Aktarımı) İhlaları ✗

Sonuç: **4 İhlal Aynı Oyunda.**

2. Cookieless Tracking Dönemi: Fingerprinting ve KVKK

Google, Safari ve Firefox son yaşanda third-party cookies'i kaldırıyor.

Oyun şirketi "tamam, o zaman fingerprinting yaparız" diyor. Hata.

Fingerprinting Nedir?

// Geleneksel Cookie
document.cookie = "user_id=12345; path=/";

// Fingerprinting (Cookie-Free)
const fingerprint = hash([
  navigator.userAgent,           // "Mozilla/5.0..."
  navigator.language,            // "tr-TR"
  navigator.hardwareConcurrency, // 8 (CPU cores)
  screen.resolution,             // "1920x1080"
  screen.colorDepth,             // 32
  navigator.getGPUTier(),        // GPU model
  navigator.battery.level,       // 85%
  timezone.getOffset(),          // +3
  installed_fonts.count(),       // 142
]);

// Sonuç: Deterministik ID
// → Üçüncü taraf cookies olmadan oyuncuyu izleyebilirsin

⚠️ Fingerprinting KVKK İhlaline Gider

KVKK Madde 5(1)(c) — Meşru Hedef:

  • Geleneksel cookie: "Oyun deneyimini kişiselleştir" ✓

  • Fingerprinting: "Gizli takip et" ✗

GDPR Recital 24:

"... online izleme araçları (cookies, fingerprinting) KURAL olarak rıza gerektirir. Deceptive tracking = İ hlal."

⚖️ HUKUKİ NOT: Cookieless tracking = Fingerprinting. Veribenim otomatik olarak Fingerprint Detection yapıyor. Eğer fingerprinting yapılıyorsa, express consent toplanıyor.


3. Oyun İçi Satın Alma: Ödeme Verisi + Oyuncu Profili = Çifte Risk

Mobil oyunlar IAP (In-App Purchase) ile para kazanıyor.

Oyuncu "100 Diamond" satın aldığında:

  1. Ödeme Kartı Bilgisi: Last 4 digits, expiry (Payment Card Industry Data Security Standard)

  2. Oyuncu Profili: Kimin aldığı (KVKK)

  3. Satın Alma Davranışı: Ne tür item aldığı, ne kadar harcadığı

  4. Cihaz Bilgisi: Hangi cihazdan satın aldı

  5. Coğrafi Veri: Nerede satın aldı (IP adresinden)

Sorun: Ödeme kartı ≠ Oyuncu Profili

Payment Gateway (Stripe, Apple Pay)
├─ KAYıTLI: Ödeme Kartı Bilgisi (PCI-DSS)
│  └─ Şifreli, izole, 3. taraf
│
└─ RISKLI: Oyun Sunucusu
   ├─ Ödeme Onayı Kaydı
   ├─ Oyuncu ID
   ├─ Satılan Item
   ├─ Harcadığı Para
   └─ Davranışsal Meta-data

Risk: Oyun şirketi, satın alınan diamond'ı oyuncu profiline bağlıyor ve:

  • "Bu oyuncu 5 ay içinde $500 harcadı" profili oluşturuyor

  • Bu davranışsal profil üçüncü taraf analytics'e gönderiliyor

  • Üçüncü taraf "bu oyuncu yüksek-spender" olarak profilini oluşturuyor

  • Reklamcılara satıyor

KVKK İhlaline Gider (Madde 8 - Veri Aktarımı, Madde 5 - Meşru Hedef).

💡 PRO TIP: Veribenim ile, ödeme verisi ve oyuncu profili tamamen izole tutunur. Ödeme gateway Stripe ile, oyuncu profili ayrı. Bağlantı sadece "satın alma başarılı/başarısız" logudur.


4. Battlepass ve Abonelik: Tekrarlayan Ödemede Rıza Yönetimi

Battlepass modeli: Oyuncu aylık abonelik başlatıyor. Her ay otomatik ödeme.

Türkiye'de Ödeme Hizmetleri Yönetmeliği ve KVKK Madde 9 altında:

Gereklilik

KVKK

Ödeme Yönetmeliği

Başlangıç Rızası

Explicit ("Evet" tıkla)

Explicit Signature

Aylık Onay

Optional (ilk onay yeter)

Optional (tekrar onay gereklidir)

İptal Hakkı

30 gün

14 gün (Tüketiciye Hak Tanıma)

Özgür İptal

Zor olmamalı

Kolay olmalı

Yenileme Aydınlatması

Gerekli

Gerekli (SMS/Email)

Senaryo: Battlepass İhlaline Gider

Oyuncu: "Battlepass başlatırım."
└─ "Her ay ₺99.99. Dilediğin zaman iptal edebilirsin."

✓ Rıza Alındı, Ilk Ödeme Alındı
✗ Ama Oyuncu Ayın 15'inde İptal Etmek İstiyor
  └─ Settings → Account → Cancelation
     └─ Button yok, chat desteğine yönlendiriliyor
     └─ KVKK İhlaline Gider (Madde 9 - Rıza Yönetimi)

Doğru Uygulama:

Settings → Subscription
├─ "Battlepass Aktif (₺99.99/ay)"
├─ "Sonraki Ödeme: 15.04.2026"
├─ 🔴 CANCEL SUBSCRIPTION (Tek Tık)
│  └─ "Emin misin? [İptal] [Devam Et]"
│  └─ İptal → Hemen devre dışı
└─ "İptal edebilirsin 14 gün içinde para iadesi ile"

5. Nasıl Entegre Edilir? — @veribenim/core ile Gaming

Şimdi kodda görelim.

Adım 1: Oyun Başlangıcında Consent Onboarding

// unity-webgl-consent-manager.js
// Unity WebGL oyununa gömülü, index.html'de yüklenecek

import { init } from '@veribenim/core';

class GameConsentManager {
  constructor() {
    this.veribenim = null;
    this.playerLocale = 'en'; // Unite'ten gelecek
    this.playerId = null;
  }

  /**
   * Oyun Unity'den şu komutla çağıracak:
   * SendMessage('ConsentManager', 'InitializeConsent', playerId);
   */
  async InitializeConsent(playerId) {
    this.playerId = playerId;

    // Oyun dilini algıla (Steam, Epic, iOS)
    this.playerLocale = this.detectGameLocale();

    // Veribenim init
    this.veribenim = init({
      token: window.VERIBENIM_TOKEN, // HTML'de inject edilecek
      lang: this.mapGameLocaleToVBLang(this.playerLocale),
      debug: false,
    });

    // Var olan onay var mı kontrol
    const savedPrefs = await this.veribenim.getPreferences(playerId);

    if (!savedPrefs) {
      // İlk kez oynuyor - Consent Overlay göster
      this.showFirstTimeConsentOverlay();
    } else {
      // Preferences yüklendi - Analytics ve Ads aktif et
      this.applyStoredPreferences(savedPrefs);
    }
  }

  detectGameLocale() {
    // Unity WebGL'den gelen platform bilgisi
    if (window.unityGame?.gameLauncher === 'steam') {
      return navigator.language || 'en'; // Steam locale
    } else if (window.unityGame?.gameLauncher === 'epic') {
      return window.epicLocale || 'en';
    } else if (window.unityGame?.platform === 'mobile') {
      return navigator.language || 'en';
    }
    return 'en';
  }

  mapGameLocaleToVBLang(locale) {
    const langMap = {
      'tr': 'tr', 'tr-TR': 'tr',
      'en': 'en', 'en-US': 'en', 'en-GB': 'en',
      'de': 'de', 'de-DE': 'de',
      'fr': 'fr', 'fr-FR': 'fr',
      'es': 'es', 'es-ES': 'es',
      'pt': 'pt', 'pt-BR': 'pt',
      'ru': 'ru', 'ru-RU': 'ru',
      'ja': 'ja', 'ja-JP': 'ja',
      'zh': 'zh', 'zh-CN': 'zh',
      'ar': 'ar', 'ar-SA': 'ar',
    };
    return langMap[locale] || 'en';
  }

  showFirstTimeConsentOverlay() {
    // HTML5/WebGL konsent overlay UI
    const overlay = document.createElement('div');
    overlay.id = 'veribenim-consent-overlay';
    overlay.innerHTML = `
      <div class="consent-modal">
        <h2>🎮 Oyuna Hoş Geldin!</h2>

        <p>Daha iyi bir oyun deneyimi için bazı verilerinizi kullanmak istiyoruz.</p>

        <div class="consent-toggle">
          <label>
            <input type="checkbox" id="toggle-analytics" checked />
            <span>
              <strong>📊 Oyun Analitiği</strong><br/>
              Oyun performansını iyileştirmemize yardımcı olur (anonim)
            </span>
          </label>
        </div>

        <div class="consent-toggle">
          <label>
            <input type="checkbox" id="toggle-marketing" />
            <span>
              <strong>🎯 Oyun İçi Teklifler</strong><br/>
              Size özel item ve kampanya önerileri gösterelim mi?
            </span>
          </label>
        </div>

        <div class="consent-toggle">
          <label>
            <input type="checkbox" id="toggle-preferences" checked />
            <span>
              <strong>⚙️ Oyun Tercihleri</strong><br/>
              Dil, ses, grafik ayarlarınız ve ilerlemeniz kaydedilsin mi?
            </span>
          </label>
        </div>

        <div class="consent-buttons">
          <button id="btn-accept-all" class="btn-primary">
            Tümünü Kabul Et
          </button>
          <button id="btn-minimal" class="btn-secondary">
            Minimal Mod (Zorunlu Yalnızca)
          </button>
          <button id="btn-settings" class="btn-tertiary">
            Özel Ayarla
          </button>
        </div>

        <p class="legal-note">
          <a href="<https://yoursgame.com/privacy>" target="_blank">Gizlilik Politikası</a> ·
          <a href="<https://yoursgame.com/kvkk>" target="_blank">KVKK Aydınlatma</a>
        </p>
      </div>
    `;

    document.body.appendChild(overlay);

    // Event listeners
    document.getElementById('btn-accept-all').addEventListener('click', () => {
      this.saveConsentAndClose({
        analytics: true,
        marketing: true,
        preferences: true,
      });
    });

    document.getElementById('btn-minimal').addEventListener('click', () => {
      this.saveConsentAndClose({
        analytics: false,
        marketing: false,
        preferences: true,
      });
    });

    document.getElementById('btn-settings').addEventListener('click', () => {
      // Custom consent ekranı aç
    });
  }

  async saveConsentAndClose(preferences) {
    await this.veribenim.savePreferences(this.playerId, preferences);

    // Unity'ye geri raporla
    SendMessageToUnity('ConsentCallback', JSON.stringify(preferences));

    // Overlay kapat
    const overlay = document.getElementById('veribenim-consent-overlay');
    overlay?.remove();

    this.applyStoredPreferences(preferences);
  }

  applyStoredPreferences(preferences) {
    if (preferences.analytics) {
      // Analytics aktif et
      window.unityGame?.SendMessage('AnalyticsManager', 'EnableAnalytics');
    }

    if (preferences.marketing) {
      // Mediation ads'i aktif et (rewarded, interstitial)
      window.unityGame?.SendMessage('AdManager', 'EnableAds');
    }

    if (preferences.preferences) {
      // Cloud save aktif et
      window.unityGame?.SendMessage('SaveManager', 'EnableCloudSave');
    }
  }
}

// Global olarak erişilebilir
window.gameConsentManager = new GameConsentManager();

// Unity WebGL çağrıları
function SendMessageToUnity(methodName, args) {
  if (window.unityGame?.Module?.dynCall_vii) {
    // Unity 2020 LTS veya yeni
    SendMessage('GameManager', methodName, args);
  }
}

Adım 2: Oyun İçi Satın Alma Consent Gate

// GameManager.cs (Unity Script)
using UnityEngine;
using UnityEngine.Purchasing;
using System.Runtime.InteropServices;

public class PurchaseManager : MonoBehaviour, IStoreListener
{
    private IStoreController controller;
    private IExtensionProvider extensionProvider;
    private bool analyticsConsent = false;

    void Start()
    {
        // Unity IAP initialize
        InitializePurchasing();

        // Consent manager'dan onay durumunu sor
        #if UNITY_WEBGL
        analyticsConsent = GetConsentFromJavaScript("analytics");
        #endif
    }

    #if UNITY_WEBGL
    [DllImport("__Internal")]
    private static extern void GetConsentFromJS(string callback);

    public void OnConsentReceived(string json)
    {
        var prefs = JsonUtility.FromJson<ConsentPreferences>(json);
        analyticsConsent = prefs.analytics;
    }
    #endif

    public void OnPurchaseClicked(string productId)
    {
        // 1. Ödeme öncesi consent kontrol et
        if (!HasRequiredConsent())
        {
            ShowConsentRequiredDialog(productId);
            return;
        }

        // 2. Ödeme başlat
        controller.InitiatePurchase(productId);
    }

    private bool HasRequiredConsent()
    {
        // "Necessary" consent her zaman var olmalı (satın alma = temel fonksiyon)
        return true; // Zorunlu onay
    }

    private void ShowConsentRequiredDialog(string productId)
    {
        // Consent ekranını göster
        // Oyuncu "Devam Et"e basarsa OnPurchaseClicked yeniden çalışır
    }

    public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    {
        this.controller = controller;
        this.extensionProvider = extensions;
    }

    public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    {
        if (args.purchasedProduct.definition.id == "diamond_100")
        {
            // Ödeme başarılı
            LogPurchaseToVeribenim(args);
            AddGemsToPlayer(100);
            return PurchaseProcessingResult.Complete;
        }

        return PurchaseProcessingResult.Pending;
    }

    private void LogPurchaseToVeribenim(PurchaseEventArgs args)
    {
        #if UNITY_WEBGL
        string purchaseLog = JsonUtility.ToJson(new
        {
            event_type = "in_app_purchase",
            product_id = args.purchasedProduct.definition.id,
            price = args.purchasedProduct.metadata.localizedPrice,
            currency = args.purchasedProduct.metadata.isoCurrencyCode,
            receipt = args.purchasedProduct.receipt,
            timestamp = System.DateTime.UtcNow.ToIso8601String(),
        });

        SendMessageToConsent("LogPurchaseConsent", purchaseLog);
        #endif
    }
}

Adım 3: Oyuncu Profili DSAR (Data Subject Access Request)

// account-settings-controller.ts
// Veribenim React ile oyuncu hesap ayarları paneli

import React, { useState } from 'react';
import { useVeribenim } from '@veribenim/react';
import { useAuthContext } from '@/hooks/useAuthContext';

export function AccountSettingsPanel() {
  const { user } = useAuthContext();
  const { submitDsar } = useVeribenim();
  const [dsarLoading, setDsarLoading] = useState(false);
  const [dsarMessage, setDsarMessage] = useState('');

  const handleExportData = async () => {
    setDsarLoading(true);
    try {
      await submitDsar({
        requestType: 'portability',
        fullName: user.username,
        email: user.email,
        description: `
          GDPR Article 20 & KVKK Madde 11/ç -
          Oyun hesabımın ve tüm verilerimin (ilerleme, satın almalar, ayarlar,
          istatistikler) makine okunabilir formatta (JSON) indirilmesini talep ediyorum.
        `.trim(),
      });
      setDsarMessage('Veri talebiniz işlemeye alındı. 30 gün içinde indirebileceğiniz bir bağlantı e-postanıza gönderilecek.');
    } catch (error) {
      setDsarMessage('Hata: Veri talebi gönderilemedi. Lütfen daha sonra deneyin.');
    } finally {
      setDsarLoading(false);
    }
  };

  const handleDeleteAccount = async () => {
    const confirmed = window.confirm(
      'Hesabınızı kalıcı olarak silmek istediğinizden emin misiniz? ' +
      'Tüm ilerleme, item'ler ve veriler silinecektir. Bu işlem geri alınamaz.'
    );

    if (!confirmed) return;

    setDsarLoading(true);
    try {
      await submitDsar({
        requestType: 'erasure',
        fullName: user.username,
        email: user.email,
        description: `
          Hesabımın ve tüm kişisel verilerimin silinmesini talep ediyorum.
          GDPR Article 17 & KVKK Madde 11/e - Veri Silme Hakkı.

          Silinecek veriler:
          - Hesap bilgileri (username, email, password hash)
          - Oyun ilerlemesi ve istatistikler
          - Satın alım geçmişi (anonim olarak tutulacak)
          - Cihaz bilgileri ve cookies
          - Tercihler ve ayarlar
        `.trim(),
      });
      setDsarMessage('Silme talebiniz alındı. 30 gün içinde hesabınız tamamen silinecektir.');
    } catch (error) {
      setDsarMessage('Hata: Silme talebi gönderilemedi.');
    } finally {
      setDsarLoading(false);
    }
  };

  const handleEditConsent = async () => {
    // Consent ayarlarını yeniden görüntüle
    window.open('/settings/privacy', '_self');
  };

  return (
    <div className="account-settings">
      <section className="privacy-section">
        <h3>🔐 Gizlilik ve Veri</h3>

        <div className="setting-item">
          <h4>Veri Taleplerini Yönet</h4>
          <p>
            GDPR ve KVKK kapsamında hesabınızla ilgili tüm verileri görebilir,
            indirebilir veya silebilirsiniz.
          </p>

          <button onClick={handleEditConsent} disabled={dsarLoading}>
            Consent Ayarlarını Düzenle
          </button>

          <button onClick={handleExportData} disabled={dsarLoading}>
            {dsarLoading ? 'İşleniyor...' : '📥 Verilerimi İndir (JSON)'}
          </button>

          <button onClick={handleDeleteAccount} disabled={dsarLoading} className="btn-danger">
            {dsarLoading ? 'İşleniyor...' : '🗑️ Hesabı Sil'}
          </button>

          {dsarMessage && (
            <p className="message" style={{ marginTop: '1rem' }}>
              {dsarMessage}
            </p>
          )}
        </div>

        <div className="setting-item">
          <h4>Oyun Analitikleri</h4>
          <label>
            <input type="checkbox" defaultChecked={true} onChange={(e) => {
              // Analytics toggle handle
            }} />
            Oyun performansı verilerini paylaşmak için izin ver
          </label>
          <small>
            Anonim olarak tutulur. Oyun iyileştirmelerine yardımcı olur.
          </small>
        </div>

        <div className="setting-item">
          <h4>Kişiselleştirilmiş İçerik</h4>
          <label>
            <input type="checkbox" onChange={(e) => {
              // Marketing consent toggle
            }} />
            Size özel item ve kampanya teklifleri göstersem mi?
          </label>
          <small>
            Oyun içi harcama davranışınıza göre kişiselleştirilmiş öneriler.
          </small>
        </div>
      </section>
    </div>
  );
}

Adım 4: Battlepass Subscription Yönetimi

// BattlepassService.php (Laravel Backend)
<?php

use Veribenim\\Laravel\\VeribenimFacade as Veribenim;

class BattlepassService
{
    /**
     * Battlepass aboneliğini başlat
     * KVKK Madde 9 - Tekrarlayan Ödeme Rızası
     */
    public function startBattlepass(User $player, string $billingPeriod = 'monthly')
    {
        if ($player->battlepass_active) {
            throw new \\Exception('Zaten aktif battlepass var.');
        }

        // 1. Consent Log - Tekrarlayan ödeme için express rıza
        $consentLog = Veribenim::logFormConsent(
            formName: 'battlepass_subscription',
            consented: true,
            consentText: sprintf(
                'Battlepass aboneliğini başlatıyorum. ' .
                'Her %s %s fiyatında otomatik ödeme yapılmasını onaylıyorum. ' .
                'Dilediğim zaman iptal edebilirim. KVKK Madde 9, Ödeme Hizmetleri Yönetmeliği. ' .
                'İptal etmek için Settings → Subscription → Cancel Subscription bölümünü kullan.',
                $billingPeriod,
                currency_symbol() . config('battlepass.price.' . $billingPeriod)
            ),
            metadata: [
                'billing_period'   => $billingPeriod,
                'renewal_day'      => now()->addMonth()->day,
                'cancellation_url' => route('battlepass.cancel'),
                'cancellation_easy'=> 'true', // KVKK Madde 9 - Rıza yönetimi kolay olmalı
            ]
        );

        // 2. Abonelik kaydını oluştur
        $subscription = BattlepassSubscription::create([
            'user_id'           => $player->id,
            'billing_period'    => $billingPeriod,
            'started_at'        => now(),
            'renewal_at'        => now()->addMonth(),
            'consent_id'        => $consentLog['id'],
            'status'            => 'active',
            'auto_renew'        => true,
            'cancellation_easy' => true, // Türk hukuku
        ]);

        // 3. İlk ödemeyi al (Stripe)
        try {
            $charge = $this->processFirstPayment($player, $subscription);

            $subscription->update([
                'stripe_subscription_id' => $charge->id,
                'last_payment_at'        => now(),
            ]);
        } catch (\\Exception $e) {
            $subscription->delete();
            throw $e;
        }

        return $subscription;
    }

    /**
     * Battlepass iptal (KVKK Madde 9 - Kolay İptal)
     */
    public function cancelBattlepass(User $player)
    {
        $subscription = BattlepassSubscription::where('user_id', $player->id)
            ->where('status', 'active')
            ->first();

        if (!$subscription) {
            throw new \\Exception('Aktif battlepass aboneliği bulunamadı.');
        }

        // 1. Stripe aboneliğini iptal et
        $this->stripeCancelSubscription($subscription->stripe_subscription_id);

        // 2. DB kaydını güncelle
        $subscription->update([
            'status'             => 'cancelled',
            'cancelled_at'       => now(),
            'cancellation_reason' => 'User initiated', // KVKK Madde 9 - İzlenebilirlik
        ]);

        // 3. Veribenim'e iptal logu kaydet
        Veribenim::submitDsar(
            requestType: 'withdrawal',
            fullName: $player->username,
            email: $player->email,
            description: sprintf(
                'Battlepass aboneliğinin iptal edildiğini log kaydet. ' .
                'KVKK Madde 9/4 - Rızadan Çekilme. ' .
                'Abonelik: %s. ' .
                'Başlangıç: %s. ' .
                'İptal: %s.',
                $subscription->billing_period,
                $subscription->started_at->format('d.m.Y'),
                now()->format('d.m.Y H:i')
            )
        );

        return ['message' => 'Aboneliğiniz iptal edildi.'];
    }

    /**
     * Aylık yenileme notifikasyonu (KVKK Madde 9 - Aydınlatma)
     */
    public function sendRenewalReminder(BattlepassSubscription $subscription)
    {
        $player = $subscription->user;

        // 3 gün öncesinden hatırlat
        if ($subscription->renewal_at->diffInDays(now()) === 3) {

            $price = config('battlepass.price.' . $subscription->billing_period);

            Mail::send('emails.battlepass-renewal', [
                'player_name' => $player->username,
                'price'       => $price,
                'renewal_at'  => $subscription->renewal_at->format('d.m.Y'),
                'cancel_url'  => route('battlepass.cancel'),
            ], function ($mail) use ($player) {
                $mail->to($player->email)
                    ->subject('Battlepass Yenileme Hatırlatması - 3 Gün Kaldı');
            });

            // Oyun içi notification
            Notification::create([
                'user_id'    => $player->id,
                'title'      => 'Battlepass Yenileniyor',
                'body'       => "Battlepass aboneliğiniz {$subscription->renewal_at->format('d.m.Y')}'de yenilenecektir.",
                'action_url' => '/settings/battlepass',
            ]);

            \\Log::info('[Battlepass] Yenileme hatırlatması gönderildi', [
                'user_id'        => $player->id,
                'renewal_at'     => $subscription->renewal_at,
                'price'          => $price,
            ]);
        }
    }
}

6. Cookieless Analytics: Consent Bazlı Tracking

// analytics-manager.ts (Unity WebGL)

class AnalyticsManager {
  private hasAnalyticsConsent = false;

  /**
   * Consent alındıktan sonra analytics başlat
   */
  public async InitializeAnalytics(preferences: ConsentPreferences) {
    this.hasAnalyticsConsent = preferences.analytics;

    if (!this.hasAnalyticsConsent) {
      console.log('[Analytics] Consent yok - Analytics deaktif');
      return;
    }

    // Fireabse initialization (anonim şekilde)
    const app = initializeApp(firebaseConfig);
    const analytics = getAnalytics(app);

    // Oyuncu IP'si ASLA log'lanmaz
    // Analytics: {
    //   session_id: hash(deviceId + timestamp),
    //   level_completed: 5,
    //   time_played: 3600,
    //   tutorial_skipped: true,
    // }

    logEvent(analytics, 'game_session_start', {
      game_version: '1.2.3',
      device_type: 'mobile',
      // Dikkat: Oyuncu ID = ASLA
    });
  }

  /**
   * Fingerprinting ASLA yapılmaz
   */
  private getClientFingerprint() {
    // ✗ KVKK İhlaline Gider
    // const fingerprint = hash([
    //   navigator.userAgent,
    //   navigator.language,
    //   navigator.hardwareConcurrency,
    //   screen.resolution,
    // ]);

    // ✓ KVKK Uyumlu: Session-based ID
    return `session_${Date.now()}_${Math.random().toString(36)}`;
  }
}

7. Data Sovereignty: Türk Oyuncuların Verisi Nerede Saklanmalı?

🌍 GLOBAL UYUM: KVKK Madde 8 - "Kişisel veriler, yurt dışına aktarılamaz."

Oyun şirketi Türkiye'de. Sunucu AWS'de mi, Google Cloud'da mı, kendi veri merkezinde mi?

✅ KVKK Uyumlu Mimarisi

Türkiye Oyuncusu
    ↓
    └─ Istanbul AWS Region (eu-central-1 equivalent)
       ├─ Oyuncu Profili
       ├─ Satın Alma Geçmişi (Ödeme Kartı Metadata Yok)
       ├─ Oyun İlerleme
       └─ Consent Logları

❌ KVKK İhlaline Gider

Türkiye Oyuncusu
    ↓
    └─ Firebase (USA, Google Cloud)
       ├─ Realtime Database
       ├─ Analytics
       ├─ Cloud Storage (CVs, Screenshots)
       └─ Automation Engine

Sorun: Firebase default'ta USA'da. Türkiye → USA = Veri aktarımı = İHLAL

Çözüm: Multi-region setup yapı:

// Laravel - Region-based storage

class GameDataRepository {
    private $regionStorage;

    public function __construct() {
        // Oyuncu IP'sine göre region belirle
        if ($this->isPlayerInTurkey()) {
            $this->regionStorage = Storage::disk('aws-turkey'); // Istanbul
        } else if ($this->isPlayerInEU()) {
            $this->regionStorage = Storage::disk('aws-eu');     // Frankfurt
        } else {
            $this->regionStorage = Storage::disk('aws-global'); // Nearest
        }
    }

    public function savePlayerProfile(User $player, array $data) {
        // KVKK Madde 8 uyumlu
        $this->regionStorage->put(
            "players/{$player->id}/profile.json",
            json_encode($data)
        );
    }
}

8. Automated Privacy: Hesap Silme ve Veri Portability

// DeleteAccountJob.php (Laravel Queue)

class DeleteAccountJob implements ShouldQueue {
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    private $player;

    public function __construct(User $player) {
        $this->player = $player;
    }

    /**
     * GDPR Article 17 & KVKK Madde 11/e - 30 gün içinde gerçekleştir
     */
    public function handle() {
        \\Log::info('[DeleteAccount] Başlıyor', ['player_id' => $this->player->id]);

        // 1. Oyun İlerleme Sil
        GameProgress::where('player_id', $this->player->id)->delete();

        // 2. Satın Alma Geçmişi (Anonim tut, vergi sebebiyle)
        Purchase::where('player_id', $this->player->id)->update([
            'player_id'     => null,        // Bağlantısını kes
            'email'         => null,
            'display_name'  => "ANONYMOUS_" . hash('sha256', $this->player->id),
            'anonymized_at' => now(),
        ]);

        // 3. Profil Alanlarını Sil
        $this->player->update([
            'username'           => "DELETED_" . hash('sha256', $this->player->id),
            'email'              => null,
            'phone'              => null,
            'birthdate'          => null,
            'country'            => null,
            'avatar_path'        => null,
            'biography'          => null,
            'social_links'       => null,
            'deleted_at'         => now(),
            'deletion_reason'    => 'User requested (GDPR Article 17)',
        ]);

        // 4. Cihaz Bağlantılarını Sil
        Device::where('player_id', $this->player->id)->delete();

        // 5. Consent Loglarını Sil
        ConsentLog::where('email', $this->player->email)->delete();

        // 6. Veribenim'e Silme Raporu
        Veribenim::submitDsar(
            requestType: 'erasure_confirmation',
            fullName: 'System',
            email: 'noreply@' . config('app.domain'),
            description: sprintf(
                'Oyun hesabı silme işlemi tamamlandı. ' .
                'Oyuncu ID: %d. ' .
                'Silinme Tarihi: %s. ' .
                'Saklanan Veriler: Satın alımlar (anonim), vergi kayıtları. ' .
                'GDPR Article 17 & KVKK Madde 11/e.',
                $this->player->id,
                now()->format('d.m.Y H:i')
            )
        );

        \\Log::info('[DeleteAccount] Tamamlandı', ['player_id' => $this->player->id]);
    }
}

9. Sonuç: Veribenim ile Global Gaming Compliance

Veribenim Compliance as a Service platformu, oyun stüdyolarının:

  1. Üç Yargı (GDPR, KVKK, COPPA) Kesişimini otomatik yönet

  2. Cookieless Tracking ile fingerprinting'i engelle

  3. IAP Consent Gate ile ödeme verisi ve oyuncu profilini izole tut

  4. Battlepass Abonelikleri KVKK Madde 9 uyumlu yönet

  5. Data Sovereignty ile Türk verilerini Türkiye'de tut

  6. DSAR (veri erişim, silme, portability) 30 gün içinde otomatik yap

Veribenim Gaming Stack

┌─────────────────────────────────────────────┐
│ Unity WebGL / React Native / Mobile Game    │
├─────────────────────────────────────────────┤
│ @veribenim/core (Consent Manager)           │
│ @veribenim/react (UI Components)            │
│ @veribenim/unity (C# SDK)                   │
├─────────────────────────────────────────────┤
│ Veribenim Backend (Laravel)                 │
│ ├─ Consent Log Store                        │
│ ├─ DSAR Processor                           │
│ ├─ Region-Based Data Storage                │
│ └─ Automated Privacy Jobs                   │
├─────────────────────────────────────────────┤
│ Compliance Dashboard                        │
│ ├─ Consent Analytics                        │
│ ├─ DSAR History                             │
│ ├─ Regional Data Compliance Report          │
│ └─ Audit Trail                              │
└─────────────────────────────────────────────┘

Sonuç: Veribenim ile oyun stüdyosu "kompliyans için kodlamaz, konfigüre eder." GDPR/KVKK/COPPA uyumluluğu otomatik.


📚 Teknik Dokümantasyon: Tüm SDK referans dökümanlarına veribenim.com/documents adresinden ulaşabilirsiniz.

 

Sonraki Adım

Veribenim'in bu sektör için sunduğu çözümleri görmek ve KVKK/GDPR uyumlu pazarlama başlamak için aşağıdaki butona tıklayın.

Ücretsiz Hesap Oluştur

Benzer Makaleler

Bu sektörle ilgili daha fazla makale yakında yayımlanacak.

Tüm makaleleri görmek için bloğa dön →