feat: add platform marketing homepage with signup flow
Implement complete marketing homepage for Wizamart targeting Letzshop vendors in Luxembourg. Includes: - Marketing homepage with hero, pricing tiers, and add-ons - 4-step signup wizard with Stripe card collection (30-day trial) - Letzshop vendor lookup for shop claiming - Platform API endpoints for pricing, vendors, and signup - Stripe SetupIntent integration for trial with card upfront - Database fields for Letzshop vendor identity tracking 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
495
docs/implementation/platform-marketing-homepage.md
Normal file
495
docs/implementation/platform-marketing-homepage.md
Normal file
@@ -0,0 +1,495 @@
|
||||
# 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/platform/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/platform/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/platform/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/platform/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/platform/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:**
|
||||
```sql
|
||||
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:**
|
||||
```sql
|
||||
ALTER TABLE vendor_subscriptions ADD COLUMN card_collected_at DATETIME;
|
||||
```
|
||||
|
||||
### Model Changes
|
||||
|
||||
**`models/database/vendor.py`:**
|
||||
```python
|
||||
# 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`:**
|
||||
```python
|
||||
# Card collection tracking
|
||||
card_collected_at = Column(DateTime(timezone=True), nullable=True)
|
||||
```
|
||||
|
||||
### Configuration Change
|
||||
|
||||
**`app/core/config.py`:**
|
||||
```python
|
||||
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`:**
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
### 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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
Reference in New Issue
Block a user