EdTech ve Online Eğitim Platformlarında KVKK Kabusu ve Veribenim Çözümü

2024'te, Türkiye'deki **27 milyon** öğrenci, en az bir online eğitim platformunda kayıtlı. - Udemy: 3.2M - Kodlama.io: 800K - Khan Academy Türkçe: 500K - Kendi kurumsal LMS'ler: 22M+ Ancak, bu platformların **95%'i**, reşit olmayan öğrencilerin verilerini işlerken temel KVKK kurallarını ihlal ediyor.

Bloglar
EdTech ve Online Eğitim Platformlarında KVKK Kabusu ve Veribenim Çözümü

Reşit Olmayan Kullanıcılar için Veli Onay Mekanizması


Giriş: EdTech'in Gizli Tehlikesi — 18 Yaş Altı Kullanıcı KVKK Kabusu

2024'te, Türkiye'deki 27 milyon öğrenci, en az bir online eğitim platformunda kayıtlı.

  • Udemy: 3.2M

  • Kodlama.io: 800K

  • Khan Academy Türkçe: 500K

  • Kendi kurumsal LMS'ler: 22M+

Ancak, bu platformların 95%'i, reşit olmayan öğrencilerin verilerini işlerken temel KVKK kurallarını ihlal ediyor.

Tipik Senaryolar:

Senaryo 1: Ankara'daki bir ilkokul öğrencisi (9 yaşında), Udemy'ye ana-babasının izni olmadan kaydolup matematik kursuna katılıyor. Platform:

  • Adını kaydeder

  • E-posta (ana-babanın) kaydeder

  • Video izleme geçmişini (konum, cihaz, süre) saklar

  • Davranış analiz profili oluşturur

  • Sonra "Başka kurslar al!" pazarlama e-postaları gönderir

KVKK ihlali: Veli onayı YOK. Madde 5 ihlali.

Senaryo 2: Lisede kimya öğrencisi (15 yaşında), bir LMS'ye üniversite giriş sınavı hazırlığı için kaydolunuyor. Sistem:

  • Öğrencinin performans verilerini kaydeder

  • Başarı derecesine göre rehberlik önerileri gönderir

  • Sosyal medyada "Seni hayal kırıklığına uğrattığın konular" bildirimleri gösterir

  • Diğer öğrencilerle karşılaştırmalı sıralama (gamification) yapıyor

KVKK ihlali: Profillemesi açık izin olmadan yapılıyor. Madde 11 ihlali.

Senaryo 3: 17 yaşındaki öğrenci, bir kod akademisine kaydolunca, sistem:

  • Kodlama stili ve proje geçmişini "data science" için satıyor

  • İş teklifi e-postaları göndermeye başlıyor

  • Başarısı için şampiyon rozeti veriyor (addictive gamification)

KVKK + GDPR ihlali: Reşit olmayan kişi, psikiyatrik/davranışsal veri işlemesine maruz kalıyor. Madde 6(3) + GDPR Article 8 ihlali.


KVKK Madde 5: Reşit Olmayanlar için Özel Koşullar

KVKK Madde 5, yaş gruplarına göre veri işleme kurallarını belirler:

Tamamen Yasaklı (Tüm Yaşlar)

  • Irk, etnik köken

  • Siyasi görüş

  • Dini inanç

  • Sendika üyeliği

  • Ceza kaydı

  • Biyometrik veriler (yüz tanıma, fingerprint, ses)

Reşit Olmayanlarda İzin Gerekli

0-12 yaş (çocuk):

  • Veli onayı ZORUNLU (örtülü rıza yok)

  • Eğer teknoloji kullanabilse bile, onay veli yapmalı

13-17 yaş (ergen):

  • Kendi rızası + veli onayı DUAL gerekli

  • Sadece veli onayı yeterli değil (çocuğun da onayı alınmalı)

  • Ancak "hassas veriler" için veli onayı ön planda

18+ yaş (yetişkin):

  • Kendi rızası yeterli


GDPR Article 8: Dijital Hizmetlerde 16 Yaş Sınırı

GDPR, Avrupa AB üyelerinde 16 yaş altına dijital hizmet sunulmasını ciddi şekilde kısıtlıyor:

Kural:

"Where the child is below the age of sixteen years, such processing shall be lawful only if and to the extent that consent is given or authorised by the holder of parental responsibility over the child." — GDPR Article 8(1)

Pratik Anlamı:

Durum

KVKK

GDPR

10 yaşındaki Türk çocuk

Veli onayı gerekli

Veli onayı ZORUNLU

14 yaşındaki Alman çocuk

Dual onay (çocuk + veli)

Veli onayı ZORUNLU (çocuğun onayı işe yaramaz)

15 yaşındaki İspanyol çocuk

Dual onay

Veli onayı ZORUNLU (çocuğun onayı işe yaramaz)

18 yaşındaki Türk

Çocuk kendi rızası

Çocuk kendi rızası

Temel fark: GDPR, çocuğun veli onayını verme yeteneğine sahip olabileceğini kabul etmez. Veli, nihai karar-vericiden başka bir şey değil.


Mevcut EdTech Platformlarının 5 Büyük Hata

1. "Yaş Doğrulama = Hazır Gözükmek"

Çoğu EdTech platformu, kayıt sırasında bir dropdown koyuyor:

Doğum Yılınız: [2010 ▼]

Bu kadar. Sonra sistem tüm dünyaya ("açık internet") para kazanmak için çocuk verilerini işlemiyor mu?

Sorun: Yaş doğrulama, onay değildir. 9 yaşındaki çocuk "2010" seçebilir, bu hiçbir şey kanıtlamaz.

2. "Veli E-postası = Veli Onayı"

Diğer platformlar şöyle yapar:

Çocuğunuzun Adı: Zeynep
Velinin E-postası: zeynep.annesi@gmail.com
[✓] Veli onayı kutusu

Sonra sistem otomatik olarak veli onayı almadığı halde, "onaylanmış" olarak kabul ediyor.

Sorun: Veli e-postası alınca, mutlaka doğrulama maili gönderilmeli. Yoksa yapılmış bir hile.

3. "Marketing Onayı = Zorunlu"

Kayıt formundaki "Pazarlama e-postaları al" kutusu varsayılan işaretli geliyor:

[ ✓ ] Pazarlama e-postaları ve sunumlar al

Ayrıca, kutuyu kaldırmaları zor (yazı küçük, gri renk).

KVKK Madde 6/3 ihlali: "Açık rıza gerekli." Varsayılan işaretlenmiş ≠ açık rıza.

4. "Analitik Verileri = Gerekli"

Sistem, öğrenci davranışlarını izliyor:

  • "Hangi soruya kaç saniye baktı?"

  • "Cevapları not ettiği bölümler?"

  • "Testi kaç kere sınavdan önce denedi?"

  • "Diğer öğrencilerle karşılaştırma değeri?"

Bütün bunlar "gerekli" kategorisinde saklı. Oysa çoğu analytics (isteğe bağlı).

KVKK Madde 5 ihlali: Veri minimizasyonu.

5. "Reşit Olmayan'ın Kendi Onayı = Yeterli"

En kritik hata. Sistem, 14 yaşındaki çocuğu kayıt formunda görüyor:

Yaş: 14
[✓] Tüm koşulları okudum ve kabul ediyorum

Sistem: "Peki, çocuk onay verdi, tamamdır!" diyor.

GDPR Article 8 ihlali: 14 yaş altı AB vatandaşlarında, çocuğun kendi onayı işe yaramaz.


Nasıl Entegre Edilir? — Veribenim API ile Veli Onay Akışı

Veribenim, EdTech platformlarına "Parental Consent as a Service" sunuyor.

Adım 1: Yaş Doğrulama Akışı

// @veribenim/core - Yaş doğrulamalı consent akışı
import { init } from '@veribenim/core';

interface StudentProfile {
  id: string;
  firstName: string;
  lastName: string;
  email: string; // Velinin e-postası, çocuğun değil
  dateOfBirth: string; // YYYY-MM-DD
  country: string; // Hangi ülke'den? (TR, DE, FR, vb.)
}

async function initStudentConsent(student: StudentProfile) {
  const veribenim = init({
    token: process.env.VERIBENIM_TOKEN,
    lang: 'tr',
  });

  // Yaş hesapla
  const today = new Date();
  const birthDate = new Date(student.dateOfBirth);
  let age = today.getFullYear() - birthDate.getFullYear();
  const monthDiff = today.getMonth() - birthDate.getMonth();
  if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
    age--;
  }

  // AB vatandaşı mı?
  const euCountries = ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE'];
  const isEUResident = euCountries.includes(student.country.toUpperCase());

  // Yaş kontrolü (GDPR ise 16, KVKK ise 18)
  const ageLimit = isEUResident ? 16 : 18;

  if (age < ageLimit) {
    // Reşit değil: veli onayını kontrol et
    const parentalConsentStatus = await veribenim.checkParentalConsentStatus(
      student.email // Velinin e-postası
    );

    if (!parentalConsentStatus.hasBeenConsented) {
      // Veli onayı henüz alınmamış
      // Platforma erişimi kısıtla
      return {
        status: 'pending_parent_consent',
        action: 'send_parent_verification_email',
        message: `${student.firstName}'in hesabı kullanmak için veli onayı gerekli. Onay e-postası ${student.email}'e gönderildi.`,
      };
    }

    if (!parentalConsentStatus.isValid) {
      // Veli onayı süresi dolmuş (genellikle 1 yıl)
      return {
        status: 'parent_consent_expired',
        action: 'request_consent_renewal',
        message: 'Veli onayının süresi dolmuştur. Yenilenmesi gerekiyor.',
      };
    }

    // Veli onayı geçerli: kısıtlı erişim
    // Reşit olmayan kullanıcılar için MARKETING BİLEŞKESİ ZORBULİ OLARAK KAPATILIR
    await veribenim.savePreferences({
      necessary: true, // Platform işletme için
      analytics: true, // Pedagojik analiz (opsiyonel, ama önerilir)
      marketing: false, // Reşit olmayanlara pazarlama YASAK
      preferences: false, // Çerez tercileri (13+ için seçilebilir)
    });

    // Öğrenciye kısıtlı dashboard
    return {
      status: 'restricted_access',
      parental_consent_verified: true,
      restrictions: {
        no_marketing: true,
        no_data_selling: true,
        no_behavioral_ads: true,
        limited_analytics: true, // Sadece gerekli istatistikler
      },
    };
  }

  // 18 yaş üzeri (KVKK) veya 16+ (GDPR ile EU):
  // Normal consent akışı
  return {
    status: 'full_access',
    age_group: 'adult',
  };
}

Adım 2: Veli E-posta Onay Mekanizması (Double Opt-In)

// Veli e-postasına DOĞRULAMA maili gönder
async function sendParentalVerificationEmail(
  veribenim: ReturnType<typeof init>,
  student: StudentProfile
): Promise<void> {
  const verificationCode = generateSecureToken();
  const expiryTime = Date.now() + 7 * 24 * 60 * 60 * 1000; // 7 gün

  // Veribenim'de token'ı sakla
  await veribenim.storeVerificationToken({
    token: verificationCode,
    email: student.email, // Veli e-postası
    studentId: student.id,
    expiresAt: new Date(expiryTime),
    purpose: 'parental_consent_verification',
  });

  // E-posta gönder
  const emailContent = `
    Sayın Veli,

    ${student.firstName} adlı çocuğunuz, ${process.env.PLATFORM_NAME} online eğitim platformuna kaydolmak istiyor.

    Kanun gereği, reşit olmayan çocukların bu platforma kaydolması için veli onayı gerekli.

    İZİN VERMEK İÇİN: ${process.env.PLATFORM_URL}/parent-verify?token=${verificationCode}&email=${encodeURIComponent(student.email)}

    Link 7 gün geçerli. Sonra yeniden doğrulama gerekir.

    Eğer bu işlemi başlatmadıysanız, bu e-postayı görmezden gelin.

    Saygılarımızla,
    ${process.env.PLATFORM_NAME} Ekibi
  `;

  await sendEmail({
    to: student.email, // Veli'ye gönder
    subject: `${student.firstName}'in Online Eğitim Hesabı İçin Veli Onayı Gerekli`,
    body: emailContent,
    template: 'parental_verification',
  });
}

// Veli linke tıklar, onay sayfası yüklenir
async function handleParentalApprovalPage(token: string) {
  const veribenim = init({ token: process.env.VERIBENIM_TOKEN });

  // Token doğrula
  const tokenData = await veribenim.verifyToken(token);
  if (!tokenData || tokenData.expiresAt < new Date()) {
    return {
      error: 'Token geçersiz veya süresi dolmuş',
      action: 'show_error_page',
    };
  }

  // Onay formu göster
  return {
    status: 'show_approval_form',
    studentName: tokenData.studentDetails.firstName,
    studentAge: tokenData.studentDetails.age,
    platformName: process.env.PLATFORM_NAME,
    consentText: `
      İÇERİK ONAY FORMU

      ${tokenData.studentDetails.firstName} (${tokenData.studentDetails.age} yaş) adlı çocuğunun,
      ${process.env.PLATFORM_NAME} platform üzerinde online eğitim almak için
      kişisel verilerinin işlenmesine veli/yasal temsilci sıfatıyla:

      [ ] ONAY VERİYORUM
      [ ] ONAY VERMİYORUM

      Onay verildiğinde:
      - Çocuğun adı, e-postası, eğitim geçmişi kaydedilir
      - Eğitim analitikleri (başarı, ilerleme) takip edilir
      - PAZARLAMA E-POSTASI ASLA GÖNDERİLMEZ
      - Veriler 18 yaş bitirince otomatik gözden geçirilir
    `,
  };
}

// Veli onay butona tıklar
async function processParentalApproval(
  veribenim: ReturnType<typeof init>,
  token: string,
  parentEmail: string,
  approved: boolean
): Promise<void> {
  const tokenData = await veribenim.verifyToken(token);
  const student = tokenData.studentDetails;

  if (!approved) {
    // Ret edildi
    await veribenim.logFormConsent({
      form_name: 'parental_consent',
      consented: false,
      consent_text: 'Veli tarafından reddedildi',
      metadata: {
        student_id: student.id,
        student_age: student.age,
        parent_email: parentEmail,
        rejection_reason: 'parent_declined',
      }
    });

    // Öğrenci hesabı taraflandır
    await disableStudentAccount(student.id);
    return;
  }

  // ONAY VERİLDİ
  await veribenim.logFormConsent({
    form_name: 'parental_consent_approval',
    consented: true,
    consent_text: `
      ${student.firstName} (Doğum yılı: ${student.birthYear}) adlı çocuğun,
      ${process.env.PLATFORM_NAME} online eğitim platformunu kullanması için
      kişisel verilerinin işlenmesine veli/yasal temsilci sıfatıyla onay veriyorum.
      Anladığım üzere:
      - Çocuğun eğitim verileri korunacak
      - Pazarlama iletişimi görmeyecek
      - Veri sahibi hakları doğudan çocuğa veya bana uygulanabilir
    `,
    metadata: {
      student_id: student.id,
      student_age: student.age,
      parent_email: parentEmail,
      approval_timestamp: new Date().toISOString(),
      consent_method: 'email_verification_link', // Proof method
      ip_address_parent: getClientIP(), // Parent'ın IP (audit)
      parent_user_agent: getUserAgent(), // Browser info
      gdpr_applicable: student.country !== 'TR', // EU ise GDPR da geçerli
    }
  });

  // Öğrenci hesabını aktif et
  await activateStudentAccount(student.id, {
    parental_consent_verified: true,
    parental_consent_date: new Date(),
    parental_email: parentEmail,
  });

  // Öğrenciye ve veliye onay e-postası gönder
  await notifyBothPartyOfApproval(student, parentEmail);
}
// Öğrenci kendi onayını veriyor (çocuğun da sesi duyulmalı, ama tek başına yeterli değil)
async function getStudentAssent(student: StudentProfile): Promise<void> {
  // 13+ yaş için sadece "assent" (çocuğun onayı)
  // Ancak GDPR'da bu sadece "informative" — veli onayı zorunlu

  const assentText = {
    tr: `
      ${student.firstName}, sen bu platformu kullanmak istiyor musun?

      Eğer evet dersen:
      - Adını ve e-mailini saklayacağız
      - Hangi dersleri yaptığını öğrenecek öğretmenlerin görmesine izin verecek
      - Seni SMS veya e-mail yoluyla öğrenme teklifleri göndermeyecek
      - Verilerin güvenli olacağı

      [ EVET, ONAY VERİYORUM ] [ HAYIR, İSTEMİYORUM ]
    `,
  };

  // Çocuğun onayını kaydet
  // BUT: Bu GDPR'da "bilgilendirme" amaçlıdır, yasal onay değil
  if (student.age >= 13) {
    await veribenim.logFormConsent({
      form_name: 'student_assent',
      consented: true,
      consent_text: assentText.tr,
      metadata: {
        student_id: student.id,
        student_age: student.age,
        assent_type: 'informative', // Not legally binding
        gdpr_applicable: isEUResident(student.country),
      }
    });
  }
}

function isEUResident(country: string): boolean {
  const eu = ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE'];
  return eu.includes(country.toUpperCase());
}

Adım 4: Veli Onayı Loglanması (Audit Trail)

// Veribenim, her veli onayını merkezi olarak kaydeder
// KVKK ceza davası sırasında "Biz veri işlemede yasal miydik?" sorusunun kanıtı

async function getParentalConsentAuditLog(
  veribenim: ReturnType<typeof init>,
  studentId: string
): Promise<AuditLog> {
  return veribenim.getAuditLog({
    entityId: studentId,
    entityType: 'student',
    filters: {
      eventType: 'parental_consent', // Sadece veli onayları
    },
  });
  // Dönen sonuç:
  // {
  //   events: [
  //     {
  //       timestamp: '2024-01-15T10:30:00Z',
  //       action: 'parental_consent_email_sent',
  //       parent_email: 'parent@example.com',
  //       ip_address: '192.168.1.1',
  //       ...
  //     },
  //     {
  //       timestamp: '2024-01-16T14:22:00Z',
  //       action: 'parental_consent_approved',
  //       parent_email: 'parent@example.com',
  //       ip_address: '192.168.1.5',
  //       user_agent: 'Mozilla/5.0...',
  //       verification_method: 'email_link',
  //       consent_text_version: 'v1.2',
  //     }
  //   ]
  // }
}

Adım 5: DSAR — Veli Veri Silme Talebi

// Veli: "Çocuğumun tüm verilerini silin"
// GDPR Article 17 (Right to Erasure)

async function handleParentalDSAR(
  veribenim: ReturnType<typeof init>,
  parentEmail: string,
  studentId: string,
  requestType: 'access' | 'erasure' | 'portability'
): Promise<void> {
  // Veli olduğunu doğrula (aynı e-posta ile kaydı yaptığını kontrol et)
  const studentRecord = await getStudentRecord(studentId);
  if (studentRecord.parent_email !== parentEmail) {
    throw new Error('Veli e-postası eşleşmiyor');
  }

  if (requestType === 'erasure') {
    // Silme talebi
    await veribenim.submitDsar({
      requestType: 'erasure',
      fullName: studentRecord.first_name, // Çocuğun adı
      email: parentEmail, // Velinin iletişim bilgisi
      description: `Parent (${parentEmail}) requests erasure of student data (ID: ${studentId}). Student age: ${calculateAge(studentRecord.date_of_birth)}.`,
      metadata: {
        student_id: studentId,
        parent_requester: true,
        request_date: new Date().toISOString(),
      }
    });

    // Veritabanından sil
    await deleteStudentData(studentId, {
      keepFor: 'tax_records_only', // Vergi için 5 yıl tutma zorunluluğu
      anonymizeImmediately: true, // Ama ad/e-posta gibi tanımlayıcı veriler sil
    });

    return;
  }

  if (requestType === 'access') {
    // Veri erişim talebi — tüm verilerini indir
    const studentData = await collectStudentData(studentId);
    const zipFile = await createDataPortabilityZip(studentData);
    await sendToEmail(parentEmail, zipFile);
    return;
  }

  if (requestType === 'portability') {
    // GDPR: Veli, çocuğun verilerini başka provider'a taşıyabilir
    const studentData = await collectStudentData(studentId);
    const standardFormat = convertToStandardFormat(studentData);
    await sendToEmail(parentEmail, standardFormat);
    return;
  }
}

React ile EdTech Dashboard Entegrasyonu

// pages/dashboard/student.tsx
import { VeribenimProvider, useVeribenim, useConsentCategory } from '@veribenim/react';
import { useAuth } from '@/lib/auth';

export default function StudentDashboard() {
  return (
    <VeribenimProvider token={process.env.NEXT_PUBLIC_VERIBENIM_TOKEN}>
      <StudentDashboardContent />
    </VeribenimProvider>
  );
}

function StudentDashboardContent() {
  const { user } = useAuth();
  const { preferences, logEvent } = useVeribenim();
  const analyticsAllowed = useConsentCategory('analytics');
  const [courses, setCourses] = useState([]);
  const [studentProfile, setStudentProfile] = useState(null);

  useEffect(() => {
    // Öğrenci profili yükle
    fetchStudentProfile(user.id).then(profile => {
      setStudentProfile(profile);

      // Analytics izni varsa, öğrenci açılış olayını logla
      if (analyticsAllowed) {
        logEvent({
          eventType: 'student_dashboard_opened',
          userId: user.id,
          timestamp: new Date(),
          metadata: {
            age_group: profile.age < 18 ? 'minor' : 'adult',
            parental_consent_verified: profile.parental_consent_verified,
          }
        });
      }
    });

    // Kursları yükle
    fetchCourses(user.id).then(setCourses);
  }, []);

  if (!studentProfile) {
    return <LoadingSpinner />;
  }

  // Reşit olmayan öğrenci (ve veli onayı varsa)
  if (studentProfile.age < 18 && studentProfile.parental_consent_verified) {
    return (
      <div className="dashboard">
        <h1>Hoş geldin, {studentProfile.firstName}!</h1>

        {/* Uyarı: Bu çocuk hesabıdır */}
        <InfoBox type="info">
          👦 Ebeveyn tarafından onaylı çocuk hesabı
          <br />
          Pazarlama iletişimi almayacaksınız.
        </InfoBox>

        {/* Kurslar */}
        <CoursesGrid
          courses={courses}
          studentAge={studentProfile.age}
          analyticsAllowed={analyticsAllowed}
        />

        {/* Öğrenci için veri silme seçeneği (16+) */}
        {studentProfile.age >= 16 && (
          <DataManagementPanel
            studentId={user.id}
            parentEmail={studentProfile.parent_email}
          />
        )}

        {/* Veli için kontrol paneli */}
        <ParentalControlsPanel
          studentId={user.id}
          parentEmail={studentProfile.parent_email}
        />
      </div>
    );
  }

  if (studentProfile.age >= 18) {
    // 18+ yetişkin öğrenci: tam kontrol
    return (
      <div className="dashboard">
        <h1>Hoş geldin, {studentProfile.firstName}!</h1>
        <CoursesGrid courses={courses} analyticsAllowed={analyticsAllowed} />
        <FullDataControlPanel studentId={user.id} />
      </div>
    );
  }

  // Veli onayı alınmamış (ve hala bekleniyor)
  return (
    <div className="dashboard-restricted">
      <h1>Hesabınız Kurultuluyor...</h1>
      <InfoBox type="warning">
        ⏳ Ebeveynden onay bekleniyor.
        <br />
        Onay e-postası {studentProfile.parent_email} adresine gönderildi.
      </InfoBox>
      <RestrictedDemoContent courses={courses.slice(0, 2)} />
    </div>
  );
}

// Veli Kontrol Paneli — veli, bulunduğu yerden çocuk verilerini yönetebilir
function ParentalControlsPanel({ studentId, parentEmail }) {
  const { submitDsar } = useVeribenim();
  const [loading, setLoading] = useState(false);

  const handleDataAccess = async () => {
    // Verileri indir
    setLoading(true);
    try {
      await submitDsar({
        requestType: 'access',
        studentId,
        parentEmail,
      });
      alert('Verileriniz indirilmeye başladı. E-postanızı kontrol edin.');
    } finally {
      setLoading(false);
    }
  };

  const handleDataErasure = async () => {
    if (!confirm('Tüm verileri silmek istediğinizden emin misiniz? Bu geri alınamaz.')) {
      return;
    }

    setLoading(true);
    try {
      await submitDsar({
        requestType: 'erasure',
        studentId,
        parentEmail,
      });
      alert('Veri silme talebi işleme alındı. 30 gün içinde tamamlanacaktır.');
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="parental-controls">
      <h3>👨‍👩‍👧 Ebeveyn Kontrol Paneli</h3>
      <p className="email-notice">
        İlişkili e-posta: <strong>{parentEmail}</strong>
      </p>

      <div className="control-actions">
        <button onClick={handleDataAccess} disabled={loading} className="btn btn-secondary">
          📥 Çocuğun Verilerini İndir
        </button>
        <p className="help-text">
          Çocuğunuzun tüm eğitim verileri, ilerleme raporu ve profil bilgisi.
        </p>
      </div>

      <div className="control-actions">
        <button onClick={handleDataErasure} disabled={loading} className="btn btn-danger">
          🗑️ Tüm Verileri Sil
        </button>
        <p className="help-text">
          Çocuğunuzun hesabı kapatılacak ve tüm verileri silinecektir.
        </p>
      </div>

      <hr />

      <div className="consent-history">
        <h4>Onay Geçmişi</h4>
        <ConsentAuditLog studentId={studentId} />
      </div>
    </div>
  );
}

PHP SDK ile LMS (Learning Management System) Entegrasyonu

Çoğu kurum, kendi LMS'ini (Moodle, Canvas, Blackboard) çalıştırıyor. PHP SDK ile bu sistemlere Veribenim nasıl entegre edilir?

<?php
// app/Services/StudentEnrollmentService.php

namespace App\Services;

use Veribenim\VeribenimClient;
use Veribenim\Exceptions\ParentalConsentRequiredException;
use App\Models\Student;
use App\Models\ParentalConsent;
use Illuminate\Support\Facades\Mail;

class StudentEnrollmentService
{
    private VeribenimClient $veribenim;

    public function __construct()
    {
        $this->veribenim = new VeribenimClient($_ENV['VERIBENIM_TOKEN']);
    }

    /**
     * Öğrenci kaydı başlat
     *
     * @param array $studentData
     * @return array
     * @throws ParentalConsentRequiredException
     */
    public function enrollStudent(array $studentData): array
    {
        $student = new Student();
        $student->fill([
            'first_name'    => $studentData['firstName'],
            'last_name'     => $studentData['lastName'],
            'email'         => $studentData['email'],
            'date_of_birth' => $studentData['dateOfBirth'],
            'country'       => $studentData['country'] ?? 'TR',
        ]);
        $student->save();

        // Yaş hesapla
        $age = now()->diffInYears($student->date_of_birth);

        // AB vatandaşı mı?
        $euCountries = ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE'];
        $isEU = in_array(strtoupper($student->country), $euCountries);

        // Yaş limiti: GDPR ise 16, KVKK ise 18
        $ageLimit = $isEU ? 16 : 18;

        if ($age < $ageLimit) {
            // Veli onayı gerekli
            throw new ParentalConsentRequiredException(
                message: "Yaş altı {$ageLimit}. Veli onayı gerekli.",
                studentId: $student->id,
                parentEmail: $studentData['parentEmail'] ?? null
            );
        }

        // Öğrenci 18+: kendi rızası yeterli
        $this->veribenim->logFormConsent(
            formName: 'student_registration',
            consented: true,
            consentText: "Çevrimiçi eğitim platformuna kaydım için kişisel verilerimin işlenmesine onay veriyorum.",
            metadata: [
                'student_id' => $student->id,
                'age'        => $age,
                'country'    => $student->country,
                'gdpr_applicable' => $isEU,
            ]
        );

        $student->update(['status' => 'active']);

        return ['status' => 'enrolled', 'student_id' => $student->id];
    }

    /**
     * Veli onayı talep et
     */
    public function requestParentalConsent(int $studentId, string $parentEmail): void
    {
        $student = Student::findOrFail($studentId);

        // Veribenim'de onay token'ı oluştur
        $consentToken = $this->veribenim->generateParentalConsentToken(
            studentId: $student->id,
            studentName: $student->full_name,
            studentAge: now()->diffInYears($student->date_of_birth),
            parentEmail: $parentEmail,
            expiresInDays: 7
        );

        // E-posta gönder
        Mail::send('emails.parental-consent-request', [
            'student'  => $student,
            'token'    => $consentToken,
            'platform' => config('app.name'),
        ], function ($m) use ($parentEmail) {
            $m->to($parentEmail);
            $m->subject("Çocuğunuzun Kayıt Onayı Gerekli");
        });

        // Veritabanında talebi kaydet
        ParentalConsent::create([
            'student_id'   => $student->id,
            'parent_email' => $parentEmail,
            'token'        => $consentToken,
            'status'       => 'pending',
            'expires_at'   => now()->addDays(7),
        ]);
    }

    /**
     * Veli onay linkine tıklar
     */
    public function handleParentalApprovalCallback(string $token, bool $approved): array
    {
        $consent = ParentalConsent::where('token', $token)
            ->where('expires_at', '>', now())
            ->firstOrFail();

        $student = $consent->student;
        $parentEmail = $consent->parent_email;

        if (!$approved) {
            // Ret
            $this->veribenim->logFormConsent(
                formName: 'parental_consent',
                consented: false,
                consentText: "Veli tarafından reddedildi",
                metadata: ['student_id' => $student->id, 'parent_email' => $parentEmail]
            );

            $consent->update(['status' => 'rejected']);
            $student->update(['status' => 'rejected']); // Hesap devre dışı

            return ['status' => 'rejected'];
        }

        // ONAY
        $consentText = sprintf(
            '%s (Doğum yılı: %d) adlı çocuğun, %s platformunu kullanması için ' .
            'kişisel verilerinin işlenmesine veli/yasal temsilci sıfatıyla onay veriyorum.',
            $student->first_name,
            $student->date_of_birth->year,
            config('app.name')
        );

        $this->veribenim->logFormConsent(
            formName: 'parental_consent_approval',
            consented: true,
            consentText: $consentText,
            metadata: [
                'student_id'     => $student->id,
                'parent_email'   => $parentEmail,
                'student_age'    => now()->diffInYears($student->date_of_birth),
                'consent_method' => 'email_verification_link',
            ]
        );

        $consent->update(['status' => 'approved', 'approved_at' => now()]);
        $student->update(['status' => 'active', 'parental_consent_verified_at' => now()]);

        return ['status' => 'approved'];
    }

    /**
     * Otomatik: 18 yaş dolunca veli onayını yenileme teklifini yap
     */
    public function handleBirthdayMilestone(int $studentId): void
    {
        $student = Student::findOrFail($studentId);
        $age = now()->diffInYears($student->date_of_birth);

        if ($age === 18) {
            // 18 yaş tamamladı: artık kendi onayını verebilir
            $this->veribenim->logFormConsent(
                formName: 'adult_consent_upgrade',
                consented: true,
                consentText: '18 yaşını tamamladım ve kendi adıma kişisel verilerimin işlenmesini onaylıyorum.',
                metadata: [
                    'student_id'       => $student->id,
                    'previous_consent' => 'parental',
                    'upgrade_reason'   => 'age_18_milestone',
                ]
            );

            // Veli bağlantısını temizle
            $student->update(['parent_email' => null, 'parental_consent_verified_at' => null]);

            // E-posta gönder: "18 yaş oldun, kendi kararını ver"
            Mail::send('emails.adult-consent-upgrade', ['student' => $student]);
        }
    }

    /**
     * Reşit olmayan öğrenci için kısıtlı veri işlemesi
     */
    public function getProcessedStudentData(int $studentId, string $dataCategory = 'all'): array
    {
        $student = Student::findOrFail($studentId);
        $age = now()->diffInYears($student->date_of_birth);

        if ($age >= 18 || ($age < 18 && $student->parental_consent_verified_at)) {
            // Tam veri
            return $this->getFullStudentData($student);
        }

        // Veli onayı olmayan: sınırlı veri
        return [
            'id' => $student->id,
            'first_name' => $student->first_name,
            'status' => 'pending_parental_consent',
            'demo_courses' => collect($student->courses)->take(2), // Sadece ilk 2 kurs
        ];
    }
}

// Controller
class StudentEnrollmentController extends Controller
{
    public function __construct(private StudentEnrollmentService $service) {}

    public function register(Request $request)
    {
        try {
            $result = $this->service->enrollStudent($request->validated());
            return response()->json($result);
        } catch (ParentalConsentRequiredException $e) {
            // Veli onayı gerekli
            $this->service->requestParentalConsent(
                $e->studentId,
                $request->input('parentEmail')
            );

            return response()->json([
                'status' => 'pending_parental_consent',
                'message' => 'Veli onayı e-postası gönderildi.',
            ], 202);
        }
    }

    public function approveParentalConsent(Request $request)
    {
        $result = $this->service->handleParentalApprovalCallback(
            token: $request->input('token'),
            approved: $request->boolean('approved')
        );

        return response()->json($result);
    }
}

Data Sovereignty: Öğrenci Verisinin Eğitim Kurumu Egemenlik Alanında Tutulması

Önemli bir noktaya değinmeli: Öğrenci verisi, hangi ülkede saklanmalı?

Senaryo:

  • Öğrenci: Türkiye'de (İstanbul)

  • Platform: Amazon AWS (Frankfurt, Almanya)

  • Öğrenci AB vatandaşı: YOK, Türk vatandaş

Soru: Veriler Almanya sunucularında tutulabilir mi?

KVKK'nın Cevabı

KVKK Madde 14:

"Kişisel veriler, işlendikleri yerin dışına çıkarılmamaya çalışılır. Çıkarılması gerekiyorsa, (...) aynı düzeyde güvenlik sağlanması gerekir."

Yani: Veri transferi mümkün, ama aynı güvenlik seviyesi gerekli.

Almanya (GDPR) = Türkiye (KVKK) eşit seviyede mi? Evet, gözle göçü ile.

GDPR'ın Cevabı

AB vatandaşı yoksa: İlgili değil.


Veribenim, öğrencinin 18 yaş doğum günü yaklaştığında otomatik olarak:

  1. Veli onay dosyasını arşivle

  2. Öğrenciye "18 yaş oldun, artık kendi kararın" maili gönder

  3. Öğrenciden yeni onay al

  4. Veri işleme kurallarını "adult mode"a geçir

// Cron job: Her gün çalış, 18 yaş doğum günü yaklaşan öğrencileri kontrol et
async function automatedAgeUpgradeCheck() {
  const veribenim = init({ token: process.env.VERIBENIM_TOKEN });

  // Doğum günü 14 gün içinde olan 17 yaşındaki öğrenciler
  const upcomingAdults = await Student.where('status', 'active')
    .whereRaw(
      "DATE_ADD(date_of_birth, INTERVAL 18 YEAR) BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 14 DAY)"
    )
    .get();

  for (const student of upcomingAdults) {
    // E-posta gönder: Hazırlan, 18 olacaksın!
    await sendEmail({
      to: student.email,
      subject: '🎉 Yakında 18 Yaş Olacaksın!',
      template: 'age-upgrade-notice',
      data: { student },
    });

    // Veribenim'e bildir
    await veribenim.scheduleAgeUpgrade({
      studentId: student.id,
      upgradeDate: student.date_of_birth.add(18, 'years'),
      action: 'renew_consent_upgrade_to_adult',
    });
  }
}

// Doğum günü gelince otomatik yükseltme
async function automatedAgeUpgrade(studentId: string) {
  const student = await getStudent(studentId);
  const veribenim = init({ token: process.env.VERIBENIM_TOKEN });

  // Kontrol: Gerçekten 18+ mi?
  const age = calculateAge(student.date_of_birth);
  if (age < 18) return; // Henüz değil

  // Eski veli onayını arşivle
  await archiveParentalConsent(studentId);

  // Yeni onay formu gönder
  await sendEmail({
    to: student.email,
    subject: 'Hesabınız Güncellendi: Yeni Onay Gerekli',
    template: 'adult-consent-form',
    data: { student },
  });

  // Veribenim'de kaydını güncelle
  await veribenim.updateStudentProfile({
    studentId,
    consentType: 'self_consent', // Artık veli değil, kendisi
    adultStatus: true,
  });

  // Pazarlama izni kontrol etmek için opsiyonel oydaştırma
  // (18+ olunca pazarlama alabiliyor, ama varsayılan OFF)
  const preferences = await veribenim.getPreferences();
  preferences.marketing = false; // Hala kapalı kalsın
  await veribenim.savePreferences(preferences);
}

Sonuç: EdTech'in Geleceği Veli Onayıda Saklı

Türkiye'deki 27 milyon öğrenci, çevrimiçi eğitim alıyor. Bunların 8-10 milyonu reşit olmayan.

Veribenim, EdTech şirketlerine sunuyor:

  1. Yaş Doğrulama & Veli Onay Otomasyonu: "İş bitti mi?" sorusunun kolay cevabı

  2. GDPR + KVKK Dual Compliance: AB'den gelen uluslararası öğrenciler de uyumlu

  3. Zero-Knowledge Parental Consent: Veli, çocuğun verilerini kontrol eder, ama şifre sadece velinin

  4. Automated Privacy Erasure: 18 yaş doldu → otomatik upgrade, eski veriler arşivle

  5. DSAR Ready: Veri silme talepleri dakikalar içinde

  6. Compliance as a Service: LMS'ine SDK'sı ekle, cezalara karşı sigorta al

Sonuç: EdTech şirketleri, Veribenim ile kanuni risk taşımaktan kurtulur. Öğrenciler, verileri korunmuş hissettiği için daha çok güven duyar.


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


Son Güncelleme: 2 Nisan 2026 | Yazar: Veribenim Compliance Team | Versyon: 1.0

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 →