Collect payments in your Django ecommerce portal using Flutterwave ~PART 2

Hello everyone and welcome to part 2 of the series where we are building an electronics store and then collect payments from users who can choose how to pay. In case you missed part 1 then here it is.We ended it where we had the following page on (http://localhost:8000/)
products
Well, in this part we are going to create a detail page for an individual product and then add a form to collect payment details including the name, email and phone number of the user. Once we have that data, we shall connect with Flutterwave to process the payment. We will then grab the response from Flutterwave in our callback function and do stuff with it. Sounds fun? Let's get dirty!

PRODUCT DETAIL

We need to find a way of accessing a single product when its name is clicked. Open views.py and add the following code to it below what we already have:

def product_detail(request, pk):
    data = Product.objects.get(id=pk)
    ctx={
        'product':data
    }
    return render(request,
                  'product.html',
                  ctx)

So we are grabbing the current product's ID and the asking the database to give us only its details. We add the details to our context variable and render it in the HTML. Open urls.py and add a path like so:

path('product/<int:pk>/details', product_detail, name='details'),

Next up let's update the 'templates/products.html' like in the paste found here
Then open 'templates/product.html' and add to it the code found here
With all that in place access the homepage and click on any product and, hopefully, you should see its detailed page like the one below:
product detail
If you encounter an error please check the terminal console and fix before moving on.

PAYMENTS FORM

We need to capture the details of the user trying to buy a given product. In django the best way to do this is to present the customer with a form which they fill and then on submission, we grab the values the user entered. Create a new file inside 'electronics' called forms.py:

touch electronics/forms.py

Add the following code to it:

from django import forms

class PaymentForm(forms.Form):
    name = forms.CharField(label='Your name', max_length=100)
    email = forms.EmailField()
    phone=forms.CharField(max_length=15)
    amount = forms.FloatField()

Our form is ready, next let's update the product detail function to contain the form and the logic to get the form data once a user clicks submit button:

def product_detail(request, pk):
    data = Product.objects.get(id=pk)
    if request.method=='POST':
        form = PaymentForm(request.POST)
        if form.is_valid():
             name=  form.cleaned_data['name']
             email = form.cleaned_data['email']
             amount = form.cleaned_data['amount']
             phone = form.cleaned_data['phone']
             return redirect(str(process_payment(name,email,amount,phone)))
    else:
        form = PaymentForm()
    ctx={
        'product':data,
        'form':form
    }
    return render(request,
                  'product.html',
                  ctx)

We are adding our form to capture payment info.We are checking if the request verb is 'POST'.We then clean the form and get the values the user entered i.e name,email,phone. The amount will be hidden since we can access it from the product details.
Let's move on to create a method that will call Flutterwave's endpoint, inside the views.py add the code below to it.

def process_payment(name,email,amount,phone):
     auth_token= env('SECRET_KEY')
     hed = {'Authorization': 'Bearer ' + auth_token}
     data = {
                "tx_ref":''+str(math.floor(1000000 + random.random()*9000000)),
                "amount":amount,
                "currency":"KES",
                "redirect_url":"http://localhost:8000/callback",
                "payment_options":"card",
                "meta":{
                    "consumer_id":23,
                    "consumer_mac":"92a3-912ba-1192a"
                },
                "customer":{
                    "email":email,
                    "phonenumber":phone,
                    "name":name
                },
                "customizations":{
                    "title":"Supa Electronics Store",
                    "description":"Best store in town",
                    "logo":"https://getbootstrap.com/docs/4.0/assets/brand/bootstrap-solid.svg"
                }
                }
     url = ' https://api.flutterwave.com/v3/payments'
     response = requests.post(url, json=data, headers=hed)
     response=response.json()
     link=response['data']['link']
     return link

Notice that this method loads a secret key from a .env file. Thus create a file in 'electronics' called .env and add the code below:

SECRET_KEY='YOUR FLUTTERWAVE SECRET KEY'

OBTAINING FLUTTERWAVE SECRET KEY

Login to your Flutterwave account and head over to API settings. Generate new keys or copy what you already have if you had earlier generated. What we want in particular is the 'SECRET KEY'. Copy it and add it to the .env file you earlier created.
Next add the following code at the top of your views.py to initialize our environment variables:

import environ
# Initialise environment variables
env = environ.Env()
environ.Env.read_env()

REDIRECT URL

When we make a post request to Flutterwave with all the details collected,they will call your specified redirect_url and post the response to you. They will also append your transaction ID (transaction_id), transaction reference (tx_ref) and the transaction status (status) as query params to the redirect_url like: /tx_ref=ref&transaction_id=30490&status=successful. Once we have this response data we can verify transaction status and provide value to our customers like saving it in the database and providing feedback to users. In this case we are going to simply print the response on the console. Feel free to play around with the response.
Add the following function to views.py:

from django.views.decorators.http import require_http_methods
from django.http import HttpResponse


@require_http_methods(['GET', 'POST'])
def payment_response(request):
    status=request.GET.get('status', None)
    tx_ref=request.GET.get('tx_ref', None)
    print(status)
    print(tx_ref)
    return HttpResponse('Finished')

Then update urls.py like so:

from os import name
from django.contrib import admin
from django.urls import path
from django.conf import settings
from django.conf.urls.static import static
from electronics.views import list_products, payment_response, product_detail

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', list_products, name='list'),
    path('product/<int:pk>/details', product_detail, name='details'),
    path('callback', payment_response, name='payment_response')
 ]
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

UPDATE PRODUCT DETAIL HTML

We have all the logic to handle payment collection. The next thing is to update 'templates/product.html' to reflect this change as well. Copy the code found on this paste and paste it on your file.
Spin the server and head over to the homepage. Select any one product and enter customer name, email and phone and click submit. You should see a payment modal similar to the one below:
pay modal
You can also opt to pay using bank card:
bank pay

DONE DEAL

Phew! If you followed up to this point then pat yourself! That was some load of work. Congratulations for completing the series. In the series we were able to create an electronics store, collect payment from the user who had a choice of picking mobile money or card and then retrieved the transaction status and reference number for value addition.
If you have any questions leave them down below and I'll do my best to answer those.
Find me on Twitter
and
My personal website.
Untill next time, happy hacking!

29