Skip to content
Advertisement

How Do I Correctly Serialize and Send PayPal Transaction ID to Django Backend Doing Standard Client Side Integration

I’m trying to get PayPal’s transaction ID after the payment was approved on the client-side. I’m doing client-side integration of PayPal and Django. I can totally get the Payment ID and order ID and so forth, but these will be discarded by PayPal after the payment got approved. PayPal only recorded Transaction ID which can be used to track the payment with PayPal. When I’m trying to serialize the return actions which capture the Transaction ID – somehow I got a status code of 500 – Internal Server Error. The funny thing is that I can totally do console.log(transaction.id) and get the transaction ID in the console. Anyway, my error prone code is below:

In payment.html I got huge portion of html stuff, but I don’t post it here. I only post where the JavaScript begins:

    <script>
          // Generating csrf_token on the fly
          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);
              return actions.order.capture().then(function(orderData) {
                // Successful capture! For dev/demo purposes:
                const transaction = orderData.purchase_units[0].payments.captures[0];
                sendTransactionID();
                function sendTransactionID() {
                    fetch(url, {
                            method: "POST",
                            headers: {
                            "Content-type": "application/json",
                            "X-CSRFToken": csrftoken,
                            },
                            body: JSON.stringify({
                                actualTransID: transaction.id,
                            }),
                        })
                    }
 
                });
              },
 
              // 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 got this:

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)
 
    for item in cart_items:
        ordered_product = OrderProduct()
        ordered_product.order_id = order.id
        ordered_product.payment = processed_payment
        ordered_product.user_id = request.user.id
        ordered_product.product_id = item.product_id
        ordered_product.quantity = item.quantity
        ordered_product.product_price = item.product.price
        ordered_product.ordered = True
        ordered_product.save()
 
        cart_item = CartItem.objects.get(id=item.id)
        product_variation = cart_item.variations.all()
        ordered_product = OrderProduct.objects.get(id=ordered_product.id)
        ordered_product.variation.set(product_variation)
        ordered_product.save()
 
        # Reduce the quantity of the sold products
        product = Product.objects.get(id=item.product_id)
        product.stock -= item.quantity
        product.save()
 
    # Clear the cart of cart items
    CartItem.objects.filter(user=request.user).delete()
 
    # 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,
    })
    to_email = order.email
    send_email = EmailMessage(mail_subject, message, to=[to_email])
    send_email.send()
 
    # 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)

If I take out this in payment.html:

return actions.order.capture().then(function(orderData) {
                // Successful capture! For dev/demo purposes:
                const transaction = orderData.purchase_units[0].payments.captures[0];
                sendTransactionID();
                function sendTransactionID() {
                    fetch(url, {
                            method: "POST",
                            headers: {
                            "Content-type": "application/json",
                            "X-CSRFToken": csrftoken,
                            },
                            body: JSON.stringify({
                                actualTransID: transaction.id,
                            }),
                        })
                    }
 
                });

Which I will be left with:

<script>
              // Generating csrf_token on the fly
              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>

This would totally work – and in my Payment model I could only record Payment ID and Order ID and so forth – but these are useless after payment went through with PayPal – because PayPal only retains the Transaction ID – and I can’t get Transaction ID to be sent to the backend – but I can only print to the console using console.log – and this is frustrating.

If I can get transaction ID to be sent to the backend using fetch – then I can do something like this:

completed_payment = Payment(
            paypal_transaction_id=body['actualTransID']
        )
        completed_payment.save()

But can this be done even though the first redirection already happened with this code:

.then((data) => {
                                    window.location.href = redirect_url + '?order_number=' + data.order_number + '&payment_id=' + data.transID;

So, is it then I need to fetch the redirect_url (such as payment_complete view) and not the previous url (such as payment view)? Basically, the JavaScript stuff really got me confused. Something is wrong with my code? Any help? Thanks…

Advertisement

Answer

An instructor of mine on Udemy solved this issue. The answer is to do this in the onApprove function using the code below:

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

Here is the completed working code for PayPal Client Side Integration with the ability to record PayPal Transaction ID into the database.

<script>
          // Generating csrf_token on the fly
          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' %}"
          const order_errors_url = "{% url 'order_errors' %}"
          // 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;
                      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>

IN the payment view, you can always do something like this:

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 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)
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement