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:
2025-12-27 10:25:36 +01:00
parent 292c66c623
commit 0fca762b33
20 changed files with 3309 additions and 15 deletions

View File

@@ -442,6 +442,118 @@ class StripeService:
logger.error(f"Webhook signature verification failed: {e}")
raise ValueError("Invalid webhook signature")
# =========================================================================
# SetupIntent & Payment Method Management
# =========================================================================
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
but don't charge until trial ends.
Args:
customer_id: Stripe customer ID
metadata: Optional metadata to attach
Returns:
Stripe SetupIntent object with client_secret for frontend
"""
if not self.is_configured:
raise ValueError("Stripe is not configured")
setup_intent = stripe.SetupIntent.create(
customer=customer_id,
payment_method_types=["card"],
metadata=metadata or {},
)
logger.info(f"Created SetupIntent {setup_intent.id} for customer {customer_id}")
return setup_intent
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 optionally set as default.
Args:
customer_id: Stripe customer ID
payment_method_id: Payment method ID from confirmed SetupIntent
set_as_default: Whether to set as default payment method
"""
if not self.is_configured:
raise ValueError("Stripe is not configured")
# Attach the payment method to the customer
stripe.PaymentMethod.attach(payment_method_id, customer=customer_id)
if set_as_default:
stripe.Customer.modify(
customer_id,
invoice_settings={"default_payment_method": payment_method_id},
)
logger.info(
f"Attached payment method {payment_method_id} to customer {customer_id} "
f"(default={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.
Customer must have a default payment method attached.
Card will be charged automatically after trial ends.
Args:
customer_id: Stripe customer ID (must have default payment method)
price_id: Stripe price ID for the subscription tier
trial_days: Number of trial days (default 30)
metadata: Optional metadata to attach
Returns:
Stripe Subscription object
"""
if not self.is_configured:
raise ValueError("Stripe is not configured")
subscription = stripe.Subscription.create(
customer=customer_id,
items=[{"price": price_id}],
trial_period_days=trial_days,
metadata=metadata or {},
# Use default payment method for future charges
default_payment_method=None, # Uses customer's default
)
logger.info(
f"Created subscription {subscription.id} with {trial_days}-day trial "
f"for customer {customer_id}"
)
return subscription
def get_setup_intent(self, setup_intent_id: str) -> stripe.SetupIntent:
"""Get a SetupIntent by ID."""
if not self.is_configured:
raise ValueError("Stripe is not configured")
return stripe.SetupIntent.retrieve(setup_intent_id)
# =========================================================================
# Price/Product Management
# =========================================================================