Search code examples
javascriptdjangopaypalpaypal-sandbox

Passing order to through paypal checkout


I have a django app and I am trying to create and Order through paypals checkout. I keep getting an error: "Uncaught Error: Expected an order id to be passed"

My order and shipping address are been successfully created on the back end but paypal isnt seeming to accept it.

I have tried many things to try and fix this. Hoping some one has some ideas: Here is my current script;

<script>
    paypal.Buttons({
    // Order is created on the server and the order id is returned
    createOrder: function() {
        // Call the /get_cart_id/ endpoint to get the cart id
        return fetch('/get_cart_id/')
        .then(function(response) {
            return response.json();
        })
        .then(function(data) {
            var cartId = data.cart_id;
            return fetch('/api/orders/', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRFToken': csrftoken
            },
            body: JSON.stringify({
                cart_id: cartId
            })
            })
            .then(function(response) {
                return response.json();
            })
            .then(function(order) {
                // Call the Checkout function to handle the order
                var shippingInfo = {
                house: form.house.value,
                street: form.street.value,
                city: form.city.value, 
                postcode: form.postcode.value
                };
                
                return fetch('/get_shipping_address/', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'X-CSRFToken': csrftoken
                },
                body: JSON.stringify({
                    shipping: shippingInfo
                })
                })
                .then(function(response) {
                    return response.json();
                })
                .then(function(data) {
                    console.log('Success:', data);
                    alert('Transaction completed');
                });
            });
        });
    },
    // Finalize the transaction on the server after payer approval
    onApprove: function(data) {
        return fetch(`/api/orders/${orderId}/`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-CSRFToken': csrftoken
        },
        body: JSON.stringify({
            order_id: data.orderId
        })
        })
        .then(function(response) {
            return response.json();
        })
        .then(function(orderData) {
            // Successful capture! For dev/demo purposes:
            console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
            var transaction = orderData.purchase_units[0].payments.captures[0];
            alert(`Transaction ${transaction.status}: ${transaction.id}\n\nSee console for all available details`);
            // When ready to go live, remove the alert and show a success message within this page. For example:
            // var element = document.getElementById('paypal-button-container');
            // element.innerHTML = '<h3>Thank you for your payment!</h3>';
            // Or go to another URL:  window.location.href = 'thank_you.html';
        });
    }
    }).render('#paypal-button-container');

Solution

  • Since you're code seems to be doing many uneccessary / inadvisable things, I'll explain a normal integration baseds on the sample in the standard checkout integration guide.

            // Order is created on the server and the order id is returned
            createOrder() {
              return fetch("/my-server/create-paypal-order", {
                method: "POST",
                headers: {
                  "Content-Type": "application/json",
                },
                // use the "body" param to optionally pass additional order information
                // like product skus and quantities
                body: JSON.stringify({
                  cart: [
                    {
                      sku: "YOUR_PRODUCT_STOCK_KEEPING_UNIT",
                      quantity: "YOUR_PRODUCT_QUANTITY",
                    },
                  ],
                }),
              })
              .then((response) => response.json())
              .then((order) => order.id);
            },
    
    

    In this sample, a cart object containing an array of items and quantities (note: never $ amounts) is posted to the server when the createOrder callback is called. This occurs when a button is clicked.

    The server then takes that information and uses it to create a PayPal order, using the v2/checkout/orders API.

    The server then returns the v2/checkout/orders object, or a subset of it. The returned information must include the id of the order, so that the PayPal JS can use it for the checkout. If for whatever reason a valid id is not returned, the error in your question is the expected behavior.

    Next the user proceeds with approving a payment, using whichever button they clicked on / payment method they selected. They select a shipping address during this process, very often whatever the default one is in their PayPal account.

    Once final approval is given at PayPal, clicking a "Pay Now" button or equivalent, the onApprove callback is called.

    A second fetch is done to your server to capture the order. The selected shipping address is returned in the capture response, among other details.


    The above flow can be adjusted if you have specific needs, but it is important to understand the default flow first and how it can best be adjusted. For example, it is possible to use user_action:'CONTINUE'and add an order review step, retrieve selected shipping information before capture, and patch the order before capture, all of which generally result in a superior and smoother checkout than possible alternatives.

    For instance, adding extra steps to collect a shipping address with your own fields, which add friction and slow the process and also still allow (by default) a payer to change that address at PayPal which is probably not what you would want if collecting your own address, although forcing the use of the address you collected would block the checkout with an error if that address is not valid for whatever reason -- which (in addition to adding friction and lowering sales) is part of why this example alternative of collecting a an address with your own fields is not a good one and generally bad design.