Replace all ~1,086 occurrences of Wizamart/wizamart/WIZAMART/WizaMart with Orion/orion/ORION across 184 files. This includes database identifiers, email addresses, domain references, R2 bucket names, DNS prefixes, encryption salt, Celery app name, config defaults, Docker configs, CI configs, documentation, seed data, and templates. Renames homepage-wizamart.html template to homepage-orion.html. Fixes duplicate file_pattern key in api.yaml architecture rule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
15 KiB
Platform Marketing Homepage
Overview
The Orion marketing homepage serves as the main public entry point for Letzshop stores looking to use the order management platform. It provides a complete self-service signup flow with Stripe payment integration.
Target Audience: Letzshop stores 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, store finder |
| Pricing Page | /pricing |
Detailed tier comparison |
| Find Your Shop | /find-shop |
Letzshop store 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-orion.html
Sections:
-
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"
-
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
-
Add-Ons Section
- 3 add-on cards (Domain, SSL, Email)
- Icon, description, and pricing for each
-
Letzshop Store Finder
- Search input for shop URL
- Real-time lookup via API
- "Claim This Shop" button for unclaimed stores
-
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 store 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 + merchant 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 Store Endpoints
GET /api/v1/platform/letzshop-stores
Query params: ?search=&category=&city=&page=1&limit=20
Returns paginated store list (placeholder for future)
Response: LetzshopStoreListResponse
POST /api/v1/platform/letzshop-stores/lookup
Body: { "url": "letzshop.lu/vendors/my-shop" }
Returns store info from URL lookup
Response: LetzshopLookupResponse
GET /api/v1/platform/letzshop-stores/{slug}
Returns store info by slug
Response: LetzshopStoreInfo
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-store
Body: { "session_id": "...", "letzshop_slug": "my-shop" }
Claims Letzshop store for session
Response: { "session_id": "...", "letzshop_slug": "...", "store_name": "..." }
POST /api/v1/platform/signup/create-account
Body: {
"session_id": "...",
"email": "user@example.com",
"password": "securepassword",
"first_name": "John",
"last_name": "Doe",
"merchant_name": "My Merchant"
}
Creates User, Merchant, Store, Stripe Customer
Response: { "session_id": "...", "user_id": 1, "store_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, "store_code": "...", "store_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_store_fields_and_trial_tracking
Store Table:
ALTER TABLE stores ADD COLUMN letzshop_store_id VARCHAR(100) UNIQUE;
ALTER TABLE stores ADD COLUMN letzshop_store_slug VARCHAR(200);
CREATE INDEX ix_stores_letzshop_store_id ON stores(letzshop_store_id);
CREATE INDEX ix_stores_letzshop_store_slug ON stores(letzshop_store_slug);
StoreSubscription Table:
ALTER TABLE store_subscriptions ADD COLUMN card_collected_at DATETIME;
Model Changes
models/database/store.py:
# Letzshop Store Identity
letzshop_store_id = Column(String(100), unique=True, nullable=True, index=True)
letzshop_store_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-store
└── Links Letzshop store to session
3. User creates account → POST /signup/create-account
├── Creates User in database
├── Creates Merchant in database
├── Creates Store in database
├── Creates Stripe Customer
└── Creates StoreSubscription (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_stores.py # Store 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 (Orion branding)
├── homepage-orion.html # Marketing homepage
├── pricing.html # Pricing page
├── find-shop.html # Letzshop finder
├── signup.html # Signup wizard
└── signup-success.html # Success page
models/database/
├── store.py # letzshop_store_id, slug fields
└── subscription.py # card_collected_at field
alembic/versions/
└── 404b3e2d2865_add_letzshop_store_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 stateshopUrl- Letzshop URL inputstoreResult- Lookup resultlookupStore()- API call for lookup
Signup Wizard (signupWizard()):
currentStep- Wizard step (1-4)sessionId- Backend session IDselectedTier- Selected tier codeisAnnual- Annual billing toggleletzshopUrl/Store- Letzshop claimaccount- User form datastripe/cardElement- Stripe integrationstartSignup()- Step 1 submissionclaimStore()- Step 2 submissioncreateAccount()- Step 3 submissioninitStripe()- Initialize Stripe ElementssubmitPayment()- 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
- Create Products for each tier (Essential, Professional, Business)
- Create Prices for monthly and annual billing
- Configure Customer Portal
- 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_stores.py |
22 | Store 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, /pricingTestLetzshopStoreLookupAPI- Store lookup and claimingTestLetzshopSlugExtraction- URL parsing edge casesTestSignupStartAPI- Signup initiationTestClaimStoreAPI- Letzshop store claimingTestCreateAccountAPI- Account creationTestSetupPaymentAPI- Stripe SetupIntentTestCompleteSignupAPI- Signup completionTestSignupFullFlow- End-to-end flow
Manual Testing
- Homepage: Visit
http://localhost:8000/ - Pricing Toggle: Click Monthly/Annual switch
- Store Lookup: Enter a Letzshop URL in finder
- 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 store
curl -X POST http://localhost:8000/api/v1/platform/letzshop-stores/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
-
Letzshop Store Cache
- Periodic sync of Letzshop store directory
- Browsable list instead of URL lookup only
-
Email Verification
- Verify email before trial starts
- Confirmation email with onboarding links
-
Referral Program
- Affiliate/referral codes
- Partner commission tracking
-
A/B Testing
- Test different pricing presentations
- Optimize conversion rates
-
Analytics
- Track signup funnel drop-off
- Monitor tier selection patterns
-
Enterprise Contact Form
- Lead capture for enterprise tier
- Sales team notification