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:
@@ -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
|
||||
# =========================================================================
|
||||
|
||||
Reference in New Issue
Block a user