docs: add consolidated dev URL reference and migrate /shop to /storefront
Some checks failed
CI / ruff (push) Successful in 10s
CI / validate (push) Has been cancelled
CI / dependency-scanning (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled
CI / pytest (push) Has been cancelled

- Add Development URL Quick Reference section to url-routing overview
  with all login URLs, entry points, and full examples
- Replace /shop/ path segments with /storefront/ across 50 docs files
- Update file references: shop_pages.py → storefront_pages.py,
  templates/shop/ → templates/storefront/, api/v1/shop/ → api/v1/storefront/
- Preserve domain references (orion.shop) and /store/ staff dashboard paths
- Archive docs left unchanged (historical)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-25 13:23:44 +01:00
parent 3df75e2e78
commit d648c921b7
50 changed files with 1104 additions and 1049 deletions

View File

@@ -215,7 +215,7 @@ COPY . .
# Build Tailwind CSS
RUN tailwindcss -i ./static/admin/css/tailwind.css -o ./static/admin/css/tailwind.output.css --minify \
&& tailwindcss -i ./static/store/css/tailwind.css -o ./static/store/css/tailwind.output.css --minify \
&& tailwindcss -i ./static/shop/css/tailwind.css -o ./static/shop/css/tailwind.output.css --minify \
&& tailwindcss -i ./static/storefront/css/tailwind.css -o ./static/storefront/css/tailwind.output.css --minify \
&& tailwindcss -i ./static/public/css/tailwind.css -o ./static/public/css/tailwind.output.css --minify
# Create non-root user

View File

@@ -27,22 +27,22 @@ class StorePaymentConfig(Base, TimestampMixin):
id = Column(Integer, primary_key=True, index=True)
store_id = Column(Integer, ForeignKey("stores.id"), nullable=False, unique=True)
# Stripe Connect configuration
stripe_account_id = Column(String(255)) # Stripe Connect account ID
stripe_account_status = Column(String(50)) # pending, active, restricted, inactive
stripe_onboarding_url = Column(Text) # Onboarding link for store
stripe_dashboard_url = Column(Text) # Store's Stripe dashboard
# Payment settings
accepts_payments = Column(Boolean, default=False)
currency = Column(String(3), default="EUR")
platform_fee_percentage = Column(Numeric(5, 2), default=2.5) # Platform commission
# Payout settings
payout_schedule = Column(String(20), default="weekly") # daily, weekly, monthly
minimum_payout = Column(Numeric(10, 2), default=20.00)
# Relationships
store = relationship("Store", back_populates="payment_config")
@@ -58,31 +58,31 @@ class Payment(Base, TimestampMixin):
store_id = Column(Integer, ForeignKey("stores.id"), nullable=False)
order_id = Column(Integer, ForeignKey("orders.id"), nullable=False)
customer_id = Column(Integer, ForeignKey("customers.id"), nullable=False)
# Stripe payment details
stripe_payment_intent_id = Column(String(255), unique=True, index=True)
stripe_charge_id = Column(String(255), index=True)
stripe_transfer_id = Column(String(255)) # Transfer to store account
# Payment amounts (in cents to avoid floating point issues)
amount_total = Column(Integer, nullable=False) # Total customer payment
amount_store = Column(Integer, nullable=False) # Amount to store
amount_platform_fee = Column(Integer, nullable=False) # Platform commission
currency = Column(String(3), default="EUR")
# Payment status
status = Column(String(50), nullable=False) # pending, succeeded, failed, refunded
payment_method = Column(String(50)) # card, bank_transfer, etc.
# Metadata
stripe_metadata = Column(Text) # JSON string of Stripe metadata
failure_reason = Column(Text)
refund_reason = Column(Text)
# Timestamps
paid_at = Column(DateTime)
refunded_at = Column(DateTime)
# Relationships
store = relationship("Store")
order = relationship("Order", back_populates="payment")
@@ -109,21 +109,21 @@ class PaymentMethod(Base, TimestampMixin):
id = Column(Integer, primary_key=True, index=True)
store_id = Column(Integer, ForeignKey("stores.id"), nullable=False)
customer_id = Column(Integer, ForeignKey("customers.id"), nullable=False)
# Stripe payment method details
stripe_payment_method_id = Column(String(255), nullable=False, index=True)
payment_method_type = Column(String(50), nullable=False) # card, sepa_debit, etc.
# Card details (if applicable)
card_brand = Column(String(50)) # visa, mastercard, etc.
card_last4 = Column(String(4))
card_exp_month = Column(Integer)
card_exp_year = Column(Integer)
# Settings
is_default = Column(Boolean, default=False)
is_active = Column(Boolean, default=True)
# Relationships
store = relationship("Store")
customer = relationship("Customer")
@@ -138,15 +138,15 @@ class PaymentMethod(Base, TimestampMixin):
# Update models/database/order.py
class Order(Base, TimestampMixin):
# ... existing fields ...
# Payment integration
payment_status = Column(String(50), default="pending") # pending, paid, failed, refunded
payment_intent_id = Column(String(255)) # Stripe PaymentIntent ID
total_amount_cents = Column(Integer, nullable=False) # Amount in cents
# Relationships
payment = relationship("Payment", back_populates="order", uselist=False)
@property
def total_amount_euros(self):
"""Convert cents to euros for display."""
@@ -185,25 +185,25 @@ class PaymentService:
self.db = db
def create_payment_intent(
self,
store_id: int,
order_id: int,
self,
store_id: int,
order_id: int,
amount_euros: Decimal,
customer_email: str,
metadata: Optional[Dict] = None
) -> Dict:
"""Create Stripe PaymentIntent for store order."""
# Get store payment configuration
payment_config = self.get_store_payment_config(store_id)
if not payment_config.accepts_payments:
raise PaymentNotConfiguredException(f"Store {store_id} not configured for payments")
# Calculate amounts
amount_cents = int(amount_euros * 100)
platform_fee_cents = int(amount_cents * (payment_config.platform_fee_percentage / 100))
store_amount_cents = amount_cents - platform_fee_cents
try:
# Create PaymentIntent with Stripe Connect
payment_intent = stripe.PaymentIntent.create(
@@ -222,7 +222,7 @@ class PaymentService:
receipt_email=customer_email,
description=f"Order payment for store {store_id}"
)
# Create payment record
payment = Payment(
store_id=store_id,
@@ -236,17 +236,17 @@ class PaymentService:
status='pending',
stripe_metadata=json.dumps(payment_intent.metadata)
)
self.db.add(payment)
# Update order
order = self.db.query(Order).filter(Order.id == order_id).first()
if order:
order.payment_intent_id = payment_intent.id
order.payment_status = 'pending'
self.db.commit()
return {
'payment_intent_id': payment_intent.id,
'client_secret': payment_intent.client_secret,
@@ -255,58 +255,58 @@ class PaymentService:
'platform_fee': platform_fee_cents / 100,
'currency': payment_config.currency
}
except stripe.error.StripeError as e:
logger.error(f"Stripe error creating PaymentIntent: {e}")
raise PaymentProcessingException(f"Payment processing failed: {str(e)}")
def confirm_payment(self, payment_intent_id: str) -> Payment:
"""Confirm payment and update records."""
try:
# Retrieve PaymentIntent from Stripe
payment_intent = stripe.PaymentIntent.retrieve(payment_intent_id)
# Find payment record
payment = self.db.query(Payment).filter(
Payment.stripe_payment_intent_id == payment_intent_id
).first()
if not payment:
raise PaymentNotFoundException(f"Payment not found for intent {payment_intent_id}")
# Update payment status based on Stripe status
if payment_intent.status == 'succeeded':
payment.status = 'succeeded'
payment.stripe_charge_id = payment_intent.charges.data[0].id if payment_intent.charges.data else None
payment.paid_at = datetime.utcnow()
# Update order status
order = self.db.query(Order).filter(Order.id == payment.order_id).first()
if order:
order.payment_status = 'paid'
order.status = 'processing' # Move order to processing
elif payment_intent.status == 'payment_failed':
payment.status = 'failed'
payment.failure_reason = payment_intent.last_payment_error.message if payment_intent.last_payment_error else "Unknown error"
# Update order status
order = self.db.query(Order).filter(Order.id == payment.order_id).first()
if order:
order.payment_status = 'failed'
self.db.commit()
return payment
except stripe.error.StripeError as e:
logger.error(f"Stripe error confirming payment: {e}")
raise PaymentProcessingException(f"Payment confirmation failed: {str(e)}")
def create_store_stripe_account(self, store_id: int, store_data: Dict) -> str:
"""Create Stripe Connect account for store."""
try:
# Create Stripe Connect Express account
account = stripe.Account.create(
@@ -333,27 +333,27 @@ class PaymentService:
'platform': 'multi_tenant_ecommerce'
}
)
# Update or create payment configuration
payment_config = self.get_or_create_store_payment_config(store_id)
payment_config.stripe_account_id = account.id
payment_config.stripe_account_status = account.charges_enabled and account.payouts_enabled and 'active' or 'pending'
self.db.commit()
return account.id
except stripe.error.StripeError as e:
logger.error(f"Stripe error creating account: {e}")
raise PaymentConfigurationException(f"Failed to create payment account: {str(e)}")
def create_onboarding_link(self, store_id: int) -> str:
"""Create Stripe onboarding link for store."""
payment_config = self.get_store_payment_config(store_id)
if not payment_config.stripe_account_id:
raise PaymentNotConfiguredException("Store does not have Stripe account")
try:
account_link = stripe.AccountLink.create(
account=payment_config.stripe_account_id,
@@ -361,13 +361,13 @@ class PaymentService:
return_url=f"{settings.frontend_url}/store/admin/payments/success",
type='account_onboarding',
)
# Update onboarding URL
payment_config.stripe_onboarding_url = account_link.url
self.db.commit()
return account_link.url
except stripe.error.StripeError as e:
logger.error(f"Stripe error creating onboarding link: {e}")
raise PaymentConfigurationException(f"Failed to create onboarding link: {str(e)}")
@@ -377,28 +377,28 @@ class PaymentService:
config = self.db.query(StorePaymentConfig).filter(
StorePaymentConfig.store_id == store_id
).first()
if not config:
raise PaymentNotConfiguredException(f"No payment configuration for store {store_id}")
return config
def webhook_handler(self, event_type: str, event_data: Dict) -> None:
"""Handle Stripe webhook events."""
if event_type == 'payment_intent.succeeded':
payment_intent_id = event_data['object']['id']
self.confirm_payment(payment_intent_id)
elif event_type == 'payment_intent.payment_failed':
payment_intent_id = event_data['object']['id']
self.confirm_payment(payment_intent_id)
elif event_type == 'account.updated':
# Update store account status
account_id = event_data['object']['id']
self.update_store_account_status(account_id, event_data['object'])
# Add more webhook handlers as needed
```
@@ -426,7 +426,7 @@ async def get_payment_config(
):
"""Get store payment configuration."""
payment_service = PaymentService(db)
try:
config = payment_service.get_store_payment_config(store.id)
return {
@@ -454,17 +454,17 @@ async def setup_payments(
):
"""Set up Stripe payments for store."""
payment_service = PaymentService(db)
store_data = {
"business_name": store.name,
"business_email": store.business_email,
"business_phone": store.business_phone,
**setup_data
}
account_id = payment_service.create_store_stripe_account(store.id, store_data)
onboarding_url = payment_service.create_onboarding_link(store.id)
return {
"stripe_account_id": account_id,
"onboarding_url": onboarding_url,
@@ -481,7 +481,7 @@ async def create_payment_intent(
):
"""Create payment intent for customer order."""
payment_service = PaymentService(db)
payment_intent = payment_service.create_payment_intent(
store_id=store_id,
order_id=payment_data['order_id'],
@@ -489,7 +489,7 @@ async def create_payment_intent(
customer_email=payment_data['customer_email'],
metadata=payment_data.get('metadata', {})
)
return payment_intent
@@ -500,10 +500,10 @@ async def stripe_webhook(
):
"""Handle Stripe webhook events."""
import stripe
payload = await request.body()
sig_header = request.headers.get('stripe-signature')
try:
event = stripe.Webhook.construct_event(
payload, sig_header, settings.stripe_webhook_secret
@@ -512,10 +512,10 @@ async def stripe_webhook(
raise HTTPException(status_code=400, detail="Invalid payload")
except stripe.error.SignatureVerificationError:
raise HTTPException(status_code=400, detail="Invalid signature")
payment_service = PaymentService(db)
payment_service.webhook_handler(event['type'], event['data'])
return {"status": "success"}
```
@@ -524,7 +524,7 @@ async def stripe_webhook(
### Checkout Process
```javascript
// frontend/js/shop/checkout.js
// frontend/js/storefront/checkout.js
class CheckoutManager {
constructor(storeId) {
this.storeId = storeId;
@@ -562,7 +562,7 @@ class CheckoutManager {
const { error } = await this.stripe.confirmPayment({
elements: this.elements,
confirmParams: {
return_url: `${window.location.origin}/shop/order-confirmation`,
return_url: `${window.location.origin}/storefront/order-confirmation`,
receipt_email: orderData.customerEmail
}
});
@@ -614,4 +614,4 @@ Webhook updates account status to 'active'
Store can now accept payments
```
This integration provides secure, compliant payment processing while maintaining store isolation and enabling proper revenue distribution between stores and the platform.
This integration provides secure, compliant payment processing while maintaining store isolation and enabling proper revenue distribution between stores and the platform.