Donor Landing Page
Donor Landing Page
Component Files: src/features/donors/components/DonorLandingPage.tsx (layout + form), src/features/donors/components/DonorLandingEmbeddedCheckout.tsx (lazy Stripe checkout) Route: Public URL (see URL Structure below) Access Level: Public (No authentication required) Last Updated: April 13, 2026 Status: ✅ Implemented
Recent Updates (April 13, 2026)
Giving-page security hardening
- Strict org slug resolution:
get-public-donation-pageand client fallback lookup no longer fall back to "first match" when duplicate page slugs exist across organizations. Iforg_slugis missing/ambiguous/mismatched, the request fails instead of rendering the wrong organization's giving page. - Success route detection tightened: public donation success detection now accepts only known success route shapes (canonical and legacy), preventing normal pages whose slug happens to be
successfrom being misclassified as receipt pages. - Reserved slug expanded: Donor Page Builder now blocks
successas a slug to avoid collisions with dedicated success routes. - Open redirect mitigation:
return_urlis sanitized against an allowlist before navigation. Unsafe values are ignored. - Public receipt privacy:
get-donation-detailsnow requiressession_idfor public/anon requests and redacts donor identity fields unless the request carries an authenticated user token. - Portal-link abuse protection:
send-donor-magic-linknow requires authenticated caller context (org member or linked donor self), closing unauthenticated donor-email probing/spam paths. - Success CTA update: standalone success page keeps a direct "Donor Portal Login" action and no longer triggers unauthenticated magic-link sends client-side.
- Purpose scope validation: purpose IDs are now validated as org-scoped or global (not merely "exists"), both during payment initialization and webhook donation posting.
Recent Updates (April 7, 2026)
Lazy-loaded Stripe checkout (embed performance)
- What:
EmbeddedPaymentFormand the payment-step `StripeProvider` wrapper live in `DonorLandingEmbeddedCheckout.tsx`. `DonorLandingPage` imports that module with `lazyWithRetry` + `Suspense` when `showPaymentForm && paymentClientSecret` (all three layouts: embed, campaign, default). - Why: Keeps `@stripe/react-stripe-js` / `vendor-stripe` off the initial `DonorLandingPage` chunk until the donor continues to payment.
- See also: 10-PUBLIC-DONATION-PAGE.md (Performance), DEVELOPER-PLAYBOOK incident log Apr 7.
Recent Updates (Apr 2026)
Footer alignment cleanup
- The public giving-page footer now uses a stable desktop 3-column row so the copyright, tax copy, and org identity sit straight across instead of appearing slanted due to asymmetric column sizing.
- The SEO/footer branding block (
PublicFooter) remains separate below the donation-page footer content.
Recent Updates (March 25, 2026)
Recurring billing day & embed selects (scroll / iframe UX)
- Problem: Billing day dropdown (29 rows: 1–28 + last day) and long purpose lists could appear huge, clip in embedded iframes, or lose stacking when
SelectContentused a customstyle(React replaced defaultz-index/max-heightentirely). - Fix: (1)
components/ui/select.tsx— merge default and callerstyle; defaultcollisionPadding={12}; viewportoverflow-y-auto+min-h-0. (2)BillingDaySelectshared component +getOrdinalSuffixexport for success copy. (3)DonorLandingPage/DonationFormuseBillingDaySelect; purpose selects usemax-h-[min(40vh,12rem)]. (4) Embed:onOpenChangeon billing day and purposeSelecttriggers debouncedpostMessageresize so hosts that implement the listener get extra height when menus open. - Files:
select.tsx,BillingDaySelect.tsx,DonorLandingPage.tsx,DonationForm.tsx,DEVELOPER-PLAYBOOK.md§2/§24,10-PUBLIC-DONATION-PAGE.md.
Overview
The Donor Landing Page is the public-facing donation page that donors see when they visit a nonprofit's custom donation URL. It displays campaign information, progress toward goals, and a donation form that integrates with the connected payment processor (Stripe).
Recent Updates (March 13, 2026)
Proration Bug Fix + Billing Schedule UX
- P0: Proration charged partial month instead of full amount —
create-payment-intentusedproration_behavior: 'create_prorations'with a futurebilling_cycle_anchor. Stripe prorated the first invoice for the stub period (e.g. $165.78 instead of $206.10 for a $200+fee subscription created Mar 13 with anchor Apr 8). Donors expected the full amount. Fixed: changed toproration_behavior: 'none'— first invoice charges the full monthly amount immediately, future invoices shift to the anchor date. NEVER usecreate_prorationsfor donation subscriptions. - P0: `calculateNextAnchorTimestamp` used server-local time — Edge function runtime timezone could drift. Fixed to use explicit UTC (
Date.UTC()) and return both Unix timestamp and ISO date string for the frontend. - P1: Success screens now show billing schedule — Standalone and embed success screens display "Next charge: April 8, 2026, then the 8th of each month" for recurring donations.
nextChargeDateandbillingAnchorDayadded toPaymentIntentResponseandPaymentIntentResult. - P1: `DonationSuccess.tsx` shows billing day — Standalone success page shows "Billed on the 8th of each month" when
billingAnchorDayis available fromget-donation-details. - P2: Ordinal suffixes fixed — Inline ternary
day === 1 ? '1st' : day === 2 ? '2nd' : day === 3 ? '3rd' : ${day}thproduced "21th", "22th", "23th". Replaced withgetOrdinalSuffix()helper that handles 11th–13th special cases and all -1st/-2nd/-3rd suffixes. Fixed in all 4 billing day selectors (3 in DonorLandingPage, 1 in DonationForm). - P2: `handleInvoicePaid` base_amount/processing_fee mismatch — Subscription metadata stores per-cycle constants (base=200, fee=6.10). If the invoice amount differed from the cycle total (prorated legacy invoices), the donation record had
amount: 165.78butbase_amount: 200. Fixed: if invoice amount doesn't match metadata total, derive base/fee proportionally from the actual amount. - P2: `billingAnchorDay` wired through `get-donation-details` — Edge function now reads
billing_anchor_dayfrom Stripe session metadata and includes it in response.DonationDetailsinterface updated in both EF and frontend.
Earlier March 13 — Billing Day Selector Overhaul
- P0: Billing day was never enforced —
create-payment-intentstored the donor's selected billing day in Stripe metadata but never setbilling_cycle_anchoron the subscription. Fixed: now appliesbilling_cycle_anchorviacalculateNextAnchorTimestamp(). - P1: Replaced 3-button picker with day-of-month Select — The old "1st of Month / Mid-Month / End of Month" 3-button layout was replaced with a
<Select>dropdown offering days 1–28 plus "Last day of each month" (value 31). Applied to all 3 render paths inDonorLandingPage.tsx(embed, campaign, default) andDonationForm.tsx. - P1: Self-service donation amount change — Donors can now change their recurring donation amount from the Donor Portal without opening the Stripe Customer Portal. New "Change Amount" button on the subscription card opens an inline form. Calls
manage-stripe-subscriptionEF withaction: 'update_amount'. Self-service auth added to the EF (matchingcreate-setup-intentpattern).
Files Modified (Proration Fix)
supabase/functions/create-payment-intent/index.ts—proration_behavior: 'none', UTC timestamps,billingAnchorDay/nextChargeDatein responsesupabase/functions/stripe-webhook/index.ts—handleInvoicePaidproportional base/fee derivationsupabase/functions/get-donation-details/index.ts—billingAnchorDayin response interface + wiringsrc/lib/db/stripe.ts—billingAnchorDay/nextChargeDateinPaymentIntentResultsrc/features/donors/components/DonorLandingPage.tsx—getOrdinalSuffix()helper,formatNextChargeDate(), billing schedule on success screens,nextChargeDatestatesrc/features/donors/components/DonationSuccess.tsx—billingAnchorDayin interface + displaysrc/components/shared/DonationForm.tsx— Fixed ordinal suffix
Files Modified (Earlier Billing Day Overhaul)
supabase/functions/create-payment-intent/index.ts— AddedcalculateNextAnchorTimestamp(), appliedbilling_cycle_anchorsupabase/functions/manage-stripe-subscription/index.ts— Added self-service auth (donor_users check,update_amountonly for self-service)src/features/donors/components/DonorLandingPage.tsx— Replaced 3× billing day 3-button pickers with Select dropdowns (1–28 + last day of month)src/components/shared/DonationForm.tsx— Same Select replacement + added Select component importssrc/features/donors/components/DonorPortal.tsx— Added "Change Amount" button, inline form,handleChangeAmounthandler,manageStripeSubscriptionimport
Earlier Updates (March 12, 2026)
Donation Success Page Overhaul
- P0: Contact Us footer empty —
contactEmailwas never wired through the data pipeline. Theorganizationstable has noemailcolumn — admin email is now fetched fromorganization_users(role=parent_org) +userstable, with parent org inheritance for child funds. Wired throughPublicDonationPage.tsxconfig builder andget-donation-detailsedge function. - P0: Confusing "Your Impact" stats removed — The inline success screen showed "Contributed: $1.00" and "Total Donors: 1" which simply echoed back the donation amount + incremented the page's donor count. This was misleading (appeared to be the donor's profile stats) and was not populated from the donor's actual giving history. Removed entirely — unauthenticated donors should not see aggregate stats that could be confused with their personal data.
- P0: `DonationSuccess.tsx` standalone had no org contact info — Was using generic
PublicFooter("Powered by Alignmint") with zero org details. Now renders full 3-column org footer (org name, Contact Us with email/phone/address, Tax Information with EIN) matching theDonorLandingPageinline success footer pattern. - P1: Donor Portal login button added — "Access Donor Portal" button on success page calls
send-donor-magic-linkedge function with donor's email + org ID. Shows loading/sent states. Lets donors immediately access their giving history, tax receipts, and recurring management. - P1: Recurring-specific content — Recurring donations now show "You can manage or cancel your recurring donation anytime from your email receipt or by contacting {contactEmail}." One-time donations don't show this.
- P1: `history.back()` fallback fixed — "Make Another Donation" / "Go Back" now checks
window.history.length > 1before callinghistory.back(). Falls back togetalignmint.orgfor direct-navigation users. - P2: `taxDeductible` no longer hardcoded —
get-donation-detailsedge function now readsis_taxablefrom the donation record instead of always returningtrue. - P2: Tax ID shown in footer — EIN from org (with parent inheritance) displayed in footer Tax Information section.
- P2: `fadeInBlurStyle` added — Standalone success page now uses fade-in animation matching other public pages.
- P3: Contact links clickable — Email uses
mailto:, phone usestel:links with hover effects. - P3: Removed unused `Target` import from
DonorLandingPage.tsx(was only used in removed Impact Recap).
Files Modified
supabase/functions/get-donation-details/index.ts— AddedcontactEmail,contactPhone,mailingAddress,taxIdto response; readsis_taxablefrom donation; fetches org contact info with parent inheritance; fetches admin email viaorganization_userssrc/features/donors/components/DonationSuccess.tsx— Full rewrite: org contact footer, portal login button, recurring text, history.back() fix, fadeInBlurStyle, removed confusing impact statssrc/features/donors/components/DonorLandingPage.tsx— Removed "Your Impact" stats section from inline success; fixed Contact Us footer to only render when contact info exists; added mailto/tel links; removed unusedTargetimportsrc/features/events/components/PublicDonationPage.tsx— WiredcontactEmailfrom org admin user into config
Earlier Updates (March 9, 2026)
Embed Iframe Blocking Fix
- P0: `ERR_BLOCKED_BY_RESPONSE` resolved — Vercel applies ALL matching header rules (not first-match). Two rules with
source: "/(.*)"sent conflictingX-Frame-Options: ALLOWALL+DENYsimultaneously. Browsers reject conflicting values per RFC 7034. Fix: replaced query-based?embed=truedetection with host-based mutual exclusion —has: "host" = "donate.alignmint.app"on permissive rule,missing: "host"on DENY rule. Only ONE rule fires per request. Alldonate.alignmint.apppages are now always embeddable without requiring?embed=true - Public org key migration —
DonorPageManagerand embed generators now preferorganizations.public_org_keyfor newly emitted links while keeping legacy 6-character UUID-prefix URLs readable during the migration window. - Currency formatting — Replaced ~20 hardcoded
$symbols withformatCurrency()/formatCurrencyStatic()across all 3 render paths (embed, standalone success, campaign/default) - Theme compliance — Replaced hardcoded
#ec4899hex color withtext-primaryclass (§19)
Earlier Updates (February 11, 2026)
Embed Layout Customization
- Configurable form width — New
maxWidthURL parameter allows embeds to expand beyond the previous 448px card limit - Banner/hero image support — Embed mode now renders the hero image as a banner above the card when available; controlled via
showBannerparameter - Logo sizing — New
logoSizeURL parameter (sm,md,lg) controls nonprofit logo display size in embed mode - Default iframe max-width increased — Generated embed code now defaults to
700px(was600px) - Dynamic card width — Card
max-widthis now driven by themaxWidthURL parameter instead of hardcodedmax-w-md
Inline Payment for Embed Mode
- All modes now use PaymentIntent + PaymentElement (inline checkout)
- Removed Stripe Checkout redirect — embed mode no longer opens a new tab
- Fixes popup blocker issue — browsers were blocking
window.openfrom within iframes - Compact embed success state — success confirmation renders inline within the iframe
- Updated iframe permissions —
allow="payment *; publickey-credentials-get *"for 3D Secure support - `showSuccess` gated for embed — standalone success page only renders in non-embed mode
Earlier Updates (February 2, 2026)
Payment Flow Clarification
- Standard pages use PaymentIntent + PaymentElement (inline checkout)
- Embed mode previously used Stripe Checkout (opened in a new tab — now removed)
Earlier Updates (January 2026)
Purpose Selector (Jan 31, 2026)
- Fund Earmarking: Donors can now select a specific purpose/fund for their donation
- Dynamic Dropdown: Purpose list fetched from
purposestable, filtered by organization - Database Integration: Selected
purpose_idstored in donation record for reporting - Metadata Flow: Purpose ID passed through payment metadata to webhook
- Recurring Support: Purpose selection works for both one-time and recurring donations
Embed Scroll Fix (Jan 29, 2026)
- Scroll Prevention: Fixed issue where embedded donation forms would scroll when mouse moved over them
- Debounced Height Updates: Height communication to parent window now debounced to prevent jitter
- Updated Embed Code: Generated embed code now includes
scrolling="no"andoverflow: hidden - Stable Measurement: Uses
data-embed-contentmarker for accurate height calculation
Tax Compliance Support (Jan 22, 2026)
- Transaction Type Selection: Pages can now be configured as Donation (tax-deductible), Service, Product, or Fee
- Sales Tax Collection: Non-donation transaction types can enable Stripe Tax for automatic sales tax calculation
- Dynamic Checkout: Payment flow passes
transactionTypeandcollectSalesTaxto Edge Function - Stripe Tax Integration: When enabled, Stripe automatically calculates and collects applicable sales tax
Phase 2 Improvements - Embed Theming (Jan 19, 2026)
- Neutral Embed Styling: Embedded donation forms no longer force the organic theme
- Custom Color Support: Embeds can be themed via URL parameters to match host page styling
- Transparent Background: Embed background is transparent by default for seamless integration
- URL Parameters for Theming:
primary,bg,fg,cardBg,btnText(hex codes without #)
Phase 1 Improvements - Conversion Optimization
- Organic Theme Enforcement: Standalone public pages display in organic theme via
data-theme="organic"wrapper - FAQ Section: Collapsible accordion with 6 essential donor questions (tax-deductible, security, receipts, etc.)
- Enhanced Trust Indicators: "Give With Confidence" badges (Secure, Tax Receipt, 100% Impact)
- Payment Method Display: Shows accepted cards (Visa, MC, Amex, Discover) and digital wallets (Apple Pay, Google Pay)
- Improved Fee Messaging: Dynamic messaging when "cover fees" is checked, showing gratitude and impact
URL Structure
Donation pages are accessed via public URLs:
- Subdomain:
https://{nonprofit-slug}.donate.alignmint.org/{page-slug} - Path-based:
https://donate.alignmint.org/{nonprofit-slug}/{page-slug}
Examples:
https://awakenings.donate.alignmint.org/annual-gala-2025https://donate.alignmint.org/awakenings/annual-gala-2025
UI Features
Header
- Nonprofit logo and name
- "Secure Donation" badge with shield icon
- Sticky on scroll
Hero Section
- Full-width hero image from campaign configuration
- Optional hero video (from VideoBlast library or direct upload)
- If both image and video provided, video takes priority
- Campaign title and description overlay
- Gradient overlay for text readability
Campaign Progress Card
- Amount raised vs goal
- Progress bar with percentage
- Donor count
- Days remaining (if applicable)
Donation Form
- Suggested Amounts: Configurable preset buttons (sorted highest first)
- Custom Amount: Optional text input for custom donations
- Purpose Selector: Dropdown to designate gift purpose/fund (populated from purposes table)
- Recurring Toggle: Monthly recurring option (if enabled)
- Cover Processing Fee: Enhanced checkbox with dynamic messaging
- Shows calculated fee amount when donation amount is entered
- When checked: Thanks donor and confirms 100% impact message
- When unchecked: Explains what processing fees are
- Fee calculation: 2.9% + $0.30 per transaction
- Donor Info: First name, last name, email
- Donate Button: Triggers payment processor checkout
FAQ Section
Collapsible accordion answering common donor questions:
- Is my donation tax-deductible?
- How will my donation be used?
- Is my payment information secure?
- Will I receive a receipt?
- Can I change or cancel my recurring donation? (conditional)
- Does my employer match donations?
Trust Indicators
- Give With Confidence section with icon badges:
- 🔒 Secure - 256-bit encryption
- ✅ Tax Receipt - Sent immediately
- 🏆 100% Impact - Direct to mission
- Payment method badges (Visa, Mastercard, Amex, Discover)
- Digital wallet support indicators (Apple Pay, Google Pay)
Success Screen
- Thank you message (configurable)
- Donation amount confirmation
- Receipt email confirmation
- "Make Another Donation" button
Data Requirements
DonorLandingPageConfig Interface
interface DonorLandingPageConfig {
id: string;
slug: string;
nonprofitSlug: string;
nonprofitName: string;
nonprofitLogo?: string;
title: string;
description: string;
heroImage: string;
heroVideo?: string; // Optional video URL (YouTube/Vimeo embed or VideoBlast)
goalAmount: number;
raisedAmount: number;
donorCount: number;
daysRemaining?: number;
suggestedAmounts: number[];
allowCustomAmount: boolean;
allowRecurring: boolean;
thankYouMessage: string;
taxId?: string;
paymentProcessor: {
connected: boolean;
processor: 'stripe' | 'paypal' | 'square' | null;
processorName: string;
};
// Tax compliance fields (added Jan 2026)
transactionType?: 'donation' | 'service' | 'product' | 'fee';
collectSalesTax?: boolean;
}Payment Flow
Standard Mode (Standalone Page)
1. Donor selects amount and enters info 2. Clicks "Donate Now" 3. System calls create-payment-intent Edge Function 4. Stripe PaymentElement renders inline 5. On success, payment_intent.succeeded webhook creates donation + journal entry 6. Donor sees inline success screen
Embed Mode (iframe)
1. Donor fills out embedded form 2. Clicks "Donate Now" 3. System calls create-payment-intent Edge Function (same as standard mode) 4. Stripe PaymentElement renders inline within the iframe 5. On success, payment_intent.succeeded webhook creates donation + journal entry 6. Compact success confirmation renders inline within the iframe
API Endpoints
# Standard flow (PaymentElement)
POST /functions/v1/create-payment-intent
{
amount: number,
coverFee: boolean,
donorEmail: string,
donorName: string,
organizationId: string,
purposeId?: string,
isRecurring: boolean,
recurringFrequency?: string
}
# Embed flow (now uses same endpoint as standard)
POST /functions/v1/create-payment-intent
{
amount: number,
coverFee: boolean,
donorEmail: string,
donorName: string,
organizationId: string,
purposeId?: string,
isRecurring: boolean
}State Management
Local State
selectedAmount- Currently selected preset amountcustomAmount- Custom amount input valueisRecurring- Monthly recurring togglecoverFee- Cover processing fee toggle (default: true)selectedPurposeId- Selected purpose/fund for donation (from purposes table)purposes- List of available purposes for the organizationisProcessing- Payment processing stateshowSuccess- Success screen visibilitydonorInfo- First name, last name, email
Business Rules
- Suggested amounts display in descending order (highest first)
- Custom amount only available if
allowCustomAmountis true - Recurring option only shown if
allowRecurringis true - Cover processing fee checkbox always shown (default: checked)
- Processing fee = (amount × 2.9%) + $0.30
- Total donation = base amount + processing fee (if coverFee is true)
- Email is required for all donations
- Payment processor must be connected for donations to work
- Tax ID displayed if provided
Error Handling
| Scenario | Behavior |
|----------|----------|
| No amount selected | Toast: "Please select or enter a donation amount" |
| No email entered | Toast: "Please enter your email address" |
| Payment processor not connected | Toast: "Payment processing is not available" |
| Checkout creation fails | Toast with error message |
Security
- HTTPS required
- No sensitive payment data stored locally
- All payment processing via Stripe (PaymentElement/Checkout APIs, PCI compliant)
- Secure donation badge displayed
return_urlvalues are sanitized and allowlisted before navigation- Additional trusted return origins can be configured via
VITE_ALLOWED_PUBLIC_RETURN_ORIGINS(comma-separated) - Public receipt lookup requires
session_id; unauthenticated requests receive redacted donor fields
Embed Mode
When accessed with ?embed=true, the donation form renders in a minimal layout suitable for iframe embedding.
Embed Features
- No header/footer - Minimal UI for seamless integration
- Transparent background - Blends with host page
- Scroll prevention - Embedded form does not scroll internally (fixed Jan 2026)
- Dynamic height - Sends debounced postMessage for iframe resizing
- Custom theming - Colors can be customized via URL parameters
- Inline PaymentElement - Stripe payment form renders directly within the iframe (no redirect)
- Banner image - Hero image renders as a banner above the card (configurable via
showBanner) - Configurable width - Form card max-width adjustable via
maxWidthURL parameter - Logo sizing - Nonprofit logo size adjustable via
logoSizeURL parameter (sm/md/lg)
Scroll Prevention (Jan 2026 Fix)
The embed mode includes multiple layers of scroll prevention to ensure the donation form integrates seamlessly without unwanted scrolling:
1. CSS overflow hidden - Applied to html, body, and container elements 2. iframe scrolling="no" - Disabled in generated embed code 3. Debounced height updates - Prevents height "bouncing" that can cause scroll jitter 4. Stable height measurement - Uses data-embed-content marker for accurate sizing
Payment Flow in Embed Mode
Embed mode uses the same inline PaymentElement as standalone mode:
1. Donor fills out amount and info in the embedded form 2. Clicks "Donate Now" 3. System calls create-payment-intent and receives a clientSecret 4. Stripe PaymentElement renders inline within the iframe card 5. Donor completes payment without leaving the page 6. Compact success confirmation displays inline
> Note: The iframe must include allow="payment *; publickey-credentials-get *" for Stripe's PaymentElement and 3D Secure to work correctly. The generated embed code includes this automatically.
Custom Theming Parameters
Add these parameters to the embed URL to match your website's theme. All colors are hex codes without the # symbol.
| Parameter | Description | Example |
|-----------|-------------|---------|
| `primary` | Button and accent color | `primary=0066cc` |
| `bg` | Background color | `bg=ffffff` |
| `fg` | Text color | `fg=333333` |
| `cardBg` | Card background color | `cardBg=f5f5f5` |
| `btnText` | Button text color | `btnText=ffffff` |
Layout Customization Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `maxWidth` | number (px) | `448` (28rem) | Maximum width of the form card |
| `logoSize` | `sm` / `md` / `lg` | `sm` | Logo display size (24px / 40px / 56px) |
| `showBanner` | `true` / `false` | `true` (if hero image exists) | Show hero image as banner above the card |
Example Embed URL with Custom Colors
https://donate.alignmint.app/abc123/nonprofit/donate?embed=true&primary=0066cc&bg=f5f5f5&fg=333333Example Embed URL with Layout Customization
https://donate.alignmint.app/abc123/nonprofit/donate?embed=true&maxWidth=800&logoSize=lg&showBanner=trueDefault Behavior (No Color Parameters)
If no color parameters are provided, the embed uses neutral styling:
- Background: Transparent
- Card: White (#ffffff)
- Text: Dark gray (#1f2937)
- Primary/Buttons: Gray (#374151)
- Card max-width: 448px (28rem)
- Logo size: Small (24px)
- Banner: Shown if hero image exists
This neutral styling works well on most light-themed websites.
Related Documentation
- 03-DONOR-PAGE-BUILDER.md - Create/edit donation pages
- 05-DONOR-PAGE-MANAGER.md - Manage all donation pages
- 06-DONOR-PAGE-PREVIEW.md - Preview before publishing
- backend/shared/07-DONOR-LANDING-PAGES.md - Historical backend specification
- STRIPE-SETUP-GUIDE.md - Payment processor setup
Synced from IFMmvp-Frontend documentation: pages/donor-hub/08-DONOR-LANDING-PAGE.md
Ready to Get Started?
See how Alignmint can simplify your nonprofit's operations. Schedule a free demo with our team and we'll walk you through everything.
Questions? Email us at steven@getalignmint.org