What is the proper way to Serialize and Transmit a PayPal Transaction ID to a Django Backend while implementing Standard Client Side Integration?

My goal is to obtain PayPal's transaction ID after the client-side payment approval. I'm currently integrating PayPal and Django on the client-side. While I can easily retrieve the Payment ID and order ID, PayPal discards these once the payment is approved. The only crucial data that PayPal retains is the Transaction ID, which is essential for tracking the payment with PayPal. Strangely, when I attempt to serialize the return actions that capture the Transaction ID, I encounter a 500 - Internal Server Error. What's interesting is that I am able to access the transaction ID in the console by using console.log(transaction.id). Below is the snippet of my problematic code:

In my payment.html file, there is a substantial amount of HTML content which I won't include here. I will just focus on where the JavaScript starts:

    <script>
          // Generating csrf_token dynamically
          function getCookie(name) {
          let cookieValue = null;
          if (document.cookie && document.cookie !== '') {
            const cookies = document.cookie.split(';');
            for (let i = 0; i < cookies.length; i++) {
                const cookie = cookies[i].trim();
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) === (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
          }
          return cookieValue;
}
 
          let amount = "{{ grand_total }}"
          const url = "{% url 'payment' %}"
          let csrftoken = getCookie('csrftoken');
          let orderID = "{{ order.order_number }}"
          const payment_method = 'PayPal'
          const redirect_url = "{% url 'order_complete' %}"
          // Render the PayPal button into #paypal-button-container
          const paypalButtonsComponent = paypal.Buttons({
              // optional styling for buttons
              // https://developer.paypal.com/docs/checkout/standard/customize/buttons-style-guide/
              style: {
                color: "gold",
                shape: "pill",
                layout: "vertical"
              },
 
              // set up the transaction
              createOrder: (data, actions) => {
                  // pass in any options from the v2 orders create call:
                  // https://developer.paypal.com/api/orders/v2/#orders-create-request-body
                  const createOrderPayload = {
                      purchase_units: [
                          {
                              amount: {
                                  value: amount
                              }
                          }
                      ]
                  };
 
                  return actions.order.create(createOrderPayload);
              },
 
              // finalize the transaction
              onApprove: (data, actions) => {
                  const captureOrderHandler = (details) => {
                      const payerName = details.payer.name.given_name;
                      console.log(details);
                      console.log('Transaction completed');
                      sendData();
                      function sendData() {
                        fetch(url, {
                            method: "POST",
                            headers: {
                            "Content-type": "application/json",
                            "X-CSRFToken": csrftoken,
                            },
                            body: JSON.stringify({
                                orderID: orderID,
                                transID: details.id,
                                payment_method: payment_method,
                                status: details.status,
                            }),
                        })
                         .then((response) => response.json())
                         .then((data) => {
                            window.location.href = redirect_url + '?order_number=' + data.order_number + '&payment_id=' + data.transID;
                        });
                      }
                  };
 
              return actions.order.capture().then(captureOrderHandler);
              },
 
              // handle unrecoverable errors
              onError: (err) => {
                  console.error('An error prevented the buyer from checking out with PayPal');
              }
          });
 
          paypalButtonsComponent
              .render("#paypal-button-container")
              .catch((err) => {
                  console.error('PayPal Buttons failed to render');
              });
 
</script>

In my order's view, I have the following logic:

def payment(request):
    body = json.loads(request.body)
    order = Order.objects.get(user=request.user, is_ordered=False, order_number=body['orderID'])
 
    # Store transaction details inside Payment model 
    processed_payment = Payment(
        user=request.user,
        payment_id=body['transID'],
        payment_method=body['payment_method'],
        amount_paid=order.order_total,
        status=body['status'],
    )
    processed_payment.save()
 
    order.payment = processed_payment
    order.is_ordered = True
    order.save()
 
    # Move the cart items to Ordered Product table
    cart_items = CartItem.objects.filter(user=request.user)
    
    ...
    
    # Send order received email to customer
    mail_subject = 'Thank you for your order!'
    message = render_to_string('order_received_email.html', {
        'user': request.user,
        'order': order,
    })
    
    ...
    
    # Send order number and transaction id back to sendData method via JsonResponse
    data = {
        'order_number': order.order_number,
        'transID': processed_payment.payment_id,
    }
    return JsonResponse(data)
...

Answer №1

Avoid the risky practice of capturing information on the client-side before sending it to a server for ecommerce payments. This approach poses significant security concerns.

Instead, opt for server-side API calls to initiate and complete a PayPal order. Utilize tools like the Checkout-Python-SDK or make direct HTTPS API calls by obtaining an access_token with your client_id and secret keys. Set up two Django routes exclusively for outputting JSON data: one for creating orders and another for capturing them based on specific identification parameters. The capture route can also handle recording transaction IDs in your database and executing additional necessary processes (e.g., sending confirmation emails or reserving products) before forwarding the results to the frontend caller in an easily interpretable format.

Upon establishing these essential routes, refer to this link for guiding the frontend approval process: https://developer.paypal.com/demo/checkout/#/pattern/server

Answer №2

A mentor I found on Udemy provided a resolution for this issue. The solution involves implementing the following code within the onApprove function:

transaction_id = details['purchase_units'][0]['payments']['captures'][0].id
// console.log(transaction_id)

Here is the finalized operational code for integrating PayPal Client Side with the capability to save PayPal Transaction ID in the database.

<script>
          // Generating csrf_token dynamically
          function getCookie(name) {
          let cookieValue = null;
          if (document.cookie && document.cookie !== '') {
            const cookies = document.cookie.split(';');
            for (let i = 0; i < cookies.length; i++) {
                const cookie = cookies[i].trim();
                // Does this cookie string start with the desired name?
                if (cookie.substring(0, name.length + 1) === (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
          }
          return cookieValue;
}

          let amount = "{{ grand_total }}"
          const url = "{% url 'payment' %}"
          let csrftoken = getCookie('csrftoken');
          let orderID = "{{ order.order_number }}"
          const payment_method = 'PayPal'
          const redirect_url = "{% url 'order_complete' %}"
          const order_errors_url = "{% url 'order_errors' %}"
          // Display the PayPal button in #paypal-button-container
          const paypalButtonsComponent = paypal.Buttons({
              // optional styling for buttons
              // https://developer.paypal.com/docs/checkout/standard/customize/buttons-style-guide/
              style: {
                color: "gold",
                shape: "pill",
                layout: "vertical"
              },

              // set up the transaction
              createOrder: (data, actions) => {
                  // pass in any options from the v2 orders create call:
                  // https://developer.paypal.com/api/orders/v2/#orders-create-request-body
                  const createOrderPayload = {
                      purchase_units: [
                          {
                              amount: {
                                  value: amount
                              }
                          }
                      ]
                  };

                  return actions.order.create(createOrderPayload);
              },

              // finalize the transaction
              onApprove: (data, actions) => {
                  const captureOrderHandler = (details) => {
                      const payerName = details.payer.name.given_name;
                      transaction_id = details['purchase_units'][0]['payments']['captures'][0].id
                      //console.log(transaction_id)
                      sendData();
                      function sendData() {
                        fetch(url, {
                            method: "POST",
                            headers: {
                            "Content-type": "application/json",
                            "X-CSRFToken": csrftoken,
                            },
                            body: JSON.stringify({
                                orderID: orderID,
                                transID: details.id,
                                paypal_transaction_id: transaction_id,
                                payment_method: payment_method,
                                status: details.status,
                            }),
                        })
                         .then((response) => response.json())
                         .then((data) => {
                            window.location.href = redirect_url + '?order_number=' + data.order_number + '&payment_id=' + data.transID;
                        });
                      }
                  };

                  return actions.order.capture().then(captureOrderHandler);
              },

              // handle unrecoverable errors
              onError: (err) => {
                  // console.error('An error prevented the buyer from checking out with PayPal');
                  window.location.href = order_errors_url
              }
          });

          paypalButtonsComponent
              .render("#paypal-button-container")
              .catch((err) => {
                  console.error('PayPal Buttons failed to render');
              });

</script>

Within the payment view, you can implement something similar to the following:

def payment(request):
    body = json.loads(request.body)
    order = Order.objects.get(user=request.user, is_ordered=False, order_number=body['orderID'])

    # Store transaction details inside Payment model
    processed_payment = Payment(
        user=request.user,
        payment_id=body['transID'],
        paypal_transaction_id=body['paypal_transaction_id'],
        payment_method=body['payment_method'],
        amount_paid=order.order_total,
        status=body['status'],
    )
    processed_payment.save()

    order.payment = processed_payment
    order.is_ordered = True
    order.save()
    # Send back order number and transaction id to sendData method via 
    # JsonResponse
    data = {
     'order_number': order.order_number,
     'transID': processed_payment.payment_id,
    }
    return JsonResponse(data)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The npm command returns an error message stating "Either an insufficient amount of arguments were provided or no matching entry was found."

I'm trying to pass a custom flag from an npm script to my webpack config, but I keep encountering the following error in the logs: Insufficient number of arguments or no entry found. Alternatively, run 'webpack(-cli) --help' for usage info. ...

The challenge of navigating through multiple redirect() calls in NoReverseMatch

I have encountered a challenge in my views.py file where I am using two functions that contain a redirect('account') call for logged-in users. When running the Django code, an error is thrown: Reverse for 'account' with arguments &apo ...

Having difficulty parsing Json in Qt 6 due to nested objects within an object

I am currently working on a C++ application that acts as a mashup by making requests to an API and parsing the JSON data it receives. The API in question is from TV Maze, specifically at this link: . By including the name of a TV show after the '=&apo ...

Prevent unsaved changes on Telerik MVC website batch editing grid upon closing the window

I am currently working on a webpage in ASP.Net MVC3 with Razor engine. I have implemented Telerik MVC Grid batch editing on this page, and I am using the onDataBinding event to ensure that users save their changes before moving to the next page. However, I ...

Updating the display text length on vue-moment

Currently, I am attempting to showcase an array of numbers const days = [1, 7, 14, 30, 60] in a more human-readable format using vue-moment Everything is functioning correctly {{ days | duration('humanize') }} // 'a day' // '7 d ...

When should we utilize the React.Children API in React applications?

Exploring the potential use cases of the React.Children API. The documentation is a bit confusing for me (Using React v.15.2.0). https://facebook.github.io/react/docs/top-level-api.html#react.children According to the documentation, this.props.children ...

Error: Attempted to search for 'height' using the 'in' operator in an undefined variable

I came across the following code snippet: $('.p1').click(function(){ var num = 10; var count = 0; var intv = setInterval(anim,800); function anim(){ count++; num--; ...

What is the best way to adjust the sprite's position in real-time

Look at this cool sprite I discovered! .common-spinner.common-spinner-40x55 { height: 55px; width: 40px; } .common-spinner { background: url("images/loading-sprite.png") no-repeat scroll 100% 100% transparent; } <div class="loading"> ...

Using jQuery to load content with a dynamic variable instead of specific element IDs

I am facing a minor jQuery issue. I am trying to load a specific area of an external HTML document into a div, but instead of loading just that particular area, the entire document is being loaded. Here's the code snippet for the trigger: $('a& ...

"Aligning the title of a table at the center using React material

I have integrated material-table into my react project and am facing an issue with centering the table title. Here is a live example on codesandbox: https://codesandbox.io/s/silly-hermann-6mfg4?file=/src/App.js I specifically want to center the title "A ...

"Modifying the height of an SVG <g> element: A step-by

Is there a way to adjust the height of a g element? Trying to set the height using {params} but it's not responding I made some changes in the comments because the svg is quite large (Scene.js) {/* Transformation */} <g transform="trans ...

Tips for triggering an event from a function instead of the window

Everything is functioning as expected, with the event listener successfully capturing the custom event when it is dispatched from the window and listened for as loading, all seems to be working well. const MyLib = mylib(); function mylib() { const re ...

Unexpected date outcomes in JavaScript when using a similar date format

Why is it that the first input produces the correct result, while the second input displays a time that is 5 hours behind? new Date("2000-1-1") Sat Jan 01 2000 00:00:00 GMT-0500 (EST) new Date("2000-01-01") Fri Dec 31 1999 19:00:00 GMT-0500 (EST) How can ...

The final piece left in stitching together an array

Issue I have been struggling with this code for some time now and I can't seem to figure out the solution. I am receiving 3 values from inputs and trying to remove all the empty spaces "" in the array, but when I execute the code, it displays the foll ...

Using jQuery to add an element at the current caret position

Within my contentEditable div, there is a table nested. Here is an example of the HTML structure: <div id="divId" contenteditable="true"> <table> <tbody> <tr> <td><br></td> ...

Looking to modify the contents of a shopping cart by utilizing javascript/jQuery to add or remove items?

I'm dealing with a challenge on my assignment. I've been tasked with creating a Shopping Cart using Javascript, HTML5, and JQuery. It needs to collect all the items from the shop inside an Array. While I believe I have most of it figured out, I a ...

What could be causing my AJAX code to malfunction?

This issue should be a simple fix, but I'm completely stumped. I'm working on a test involving an AJAX request and trying to display a variable from a drop-down menu. Unfortunately, the code isn't running at all. Can someone please point ou ...

Oops! An error occurred because it seems like the property 'render' is being read from an undefined value

While experimenting with ThreeJS to create some animations, I encountered an error message that says "Uncaught TypeError: Cannot read properties of undefined (reading 'render')". I am completely lost on what this error means as this is my first ...

Modify row attribute in php to change json file name

I am creating a JSON file using PHP and an ODBC connection. The query I have translates well into the JSON file. This is my SQL query: SELECT Date1, Nett FROM database WHERE Date1 BETWEEN '$bdate' AND '$edate' AND String13='$sr& ...

Bootstraps paves the way for the destruction of a dropdown element

https://getbootstrap.com/docs/4.0/components/dropdowns/ $().dropdown('dispose') Erases the dropdown functionality of an element. What exactly does this mean? Does it remove the tag entirely? Does it render the tag unusable as a dropdown lis ...