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/)
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:
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:
You can also opt to pay using bank card:
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!
Top comments (8)
The code snippet for product detail has expired, please is this code not on Github?
I did this and I'm proud to say that I learnt a lot.
Thanks Nick!
Thank you brotherly, you saved my day!
Thanks allot man, you just save my ass!!
bro i did the same and it redirects to flutterwave and completes payment but the callback function is giving problems. i would appreciate any halps please..
I'm integrating it in my code is not working
Thanks for the great tutorial, but all the links to the code are returning a 404 page not found error. Would be great if you updated the links.
thanks bro. this has just saved me. Been looking for this for a very long time, the links do not work any more but i made my own htmls, worked well. God bless you