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.

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);
}
Adım 3: Reşit Olmayan için Kısıtlı Consent Formu
// Öğ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.
Automated Privacy: 18 Yaş Dolduğunda Otomatik Consent Yenileme
Veribenim, öğrencinin 18 yaş doğum günü yaklaştığında otomatik olarak:
Veli onay dosyasını arşivle
Öğrenciye "18 yaş oldun, artık kendi kararın" maili gönder
Öğrenciden yeni onay al
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:
Yaş Doğrulama & Veli Onay Otomasyonu: "İş bitti mi?" sorusunun kolay cevabı
GDPR + KVKK Dual Compliance: AB'den gelen uluslararası öğrenciler de uyumlu
Zero-Knowledge Parental Consent: Veli, çocuğun verilerini kontrol eder, ama şifre sadece velinin
Automated Privacy Erasure: 18 yaş doldu → otomatik upgrade, eski veriler arşivle
DSAR Ready: Veri silme talepleri dakikalar içinde
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