Running a campaign
Compliance & security
Canadian political fundraising has rules. TesterDonate gives your team tools that enforce the ones a platform can — caps, eligibility gates, contribution-time records — at the database, the API, and the UI. Compliance responsibility stays with your campaign and its official agent; our job is making that job easier.
Canadian compliance
Per-jurisdiction contribution caps
Configurable per page. The donation intent route aggregates the donor's year-to-date giving to your tenant and rejects any donation that would push them over the applicable cap. Monthly subscriptions are checked against the 12-month projection so a $250/month sign-up can't silently exceed a yearly limit.
Corporate-donation rules per jurisdiction
Some jurisdictions permit corporate donations, some prohibit them outright. Configure each page with its jurisdiction's rules and the platform enforces them — the donor form shows or hides the "Corporate" donor-type option accordingly, and the API rejects mismatched submissions server-side.
Citizenship attestation
Per-page configuration. When required, the donate form gates submission until the donor checks the attestation; the API double-checks server-side. Attestation flag persists on the donor row and the donation row for audit.
Contribution records + exports
Every donation snapshots the donor's details as they were at contribution time — name, address, employer, attestation — so your records reflect what the regulator audits. Donors get an automatic confirmation email (your official agent still issues official receipts through your usual process), and CSV exports carry the full contribution-time record for preparing your filings.
Money handling
Stripe Connect — your account, your money
Each tenant connects their own Stripe account via OAuth. Donations settle directly to the tenant's balance; the platform takes its fee via Stripe's native application-fee mechanism. No escrow, no 14-day holds, no platform-aggregator headaches.
Application-fee transparency
The platform fee is computed on the donor-charged amount (including the cover-fees uplift if any) and itemized on the Stripe balance. Treasurers reconcile cleanly.
Disputes + refunds
Webhook-driven status updates so the dispute, refund, and net- contribution math stay accurate in the admin UI without manual intervention. The legally-defensible "net contribution" is computed as gross − succeeded refunds − lost disputes.
Stripe Link disabled by design
Link auto-fills cards across merchants, which would undermine the donor-of-record provenance Elections Canada requires. We force Stripe's PaymentElement to omit Link from the options.
Security & data isolation
Postgres row-level security
Every tenant-scoped table has RLS policies that gate reads and writes by
tenant_id = current_user_tenant_id(). Even a bug in our UI can't leak data across tenants — the database itself refuses to return the rows.Per-tenant verified sending domains
Resend-verified DKIM + SPF per tenant. Account confirmations, receipts, and admin invites all send from the tenant's branded domain. Falls back to the platform domain only when a tenant's domain isn't yet verified.
Rate limiting
Upstash Redis-backed sliding-window limiters on auth callbacks, checkout intents, password resets, receipt re-sends. Donor-scoped for the donor portal so a shared-wifi roommate can't eat another donor's receipt-resend allowance.
IP hashing
Donor IP addresses are HMAC-hashed with a peppered key before storage. We can still detect abuse patterns; we can't hand out IPs to anyone (including ourselves) without the pepper.
Auth & sessions
Supabase Auth with custom email hook
Account signup, magic links, password resets, email change — all go through Supabase Auth but the email itself is rendered + sent by TesterDonate via Resend with the tenant's branding.
Anti-email-scanner confirmation
Confirmation URLs land on a click-to-continue page. The actual token verification happens on POST when a human clicks. Email scanners (Microsoft Defender, Gmail safe-browsing) GET but don't POST, so they can't burn single-use tokens before donors arrive.
Sentry error monitoring
Production errors stream to Sentry with tenant context, user context, and route metadata. Issues that affect money — Stripe failures, RLS rejections — escalate.