Implementing consent management for data processing on a website
Consent Management is a system that captures, stores, and processes legally significant user consents for personal data processing in accordance with GDPR, 152-ФЗ, and other regulatory requirements.
Types of consents
| Type | Examples | Required |
|---|---|---|
| Personal data processing | Registration, order form | Yes |
| Marketing communications | Email newsletter, SMS | On request |
| Profiling | Recommendations, analytics | On request |
| Third-party transfer | Partners, ad networks | On request |
| Non-essential cookies | Analytics, remarketing | On request |
Database structure
CREATE TABLE consent_types (
id SERIAL PRIMARY KEY,
code VARCHAR(50) UNIQUE NOT NULL,
title VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
version VARCHAR(20) NOT NULL,
is_required BOOLEAN DEFAULT FALSE,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE user_consents (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT REFERENCES users(id) ON DELETE CASCADE,
consent_type_id INT REFERENCES consent_types(id),
status VARCHAR(20) NOT NULL,
version_accepted VARCHAR(20) NOT NULL,
ip_address INET,
user_agent TEXT,
source VARCHAR(100),
granted_at TIMESTAMPTZ,
withdrawn_at TIMESTAMPTZ,
expires_at TIMESTAMPTZ,
UNIQUE (user_id, consent_type_id, version_accepted)
);
Consent management service (Laravel)
class ConsentService
{
public function grant(User $user, string $consentCode, string $source): UserConsent
{
$consentType = ConsentType::where('code', $consentCode)->firstOrFail();
return UserConsent::updateOrCreate(
[
'user_id' => $user->id,
'consent_type_id' => $consentType->id,
'version_accepted' => $consentType->version,
],
[
'status' => 'granted',
'ip_address' => request()->ip(),
'user_agent' => request()->userAgent(),
'source' => $source,
'granted_at' => now(),
'withdrawn_at' => null,
]
);
}
public function withdraw(User $user, string $consentCode): void
{
$consentType = ConsentType::where('code', $consentCode)->firstOrFail();
UserConsent::where('user_id', $user->id)
->where('consent_type_id', $consentType->id)
->where('status', 'granted')
->update([
'status' => 'withdrawn',
'withdrawn_at' => now(),
]);
event(new ConsentWithdrawn($user, $consentCode));
}
public function hasConsent(User $user, string $consentCode): bool
{
$consentType = ConsentType::where('code', $consentCode)->first();
if (!$consentType) return false;
return UserConsent::where('user_id', $user->id)
->where('consent_type_id', $consentType->id)
->where('status', 'granted')
->where('version_accepted', $consentType->version)
->exists();
}
}
Registration consent form
<form method="POST" action="/register">
@csrf
<label class="required-consent">
<input type="checkbox" name="consents[]" value="data_processing" required>
I agree with <a href="/privacy-policy" target="_blank">Privacy Policy</a>
and grant consent for personal data processing
</label>
<label>
<input type="checkbox" name="consents[]" value="marketing_email">
I want to receive information about promotions and news via email
</label>
</form>
Re-consent on policy changes
When privacy policy version changes, users with outdated consent must give new:
class RequireFreshConsent
{
public function handle(Request $request, Closure $next)
{
$user = $request->user();
if (!$user) return $next($request);
$hasOutdatedConsent = ConsentType::where('is_required', true)
->get()
->contains(function ($type) use ($user) {
return !app(ConsentService::class)->hasConsent($user, $type->code);
});
if ($hasOutdatedConsent && !$request->is('consent*', 'logout*')) {
return redirect()->route('consent.update');
}
return $next($request);
}
}
Implementation Timeline
- Basic model + registration form: 3–4 days
- Account settings + re-consent: 3–4 days
- Data export/deletion API: 2–3 days







