'Why does Stripe webhook work in test mode but not live: error 403 CSRF verification failed. Request aborted

I have a django website with shopping cart hooked up to Stripe. On successful payment, the system emails the customer with their purchased electronic products (links to videos and online meetings).

This works perfectly when Stripe is in test mode, but when live, the webhook that triggers the email fails to complete. I don't think it is a browser cookie problem as it happened with a remote user, not just me on my own computer.

I assume it is something to do with CRSRF_Token but I don't have the knowledge or experience to understand it yet. Any help would be greatly appreciated. Code below.

Views:

 @csrf_exempt
def stripe_webhook(request):
    payload = request.body
    sig_header = request.META['HTTP_STRIPE_SIGNATURE']
    event = None

    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, settings.STRIPE_WEBHOOK_SECRET
        )
    except ValueError as e:
        # Invalid payload
        return HttpResponse(status=400)
    except stripe.error.SignatureVerificationError as e:
        # Invalid signature
        return HttpResponse(status=400)

    # Handle the checkout.session.completed event
    if event['type'] == 'checkout.session.completed':
        session = event['data']['object']

        # Save an order in your database, marked as 'awaiting payment'
        create_order(session)

        # Check if the order is already paid (e.g., from a card payment)
        #
        # A delayed notification payment will have an `unpaid` status, as
        # you're still waiting for funds to be transferred from the customer's
        # account.
        if session.payment_status == "paid":
            # Fulfill the purchase
            fulfill_order(session)

    elif event['type'] == 'checkout.session.async_payment_succeeded':
        session = event['data']['object']

        # Fulfill the purchase
        fulfill_order(session)

    elif event['type'] == 'checkout.session.async_payment_failed':
        session = event['data']['object']

        # Send an email to the customer asking them to retry their order
        email_customer_about_failed_payment(session)

    # Passed signature verification
    return HttpResponse(status=200)


def create_order(session):
    print(">>>>>>>>>>>>>>>>create_order")
    customer_email = session["customer_details"]["email"]
    transaction_id = session["payment_intent"]
    customer = Customer.objects.get(
        email=customer_email
    )

    # get or create order
    try:
        print("tryingg")
        order = Order.objects.get(transaction_id=transaction_id)
    except Order.DoesNotExist:
        print("exception")
        order = Order(customer=customer, complete=False,
                      order_status='W', transaction_id=transaction_id)
        order.save()

    digital_products_url = []
    product_info = []
    for key, value in session["metadata"].items():
        if "item" in key:
            item_list = value.split(",")
            product_id = item_list[0]
            product_quantity = item_list[1]

            product = Product.objects.get(id=product_id)
            if product.digital:
                digital_products_url.append(product.product_url)
                product_info.append({
                    "name": product.name,
                    "event_type": product.event_type,
                    "product_url" : product.product_url,
                    "meeting_passcode" : product.meeting_passcode,
                    "subtitle" : product.subtitle,
                    "intro_text" : product.intro_text,
                    "short_description" : product.short_description,
                    "price" : product.price,
                    "image" : product.image,
                    "start_time" : product.start_time,
                    "duration" : product.duration,
                    "description" : product.description
                    })

            orderItem = OrderItem.objects.create(
                product=product,
                order=order,
                quantity=product_quantity,
            )
    session["metadata"]["digital_product_url"] = digital_products_url
    session["metadata"]["product_info"] = product_info


def fulfill_order(session):
    print("fulfill_order")
    transaction_id = session["payment_intent"]
    order = Order.objects.get(
        transaction_id=transaction_id
    )
    order.complete = True
    order.order_status = 'P'
    order.save()

    digital_products_url = session["metadata"]["digital_product_url"]
    customer_email = session["customer_details"]["email"]
    customer = Customer.objects.get(
        email=customer_email
    )

    if order.shipping == True:
        ShippingAddress.objects.create(
            customer=customer,
            order=order,
            address=session["metadata"]['address'],
            city=session["metadata"]['city'],
            state=session["metadata"]['state'],
            zipcode=session["metadata"]['zipcode'],
        )

    if digital_products_url:
        # send email to user
        try:
            from_email = settings.EMAIL_HOST_USER
            to_email = [customer_email]

            # import html message.html file
            html_template = 'store/mail.html'

            html_message = render_to_string(
                html_template, {'context': digital_products_url, "product_info": session["metadata"]["product_info"] })

            message = EmailMessage(
                'North Wales Early Music Festival', html_message, from_email, to_email)
            # this is required because there is no plain text email message
            message.content_subtype = 'html'
            message.send()

            order.email_sent = True
            order.save()

        except Exception as e:
            order.email_sent = False
            order.save()

Checkout Utility code which contains alos uses csrf_token:

    var form = document.getElementById('form');
      form.addEventListener('submit', function (e) {
        e.preventDefault();
        console.log('Form Submitted...');
        payment_btn = document.getElementById('make-payment');
        payment_btn.click();
        // document.getElementById('form-button').classList.add('hidden');
        // document.getElementById('payment-info').classList.remove('hidden');
      });


  document
    .getElementById('make-payment')
    .addEventListener('click', function (e) {
      submitFormData();
    });

  function submitFormData() {
    console.log('Payment button clicked');

    var userFormData = {
      name: null,
      email: null,
      total: total,
    };

    var shippingInfo = {
      address: null,
      city: null,
      state: null,
      zipcode: null,
    };

    if (shipping != 'False') {
      shippingInfo.address = form.address.value;
      shippingInfo.city = form.city.value;
      shippingInfo.state = form.state.value;
      shippingInfo.zipcode = form.zipcode.value;
    }

    if (user == 'AnonymousUser') {
      userFormData.name = form.name.value;
      userFormData.email = form.email.value;
    }

    console.log('Shipping Info:', shippingInfo);
    console.log('User Info:', userFormData);

    var url = '/create-checkout-session/';
    fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'applicaiton/json',
        'X-CSRFToken': csrftoken,
      },
      body: JSON.stringify({ form: userFormData, shipping: shippingInfo}),
    })
    .then(function (response) {
          return response.json();
        })
        
        .then(function (session) {
          if (session.status == "success"){
            // delete cookie
            cart = {};
            document.cookie = 'cart=' + JSON.stringify(cart) + ';domain=;path=/';
            console.log("cart deleted")
          }
          return stripe.redirectToCheckout({ sessionId: session.id });
        })
        .then(function (result) {
          // If redirectToCheckout fails due to a browser or network
          // error, you should display the localized error message to your
          // customer using error.message.
          if (result.error) {
            alert(result.error.message);
          }
        })
        .catch(function (error) {
          console.error("Error:", error);
        });
  }
</script>


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source