Django Stripe InvalidRequestError: Request req_******: Not a valid URL

Question:

I’m using Stripe with django, but while clicking the checkout it return the stripe.error.InvalidRequestError: Request req_N8rlEhXn42Cyxz: Not a valid URL (due to this question, First i tried my localhost:8000/payment/done then tried it using ngrok so that it could be accessible globaly, but still not working).

it’s my settings.py

.....
STRIPE_PUBLIC_KEY = (
"pk_test_51J5kFsFfjIr2jaBwRlGyDJ9McmvvBGclXxkasIGggbh3Fz076sWsGv9z7pSWxy7WuncldIMXDFOq1U3j2bv2Zstu00lMmMnLjD"
)
STRIPE_SECRET_KEY = (
"sk_test_51J5kFsFfjIr2jaBwg1Khel6thuio8gTshaTeIwe0GjqaM57x6XvjZNxQE0udNk8BAVqjjEkCHtFs8KXBEglu2zbR005LkwAzaq"
)
STRIPE_WEBHOOK_SECRET = ""

and my views.py

class CreateCheckOutSessoionView(View):
    def post(self, request, *args, **kwargs):
        ng = "https://localhost:8000"
        product_id = kwargs.get("pk")
        product = Product.objects.get(id=product_id)
        checkout_session = stripe.checkout.Session.create(
            payment_method_types=["card"],
            line_items=[
                {
                    "price_data": {
                        "currency": "usd",
                        "unit_amount": product.get_cent_price,
                        "product_data": {
                            "name": product.name,
                            "images": [product.image.url],
                        },
                    },
                    "quantity": 1,
                },
            ],
            mode="payment",
            success_url=ng + "/payment/done",
            cancel_url=ng + "/payment/cancel",
        )
        return JsonResponse({"id": checkout_session.id})

and it’s how i’m sending post to this view

<button type="button" id="checkout-button">Checkout</button>
<script type="text/javascript">
    const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;

    // Create an instance of the Stripe object with your publishable API key
    var stripe = Stripe("{{ STRIPE_PUBLIC_KEY }}");
    var checkoutButton = document.getElementById("checkout-button");

    checkoutButton.addEventListener("click", function () {
      fetch("{% url 'payment:create-checkout-session' 1 %}", {
        method: "POST",
        headers: {
            'X-CSRFToken': csrftoken,
        },
      })
        .then(function (response) {
          return response.json();
        })
        .then(function (session) {
          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>

I’ve no idea on..

Asked By: Ali Aref

||

Answers:

product.image.url is a relative URL. It needs to be an absolute URL.

Answered By: floatingLomas

I just ran into this issue and was able to fix it, here’s what worked for me:

Problem

When calling stripe.checkout.Session.create and passing success_url and cancel_url, the method will fail if the URLs are not publicly accessible. If you’re working on localhost, Stripe is unable to verify that the URLs are accessible and the checkout session fails.

Solution

Set up a tunnel to make the application URLs publicly accessible without pushing to a staging or production server.

Steps

I used a command-line utility called localtunnel, although there are other options out there (ngrok for instance).

  1. Install localtunnel by running the command npm install -g localtunnel from your command line
  2. In your settings.py, add these lines. They will allow your application to be accessed over a localtunnel URL, and enable CSRF features like logging in:
ALLOWED_HOSTS = ['.loca.lt']

CSRF_TRUSTED_ORIGINS = ['http://*.loca.lt', 'https://*.loca.lt']
  1. Pick a subdomain URL for your localtunnel instance (i.e. my-unique-subdomain.loca.lt) and update the success_url and cancel_url arguments to use that URL. Make sure the subdomain you pick is unique enough to be unlikely to already be in use by another instance of localtunnel. Your code should look something like this:
...
baseURL = "https://my-unique-subdomain.loca.lt"
checkout_session = stripe.checkout.Session.create(
  ...
  success_url = baseURL + "/payment/done",
  cancel_url = baseURL + "/payment/cancel",
  ...
  1. Start up your Django app and note what port it’s running on (I believe it’s 8000 by default)
  2. Start up localtunnel by running lt --subdomain my-unique-subdomain --port 8000 (make sure to replace my-unique-subdomain and 8000 with your own subdomain and port number)
  3. Go to the localtunnel URL, log in to your app, and visit the endpoint for your checkout session. It should successfully be returning a checkout ID (if not, make sure you’re using https)

Hope that helps!

Answered By: Chris Gustin
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.