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:
@@ -3,6 +3,8 @@ export interface CrmExportRow {
|
||||
donor_name: string
|
||||
donor_email: string
|
||||
donor_phone: string
|
||||
donor_address: string
|
||||
donor_postcode: string
|
||||
amount_gbp: string
|
||||
payment_method: string
|
||||
status: string
|
||||
@@ -11,6 +13,10 @@ export interface CrmExportRow {
|
||||
volunteer_name: string
|
||||
table_name: string
|
||||
gift_aid: string
|
||||
gift_aid_declared_at: string
|
||||
is_zakat: string
|
||||
email_opt_in: string
|
||||
whatsapp_opt_in: string
|
||||
pledged_at: string
|
||||
paid_at: string
|
||||
days_to_collect: string
|
||||
|
||||
@@ -25,8 +25,41 @@ export const createPledgeSchema = z.object({
|
||||
donorName: z.string().max(200).optional().default(''),
|
||||
donorEmail: z.string().max(200).optional().default(''),
|
||||
donorPhone: z.string().max(20).optional().default(''),
|
||||
|
||||
// Home address (required for HMRC Gift Aid)
|
||||
donorAddressLine1: z.string().max(300).optional().default(''),
|
||||
donorPostcode: z.string().max(10).optional().default(''),
|
||||
|
||||
// Gift Aid
|
||||
giftAid: z.boolean().default(false),
|
||||
isZakat: z.boolean().default(false),
|
||||
|
||||
// Communication consent (GDPR/PECR)
|
||||
emailOptIn: z.boolean().default(false),
|
||||
whatsappOptIn: z.boolean().default(false),
|
||||
|
||||
// Consent audit trail
|
||||
consentMeta: z.object({
|
||||
giftAid: z.object({
|
||||
declared: z.boolean(),
|
||||
declarationText: z.string(),
|
||||
declaredAt: z.string(),
|
||||
}).optional(),
|
||||
email: z.object({
|
||||
granted: z.boolean(),
|
||||
consentText: z.string(),
|
||||
grantedAt: z.string(),
|
||||
}).optional(),
|
||||
whatsapp: z.object({
|
||||
granted: z.boolean(),
|
||||
consentText: z.string(),
|
||||
grantedAt: z.string(),
|
||||
}).optional(),
|
||||
ip: z.string().optional(),
|
||||
userAgent: z.string().optional(),
|
||||
consentVersion: z.string().default('v1'),
|
||||
}).optional(),
|
||||
|
||||
eventId: z.string(),
|
||||
qrSourceId: z.string().nullable().optional(),
|
||||
// Payment scheduling
|
||||
@@ -39,6 +72,8 @@ export const createPledgeSchema = z.object({
|
||||
donorEmail: data.donorEmail && data.donorEmail.includes('@') ? data.donorEmail : undefined,
|
||||
donorPhone: data.donorPhone && data.donorPhone.length >= 10 ? data.donorPhone : undefined,
|
||||
donorName: data.donorName || undefined,
|
||||
donorAddressLine1: data.donorAddressLine1 || undefined,
|
||||
donorPostcode: data.donorPostcode || undefined,
|
||||
qrSourceId: data.qrSourceId || undefined,
|
||||
}))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user