The Donor Information Validation System implements FEC "best efforts" compliance requirements to help recipient committees meet federal election regulations. The system validates donor records and identifies entries that are missing, incomplete, inconsistent, or obviously false so that recipient committees can make informed decisions about accepting or rejecting contributions.
POWERBACK operates as a conduit platform that:
- Accepts contributions from donors through Stripe payment processing
- Holds funds in escrow until donor contingency conditions are met
- Validates donor information and flags any issues for recipient committee review
- Makes funds and donor info available (with flags attached) so the recipient committee or PAC operator can receive and report them when conditions are satisfied; delivery of funds and FEC filing are done manually by the operator, not by automated backend systems
- Executes refunds when recipient committees reject flagged contributions (refunds are initiated by the operator via Stripe)
Key Compliance Point: The legal burden sits with the recipient committee (to decide and to report). The mechanical burden for moving money sits with POWERBACK as the registered PAC (since only we can move money back to the donor via Stripe). Forwarding funds to campaigns and filing with the FEC are handled manually by the PAC operator.
- Basic account information only
- No additional donor information required beyond login
- Per-donation limit: $50
- Annual cap: $200 across all candidates
- Name: First name + last name
- Mailing address: Street (or PO Box), city, state/territory, ZIP/postal code
- Occupation and Employer (if truly not employed/retired/student/etc, handled per rules)
- Per-candidate per-election limit: $3,500
A comprehensive validation service that:
- Normalizes donor information while preserving original meaning
- Flags issues with hard/soft severity levels
- Validates required fields based on compliance tier (guest/compliant)
- Detects problematic content (placeholders, profanity, gibberish)
- Handles employment status inconsistencies
- Validates address formats and geography
Key Features:
- Text normalization (trim, title-case, standardize values)
- Comprehensive keyword detection for placeholders/profanity
- Regex pattern matching for gibberish detection
- Employment status consistency checking
- Address format validation with PO Box support
- State/territory code validation
- ZIP code format validation
Three new API endpoints for donor validation:
POST /api/donor-validation/validate- Single donor validationPOST /api/donor-validation/batch- Batch validation (up to 100 donors)GET /api/donor-validation/health- Service health check
The Celebration model now includes a donorInfo object that captures the user's information at the time of donation:
donorInfo: {
// Basic identification (required for compliant tier)
firstName: { type: String, default: '' },
lastName: { type: String, default: '' },
// Address information (required for compliant tier)
address: { type: String, default: '' },
city: { type: String, default: '' },
state: { type: String, default: '' },
zip: { type: String, default: '' },
country: { type: String, default: 'United States' },
passport: { type: String, default: '' },
// Employment information (required for compliant tier)
isEmployed: { type: Boolean, default: true },
occupation: { type: String, default: '' },
employer: { type: String, default: '' },
// Compliance tier at time of donation
compliance: { type: String, required: true },
// Contact information for receipts
email: { type: String, default: '' },
username: { type: String, default: '' },
phoneNumber: { type: String, default: '' },
// Additional validation and audit fields
ocd_id: { type: String, default: '' },
locked: { type: Boolean, default: false },
understands: { type: Boolean, default: false }
}The system normalizes donor information while preserving the original meaning:
- Text Processing: Trim spaces, collapse repeated whitespace, title-case names, uppercase state/territory codes
- Standard Values: Normalize common occupation and employer values
- Preservation: Store original user text in
rawfield, standardized value innormalizedfield
Simple Boolean Flagging: Donor information is either flagged or not flagged for recipient committee review.
Flagged Issues Include:
- Missing required fields
- Obscene/joke/gibberish content
- Impossible geography
- Profanity or insult content
- Keyboard mashing or repeated characters
- Ambiguous or inconsistent information
- Low-confidence data
- Generic occupations that may need clarification
- Employment status inconsistencies
Important: All flagged contributions are made available to recipient committees with flag information attached (forwarding and FEC reporting are done manually by the PAC operator). POWERBACK does not block donations - recipient committees make the final decision to accept or reject flagged contributions.
Flagged Issues:
- Single word name (unless recognized mononyms allowed)
- Initials only (e.g., "J. D.")
- Placeholder content (n/a, unknown, test, etc.)
- Profanity or joke content
- Gibberish patterns (repeated characters, keyboard mashing)
Normalization:
- Title-case names with special handling for prefixes (Mc, O', Van, Von, etc.)
Flagged Issues:
- Missing required address fields
- Celestial/impossible places (Mars, Moon, Area 51, etc.)
- Placeholder content
- City containing only numbers
- ZIP code with all same digits
- Invalid state code (possible international address)
- Invalid ZIP code format
- Address format may be invalid (missing street type)
Accepted Formats:
- Street address: Requires digit and letters
- PO Box:
P.O. Box 123format - Valid USPS state/territory codes
- US ZIP formats:
12345or12345-6789
Flagged Issues:
- Missing required occupation at compliant tier
- Placeholder or junk content
- Profanity or joke content
- Gibberish patterns
- Generic occupations (Worker, Employee, Staff, Manager, Owner)
Standard Values:
Retired,Student,Homemaker,Not employed,Unemployed,Disabled,Self-employed
Flagged Issues:
- Missing required employer at compliant tier
- Placeholder or junk content
- Profanity or joke content
- Gibberish patterns
- Too short employer name (unless known acronym like IBM, 3M)
- Employment status inconsistency
Standard Values:
- Not employed:
None - Self-employed:
Self-employed
Self-employment Indicators:
self,myself,me,freelance,independent,sole proprietor,own business,consultant
POST /api/donor-validation/validate
{
"firstName": "John",
"lastName": "Doe",
"address": "123 Main St",
"city": "Anytown",
"state": "CA",
"zip": "12345",
"occupation": "Engineer",
"employer": "Tech Corp",
"compliance": "gold"
}Response:
{
"success": true,
"validation": {
"normalized": {
"firstName": "John",
"lastName": "Doe",
"address": "123 Main St",
"city": "Anytown",
"state": "CA",
"zip": "12345",
"occupation": "Engineer",
"employer": "Tech Corp",
"compliance": "gold"
},
"flags": [],
"raw": { ... }
},
"summary": {
"totalFlags": 0,
"hardFlags": 0,
"softFlags": 0,
"isCompliant": true,
"needsFollowUp": false
}
}POST /api/donor-validation/batch
{
"compliance": "gold",
"donors": [
{
"firstName": "John",
"lastName": "Doe",
"address": "123 Main St",
"city": "Anytown",
"state": "CA",
"zip": "12345",
"occupation": "Engineer",
"employer": "Tech Corp"
}
]
}GET / api / donor - validation / health;// Validate donor information before submission
async function validateDonorInfo(donorData, compliance) {
try {
const response = await fetch('/api/donor-validation/validate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
...donorData,
compliance,
}),
});
const result = await response.json();
if (result.success) {
const { validation, summary } = result;
// Check for flags (for recipient committee review)
if (summary.totalFlags > 0) {
console.warn(
'Validation flags found - flagged for recipient committee review:',
validation.flags
);
return { isValid: true, isFlagged: true, flags: validation.flags };
}
return { isValid: true, normalizedData: validation.normalized };
}
} catch (error) {
console.error('Validation error:', error);
return { isValid: false, error: error.message };
}
}// Validate donor information during donation processing
const {
validateDonorInfo,
getValidationSummary,
} = require('../services/donorValidation');
async function processDonation(donorInfo, compliance) {
// Validate donor information
const validationResult = validateDonorInfo(donorInfo, compliance);
const summary = getValidationSummary(validationResult);
// Log validation results
logger.info('Donor validation completed', {
compliance,
flagCount: validationResult.flags.length,
isFlagged: summary.totalFlags > 0,
});
// Handle validation flags - all donations proceed with flags attached
if (summary.totalFlags > 0) {
// Log flags for recipient committee review
logger.warn(
'Donation has validation flags - flagged for recipient committee review',
{
flagCount: summary.totalFlags,
flags: validationResult.flags,
}
);
}
// Use normalized data for storage
return validationResult.normalized;
}Run the test suite to verify validation functionality:
node scripts/tests/test-donor-validation.jsThe test suite includes various scenarios:
- Valid donor information
- Missing required fields
- Placeholder content
- Profanity/joke content
- Employment inconsistencies
- Self-employment scenarios
- Edge cases and error conditions
The system uses configurable keyword lists for detecting various types of problematic content:
- Placeholder Keywords: n/a, unknown, test, sample, etc.
- Profanity Keywords: god, jesus, santa, batman, etc.
- Jokey Address Keywords: mars, moon, area 51, etc.
- Self-employment Indicators: self, myself, freelance, etc.
- Not Employed Categories: retired, student, homemaker, etc.
Valid USPS state and territory codes are supported:
- All 50 states (AL, AK, AZ, ..., WY)
- District of Columbia (DC)
- Territories: PR, VI, GU, AS, MP
This system helps meet FEC "best efforts" requirements by:
- Identifying Missing Information: Flags records missing required fields
- Detecting Invalid Content: Identifies placeholder, joke, or profane content
- Normalizing Data: Standardizes common values while preserving meaning
- Providing Audit Trail: Maintains original and normalized data for compliance verification
- Do Not Invent Data: The system normalizes but never fabricates information
- Respectful Normalization: Preserves user intent while standardizing formats
- Comprehensive Flagging: Identifies both hard and soft issues for appropriate handling
- Audit Trail: Maintains complete records for compliance verification
- Donor submits contribution through POWERBACK platform
- Stripe processes payment and deposits funds into POWERBACK conduit account
- Donor information is validated and flagged if issues are detected
- Contribution sits in escrow until donor's contingency condition is met
- Funds and donor info (with flags) are delivered to the recipient committee by the PAC operator when condition is satisfied (manual process; not automated)
- Recipient committee reviews the contribution:
- If they accept → they keep and report it
- If they reject → they instruct POWERBACK to refund
- POWERBACK executes refund via Stripe back to donor's original payment method (operator-initiated)
- Recipient committee reports contributions and any refunds on their FEC filings (FEC filing is done manually by the committee/operator, not by the app)
- Maintain records of all validation results and flags
- Log all flagging decisions for audit purposes
- Track contribution status (escrow → delivered/accepted/rejected; delivery and FEC filing are manual)
- Regular review of flagged records for patterns and improvements
- FEC Compliance Guide - Comprehensive FEC compliance
- Donation Limits - Compliance tier limits
- Email System - Email notifications
- API Documentation - API endpoints
- User Management - User model and management
- Payment Processing - Payment flow integration