最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

nginx - Django Stripe Webhook not consistent Statuscode 500 sometimes 201 - Stack Overflow

programmeradmin4浏览0评论

L.S.

I am working on my first Django project and struggling with the Stripe integration. My webhook is acting inconsistently—it worked fine when running locally on localhost with the Stripe CLI. However, after deploying with Nginx and Gunicorn, it has become unreliable. Sometimes it goes through, but in most cases, I receive a 500 status code.

I want to send an email to the customer with a PDF attachment when the checkout.sessionpleted webhook is triggered. I read that the webhook should return a 200 status code as quickly as possible; otherwise, Stripe might time out. Or could the issue be caused by a time difference between the request and the server?

Regardless, I’m unsure how to properly fix this. I’d really appreciate any guidance from someone with experience.

import datetime
import stripe
from django.conf import settings
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from webshop.models import Product, Customer, Order, OrderItem
from django.core.mail import EmailMessage

@csrf_exempt
def stripe_webhook(request):
    payload = request.body
    sig_header = request.META.get("HTTP_STRIPE_SIGNATURE")
    endpoint_secret = settings.STRIPE_WEBHOOK_SECRET

    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, endpoint_secret
        )
    except ValueError as e:
        return JsonResponse({"error": str(e)}, status=400)
    except stripe.error.SignatureVerificationError as e:
        return JsonResponse({"error": "Invalid signature"}, status=400)

    # Handle checkout success
    if event["type"] == "checkout.sessionpleted":
        session = event["data"]["object"]
        session_id = session["id"]

        # Fetch line items from Stripe
        line_items = stripe.checkout.Session.list_line_items(session_id)

        # Extract order details
        customer_email = session.get("customer_details").get("email")
        customer_name = session.get("customer_details").get("name")
        amount_total = session.get("amount_total") / 100 
        payment_status = session.get("payment_status") == "paid"

        # Get or create customer
        customer, created = Customer.objects.get_or_create(email=customer_email, defaults={"name": customer_name})

        # Create new order
        order = Order.objects.create(
            customer=customer,
            date_ordered=datetime.datetime.now(),
            payment_status=payment_status,
            total_price=amount_total,
        )
        
        ordered_products = []  # Store product names for email

        # Add order items
        for item in line_items["data"]:
            product_name = item["description"]
            quantity = item["quantity"]

            # Get product from database
            try:
                product = Product.objects.get(name=product_name)
                OrderItem.objects.create(order=order, product=product, quantity=quantity)
                ordered_products.append(product_name)
            except Product.DoesNotExist:
                print(f"⚠️ Product '{product_name}' not found in database, skipping.")

        print(f"✅ Order created: {order}")
        
        if ordered_products:
            send_email_with_pdf(ordered_products, customer_email)
        
        return JsonResponse({"status": "Order created"}, status=201)

    return JsonResponse({"status": "success"}, status=200)

def send_email_with_pdf(product_name_list, recipient_email):
    try:
        email = EmailMessage(
            subject='Your PDF(s)',
            body='Please find the attached PDFs.',
            from_email='[email protected]',
            to=[recipient_email],
        )

        # Attach multiple PDFs
        for name in product_name_list:
            try:
                product_obj = Product.objects.get(name=name)
                if product_obj.remschema:
                    pdf_name = product_obj.remschema.name
                    pdf_content = product_obj.remschema.read()
                    email.attach(pdf_name, pdf_content, 'application/pdf')
            except Product.DoesNotExist:
                print(f"Product '{name}' not found.")

        # Send the email only if there are attachments
        if email.attachments:
            email.send()
            return "Email sent successfully!"
        else:
            return "No PDFs found to send."
    
    except Product.DoesNotExist:
        print("Product nog found")
        return "PDF not found."
    except Exception as e:
        print(f"Error: {e}")
        return f"Error: {e}"

stripe dashboard sc

I have the feeling that maybe the view is to slow?

L.S.

I am working on my first Django project and struggling with the Stripe integration. My webhook is acting inconsistently—it worked fine when running locally on localhost with the Stripe CLI. However, after deploying with Nginx and Gunicorn, it has become unreliable. Sometimes it goes through, but in most cases, I receive a 500 status code.

I want to send an email to the customer with a PDF attachment when the checkout.sessionpleted webhook is triggered. I read that the webhook should return a 200 status code as quickly as possible; otherwise, Stripe might time out. Or could the issue be caused by a time difference between the request and the server?

Regardless, I’m unsure how to properly fix this. I’d really appreciate any guidance from someone with experience.

import datetime
import stripe
from django.conf import settings
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from webshop.models import Product, Customer, Order, OrderItem
from django.core.mail import EmailMessage

@csrf_exempt
def stripe_webhook(request):
    payload = request.body
    sig_header = request.META.get("HTTP_STRIPE_SIGNATURE")
    endpoint_secret = settings.STRIPE_WEBHOOK_SECRET

    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, endpoint_secret
        )
    except ValueError as e:
        return JsonResponse({"error": str(e)}, status=400)
    except stripe.error.SignatureVerificationError as e:
        return JsonResponse({"error": "Invalid signature"}, status=400)

    # Handle checkout success
    if event["type"] == "checkout.sessionpleted":
        session = event["data"]["object"]
        session_id = session["id"]

        # Fetch line items from Stripe
        line_items = stripe.checkout.Session.list_line_items(session_id)

        # Extract order details
        customer_email = session.get("customer_details").get("email")
        customer_name = session.get("customer_details").get("name")
        amount_total = session.get("amount_total") / 100 
        payment_status = session.get("payment_status") == "paid"

        # Get or create customer
        customer, created = Customer.objects.get_or_create(email=customer_email, defaults={"name": customer_name})

        # Create new order
        order = Order.objects.create(
            customer=customer,
            date_ordered=datetime.datetime.now(),
            payment_status=payment_status,
            total_price=amount_total,
        )
        
        ordered_products = []  # Store product names for email

        # Add order items
        for item in line_items["data"]:
            product_name = item["description"]
            quantity = item["quantity"]

            # Get product from database
            try:
                product = Product.objects.get(name=product_name)
                OrderItem.objects.create(order=order, product=product, quantity=quantity)
                ordered_products.append(product_name)
            except Product.DoesNotExist:
                print(f"⚠️ Product '{product_name}' not found in database, skipping.")

        print(f"✅ Order created: {order}")
        
        if ordered_products:
            send_email_with_pdf(ordered_products, customer_email)
        
        return JsonResponse({"status": "Order created"}, status=201)

    return JsonResponse({"status": "success"}, status=200)

def send_email_with_pdf(product_name_list, recipient_email):
    try:
        email = EmailMessage(
            subject='Your PDF(s)',
            body='Please find the attached PDFs.',
            from_email='[email protected]',
            to=[recipient_email],
        )

        # Attach multiple PDFs
        for name in product_name_list:
            try:
                product_obj = Product.objects.get(name=name)
                if product_obj.remschema:
                    pdf_name = product_obj.remschema.name
                    pdf_content = product_obj.remschema.read()
                    email.attach(pdf_name, pdf_content, 'application/pdf')
            except Product.DoesNotExist:
                print(f"Product '{name}' not found.")

        # Send the email only if there are attachments
        if email.attachments:
            email.send()
            return "Email sent successfully!"
        else:
            return "No PDFs found to send."
    
    except Product.DoesNotExist:
        print("Product nog found")
        return "PDF not found."
    except Exception as e:
        print(f"Error: {e}")
        return f"Error: {e}"

stripe dashboard sc

I have the feeling that maybe the view is to slow?

Share asked Mar 10 at 21:19 TSG16TSG16 1
Add a comment  | 

1 Answer 1

Reset to default 0

Its hard to know where something went wrong just based on this - could you get some logging going - maybe even with something like Sentry so you can easily see if there is anything going unhandled? then share here.

That being said, there are some easy performance improvements you can make here. Without knowing about your DB, rest of infrastructure, how many order items etc that we are iterating through, I don't know if they will be causing this issue. You should still look at making them though.

  1. look into making one query in your main view for the products. rather than .get() n amount of times based on your line_items, grab all of those product names and do one query - Product.objects.filter(name__in=[YOUR_NAMES]). iterate over them for your OrderItem stuff if you need to
  2. you can get all the products you need with that ^ filter() and pass the products as a queryset to the send_email_with_pdf function. That will prevent you having to do do all those .get() calls again in there, each of which are DB hits.
  3. in your view, each of your OrderItem.objects.create() calls are also new queries. instead. everytime you iterate here, instantiate an OrderItem with your data and append it to a list. Then when you're done, call OrderItem.objects.bulk_create([YOUR_ORDER_ITEM_LIST]) with your list of instances. Again this will be just one db hit instead of n

These 3 ideas will drastically improve your performance of this view, especially if you are dealing with a lot of products/items here. That being said, more logging will help and you might find that you need to move this email sending logic to something in the background - Celery, etc

发布评论

评论列表(0)

  1. 暂无评论