Files
orion/docs/implementation/platform-marketing-homepage.md
Samir Boulahtit fb8cb14506 refactor: rename public routes and templates to platform
Complete the public -> platform naming migration across the codebase.
This aligns with the naming convention where "platform" refers to
the marketing/public-facing pages of the platform itself.

Changes:
- Update all imports from public to platform modules
- Update template references from public/ to platform/
- Update route registrations to use platform prefix
- Update documentation to reflect new naming
- Update test files for platform API endpoints

Files affected:
- app/api/main.py - router imports
- app/modules/*/routes/*/platform.py - route definitions
- app/modules/*/templates/*/platform/ - template files
- app/modules/routes.py - route discovery
- docs/* - documentation updates
- tests/integration/api/v1/platform/ - test files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 18:49:39 +01:00

15 KiB

Platform Marketing Homepage

Overview

The Wizamart marketing homepage serves as the main public entry point for Letzshop vendors looking to use the order management platform. It provides a complete self-service signup flow with Stripe payment integration.

Target Audience: Letzshop vendors in Luxembourg seeking order management solutions.

Key Value Proposition: "Lightweight OMS for Letzshop Sellers" - Order management, inventory, and invoicing built for Luxembourg e-commerce.

Features Summary

Feature URL Description
Marketing Homepage / Hero, pricing, add-ons, vendor finder
Pricing Page /pricing Detailed tier comparison
Find Your Shop /find-shop Letzshop vendor lookup
Signup Wizard /signup 4-step registration flow
Signup Success /signup/success Welcome & next steps

Pricing Tiers

Based on docs/marketing/pricing.md:

Tier Monthly Annual Orders/mo Products Users Key Features
Essential €49 €490 100 200 1 LU invoicing, basic inventory
Professional €99 €990 500 Unlimited 3 EU VAT, warehouse locations
Business €199 €1,990 2,000 Unlimited 10 Analytics, API, automation
Enterprise €399+ Custom Unlimited Unlimited Unlimited White-label, SLA, dedicated support

Annual Discount: 2 months free (17% savings)

Trial Period: 30 days with card collection upfront (no charge until trial ends)


Add-On Products

Add-On Price Billing Description
Custom Domain €15 Annual Use your own domain (mydomain.com)
Premium SSL €49 Annual EV certificate for trust badges
Email Package (5) €5 Monthly 5 professional email addresses
Email Package (10) €9 Monthly 10 professional email addresses
Email Package (25) €19 Monthly 25 professional email addresses

Page Descriptions

1. Marketing Homepage (/)

Template: app/templates/public/homepage-wizamart.html

Sections:

  1. Hero Section

    • Headline: "Lightweight OMS for Letzshop Sellers"
    • Subheadline: Order management, inventory, and invoicing
    • CTAs: "Start Free Trial" and "Find Your Letzshop Shop"
    • Badge: "30-Day Free Trial - No Credit Card Required to Start"
  2. Pricing Section

    • 4 tier cards (Essential, Professional, Business, Enterprise)
    • Monthly/Annual toggle with savings indicator
    • Feature lists per tier
    • "Start Free Trial" buttons linked to signup
  3. Add-Ons Section

    • 3 add-on cards (Domain, SSL, Email)
    • Icon, description, and pricing for each
  4. Letzshop Vendor Finder

    • Search input for shop URL
    • Real-time lookup via API
    • "Claim This Shop" button for unclaimed vendors
  5. Final CTA Section

    • Gradient background
    • Strong call to action for trial signup

2. Pricing Page (/pricing)

Template: app/templates/public/pricing.html

Standalone page with:

  • Large tier cards
  • Monthly/Annual toggle
  • Detailed feature lists
  • Back to home link

3. Find Your Shop (/find-shop)

Template: app/templates/public/find-shop.html

  • URL input with examples
  • Real-time Letzshop vendor lookup
  • Claim button for unclaimed shops
  • Help section with alternatives

4. Signup Wizard (/signup)

Template: app/templates/public/signup.html

4-Step Flow:

Step Name Description
1 Select Plan Choose tier + billing period
2 Claim Shop Optional Letzshop connection
3 Create Account User details + company info
4 Payment Stripe card collection

URL Parameters:

  • ?tier=professional - Pre-select tier
  • ?annual=true - Pre-select annual billing
  • ?letzshop=my-shop - Pre-fill Letzshop slug

5. Signup Success (/signup/success)

Template: app/templates/public/signup-success.html

  • Success confirmation
  • Next steps checklist
  • Dashboard link
  • Support contact

API Endpoints

All endpoints under /api/v1/platform/:

Pricing Endpoints

GET /api/v1/platform/tiers
    Returns all public subscription tiers
    Response: TierResponse[]

GET /api/v1/platform/tiers/{tier_code}
    Returns specific tier by code
    Response: TierResponse

GET /api/v1/platform/addons
    Returns all active add-on products
    Response: AddOnResponse[]

GET /api/v1/platform/pricing
    Returns complete pricing info (tiers + addons + trial_days)
    Response: PricingResponse

Letzshop Vendor Endpoints

GET /api/v1/platform/letzshop-vendors
    Query params: ?search=&category=&city=&page=1&limit=20
    Returns paginated vendor list (placeholder for future)
    Response: LetzshopVendorListResponse

POST /api/v1/platform/letzshop-vendors/lookup
    Body: { "url": "letzshop.lu/vendors/my-shop" }
    Returns vendor info from URL lookup
    Response: LetzshopLookupResponse

GET /api/v1/platform/letzshop-vendors/{slug}
    Returns vendor info by slug
    Response: LetzshopVendorInfo

Signup Endpoints

POST /api/v1/platform/signup/start
    Body: { "tier_code": "professional", "is_annual": false }
    Creates signup session
    Response: { "session_id": "...", "tier_code": "...", "is_annual": false }

POST /api/v1/platform/signup/claim-vendor
    Body: { "session_id": "...", "letzshop_slug": "my-shop" }
    Claims Letzshop vendor for session
    Response: { "session_id": "...", "letzshop_slug": "...", "vendor_name": "..." }

POST /api/v1/platform/signup/create-account
    Body: {
        "session_id": "...",
        "email": "user@example.com",
        "password": "securepassword",
        "first_name": "John",
        "last_name": "Doe",
        "company_name": "My Company"
    }
    Creates User, Company, Vendor, Stripe Customer
    Response: { "session_id": "...", "user_id": 1, "vendor_id": 1, "stripe_customer_id": "cus_..." }

POST /api/v1/platform/signup/setup-payment
    Body: { "session_id": "..." }
    Creates Stripe SetupIntent
    Response: { "session_id": "...", "client_secret": "seti_...", "stripe_customer_id": "cus_..." }

POST /api/v1/platform/signup/complete
    Body: { "session_id": "...", "setup_intent_id": "seti_..." }
    Completes signup, attaches payment method
    Response: { "success": true, "vendor_code": "...", "vendor_id": 1, "redirect_url": "...", "trial_ends_at": "..." }

GET /api/v1/platform/signup/session/{session_id}
    Returns session status for resuming signup
    Response: { "session_id": "...", "step": "...", ... }

Database Schema Changes

Migration: 404b3e2d2865_add_letzshop_vendor_fields_and_trial_tracking

Vendor Table:

ALTER TABLE vendors ADD COLUMN letzshop_vendor_id VARCHAR(100) UNIQUE;
ALTER TABLE vendors ADD COLUMN letzshop_vendor_slug VARCHAR(200);
CREATE INDEX ix_vendors_letzshop_vendor_id ON vendors(letzshop_vendor_id);
CREATE INDEX ix_vendors_letzshop_vendor_slug ON vendors(letzshop_vendor_slug);

VendorSubscription Table:

ALTER TABLE vendor_subscriptions ADD COLUMN card_collected_at DATETIME;

Model Changes

models/database/vendor.py:

# Letzshop Vendor Identity
letzshop_vendor_id = Column(String(100), unique=True, nullable=True, index=True)
letzshop_vendor_slug = Column(String(200), nullable=True, index=True)

models/database/subscription.py:

# Card collection tracking
card_collected_at = Column(DateTime(timezone=True), nullable=True)

Configuration Change

app/core/config.py:

stripe_trial_days: int = 30  # Changed from 14 to 30

Stripe Integration

Trial Flow with Card Collection

The signup uses Stripe SetupIntent (not PaymentIntent) to collect card details without immediate charge:

1. User selects tier → POST /signup/start
   └── Creates signup session

2. User claims Letzshop shop (optional) → POST /signup/claim-vendor
   └── Links Letzshop vendor to session

3. User creates account → POST /signup/create-account
   ├── Creates User in database
   ├── Creates Company in database
   ├── Creates Vendor in database
   ├── Creates Stripe Customer
   └── Creates VendorSubscription (status: trial)

4. User enters card → POST /signup/setup-payment
   └── Creates Stripe SetupIntent
       └── Returns client_secret for frontend

5. Frontend confirms card → stripe.confirmCardSetup(client_secret)
   └── Validates card (no charge)

6. Signup completes → POST /signup/complete
   ├── Retrieves SetupIntent
   ├── Attaches PaymentMethod to Customer
   ├── Sets as default payment method
   ├── Records card_collected_at
   └── Subscription starts 30-day trial

7. After 30 days → Stripe automatically charges card

New StripeService Methods

app/services/stripe_service.py:

def create_setup_intent(
    self,
    customer_id: str,
    metadata: dict | None = None,
) -> stripe.SetupIntent:
    """
    Create a SetupIntent to collect card without charging.
    Used for trial signups where we collect card upfront.
    """

def attach_payment_method_to_customer(
    self,
    customer_id: str,
    payment_method_id: str,
    set_as_default: bool = True,
) -> None:
    """
    Attach a payment method to customer and set as default.
    """

def create_subscription_with_trial(
    self,
    customer_id: str,
    price_id: str,
    trial_days: int = 30,
    metadata: dict | None = None,
) -> stripe.Subscription:
    """
    Create subscription with trial period.
    Card will be charged automatically after trial ends.
    """

def get_setup_intent(self, setup_intent_id: str) -> stripe.SetupIntent:
    """Get a SetupIntent by ID."""

File Structure

app/
├── api/
│   └── v1/
│       └── platform/
│           ├── __init__.py          # Router aggregation
│           ├── pricing.py           # Tier & addon endpoints
│           ├── letzshop_vendors.py  # Vendor lookup endpoints
│           └── signup.py            # Signup flow endpoints
├── routes/
│   └── platform_pages.py            # Page routes (/, /pricing, etc.)
├── services/
│   └── stripe_service.py            # SetupIntent methods (updated)
└── templates/
    └── platform/
        ├── base.html                # Base template (Wizamart branding)
        ├── homepage-wizamart.html   # Marketing homepage
        ├── pricing.html             # Pricing page
        ├── find-shop.html           # Letzshop finder
        ├── signup.html              # Signup wizard
        └── signup-success.html      # Success page

models/database/
├── vendor.py                        # letzshop_vendor_id, slug fields
└── subscription.py                  # card_collected_at field

alembic/versions/
└── 404b3e2d2865_add_letzshop_vendor_fields_and_trial_.py

main.py                              # Platform routes registered
app/api/main.py                      # Platform API router added
app/core/config.py                   # stripe_trial_days = 30

Frontend Technology

  • Tailwind CSS - Utility-first styling
  • Alpine.js - Reactive components
  • Stripe.js - Payment form (Stripe Elements)

JavaScript Components (Embedded)

Homepage (homepageData()):

  • annual - Billing toggle state
  • shopUrl - Letzshop URL input
  • vendorResult - Lookup result
  • lookupVendor() - API call for lookup

Signup Wizard (signupWizard()):

  • currentStep - Wizard step (1-4)
  • sessionId - Backend session ID
  • selectedTier - Selected tier code
  • isAnnual - Annual billing toggle
  • letzshopUrl/Vendor - Letzshop claim
  • account - User form data
  • stripe/cardElement - Stripe integration
  • startSignup() - Step 1 submission
  • claimVendor() - Step 2 submission
  • createAccount() - Step 3 submission
  • initStripe() - Initialize Stripe Elements
  • submitPayment() - Step 4 submission

Configuration Requirements

Environment Variables

# Required for payment step
STRIPE_SECRET_KEY=sk_test_...
STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...  # For webhook handling

# Trial period (defaults to 30)
STRIPE_TRIAL_DAYS=30

Stripe Dashboard Setup

  1. Create Products for each tier (Essential, Professional, Business)
  2. Create Prices for monthly and annual billing
  3. Configure Customer Portal
  4. Set up webhook endpoint for subscription events

Testing

Automated Tests

Test files located in tests/integration/api/v1/platform/:

File Tests Description
test_pricing.py 17 Tier and add-on pricing endpoints
test_letzshop_vendors.py 22 Vendor lookup and listing endpoints
test_signup.py 28 Multi-step signup flow

Run tests:

pytest tests/integration/api/v1/platform/ -v

Test categories:

  • TestPlatformPricingAPI - GET /tiers, /addons, /pricing
  • TestLetzshopVendorLookupAPI - Vendor lookup and claiming
  • TestLetzshopSlugExtraction - URL parsing edge cases
  • TestSignupStartAPI - Signup initiation
  • TestClaimVendorAPI - Letzshop vendor claiming
  • TestCreateAccountAPI - Account creation
  • TestSetupPaymentAPI - Stripe SetupIntent
  • TestCompleteSignupAPI - Signup completion
  • TestSignupFullFlow - End-to-end flow

Manual Testing

  1. Homepage: Visit http://localhost:8000/
  2. Pricing Toggle: Click Monthly/Annual switch
  3. Vendor Lookup: Enter a Letzshop URL in finder
  4. Signup Flow:
    • Click "Start Free Trial"
    • Select tier
    • Skip or enter Letzshop URL
    • Fill account form
    • Enter test card (4242 4242 4242 4242)
    • Complete signup

Test Cards (Stripe)

Card Scenario
4242 4242 4242 4242 Success
4000 0000 0000 0002 Decline
4000 0000 0000 3220 3D Secure required

API Testing

# Get pricing
curl http://localhost:8000/api/v1/platform/pricing

# Lookup vendor
curl -X POST http://localhost:8000/api/v1/platform/letzshop-vendors/lookup \
  -H "Content-Type: application/json" \
  -d '{"url": "letzshop.lu/vendors/test-shop"}'

# Start signup
curl -X POST http://localhost:8000/api/v1/platform/signup/start \
  -H "Content-Type: application/json" \
  -d '{"tier_code": "professional", "is_annual": false}'

Future Enhancements

  1. Letzshop Vendor Cache

    • Periodic sync of Letzshop vendor directory
    • Browsable list instead of URL lookup only
  2. Email Verification

    • Verify email before trial starts
    • Confirmation email with onboarding links
  3. Referral Program

    • Affiliate/referral codes
    • Partner commission tracking
  4. A/B Testing

    • Test different pricing presentations
    • Optimize conversion rates
  5. Analytics

    • Track signup funnel drop-off
    • Monitor tier selection patterns
  6. Enterprise Contact Form

    • Lead capture for enterprise tier
    • Sales team notification