-
Notifications
You must be signed in to change notification settings - Fork 0
Home
That's a working title
Description: A straightforward mobile webpage for adventure services targeted at people on a city-trip.
- Landing/Home Page - Introduces the service, shows value proposition, displays pricing
- City Selection Page - Users choose their destination city
- Questionnaire Page - Collects user preferences (budget, activity level, time, experience type)
- Payment Page - Handles the per-city payment
- Results/Activities Page - Displays the curated list of activities
- Activity Detail Page (optional but recommended) - Shows detailed information about a specific activity
- About/How It Works - Explains the service
- FAQ Page - Common questions
- Terms & Privacy - Legal requirements
Option A (Recommended - Payment Before Results):
Landing → City Selection → Questionnaire → Payment → Results
Why this works best:
- Users are invested after filling out the questionnaire
- They've spent time on preferences, creating commitment
- Clear value proposition before payment (they know what they're getting)
- Prevents free browsing of curated content
Option B (Alternative - Preview First):
Landing → City Selection → Questionnaire → Preview (3 activities) → Payment → Full Results
Benefits: Shows sample quality before payment Builds trust Higher conversion for hesitant users
Option A with these enhancements:
- Show pricing clearly on the landing page
- Remind users of the price on the city selection page
- After questionnaire completion, show a summary screen: "Your personalized guide for [City] is ready! Unlock [X] curated activities for $[price]"
- This creates anticipation and justifies the payment
- City
- Budget - Ranges:
$, $ $, $$$, $$$$ or specific amounts - Activity Level - Low/Moderate/High or Relaxed/Balanced/Active
- Available Time - Half day/Full day/Multiple days or specific hours
- Experience Type - Cultural/Nature/Entertainment/Food/Shopping/Nightlife/Adventure
- Travel Companions - Solo/Couple/Family with kids/Friends group
This significantly affects recommendations
- Dietary Restrictions (if food is involved) - None/Vegetarian/Vegan/Gluten-free/Halal/Kosher
- Mobility Considerations - Fully mobile/Prefer easy access/Wheelchair accessible needed
- Interests (multi-select) - Art/History/Music/Sports/Photography/Local cuisine/Shopping/Wellness
- Pace Preference - Packed schedule/Balanced/Leisurely with downtime
- Previously Visited? - First time/Been
- Weather Preference - Indoor/Outdoor/Mix/No preference
Each activity card should include
- Hero Image - Eye-catching photo/visual
- Activity Name - Clear, descriptive title
- Quick Stats Bar:
- Duration (e.g., "2-3 hours")
- Price range ($$$)
- Activity level icon (walking shoe with intensity)
- Best time (morning/afternoon/evening icon)
- Short Description - 2-3 sentences max
- Why We Picked This - Personalized note based on their preferences
- Category Tags - Cultural, Outdoor, etc.
- "View Details" button
- By Time of Day - Morning activities, Afternoon, Evening
- By Priority - "Must-Do", "Highly Recommended", "If You Have Time"
- By Category - Group similar experiences together
- By Location - Neighborhood/district grouping
- Save/Favorite - Let users mark itineraries they're interested in
- Map View Toggle - Show activities on a map
- Filter/Sort - By price, duration, category, distance
- Multiple photos
- Full description
- Exact address with map
- Opening hours
- Estimated cost
- How to get there (transport options)
- Insider tips
- What to bring/wear
- Booking information (if needed)
- Similar activities
- Use vertical scrolling (natural for mobile)
- Large, tappable cards
- Sticky header with city name and filter button
- Bottom navigation for easy thumb access
- Swipeable image galleries
- One-tap actions (save, share, get directions)
- Use Stripe Checkout (recommended for simplicity) or Stripe Payment Element for custom UI
- For per-city payments, create products in Stripe for each city or use dynamic pricing
- Consider using Stripe Payment Links for the simplest implementation
- Products & Prices: Create a product for each city or a single product with variable pricing
- Payment Intents: For custom checkout flows with more control
- Webhooks: Critical for handling payment confirmations securely
- Customer Objects: Store customer data for repeat purchases
- Never process payments client-side only - always verify on the backend
- Use Stripe's webhook signatures to verify events
- Store Stripe customer IDs and payment IDs, not card details
- Implement idempotency keys to prevent duplicate charges
User completes questionnaire -> Create Stripe Checkout Session (server-side via Supabase Edge Function) -> Redirect to Stripe Checkout -> User completes payment -> Stripe webhook notifies your backend -> Update Supabase database with payment status -> Redirect user to results page -> Verify payment status before showing activities
- users table
- id (uuid, primary key)
- email (text, unique)
- created_at (timestamp)
- stripe_customer_id (text, nullable)
- cities table
- id (uuid, primary key)
- name (text)
- slug (text, unique)
- price (numeric)
- is_active (boolean)
- created_at (timestamp)
- questionnaires table
- id (uuid, primary key)
- user_id (uuid, foreign key to users)
- city_id (uuid, foreign key to cities)
- budget (text)
- activity_level (text)
- available_time (text)
- experience_types (text[] or jsonb)
- travel_companions (text, nullable)
- interests (text[] or jsonb)
- created_at (timestamp)
- purchases table (Critical!)
- id (uuid, primary key)
- user_id (uuid, foreign key to users)
- city_id (uuid, foreign key to cities)
- questionnaire_id (uuid, foreign key to questionnaires)
- stripe_payment_intent_id (text, unique)
- stripe_checkout_session_id (text, nullable)
- amount (numeric)
- currency (text)
- status (text) - 'pending', 'completed', 'failed', 'refunded'
- paid_at (timestamp, nullable)
- created_at (timestamp)
- activities table
- id (uuid, primary key)
- city_id (uuid, foreign key to cities)
- name (text)
- description (text)
- short_description (text)
- category (text)
- budget_level (text) - '$', '$$', '$$$', '$$$$'
- activity_level (text) - 'low', 'moderate', 'high'
- duration_hours (numeric)
- best_time_of_day (text[])
- tags (text[] or jsonb)
- image_url (text)
- location (jsonb) - {address, lat, lng, neighborhood}
- is_active (boolean)
- priority_score (integer) - for ranking
- created_at (timestamp)
- user_saved_activities table (Optional but recommended)
- id (uuid, primary key)
- user_id (uuid, foreign key to users)
- activity_id (uuid, foreign key to activities)
- purchase_id (uuid, foreign key to purchases)
- created_at (timestamp)
- UNIQUE(user_id, activity_id, purchase_id)
- Access Control with Row Level Security (RLS)
-- Users can only see their own purchases CREATE POLICY "Users can view own purchases" ON purchases FOR SELECT USING (auth.uid() = user_id);
-- Users can only see activities for cities they've purchased CREATE POLICY "Users can view purchased city activities" ON activities FOR SELECT USING ( city_id IN ( SELECT city_id FROM purchases WHERE user_id = auth.uid() AND status = 'completed' ) );
- Supabase Edge Functions Needed
create-checkout-session - Creates Stripe checkout
// Called from frontend after questionnaire completion // Returns Stripe Checkout URL // Stores pending purchase in database
stripe-webhook - Handles Stripe events
// Listens for payment_intent.succeeded // Updates purchase status to 'completed' // Triggers any post-payment logic
get-activities - Returns curated activities
// Verifies user has paid for city // Filters activities based on questionnaire responses // Returns personalized activity list
- Payment Verification Flow
Always verify payment status server-side before showing activities Check purchases table for status = 'completed' AND city_id matches Don't rely on client-side state alone
- Handling Edge Cases
Duplicate payments: Use Stripe's idempotency keys Abandoned checkouts: Set expiration on checkout sessions (default 24 hours) Refunds: Update purchase status, consider time-based access revocation Multiple purchases: Allow users to purchase multiple cities, track separately
- Data Privacy & Compliance
Store minimal user data (email only if needed) Don't store payment card details (Stripe handles this) Implement data retention policies Add terms acceptance tracking in purchases table
- Performance Optimization
Index foreign keys (user_id, city_id, stripe_payment_intent_id) Cache activity results for common questionnaire combinations Use Supabase's built-in caching for frequently accessed data
- Testing Strategy
Use Stripe's test mode with test cards Create test purchases in Supabase Test webhook handling with Stripe CLI Verify RLS policies prevent unauthorized access