You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
padelclub_backend/shop/views.py

469 lines
15 KiB

from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from .models import Product, Order, OrderItem, GuestUser, OrderStatus
from django.db.models import Sum
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import login
from django.contrib.auth.decorators import login_required
from .forms import GuestCheckoutForm
import stripe
from django.conf import settings
from django.urls import reverse
from django.http import JsonResponse
from django.views.decorators.http import require_POST
from django.views.decorators.csrf import ensure_csrf_cookie
from . import cart
# Shared helper methods
def _check_stripe_config():
"""Check if Stripe API keys are properly configured"""
return hasattr(settings, 'STRIPE_SECRET_KEY') and settings.STRIPE_SECRET_KEY
def _create_stripe_line_items(order_items):
"""Create line items for Stripe checkout from order items"""
line_items = []
for item in order_items:
item_price = int(float(item.price) * 100) # Convert to cents
line_items.append({
'price_data': {
'currency': 'eur',
'product_data': {
'name': item.product.title,
'description': f"Color: {item.color.name if item.color else 'N/A'}, Size: {item.size.name if item.size else 'N/A'}",
},
'unit_amount': item_price,
},
'quantity': item.quantity,
})
return line_items
def _create_stripe_checkout_session(request, order, line_items):
"""Create a Stripe checkout session for the order"""
# Create success and cancel URLs
success_url = request.build_absolute_uri(reverse('shop:payment_success', args=[order.id]))
cancel_url = request.build_absolute_uri(reverse('shop:payment_cancel', args=[order.id]))
# Create metadata to identify this order
metadata = {
'order_id': order.id,
}
# Add user info to metadata if available
if request.user.is_authenticated:
metadata['user_id'] = request.user.id
elif 'guest_email' in request.session:
metadata['guest_email'] = request.session.get('guest_email', '')
try:
# Initialize Stripe with your secret key
stripe.api_key = settings.STRIPE_SECRET_KEY
# Create session
checkout_session = stripe.checkout.Session.create(
payment_method_types=['card'],
line_items=line_items,
mode='payment',
success_url=success_url,
cancel_url=cancel_url,
metadata=metadata,
)
# Save the checkout session ID to the order
order.stripe_checkout_session_id = checkout_session.id
order.save()
return checkout_session
except Exception as e:
# Log error and re-raise
print(f"Stripe error: {str(e)}")
raise
# View functions
def product_list(request):
products = Product.objects.all()
cart_items = cart.get_cart_items(request)
total = cart.get_cart_total(request)
return render(request, 'shop/product_list.html', {
'products': products,
'cart_items': cart_items,
'total': total
})
def view_cart(request):
"""Display the shopping cart"""
cart_items = cart.get_cart_items(request)
total = cart.get_cart_total(request)
total_quantity = cart_items.aggregate(total_quantity=Sum('quantity'))['total_quantity']
display_data = prepare_item_display_data(cart_items, is_cart=True)
context = {
'display_data': display_data,
'total': total,
'total_quantity': total_quantity,
'settings': settings, # Add this line to pass settings to template
}
# Add Stripe publishable key for authenticated users
if request.user.is_authenticated:
context['stripe_publishable_key'] = settings.STRIPE_PUBLISHABLE_KEY
return render(request, 'shop/cart.html', context)
@ensure_csrf_cookie
def add_to_cart_view(request, product_id):
"""Add a product to the cart"""
product = get_object_or_404(Product, id=product_id)
quantity = int(request.POST.get('quantity', 1))
color_id = request.POST.get('color')
size_id = request.POST.get('size')
cart_item = cart.add_to_cart(request, product_id, quantity, color_id, size_id)
messages.success(request, f'{cart_item.quantity} x {product.title} added to your cart')
return redirect('shop:product_list')
def update_cart_view(request, product_id):
"""Update cart item quantity"""
if request.method == 'POST':
quantity = int(request.POST.get('quantity', 0))
cart.update_cart_item(request, product_id, quantity)
return redirect('shop:view_cart')
def remove_from_cart_view(request, product_id):
"""Remove item from cart"""
cart.remove_from_cart(request, product_id)
return redirect('shop:view_cart')
def clear_cart(request):
"""Clear the cart"""
cart.clear_cart(request)
messages.success(request, "Your cart has been cleared.")
return redirect('shop:product_list')
def create_order(request):
"""Create an order from the current cart"""
cart_items = cart.get_cart_items(request)
# Check if cart is empty
if not cart_items.exists():
return None
total_price = sum(item.get_total_price() for item in cart_items)
# Check if total price is valid
if total_price <= 0:
return None
if request.user.is_authenticated:
# Authenticated user order
order = Order.objects.create(
user=request.user,
total_price=total_price
)
else:
# Guest user order
try:
guest_user = GuestUser.objects.get(email=request.session['guest_email'])
order = Order.objects.create(
guest_user=guest_user,
total_price=total_price
)
except (KeyError, GuestUser.DoesNotExist):
# No guest user information, create order without user
order = Order.objects.create(
total_price=total_price
)
# Create order items
for cart_item in cart_items:
OrderItem.objects.create(
order=order,
product=cart_item.product,
quantity=cart_item.quantity,
color=cart_item.color,
size=cart_item.size,
price=cart_item.product.price
)
# Note: Cart is not cleared here, only after successful payment
return order
def checkout(request):
"""Handle checkout process for both authenticated and guest users"""
# Check if cart is empty
cart_items = cart.get_cart_items(request)
if not cart_items.exists():
messages.error(request, "Your cart is empty. Please add items before checkout.")
return redirect('shop:product_list')
if request.user.is_authenticated:
# Create order for authenticated user and go directly to payment
return _handle_authenticated_checkout(request)
# Handle guest checkout
if request.method == 'GET':
form = GuestCheckoutForm()
return render(request, 'shop/checkout.html', {'form': form})
elif request.method == 'POST':
return _handle_guest_checkout_post(request)
return redirect('shop:product_list')
def _handle_authenticated_checkout(request):
"""Helper function to handle checkout for authenticated users"""
order = create_order(request)
if not order:
messages.error(request, "There was an issue creating your order. Please try again.")
return redirect('shop:view_cart')
return redirect('shop:payment', order_id=order.id)
def _handle_guest_checkout_post(request):
"""Helper function to handle POST requests for guest checkout"""
form = GuestCheckoutForm(request.POST)
if form.is_valid():
# Create or get guest user
email = form.cleaned_data['email']
phone = form.cleaned_data['phone']
guest_user, created = GuestUser.objects.get_or_create(
email=email,
defaults={'phone': phone}
)
# Store email in session
request.session['guest_email'] = email
# Create order
order = create_order(request)
if not order:
messages.error(request, "There was an issue creating your order. Please try again.")
return redirect('shop:view_cart')
return redirect('shop:payment', order_id=order.id)
# Form invalid
return render(request, 'shop/checkout.html', {'form': form})
def payment(request, order_id):
"""Handle payment for an existing order"""
order = get_object_or_404(Order, id=order_id)
order.status = OrderStatus.PENDING
order.payment_status = 'PENDING'
order_items = order.items.all()
# Check for valid order
if not order_items.exists() or order.total_price <= 0:
messages.error(request, "Cannot process an empty order.")
return redirect('shop:product_list')
# Log payment processing
print(f"Processing payment for order #{order_id}, total: {order.total_price}")
# Check Stripe configuration
if not _check_stripe_config():
messages.error(request, "Stripe API keys not configured properly.")
return redirect('shop:view_cart')
# Create line items
line_items = _create_stripe_line_items(order_items)
if not line_items:
messages.error(request, "Cannot create payment with no items.")
return redirect('shop:view_cart')
# Create checkout session
try:
checkout_session = _create_stripe_checkout_session(request, order, line_items)
checkout_session_id = checkout_session.id
except Exception as e:
messages.error(request, f"Payment processing error: {str(e)}")
return redirect('shop:view_cart')
# Render payment page
return render(request, 'shop/payment.html', {
'order': order,
'order_items': order_items,
'checkout_session_id': checkout_session_id,
'stripe_publishable_key': settings.STRIPE_PUBLISHABLE_KEY,
})
def payment_success(request, order_id):
"""Handle successful payment"""
order = get_object_or_404(Order, id=order_id)
# Clear cart after successful payment
cart.clear_cart(request)
# Update order status
print("payment_success")
order.status = OrderStatus.PAID
order.payment_status = 'PAID'
order.save()
# Get order items for template
order_items = order.items.all()
total = sum(item.get_total_price() for item in order_items)
display_data = prepare_item_display_data(order_items, is_cart=False)
print(display_data)
return render(request, 'shop/payment_success.html', {
'order': order,
'display_data' : display_data,
})
def payment_cancel(request, order_id):
"""Handle cancelled payment"""
order = get_object_or_404(Order, id=order_id)
# Update order status
order.status = OrderStatus.CANCELED
order.payment_status = 'FAILED'
order.save()
messages.warning(request, "Your payment was cancelled.")
return render(request, 'shop/payment_cancel.html', {'order': order})
@require_POST
def create_checkout_session(request):
"""API endpoint to create a Stripe checkout session directly from cart"""
if not request.user.is_authenticated:
return JsonResponse({'error': 'User must be authenticated'}, status=403)
# Create the order
order = create_order(request)
if not order:
return JsonResponse({'error': 'Could not create order from cart'}, status=400)
# Get order items
order_items = order.items.all()
# Create line items
line_items = _create_stripe_line_items(order_items)
# Create checkout session
try:
checkout_session = _create_stripe_checkout_session(request, order, line_items)
return JsonResponse({'id': checkout_session.id})
except Exception as e:
return JsonResponse({'error': str(e)}, status=400)
@require_POST
def update_cart_item(request):
"""Update a cart item quantity (increase/decrease)"""
item_id = request.POST.get('item_id')
action = request.POST.get('action')
try:
cart_item = cart.get_cart_item(request, item_id)
if action == 'increase':
# Increase quantity by 1
cart_item.quantity += 1
cart_item.save()
messages.success(request, "Quantity increased.")
elif action == 'decrease' and cart_item.quantity > 1:
# Decrease quantity by 1
cart_item.quantity -= 1
cart_item.save()
messages.success(request, "Quantity decreased.")
except Exception as e:
messages.error(request, f"Error updating cart: {str(e)}")
return redirect('shop:view_cart')
@require_POST
def remove_from_cart(request):
"""Remove an item from cart by item_id"""
item_id = request.POST.get('item_id')
try:
cart_item = cart.get_cart_item(request, item_id)
cart_item.delete()
messages.success(request, "Item removed from cart.")
except Exception as e:
messages.error(request, f"Error removing item: {str(e)}")
return redirect('shop:view_cart')
def prepare_item_display_data(items, is_cart=True):
"""
Transform cart items or order items into a standardized format for display
Args:
items: QuerySet of CartItem or OrderItem
is_cart: True if items are CartItems, False if OrderItems
Returns:
Dictionary with standardized item data
"""
prepared_items = []
total_quantity = 0
total_price = 0
for item in items:
if is_cart:
# For CartItem
item_data = {
'id': item.id,
'product_title': item.product.title,
'color_name': item.color.name if item.color else 'N/A',
'color_hex': item.color.colorHex if item.color else '#FFFFFF',
'size_name': item.size.name if item.size else 'N/A',
'quantity': item.quantity,
'total_price': item.get_total_price()
}
total_price += item.get_total_price()
else:
# For OrderItem
item_data = {
'id': item.id,
'product_title': item.product.title,
'color_name': item.color.name if item.color else 'N/A',
'color_hex': item.color.colorHex if item.color else '#FFFFFF',
'size_name': item.size.name if item.size else 'N/A',
'quantity': item.quantity,
'total_price': item.get_total_price()
}
total_price += item.get_total_price()
total_quantity += item.quantity
prepared_items.append(item_data)
return {
'items': prepared_items,
'total_quantity': total_quantity,
'total_price': total_price
}
def simulate_payment_success(request):
"""Debug function to simulate successful payment without Stripe"""
# Create an order from the cart
order = create_order(request)
if not order:
messages.error(request, "Could not create order from cart")
return redirect('shop:view_cart')
# Clear the cart
cart.clear_cart(request)
return redirect('shop:payment_success', order_id=order.id)
def simulate_payment_failure(request):
"""Debug function to simulate failed payment without Stripe"""
# Create an order from the cart
order = create_order(request)
if not order:
messages.error(request, "Could not create order from cart")
return redirect('shop:view_cart')
return redirect('shop:payment_cancel', order_id=order.id)