VideoBlast Landing Page
VideoBlast Landing Page
Component File: src/features/fundraising/components/VideoBlastLandingPage.tsx Route: /vb/:publicUrl Access Level: Public (No authentication required) Last Updated: April 21, 2026 Status: ✅ Fully Implemented with Stripe Integration
Overview
The VideoBlast Landing Page is a public-facing video donation page that displays a personalized video message alongside a donation form. It's designed for video-based fundraising campaigns where supporters can watch a video and then make a donation. The page fetches video blast data from the database, tracks views (deduped per session), and processes donations through Stripe Checkout.
> Apr 21 2026: stripped the legacy mocked "Our Impact" / fake mission / Unsplash hero block — every donor was seeing the same fake nonprofit narrative regardless of org. The page now renders only the real video, the real org name, and the donation form. Re-introduce mission / impact stats only when there is a real per-org data source.
Implementation Status
Backend Integration
- ✅ Fetches video bomb data (plus org name) via
fetchVideoBlastPublic()SECURITY DEFINER RPC - ✅ Fetches organization name for display
- ✅ Increments view count on page load via
incrementVideoBlastViews() - ✅ Uses `createCheckoutSession()` from `src/lib/db/stripe.ts` (wraps the `create-checkout-session` Edge Function). Amount is in dollars (same contract as Donations Manager / donor portal)—not cents.
- ✅ Passes
videoBlastIdinto checkout metadata for webhook donation attribution - ✅ Handles success/cancel redirects from Stripe Checkout
- ✅ Checkout errors surface via `getPaymentInitErrorMessage` (e.g. `connect_not_ready`, `charges_not_enabled`, `demo_org_blocked`)
- ✅ Loading and error states for better UX
UI Features
Header
- Organization name display (fetched from database)
- Clean, minimal branding
Title
- Campaign title (from
video_bombs.title) - Subtitle:
Shared by {organization name}
Video Section
- Video player with controls
- Video loaded from Supabase Storage URL (public bucket)
Donation Form
- Preset Amounts: $25, $50, $100, $250, $500
- Custom Amount: Text input for other amounts
- Donation Type: One-time or monthly recurring
- Donor Info:
- Name (required)
- Email (required)
- Personal message (optional)
- Donate Button: Redirects to Stripe Checkout
Data Flow
Props Interface
interface VideoBlastLandingPageProps {
publicUrl?: string; // Can be passed as prop or from URL params
}State Management
videoBlast- Video Blast data from database (camelCase to avoid shadowing theVideoBlasttype)organizationName- Organization name for displayisLoading- Loading stateerror- Error message if video bomb not foundisProcessing- Processing state during checkoutamount- Selected preset amount or 'custom'customAmount- Custom amount input valuedonationType- 'one-time' or 'monthly'donorInfo- Name, email, message
Donation Flow
1. Visitor arrives via shared VideoBlast link (/vb/:publicUrl) 2. Page fetches video bomb data from database 3. View count is incremented (fire and forget) 4. Visitor watches video content 5. Selects donation amount (preset or custom) 6. Chooses one-time or monthly recurring 7. Enters contact information 8. Clicks "Complete Donation" 9. Redirected to Stripe Checkout with:
10. After payment, redirected back to landing page with success/cancel param 11. Toast notification shown based on result
- Amount in dollars (preset or custom; passed through to the Edge Function as `amount`, which expects USD whole/currency units — not cents)
- Organization ID and optional organization name
- Donor info (name, email, optional note as `donorNote` in metadata)
- Video Blast attribution (`videoBlastId` →
video_blast_idmetadata) - Recurring flag (monthly Checkout subscription when enabled)
- Success/cancel URLs
Stripe Integration
Checkout session parameters (client → createCheckoutSession)
Matches `src/lib/db/stripe.ts` / Edge Function contract:
{
amount: number, // Dollars (e.g. 50 for $50) — NOT cents
organizationId: string,
organizationName?: string,
donorName: string,
donorEmail: string,
donorNote?: string, // Optional message → checkout metadata
videoBlastId?: string, // Video Blast attribution (webhook updates stats)
isRecurring: boolean, // Monthly when true
successUrl: string,
cancelUrl: string,
}Routing: The Edge Function uses `resolvePaymentTargetForOrg` (Connect on ancestor chain, else `legacy_direct` under `DIRECT_STRIPE_PAYOUT_ORG_ID`). If the org cannot accept card payments, the invoke fails with `PaymentInitError` and a user-safe message from `getPaymentInitErrorMessage`.
Error Handling
| Scenario | Behavior |
|----------|----------|
| Video bomb not found | Error card: "Video Not Found" |
| Loading | Spinner with "Loading..." text |
| No amount selected | Toast: "Please select or enter a valid amount" |
| Missing contact info | Toast: "Please fill in your contact information" |
| Checkout fails (e.g. Connect not ready) | Toast: **`getPaymentInitErrorMessage`** result, or generic fallback |
| Payment success | Toast: "Thank you for your donation!" |
| Payment canceled | Toast: "Donation was canceled." |
Validation
Frontend Validations
- Amount must be a finite number, `>= $1` (Stripe rejects sub-$0.50 charges) and `<= $100,000` (fat-finger guard).
- Email must match
^[^\s@]+@[^\s@]+\.[^\s@]+$(HTML5 alone is bypassed when Stripe metadata reaches the receipt). - Name normalized via
normalizePersonNameand required non-empty. - Video Blast row must exist and be
status = 'active'.
View Counter Dedup
View increments are deduped per tab session per slug via sessionStorage[vb-viewed:{slug}]. Refreshing the page or returning from Stripe checkout in the same tab does not double-count. Cross-tab and cross-IP dedup is intentionally out of scope.
Related Documentation
- 03-VideoBlast-MANAGER.md - Create and manage VideoBlast campaigns
- 00-FUNDRAISING-HUB.md - Fundraising Hub overview
- STRIPE-SETUP-GUIDE.md - Payment processor setup
- EDGE-FUNCTIONS.md - Edge function documentation
Synced from IFMmvp-Frontend documentation: pages/fundraising/04-VIDEOBLAST-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