Design a 3-day donation platform
Design: Online Donation Platform for 3-Day Campaigns
Context
You are tasked with designing an online donation platform specifically optimized for short, 3-day fundraising campaigns. These campaigns are characterized by large traffic spikes at their launch and close. The platform must reliably process payments, display near real-time donation totals, and strictly adhere to all relevant privacy and payment regulations.
Functional Requirements
1. Donor Management
- Donor signup and login, supporting email, phone, and optionally social login.
- Storage of minimal donor profile information and preferences.
2. Campaign Management
- Admins can create, configure, and schedule 3-day campaigns, defining start/end times, fundraising goals, currency, and geographical targeting.
- Public-facing campaign landing pages featuring progress bars and leaderboards.
3. Donations
- Initiate and confirm one-time donations, supporting multiple currencies.
- Display near real-time campaign totals (total amount and count) to donors.
- Generate receipts and send email/SMS confirmations.
- Handle full or partial refunds and manage chargebacks.
4. Operations
- Admin dashboards for monitoring campaign performance, exporting data, and reconciling financial records.
- Provide webhooks or data exports for integration with finance and business intelligence (BI) systems.
Non-Functional Goals
- Availability SLOs
- End-to-end donation processing: 99.95% monthly.
- Read paths (campaign pages, totals): 99.99% monthly.
- Latency Budgets (from API gateway)
- P50 ≤ 200 ms, P95 ≤ 500 ms, P99 ≤ 1 s for non-payment endpoints.
- Payment confirmation may take up to 3–5 s (dependent on Payment Service Provider - PSP); provide an asynchronous user experience.
- Throughput Targets (example sizing; justify assumptions)
- Peak QPS for donation creation: 3–8k QPS during spikes.
- Reads (landing pages, totals): 10× write QPS at peaks.
- Webhook handling: 1–2k QPS bursts.
- Durability and Integrity
- No lost accepted donations; ensure exactly-once ledger semantics through idempotency and reconciliation.
Traffic Estimates (Example)
- Assume 5 campaigns per week, each lasting 3 days.
- Each campaign expects 500k–2M visits and 50k–300k donations.
- Donation arrivals are spiky: 40% of donations occur in the first 6 hours, and another 40% in the last 6 hours of a campaign.
- Peak write QPS example: 250k donations over 6 hours averages to ~11.6/s, but with bursts 10–100× higher, requiring a plan for 3–8k QPS.
- Real-time totals reads: Expect 10–20 reads for every write (due to progress bars, leaderboards).
APIs and Data Models
Design using REST (or gRPC) with idempotency keys and asynchronous confirmations.
- Core Entities: Campaign, Donor, Donation, PaymentIntent, Receipt.
- Key Endpoints (high-level):
POST /campaignsGET /campaigns/{id}GET /campaigns/{id}/totalsPOST /donorsPOST /payment_intents(idempotent)POST /donations/confirm(with PSP payment_method or token)POST /donations/{id}/refundsGET /donations/{id},GET /receipts/{id}POST /webhooks/payments(from PSP to our system)
- Data Models (minimal fields):
- Campaign:
id(uuid),name,start_at,end_at,currency,goal_amount,status,settings(metadata),created_by,totals(amount_agg,count_agg- eventually consistent),version. - Donor:
id(uuid),email,phone(optional),name(optional),country,marketing_opt_in,created_at. - PaymentIntent:
id(uuid),campaign_id,donor_id(optional),client_secret/token(from PSP),amount,currency,status(requires_payment_method|requires_confirmation|processing|succeeded|canceled),idempotency_key,expires_at. - Donation:
id(uuid),campaign_id,donor_id(optional),payment_intent_id,amount,currency,status(pending|authorized|captured|refunded|failed|chargeback),external_payment_id,risk_score,created_at. - Receipt:
id(uuid),donation_id,receipt_number,issued_at,tax_fields,pdf_url/hash.
- Campaign:
Payment Processing
- Idempotency: All create/confirm endpoints require an
Idempotency-Key. Store request hash + response to deduplicate retries. - Flow:
- Client requests a
PaymentIntent(server calls PSP to create intent/client_secret). - Client collects payment method via PSP SDK (minimizing PCI scope).
- Server confirms:
POST /donations/confirmwithpayment_intent_id+ token, then calls PSP confirm API. - Statuses:
- Synchronous success: Mark
Donationcaptured, emit events, generate receipt. - Asynchronous (e.g., 3DS/SCA): Mark
processing; rely on PSP webhooks to finalize. - Failure: Surface message; allow retry with the same intent when possible.
- Synchronous success: Mark
- Client requests a
- Retries: Network timeouts are safe-to-retry with the same
Idempotency-Key. PSP confirm operations are idempotent; reconcile usingexternal_payment_id. - Reconciliation: Ingest PSP webhooks (
payment_intent.succeeded/failed/refunded/chargeback). Run a nightly job to diff our ledger against PSP payouts, resolve mismatches, and escalate if needed. - Refunds:
POST /donations/{id}/refundswith amount; call PSP refund API; update status via webhook; generate refund receipt. - Receipts: On capture/refund, create an immutable receipt record; send email; allow re-download; include tax fields per locale.
Fraud and Abuse Mitigation
- Pre-authorization checks: Implement velocity limits (per IP/device/card/email), disposable email detection, proxy/VPN detection.
- Risk scoring: Utilize rules-based and machine learning (ML) models (features: IP geolocation, BIN, AVS/CVV result, device fingerprint, donation velocity, amount anomalies).
- Strong customer authentication: Enable 3DS/SCA where required or beneficial.
- Block/allow lists: Maintain lists for BIN ranges, IPs, emails, devices.
- Chargeback handling: Store evidence packets; export to PSP.
- Bot mitigation: Implement CAPTCHA after certain thresholds; apply WAF rules for traffic spikes.
Rate Limiting
- Token bucket per IP and per donor account (e.g., 20 req/s burst, 5 req/s sustained).
- Endpoint-specific tighter limits for payment endpoints (e.g., 2 confirms/s per donor, 10/min per card).
- Global circuit breaker to protect the PSP and ledger; return 429 with
Retry-Afterheader.
Privacy and Compliance
- PCI DSS: Use PSP hosted fields/SDK; never handle raw PAN/CVV; store only tokens/last4/brand/exp; target SAQ-A compliance.
- PII: Practice data minimization; encrypt data at rest (FIPS 140-2) and in transit (TLS 1.2+); implement field-level encryption for email/phone. Apply access controls (RBAC/ABAC), audit logs, and Data Loss Prevention (DLP) monitoring.
- CCPA/GDPR: Implement consent tracking, privacy policy links, purpose limitation. Support data subject rights: access, deletion, rectification, portability; design deletion workflows with event propagation. Configure data retention policies; pseudonymize data for analytics; address regional data residency where applicable.
Architecture
- Edge: CDN + WAF + Bot protection; API Gateway (for rate limiting, authentication, idempotency middleware).
- Services (separate deployable units):
- Campaign Service (CRUD, totals read)
- Donor Service (profiles, consent)
- Donation Service (Donation lifecycle, receipts)
- Payment Service (PSP integration, idempotency, webhook handler)
- Risk Service (risk scoring, rules engine)
- Notification Service (email/SMS, receipt generation)
- Reporting/Reconciliation Service (exports, nightly diff)
- Data:
- Primary OLTP DB (Postgres/MySQL) for strong consistency of transactional data; utilize read replicas.
- Redis/Memcached for session management, idempotency keys, rate limits, and real-time counters.
- Event bus/stream (Kafka/PubSub) for outbox events, receipts, and analytics.
- Object storage for receipts (PDF) and exports; use signed URLs for access.
- Patterns:
- Outbox/transactional messaging for exactly-once event publication.
- Sagas for cross-service workflows (e.g., donation confirmed → receipt → email).
- Webhook endpoint isolated and heavily rate-limited; designed to be retry-friendly and idempotent.
Scaling and Sharding Strategy
- Shard by
campaign_idfor write-heavy tables (donations, intents) when exceeding single-node limits; keep donor data global. - Scale reads via database replicas and Redis caching (for campaign pages, totals).
- PSP integration: Use connection pools, circuit breakers, and batch operations where available.
- Autoscale stateless services based on CPU/QPS; pre-warm capacity during campaign launch windows.
Consistency Model for Counters (Real-Time Totals)
- Requirements: UI needs near real-time totals; financial correctness must be exact.
- Approach:
- Strong source of truth: Ledger in OLTP database (exact totals, with eventual propagation).
- Real-time counter: Sharded Redis counters per campaign (
INCRBYon capture), published via pub/sub to websockets for UI updates. - Periodic true-up: Every N seconds/minutes, recompute totals from authoritative events in the OLTP database to correct any drift.
- Tolerate small staleness (≤ 1–2 s) on the UI; display a "Last updated" badge.
Handling Launch/Closing Traffic Spikes
- Pre-provision capacity: Autoscale minimum replicas; warm caches; CDN priming.
- Queue writes under extreme burst: Accept payment intent, enqueue confirm worker; inform user if processing is asynchronous.
- Graceful degradation: Implement a "slow mode" during launch/close (e.g., lower image/video weight, cache totals more aggressively).
- Backpressure: Tighten rate limits, use exponential backoff; shed non-essential traffic (e.g., analytics).
Observability
- Metrics (RED/USE):
- Request rate, errors, latency per endpoint; PSP call success/latency; webhook lag; queue depth; risk scores distribution.
- Business metrics: donations/min, authorization rate, capture rate, refund rate, chargeback rate, conversion funnel.
- Logs: Structured JSON with correlation IDs; PII scrubbing; sampling for high-volume routes.
- Tracing: Distributed tracing across gateway → services → PSP; annotate spans with idempotency keys and
external_payment_id. - Alerting & SLOs: Error budget tracking; alerts on SLO burn, PSP degradation, counter drift, reconciliation mismatches.
- Conduct synthetic tests during campaigns; utilize canary deploys.
Disaster Recovery (DR)
- Multi-AZ active by default; implement a multi-region strategy.
- Active/active for stateless services; active/passive for databases with asynchronous replication.
- RPO/RTO targets: RPO ≤ 5 minutes (binlog/stream replication); RTO ≤ 30 minutes for failover.
- Regular backups, point-in-time recovery; conduct DR drills and maintain runbooks.
- Idempotent reprocessing on failover using outbox pattern and event replay.
Backfills and Reprocessing
- Store raw PSP events and our emitted events in append-only topics.
- Use idempotent consumers keyed by
donation_id/external_payment_id. - Develop backfill jobs for receipts/totals recomputation; manage versioned schemas and migration playbooks.
Extensibility
- Recurring Donations:
- Store
PaymentMethodtokens; implement a scheduler service to createPaymentIntentson a defined cadence; send email pre-notifications; design retry ladders; define proration rules for campaign windows.
- Store
- Corporate Matching:
- Develop an employer registry and matching policies; collect employer information; manage match pledge vs. funded states; implement reporting and disbursement workflows; integrate with partner APIs for verification.
AI interview prep
Core dilemma
Balancing extreme, spiky write throughput and near real-time read consistency with strict financial integrity, external payment processor dependencies, and stringent compliance requirements.
3 tradeoffs and 3 stress tests locked
Interview prep
Unlock the interviewer X-Ray rubric, tradeoff matrix, stress tests, clarifying questions, and key concepts.
Design a 3-day donation platform
Design: Online Donation Platform for 3-Day Campaigns
Context
You are tasked with designing an online donation platform specifically optimized for short, 3-day fundraising campaigns. These campaigns are characterized by large traffic spikes at their launch and close. The platform must reliably process payments, display near real-time donation totals, and strictly adhere to all relevant privacy and payment regulations.
Functional Requirements
1. Donor Management
- Donor signup and login, supporting email, phone, and optionally social login.
- Storage of minimal donor profile information and preferences.
2. Campaign Management
- Admins can create, configure, and schedule 3-day campaigns, defining start/end times, fundraising goals, currency, and geographical targeting.
- Public-facing campaign landing pages featuring progress bars and leaderboards.
3. Donations
- Initiate and confirm one-time donations, supporting multiple currencies.
- Display near real-time campaign totals (total amount and count) to donors.
- Generate receipts and send email/SMS confirmations.
- Handle full or partial refunds and manage chargebacks.
4. Operations
- Admin dashboards for monitoring campaign performance, exporting data, and reconciling financial records.
- Provide webhooks or data exports for integration with finance and business intelligence (BI) systems.
Non-Functional Goals
- Availability SLOs
- End-to-end donation processing: 99.95% monthly.
- Read paths (campaign pages, totals): 99.99% monthly.
- Latency Budgets (from API gateway)
- P50 ≤ 200 ms, P95 ≤ 500 ms, P99 ≤ 1 s for non-payment endpoints.
- Payment confirmation may take up to 3–5 s (dependent on Payment Service Provider - PSP); provide an asynchronous user experience.
- Throughput Targets (example sizing; justify assumptions)
- Peak QPS for donation creation: 3–8k QPS during spikes.
- Reads (landing pages, totals): 10× write QPS at peaks.
- Webhook handling: 1–2k QPS bursts.
- Durability and Integrity
- No lost accepted donations; ensure exactly-once ledger semantics through idempotency and reconciliation.
Traffic Estimates (Example)
- Assume 5 campaigns per week, each lasting 3 days.
- Each campaign expects 500k–2M visits and 50k–300k donations.
- Donation arrivals are spiky: 40% of donations occur in the first 6 hours, and another 40% in the last 6 hours of a campaign.
- Peak write QPS example: 250k donations over 6 hours averages to ~11.6/s, but with bursts 10–100× higher, requiring a plan for 3–8k QPS.
- Real-time totals reads: Expect 10–20 reads for every write (due to progress bars, leaderboards).
APIs and Data Models
Design using REST (or gRPC) with idempotency keys and asynchronous confirmations.
- Core Entities: Campaign, Donor, Donation, PaymentIntent, Receipt.
- Key Endpoints (high-level):
POST /campaignsGET /campaigns/{id}GET /campaigns/{id}/totalsPOST /donorsPOST /payment_intents(idempotent)POST /donations/confirm(with PSP payment_method or token)POST /donations/{id}/refundsGET /donations/{id},GET /receipts/{id}POST /webhooks/payments(from PSP to our system)
- Data Models (minimal fields):
- Campaign:
id(uuid),name,start_at,end_at,currency,goal_amount,status,settings(metadata),created_by,totals(amount_agg,count_agg- eventually consistent),version. - Donor:
id(uuid),email,phone(optional),name(optional),country,marketing_opt_in,created_at. - PaymentIntent:
id(uuid),campaign_id,donor_id(optional),client_secret/token(from PSP),amount,currency,status(requires_payment_method|requires_confirmation|processing|succeeded|canceled),idempotency_key,expires_at. - Donation:
id(uuid),campaign_id,donor_id(optional),payment_intent_id,amount,currency,status(pending|authorized|captured|refunded|failed|chargeback),external_payment_id,risk_score,created_at. - Receipt:
id(uuid),donation_id,receipt_number,issued_at,tax_fields,pdf_url/hash.
- Campaign:
Payment Processing
- Idempotency: All create/confirm endpoints require an
Idempotency-Key. Store request hash + response to deduplicate retries. - Flow:
- Client requests a
PaymentIntent(server calls PSP to create intent/client_secret). - Client collects payment method via PSP SDK (minimizing PCI scope).
- Server confirms:
POST /donations/confirmwithpayment_intent_id+ token, then calls PSP confirm API. - Statuses:
- Synchronous success: Mark
Donationcaptured, emit events, generate receipt. - Asynchronous (e.g., 3DS/SCA): Mark
processing; rely on PSP webhooks to finalize. - Failure: Surface message; allow retry with the same intent when possible.
- Synchronous success: Mark
- Client requests a
- Retries: Network timeouts are safe-to-retry with the same
Idempotency-Key. PSP confirm operations are idempotent; reconcile usingexternal_payment_id. - Reconciliation: Ingest PSP webhooks (
payment_intent.succeeded/failed/refunded/chargeback). Run a nightly job to diff our ledger against PSP payouts, resolve mismatches, and escalate if needed. - Refunds:
POST /donations/{id}/refundswith amount; call PSP refund API; update status via webhook; generate refund receipt. - Receipts: On capture/refund, create an immutable receipt record; send email; allow re-download; include tax fields per locale.
Fraud and Abuse Mitigation
- Pre-authorization checks: Implement velocity limits (per IP/device/card/email), disposable email detection, proxy/VPN detection.
- Risk scoring: Utilize rules-based and machine learning (ML) models (features: IP geolocation, BIN, AVS/CVV result, device fingerprint, donation velocity, amount anomalies).
- Strong customer authentication: Enable 3DS/SCA where required or beneficial.
- Block/allow lists: Maintain lists for BIN ranges, IPs, emails, devices.
- Chargeback handling: Store evidence packets; export to PSP.
- Bot mitigation: Implement CAPTCHA after certain thresholds; apply WAF rules for traffic spikes.
Rate Limiting
- Token bucket per IP and per donor account (e.g., 20 req/s burst, 5 req/s sustained).
- Endpoint-specific tighter limits for payment endpoints (e.g., 2 confirms/s per donor, 10/min per card).
- Global circuit breaker to protect the PSP and ledger; return 429 with
Retry-Afterheader.
Privacy and Compliance
- PCI DSS: Use PSP hosted fields/SDK; never handle raw PAN/CVV; store only tokens/last4/brand/exp; target SAQ-A compliance.
- PII: Practice data minimization; encrypt data at rest (FIPS 140-2) and in transit (TLS 1.2+); implement field-level encryption for email/phone. Apply access controls (RBAC/ABAC), audit logs, and Data Loss Prevention (DLP) monitoring.
- CCPA/GDPR: Implement consent tracking, privacy policy links, purpose limitation. Support data subject rights: access, deletion, rectification, portability; design deletion workflows with event propagation. Configure data retention policies; pseudonymize data for analytics; address regional data residency where applicable.
Architecture
- Edge: CDN + WAF + Bot protection; API Gateway (for rate limiting, authentication, idempotency middleware).
- Services (separate deployable units):
- Campaign Service (CRUD, totals read)
- Donor Service (profiles, consent)
- Donation Service (Donation lifecycle, receipts)
- Payment Service (PSP integration, idempotency, webhook handler)
- Risk Service (risk scoring, rules engine)
- Notification Service (email/SMS, receipt generation)
- Reporting/Reconciliation Service (exports, nightly diff)
- Data:
- Primary OLTP DB (Postgres/MySQL) for strong consistency of transactional data; utilize read replicas.
- Redis/Memcached for session management, idempotency keys, rate limits, and real-time counters.
- Event bus/stream (Kafka/PubSub) for outbox events, receipts, and analytics.
- Object storage for receipts (PDF) and exports; use signed URLs for access.
- Patterns:
- Outbox/transactional messaging for exactly-once event publication.
- Sagas for cross-service workflows (e.g., donation confirmed → receipt → email).
- Webhook endpoint isolated and heavily rate-limited; designed to be retry-friendly and idempotent.
Scaling and Sharding Strategy
- Shard by
campaign_idfor write-heavy tables (donations, intents) when exceeding single-node limits; keep donor data global. - Scale reads via database replicas and Redis caching (for campaign pages, totals).
- PSP integration: Use connection pools, circuit breakers, and batch operations where available.
- Autoscale stateless services based on CPU/QPS; pre-warm capacity during campaign launch windows.
Consistency Model for Counters (Real-Time Totals)
- Requirements: UI needs near real-time totals; financial correctness must be exact.
- Approach:
- Strong source of truth: Ledger in OLTP database (exact totals, with eventual propagation).
- Real-time counter: Sharded Redis counters per campaign (
INCRBYon capture), published via pub/sub to websockets for UI updates. - Periodic true-up: Every N seconds/minutes, recompute totals from authoritative events in the OLTP database to correct any drift.
- Tolerate small staleness (≤ 1–2 s) on the UI; display a "Last updated" badge.
Handling Launch/Closing Traffic Spikes
- Pre-provision capacity: Autoscale minimum replicas; warm caches; CDN priming.
- Queue writes under extreme burst: Accept payment intent, enqueue confirm worker; inform user if processing is asynchronous.
- Graceful degradation: Implement a "slow mode" during launch/close (e.g., lower image/video weight, cache totals more aggressively).
- Backpressure: Tighten rate limits, use exponential backoff; shed non-essential traffic (e.g., analytics).
Observability
- Metrics (RED/USE):
- Request rate, errors, latency per endpoint; PSP call success/latency; webhook lag; queue depth; risk scores distribution.
- Business metrics: donations/min, authorization rate, capture rate, refund rate, chargeback rate, conversion funnel.
- Logs: Structured JSON with correlation IDs; PII scrubbing; sampling for high-volume routes.
- Tracing: Distributed tracing across gateway → services → PSP; annotate spans with idempotency keys and
external_payment_id. - Alerting & SLOs: Error budget tracking; alerts on SLO burn, PSP degradation, counter drift, reconciliation mismatches.
- Conduct synthetic tests during campaigns; utilize canary deploys.
Disaster Recovery (DR)
- Multi-AZ active by default; implement a multi-region strategy.
- Active/active for stateless services; active/passive for databases with asynchronous replication.
- RPO/RTO targets: RPO ≤ 5 minutes (binlog/stream replication); RTO ≤ 30 minutes for failover.
- Regular backups, point-in-time recovery; conduct DR drills and maintain runbooks.
- Idempotent reprocessing on failover using outbox pattern and event replay.
Backfills and Reprocessing
- Store raw PSP events and our emitted events in append-only topics.
- Use idempotent consumers keyed by
donation_id/external_payment_id. - Develop backfill jobs for receipts/totals recomputation; manage versioned schemas and migration playbooks.
Extensibility
- Recurring Donations:
- Store
PaymentMethodtokens; implement a scheduler service to createPaymentIntentson a defined cadence; send email pre-notifications; design retry ladders; define proration rules for campaign windows.
- Store
- Corporate Matching:
- Develop an employer registry and matching policies; collect employer information; manage match pledge vs. funded states; implement reporting and disbursement workflows; integrate with partner APIs for verification.