bulletproof consent: Gift Aid (HMRC), email opt-in, WhatsApp opt-in with full audit trail

GIFT AID (HMRC compliance):
- Exact HMRC model declaration text displayed and recorded
- Home address (line 1 + postcode) collected when Gift Aid is ticked
- giftAidAt timestamp recorded separately from the boolean
- Declaration text, donor name, timestamp stored in consentMeta JSON

EMAIL + WHATSAPP (GDPR/PECR compliance):
- Separate, granular opt-in checkboxes (not bundled, not pre-ticked)
- Each consent records: exact text shown, timestamp, consent version
- Consent checkboxes only appear when relevant contact info is provided
- Cron reminders gated on consent — no sends without opt-in
- Pledge creation WhatsApp receipt gated on whatsappOptIn

AUDIT TRAIL (consentMeta JSON on every pledge):
- giftAid: {declared, declarationText, declaredAt}
- email: {granted, consentText, grantedAt}
- whatsapp: {granted, consentText, grantedAt}
- IP address captured server-side from x-forwarded-for
- User agent captured client-side
- consentVersion field for tracking wording changes

EXPORTS:
- CRM CSV now includes: donor_address, donor_postcode, gift_aid_declared_at,
  is_zakat, email_opt_in, whatsapp_opt_in
- Gift Aid export has full HMRC-required fields

Schema: 6 new columns on Pledge (donorAddressLine1, donorPostcode,
giftAidAt, emailOptIn, whatsappOptIn, consentMeta)
This commit is contained in:
2026-03-03 07:38:51 +08:00
parent e6b7f325da
commit 865c5a1f93
10 changed files with 468 additions and 154 deletions

View File

@@ -102,8 +102,24 @@ model Pledge {
donorName String?
donorEmail String?
donorPhone String?
giftAid Boolean @default(false)
isZakat Boolean @default(false) // donor marked this as Zakat
// --- Home address (required by HMRC for Gift Aid claims) ---
donorAddressLine1 String?
donorPostcode String?
// --- Gift Aid (HMRC) ---
giftAid Boolean @default(false)
giftAidAt DateTime? // when the declaration was made
isZakat Boolean @default(false) // donor marked this as Zakat
// --- Communication consent (GDPR / PECR) ---
emailOptIn Boolean @default(false)
whatsappOptIn Boolean @default(false)
// --- Consent audit trail (immutable evidence) ---
// Stores exact text shown, timestamps, IP, user agent per consent type
consentMeta Json?
iPaidClickedAt DateTime?
notes String?