shop
Raz 8 months ago
parent 027ce2d9bc
commit 8365e548be
  1. 6
      padelclub_backend/settings.py
  2. 28
      shop/migrations/0012_order_payment_status_and_more.py
  3. 7
      shop/models.py
  4. 47
      shop/stripe_utils.py
  5. 70
      shop/templates/shop/cart.html
  6. 87
      shop/templates/shop/payment.html
  7. 20
      shop/templates/shop/payment_cancel.html
  8. 31
      shop/templates/shop/payment_success.html
  9. 7
      shop/urls.py
  10. 276
      shop/views.py
  11. 2
      tournaments/urls.py

@ -156,5 +156,11 @@ AUTHENTICATION_BACKENDS = [
CSRF_COOKIE_SECURE = True # if using HTTPS CSRF_COOKIE_SECURE = True # if using HTTPS
# Stripe Settings
STRIPE_PUBLISHABLE_KEY = 'pk_test_51R4LrTPEZkECCx484C2KbmRpcO2ZkZb0NoNi8QJB4X3E5JFu3bvLk4JZQmz9grKbk6O40z3xI8DawHrGyUY0fOT600VEKC9ran' # Replace with your actual key
STRIPE_SECRET_KEY = 'sk_test_51R4LrTPEZkECCx48PkSbEYarhts7J7XNYpS1mJgows5z5dcv38l0G2tImvhXCjzvMgUH9ML0vLMOEPeyUBtYVf5H00Qvz8t3rE' # Replace with your actual key
STRIPE_WEBHOOK_SECRET = 'your_webhook_secret' # Optional for later
STRIPE_CURRENCY = 'eur' # Set to your preferred currency
from .settings_local import * from .settings_local import *
from .settings_app import * from .settings_app import *

@ -0,0 +1,28 @@
# Generated by Django 4.2.11 on 2025-03-19 12:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shop', '0011_order_guest_user'),
]
operations = [
migrations.AddField(
model_name='order',
name='payment_status',
field=models.CharField(choices=[('UNPAID', 'Unpaid'), ('PAID', 'Paid'), ('FAILED', 'Failed')], default='UNPAID', max_length=20),
),
migrations.AddField(
model_name='order',
name='stripe_checkout_session_id',
field=models.CharField(blank=True, max_length=255, null=True),
),
migrations.AddField(
model_name='order',
name='stripe_payment_intent_id',
field=models.CharField(blank=True, max_length=255, null=True),
),
]

@ -86,6 +86,13 @@ class Order(models.Model):
status = models.CharField(max_length=20, choices=OrderStatus.choices, default=OrderStatus.PENDING) status = models.CharField(max_length=20, choices=OrderStatus.choices, default=OrderStatus.PENDING)
total_price = models.DecimalField(max_digits=10, decimal_places=2, default=0.00) total_price = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
guest_user = models.ForeignKey(GuestUser, on_delete=models.CASCADE, null=True, blank=True) guest_user = models.ForeignKey(GuestUser, on_delete=models.CASCADE, null=True, blank=True)
stripe_payment_intent_id = models.CharField(max_length=255, blank=True, null=True)
stripe_checkout_session_id = models.CharField(max_length=255, blank=True, null=True)
payment_status = models.CharField(max_length=20, default='UNPAID', choices=[
('UNPAID', 'Unpaid'),
('PAID', 'Paid'),
('FAILED', 'Failed'),
])
def __str__(self): def __str__(self):
return f"Order #{self.id} - {self.status}" return f"Order #{self.id} - {self.status}"

@ -0,0 +1,47 @@
import stripe
from django.conf import settings
# Configure Stripe with your secret key
stripe.api_key = settings.STRIPE_SECRET_KEY
def create_payment_intent(amount, currency=settings.STRIPE_CURRENCY, metadata=None):
"""
Create a payment intent with Stripe
Args:
amount: Amount in cents (e.g., 1000 for 10.00)
currency: Currency code (default: settings.STRIPE_CURRENCY)
metadata: Additional info to attach to the payment intent
Returns:
The created payment intent object
"""
intent = stripe.PaymentIntent.create(
amount=amount,
currency=currency,
metadata=metadata or {},
)
return intent
def create_checkout_session(line_items, success_url, cancel_url, metadata=None):
"""
Create a Stripe Checkout Session for one-time payments
Args:
line_items: List of items to purchase
success_url: URL to redirect on successful payment
cancel_url: URL to redirect on canceled payment
metadata: Additional info to attach to the session
Returns:
The created checkout session
"""
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 or {},
)
return session

@ -57,12 +57,80 @@
<a class="cancel-nav-button" href="{% url 'shop:clear_cart' %}">Vider le panier</a> <a class="cancel-nav-button" href="{% url 'shop:clear_cart' %}">Vider le panier</a>
{% endif %} {% endif %}
{% if user.is_authenticated %}
<!-- For authenticated users: Direct Stripe payment button -->
<button id="checkout-button" class="confirm-nav-button">Procéder au paiement</button>
{% else %}
<!-- For guest users: Regular checkout path -->
<a class="confirm-nav-button" href="{% url 'shop:checkout' %}">Passer la commande</a> <a class="confirm-nav-button" href="{% url 'shop:checkout' %}">Passer la commande</a>
<div class="guest-checkout-notice">
<p>Connectez-vous pour un paiement plus rapide.</p>
<a href="{% url 'login' %}?next={% url 'shop:view_cart' %}">Se connecter</a>
</div>
{% endif %}
</div> </div>
{% else %} {% else %}
<p>Votre panier est vide.</p> <p>Votre panier est vide.</p>
{% endif %} {% endif %}
</div> </div>
</div> </div>
</div>
{% if user.is_authenticated and cart_items %}
<!-- Stripe JavaScript for authenticated users -->
<script src="https://js.stripe.com/v3/"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const checkoutButton = document.getElementById('checkout-button');
checkoutButton.addEventListener('click', function() {
// Show a loading indicator
checkoutButton.textContent = 'Chargement...';
checkoutButton.disabled = true;
// Create order and get checkout session
fetch('{% url "shop:create_checkout_session" %}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}'
},
credentials: 'same-origin',
})
.then(function(response) {
return response.json();
})
.then(function(session) {
if (session.error) {
// Handle error
alert(session.error);
checkoutButton.textContent = 'Procéder au paiement';
checkoutButton.disabled = false;
return;
}
// Initialize Stripe
const stripe = Stripe('{{ stripe_publishable_key }}');
// Redirect to Stripe Checkout
return stripe.redirectToCheckout({ sessionId: session.id });
})
.then(function(result) {
// If redirectToCheckout fails
if (result && result.error) {
alert(result.error.message);
checkoutButton.textContent = 'Procéder au paiement';
checkoutButton.disabled = false;
}
})
.catch(function(error) {
console.error('Error:', error);
alert('Une erreur est survenue. Veuillez réessayer.');
checkoutButton.textContent = 'Procéder au paiement';
checkoutButton.disabled = false;
});
});
});
</script>
{% endif %}
{% endblock %} {% endblock %}

@ -0,0 +1,87 @@
{% extends 'tournaments/base.html' %}
{% block head_title %}Paiement{% endblock %}
{% block first_title %}La boutique Padel Club{% endblock %}
{% block second_title %}Paiement{% endblock %}
{% block content %}
<nav class="margin10">
<a href="{% url 'shop:product_list' %}">La Boutique</a>
<a href="{% url 'index' %}" class="orange">Accueil</a>
<a href="{% url 'clubs' %}" class="orange">Clubs</a>
{% if user.is_authenticated %}
<a href="{% url 'my-tournaments' %}" class="orange">Mes tournois</a>
<a href="{% url 'profile' %}">Mon compte</a>
{% else %}
<a href="{% url 'login' %}">Se connecter</a>
{% endif %}
</nav>
<div class="grid-x">
<div class="cell medium-6 large-6 my-block">
<h1 class="club my-block topmargin20">Paiement</h1>
<div class="bubble">
<h2>Résumé de votre commande</h2>
<!-- Display order items in a table -->
<table class="cart-table">
<thead>
<tr>
<th>Produit</th>
<th>Couleur</th>
<th>Taille</th>
<th>Quantité</th>
<th class="price-column">Prix</th>
</tr>
</thead>
<tbody>
{% for item in order_items %}
<tr class="{% cycle 'odd' 'even' %}">
<td>{{ item.product.title }}</td>
<td>{{ item.color.name|default:"N/A" }}</td>
<td>{{ item.size.name|default:"N/A" }}</td>
<td>{{ item.quantity }}</td>
<td class="price-column">{{ item.get_total_price }} €</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td colspan="3" class="total-label">Total:</td>
<td class="total-quantity">{{ order_items.count }}</td>
<td class="price-column total-price">{{ order.total_price }} €</td>
</tr>
</tfoot>
</table>
<!-- Stripe checkout button -->
<div id="payment-container">
<button id="checkout-button" class="confirm-nav-button">Procéder au paiement</button>
</div>
</div>
</div>
</div>
<!-- Stripe JavaScript -->
<script src="https://js.stripe.com/v3/"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Initialize Stripe
const stripe = Stripe('{{ stripe_publishable_key }}');
const checkoutButton = document.getElementById('checkout-button');
checkoutButton.addEventListener('click', function() {
// Redirect to Stripe Checkout
stripe.redirectToCheckout({
sessionId: '{{ checkout_session_id }}'
}).then(function(result) {
// If `redirectToCheckout` fails due to a browser or network
// error, display the localized error message to your customer
if (result.error) {
alert(result.error.message);
}
});
});
});
</script>
{% endblock %}

@ -0,0 +1,20 @@
<!-- padelclub_backend/shop/templates/shop/payment_cancel.html -->
{% extends 'tournaments/base.html' %}
{% block head_title %}Paiement annulé{% endblock %}
{% block content %}
<div class="grid-x">
<div class="cell medium-6 large-6 my-block">
<h1 class="club my-block topmargin20">Paiement annulé</h1>
<div class="bubble">
<h2>Le paiement a été annulé</h2>
<p>Votre commande n'a pas été finalisée car le paiement a été annulé.</p>
<div class="cart-summary">
<a class="confirm-nav-button" href="{% url 'shop:view_cart' %}">Retour au panier</a>
</div>
</div>
</div>
</div>
{% endblock %}

@ -1,11 +1,10 @@
{% extends 'tournaments/base.html' %} {% extends 'tournaments/base.html' %}
{% block head_title %}Confirmation de commande{% endblock %} {% block head_title %}Paiement réussi{% endblock %}
{% block first_title %}Confirmation de commande{% endblock %} {% block first_title %}La boutique Padel Club{% endblock %}
{% block second_title %}Merci pour votre commande !{% endblock %} {% block second_title %}Paiement réussi{% endblock %}
{% block content %} {% block content %}
<nav class="margin10"> <nav class="margin10">
<a href="{% url 'shop:product_list' %}">La Boutique</a> <a href="{% url 'shop:product_list' %}">La Boutique</a>
<a href="{% url 'index' %}" class="orange">Accueil</a> <a href="{% url 'index' %}" class="orange">Accueil</a>
@ -17,11 +16,17 @@
<a href="{% url 'login' %}">Se connecter</a> <a href="{% url 'login' %}">Se connecter</a>
{% endif %} {% endif %}
</nav> </nav>
<div class="grid-x"> <div class="grid-x">
<div class="cell medium-6 large-6 my-block"> <div class="cell medium-6 large-6 my-block">
<h1 class="club my-block topmargin20">Détails de la commande</h1> <h1 class="club my-block topmargin20">Paiement réussi</h1>
<div class="bubble"> <div class="bubble">
{% if order_items %} <h2>Merci pour votre commande !</h2>
<p>Votre paiement a été traité avec succès.</p>
<p>Numéro de commande: {{ order.id }}</p>
<!-- Order details -->
<h3>Détails de la commande</h3>
<table class="cart-table"> <table class="cart-table">
<thead> <thead>
<tr> <tr>
@ -36,8 +41,8 @@
{% for item in order_items %} {% for item in order_items %}
<tr class="{% cycle 'odd' 'even' %}"> <tr class="{% cycle 'odd' 'even' %}">
<td>{{ item.product.title }}</td> <td>{{ item.product.title }}</td>
<td>{{ item.color.name }}</td> <td>{{ item.color.name|default:"N/A" }}</td>
<td>{{ item.size.name }}</td> <td>{{ item.size.name|default:"N/A" }}</td>
<td>{{ item.quantity }}</td> <td>{{ item.quantity }}</td>
<td class="price-column">{{ item.get_total_price }} €</td> <td class="price-column">{{ item.get_total_price }} €</td>
</tr> </tr>
@ -46,19 +51,15 @@
<tfoot> <tfoot>
<tr> <tr>
<td colspan="3" class="total-label">Total:</td> <td colspan="3" class="total-label">Total:</td>
<td class="total-quantity">{{ order_items.total_quantity }}</td> <td class="total-quantity">{{ order_items.count }}</td>
<td class="price-column total-price">{{ total }} €</td> <td class="price-column total-price">{{ total }} €</td>
</tr> </tr>
</tfoot> </tfoot>
</table> </table>
<div class="order-summary"> <div class="cart-summary">
<p>Votre commande a été passée avec succès !</p> <a class="confirm-nav-button" href="{% url 'shop:product_list' %}">Retour à la boutique</a>
<a class="back-to-shop-button" href="{% url 'shop:product_list' %}">Retour à la boutique</a>
</div> </div>
{% else %}
<p>Aucun élément dans la commande.</p>
{% endif %}
</div> </div>
</div> </div>
</div> </div>

@ -13,5 +13,10 @@ urlpatterns = [
path('cart/remove/<int:product_id>/', views.remove_from_cart_view, name='remove_from_cart'), path('cart/remove/<int:product_id>/', views.remove_from_cart_view, name='remove_from_cart'),
path('clear-cart/', views.clear_cart, name='clear_cart'), path('clear-cart/', views.clear_cart, name='clear_cart'),
path('checkout/', views.checkout, name='checkout'), path('checkout/', views.checkout, name='checkout'),
path('order/<int:order_id>/confirmation/', views.order_confirmation, name='order_confirmation'), path('payment/<int:order_id>/', views.payment, name='payment'),
path('payment/success/<int:order_id>/', views.payment_success, name='payment_success'),
path('payment/cancel/<int:order_id>/', views.payment_cancel, name='payment_cancel'),
# path('webhook/stripe/', views.stripe_webhook, name='stripe_webhook'),
path('create-checkout-session/', views.create_checkout_session, name='create_checkout_session'),
] ]

@ -1,15 +1,86 @@
from django.shortcuts import render, redirect, get_object_or_404 from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages from django.contrib import messages
from .models import Product, Order, OrderItem, GuestUser from .models import Product, Order, OrderItem, GuestUser, OrderStatus
from django.db.models import Sum from django.db.models import Sum
from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import login from django.contrib.auth import login
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from .forms import GuestCheckoutForm 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 . import cart from . import cart
# Create your views here. # 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): def product_list(request):
products = Product.objects.all() products = Product.objects.all()
cart_items = cart.get_cart_items(request) cart_items = cart.get_cart_items(request)
@ -25,11 +96,18 @@ def view_cart(request):
cart_items = cart.get_cart_items(request) cart_items = cart.get_cart_items(request)
total = cart.get_cart_total(request) total = cart.get_cart_total(request)
total_quantity = cart_items.aggregate(total_quantity=Sum('quantity'))['total_quantity'] total_quantity = cart_items.aggregate(total_quantity=Sum('quantity'))['total_quantity']
return render(request, 'shop/cart.html', {
context = {
'cart_items': cart_items, 'cart_items': cart_items,
'total': total, 'total': total,
'total_quantity': total_quantity 'total_quantity': total_quantity,
}) }
# 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)
def add_to_cart_view(request, product_id): def add_to_cart_view(request, product_id):
"""Add a product to the cart""" """Add a product to the cart"""
@ -62,17 +140,27 @@ def clear_cart(request):
return redirect('shop:product_list') return redirect('shop:product_list')
def create_order(request): def create_order(request):
"""Create an order from the current cart"""
cart_items = cart.get_cart_items(request) 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) 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: if request.user.is_authenticated:
# L'utilisateur est authentifié, créer la commande avec l'utilisateur # Authenticated user order
order = Order.objects.create( order = Order.objects.create(
user=request.user, user=request.user,
total_price=total_price total_price=total_price
) )
else: else:
# L'utilisateur n'est pas authentifié, créer la commande avec l'utilisateur invité # Guest user order
try: try:
guest_user = GuestUser.objects.get(email=request.session['guest_email']) guest_user = GuestUser.objects.get(email=request.session['guest_email'])
order = Order.objects.create( order = Order.objects.create(
@ -80,12 +168,12 @@ def create_order(request):
total_price=total_price total_price=total_price
) )
except (KeyError, GuestUser.DoesNotExist): except (KeyError, GuestUser.DoesNotExist):
# Si l'utilisateur invité n'existe pas, créer une commande sans utilisateur # No guest user information, create order without user
order = Order.objects.create( order = Order.objects.create(
total_price=total_price total_price=total_price
) )
# Create order items
for cart_item in cart_items: for cart_item in cart_items:
OrderItem.objects.create( OrderItem.objects.create(
order=order, order=order,
@ -96,53 +184,163 @@ def create_order(request):
price=cart_item.product.price price=cart_item.product.price
) )
# Clear the cart after creating the order # Note: Cart is not cleared here, only after successful payment
cart.clear_cart(request)
return order return order
def order_confirmation(request, order_id):
order = get_object_or_404(Order, id=order_id)
order_items = order.items.all()
total = sum(item.get_total_price() for item in order_items)
return render(request, 'shop/order_confirmation.html', {
'order': order,
'order_items': order_items,
'total': total
})
def checkout(request): def checkout(request):
if request.user.is_authenticated: """Handle checkout process for both authenticated and guest users"""
# Créer la commande pour l'utilisateur authentifié # Check if cart is empty
order = create_order(request) 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')
# Rediriger vers la confirmation de commande if request.user.is_authenticated:
return redirect('shop:order_confirmation', order.id) # Create order for authenticated user and go directly to payment
return _handle_authenticated_checkout(request)
# Handle guest checkout
if request.method == 'GET': if request.method == 'GET':
form = GuestCheckoutForm() form = GuestCheckoutForm()
return render(request, 'shop/checkout.html', {'form': form}) return render(request, 'shop/checkout.html', {'form': form})
elif request.method == 'POST': elif request.method == 'POST':
# Gérer la soumission du formulaire ici 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) form = GuestCheckoutForm(request.POST)
if form.is_valid(): if form.is_valid():
# Créer ou récupérer l'utilisateur invité # Create or get guest user
email = form.cleaned_data['email'] email = form.cleaned_data['email']
phone = form.cleaned_data['phone'] phone = form.cleaned_data['phone']
guest_user, created = GuestUser.objects.get_or_create(email=email, defaults={'phone': phone}) guest_user, created = GuestUser.objects.get_or_create(
email=email,
defaults={'phone': phone}
)
# Stocker l'e-mail de l'utilisateur invité dans la session # Store email in session
request.session['guest_email'] = email request.session['guest_email'] = email
# Simuler le processus de paiement # Create order
# ...
# Créer la commande
order = create_order(request) order = create_order(request)
# Rediriger vers la confirmation de commande if not order:
return redirect('shop:order_confirmation', order.id) 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_items = order.items.all()
# Gérer les autres méthodes (par exemple, PUT, DELETE) si nécessaire # 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') 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
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)
return render(request, 'shop/payment_success.html', {
'order': order,
'order_items': order_items,
'total': total
})
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)

@ -77,8 +77,6 @@ urlpatterns = [
path('admin/users-export/', views.UserListExportView.as_view(), name='users_export'), path('admin/users-export/', views.UserListExportView.as_view(), name='users_export'),
path('activation-success/', views.activation_success, name='activation_success'), path('activation-success/', views.activation_success, name='activation_success'),
path('activation-failed/', views.activation_failed, name='activation_failed'), path('activation-failed/', views.activation_failed, name='activation_failed'),
path('shop/', include('shop.urls')),
] ]
if settings.DEBUG: if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Loading…
Cancel
Save