@ -2,9 +2,6 @@ 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
@ -12,6 +9,8 @@ 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 django . http import HttpResponse
from django . views . decorators . csrf import csrf_exempt
from . import cart
@ -47,12 +46,12 @@ def _create_stripe_checkout_session(request, order, line_items):
# Create metadata to identify this order
metadata = {
' order_id ' : order . id ,
' order_id ' : str ( order . id ) , # Convert to string to be safe
}
# Add user info to metadata if available
if request . user . is_authenticated :
metadata [ ' user_id ' ] = request . user . id
metadata [ ' user_id ' ] = str ( request . user . id )
elif ' guest_email ' in request . session :
metadata [ ' guest_email ' ] = request . session . get ( ' guest_email ' , ' ' )
@ -298,31 +297,36 @@ def payment_success(request, 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 ( )
# Only update if not already processed by webhook
if not order . webhook_processed :
print ( f " Updating order { order_id } via redirect (not webhook) " )
order . status = OrderStatus . PAID
order . payment_status = ' PAID '
order . save ( )
else :
print ( f " Order { order_id } already processed by webhook " )
# 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 ,
' 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 ( )
# Only update if not already processed by webhook
if not order . webhook_processed :
print ( f " Updating order { order_id } to CANCELED via redirect (not webhook) " )
order . status = OrderStatus . CANCELED
order . payment_status = ' FAILED '
order . save ( )
else :
print ( f " Order { order_id } already processed by webhook " )
messages . warning ( request , " Your payment was cancelled. " )
return render ( request , ' shop/payment_cancel.html ' , { ' order ' : order } )
@ -467,3 +471,114 @@ def simulate_payment_failure(request):
return redirect ( ' shop:view_cart ' )
return redirect ( ' shop:payment_cancel ' , order_id = order . id )
@require_POST
@csrf_exempt # Stripe can't provide CSRF tokens
def stripe_webhook ( request ) :
""" Handle Stripe webhook events """
payload = request . body
sig_header = request . META . get ( ' HTTP_STRIPE_SIGNATURE ' )
# Log the received webhook for debugging
print ( f " Webhook received: { sig_header } " )
if not sig_header :
print ( " No signature header " )
return HttpResponse ( status = 400 )
try :
# Initialize Stripe
stripe . api_key = settings . STRIPE_SECRET_KEY
# Verify the event using webhook signing secret
event = stripe . Webhook . construct_event (
payload , sig_header , settings . STRIPE_WEBHOOK_SECRET
)
# Log the event type
print ( f " Webhook event type: { event [ ' type ' ] } " )
except ValueError as e :
# Invalid payload
print ( f " Invalid payload: { str ( e ) } " )
return HttpResponse ( status = 400 )
except stripe . error . SignatureVerificationError as e :
# Invalid signature
print ( f " Invalid signature: { str ( e ) } " )
return HttpResponse ( status = 400 )
# Handle the event based on type
event_type = event [ ' type ' ]
if event_type == ' checkout.session.completed ' :
handle_checkout_session_completed ( event [ ' data ' ] [ ' object ' ] )
elif event_type == ' payment_intent.succeeded ' :
handle_payment_intent_succeeded ( event [ ' data ' ] [ ' object ' ] )
elif event_type == ' payment_intent.payment_failed ' :
handle_payment_intent_failed ( event [ ' data ' ] [ ' object ' ] )
# Return success response
return HttpResponse ( status = 200 )
def handle_checkout_session_completed ( session ) :
""" Handle completed checkout session webhook """
print ( f " Processing checkout.session.completed for session { session . id } " )
# Get order from metadata
order_id = session . get ( ' metadata ' , { } ) . get ( ' order_id ' )
if not order_id :
print ( f " Warning: No order_id in metadata for session { session . id } " )
return
try :
order = Order . objects . get ( id = order_id )
# Update order status if not already processed
if not order . webhook_processed :
print ( f " Updating order { order_id } to PAID via webhook " )
order . status = OrderStatus . PAID
order . payment_status = ' PAID '
order . webhook_processed = True
order . save ( )
# You could add additional processing here
# - Send order confirmation email
# - Update inventory
# - Etc.
else :
print ( f " Order { order_id } already processed by webhook " )
except Order . DoesNotExist :
print ( f " Error: Order { order_id } not found for session { session . id } " )
def handle_payment_intent_succeeded ( payment_intent ) :
""" Handle successful payment intent webhook """
print ( f " Processing payment_intent.succeeded for intent { payment_intent . id } " )
# If you're using payment intents directly, handle them here
# For Checkout Sessions, you'll likely rely on checkout.session.completed instead
def handle_payment_intent_failed ( payment_intent ) :
""" Handle failed payment intent webhook """
print ( f " Processing payment_intent.payment_failed for intent { payment_intent . id } " )
# Get order from metadata
order_id = payment_intent . get ( ' metadata ' , { } ) . get ( ' order_id ' )
if not order_id :
print ( f " No order_id in metadata for payment intent { payment_intent . id } " )
return
try :
order = Order . objects . get ( id = order_id )
# Update order status
if not order . webhook_processed or order . payment_status != ' FAILED ' :
print ( f " Updating order { order_id } to FAILED via webhook " )
order . status = OrderStatus . CANCELED
order . payment_status = ' FAILED '
order . webhook_processed = True
order . save ( )
except Order . DoesNotExist :
print ( f " Error: Order { order_id } not found for payment intent { payment_intent . id } " )