43. Finalizing orders and treating errors

Currently, in our checkout.html file, submitting the forms does not trigger any action since no action is linked to them. We need to capture the form data and integrate it with the Mercado Pago API to handle payments.

For better organization, whenever we use an external API, we will create a separate file dedicated to consolidating all functions and functionalities related to that API.

Therefore, inside our store folder, we will create a new file named api_mercadopago.py to manage all functions and integrations related to the Mercado Pago API.

When the user finalizes their order, we want to redirect them to a page where they can complete the purchase. To do this, we will create a new view function called finish_order. For now, this function will simply redirect the user back to the store.

views.py
def finish_order(request) :
    return redirect("store")

Don't forget to add the new finish_order URL pattern to the urls.py file to ensure the view function is properly linked and accessible.

urls.py
    path('finishorder/',finish_order, name="finish_order"),

The finish_order view function will handle the integration of the features from api_mercadopago.py with the form data. Therefore, inside the checkout.html file, we will reference this view function in the form's action attribute to ensure the data is processed correctly during checkout.

checkout.html
<form method="POST" action="{% url 'finish_order' %}">

Before integrating the payment system, we need to handle potential error cases. For example, we should ensure that the user has provided their email address if they are not logged in. This will prevent issues during the checkout process and ensure all necessary information is collected.

First, we need to identify which order is being finalized. To do this, we will pass the order ID in the URL within the checkout.html file. This will allow the order ID to be accessed and utilized in the finish_order view function.

checkout.html
<form method="POST" action="{% url 'finish_order' order.id %}">

As done previously, we need to include the order ID as a parameter in the finish_order URL pattern within the urls.py file. This will ensure that the ID is properly passed to the finish_order view function.

urls.py
    path('finishorder/<int:order_id>/',finish_order, name="finish_order"),

Lastly, in the checkout.html file, we need to add a name attribute to the email input box. Without it, we won't be able to capture the email data in our view function.

checkout.html
        <input type="email" name="email" placeholder="email"> <!--Placeholder is the transparent text that appears before-->

Now, we will begin updating the finish_order function to handle error cases, such as:

  • Missing address or email fields.

  • Incorrect pricing information.

This will ensure that any issues are caught and addressed before proceeding with the payment process.

views.py
def finish_order(request, order_id) :
    if request.method == "POST" :
        error = None
        data = request.POST.dict()
        total = data.get("total")
        order = Order.objects.get(id=order_id) #? used get because it is only one order

        if float(total) != float(order.total_cost) :
            error = "conflicting_cost" #? in case the user tries to manipulate the html

        if not "adress" in data :
            error = "adress"
        else :
            adress = data.get("adress")
        
        if not request.user.is_authenticated :
            email = data.get("email")
            try :
                validate_email(email)
            except ValidationError :
                error = "email"


        context = {"error" : error}
        return redirect("checkout")
    else :
        return redirect("store")

Explanation of the code above:

  • Function Definition: def finish_order(request, order_id)

    • Defines the view function for finalizing an order with a given order_id.

  • Check Request Method: if request.method == "POST"

    • Handles form submissions made via POST.

  • Initialization:

    • error = None: Initializes an error variable to store any error messages.

    • data = request.POST.dict(): Converts the POST data into a dictionary for easier access.

  • Retrieve Order: order = Order.objects.get(id=order_id)

    • Retrieves the Order object with the specified order_id. Assumes only one order with this ID exists.

  • Cost Validation:

    • if float(total) != float(order.total_cost): Checks if the total amount from the POST data matches the order’s total_cost.

    • If they don't match, sets error = "conflicting_cost" to indicate a potential manipulation attempt.

  • Address Validation:

    • if not "adress" in data: Checks if the address field is missing from the POST data.

    • If missing, sets error = "adress" to indicate that the address is required.

    • If present, retrieves the address value.

  • Email Validation (if unauthenticated):

    • if not request.user.is_authenticated: If the user is not authenticated, validates the provided email.

    • Retrieves the email from POST data and attempts to validate it using validate_email(email).

    • If validation fails, sets error = "email".

  • Context Preparation: context = {"error": error}

    • Prepares a context dictionary with the error message.

  • Redirect:

    • return redirect("checkout"): Redirects to the checkout page if there are no errors.

    • else: return redirect("store"): Redirects to the store page if the request method is not POST.

Last updated