What We'll Cover
Constructing a frontend for your web application is the first step towards providing an interactive user experience. The next step is building a working backend, or making your web application functional. Buttons are nice to have, but it's more interesting to have those buttons do something. That's what we'll focus on today.
Through a step-by-step process, we'll develop this functionality. We'll use Flask and a frontend template to create a web application that allows users to buy products through Stripe Checkout (a tool for creating a "checkout" process for products) and subscribe to an email list with help from the Mailgun email API service.
Then, we'll host the web application on Code Capsules so people around the world can buy your product and subscribe to your mailing list.
Requirements
To successfully complete this project, we'll need:
- A text editor (like Sublime or VSCode) installed.
- Python 3.XX+ installed.
- Virtualenv installed.
- Git installed and a GitHub account.
- A Code Capsules account.
Setting Up the Frontend
We'll use the Laurel frontend template from https://cruip.com to add our functionality. This template is perfect for our project – there is already an email subscription box that just needs to be implemented and, with a few modifications, we'll implement a "Buy Now" button.
After downloading the Laurel template:
Create a directory named
project
.Within the
project
directory, create a sub-directory namedtemplates
.Open the downloaded template and extract the files within the
laurel
directory into thetemplates
subdirectory.
You can view the template by opening the index.html
file in the templates
subdirectory. We'll change the first "Early access" button to "Buy Now" and implement Stripe Checkout functionality for it.
Then we'll change the second "Early access" button at the bottom of the template to "Subscribe", and implement the email subscription functionality for it.
First, let's change the "Early access" button texts.
Modifying the "Early access" text
We'll start with changing the first "Early access" button to "Buy Now". Open the index.html
file in a text editor.
Find this line:
<div class="hero-cta"><a class="button button-shadow" href="#">Learn more</a><a class="button button-primary button-shadow" href="#">Early access</a></div>
Replace it with:
<div class="hero-cta">
<a class="button button-shadow" href="#">Learn more</a>
<a id="checkout-button" class="button button-primary button-shadow" href="#">Buy Now</a>
</div>
As well as changing "Early access" to "Buy Now", we've added [SOMETHING?], and given it an ID with id="checkout-button"
. This will be useful when implementing Stripe Checkout.
Next, find this line:
<a class="button button-primary button-block button-shadow" href="#">Early access</a>
Replace "Early access" with "Subscribe".
View the changes by saving the index.html
file and re-opening it in a web browser. We have one more task before building our Flask backend.
Project directory restructuring
To make a functional web application out of our template, Flask requires a specific directory structure, so we will need to reorganise the project
directory. To do this:
Create a new directory named
static
in theproject
directory.Navigate to the
templates
directory and then thedist
directory.Copy all of the directories located in
dist
into thestatic
directory that we created above.
Your project
file structure should look like this:
project
static
css
+ style.css
images
+ iphone-mockup.png
js
+ main.min.js
templates
This was necessary because Flask strictly serves CSS, JavaScript, and images from the static
directory, and renders HTML files in the templates
directory. Because we've moved our template's files around, we now need to edit our index.html
file to point to their new locations.
Flask uses the Jinja templating library to allow us to embed backend code in HTML. This code will be executed on the webserver before a given page is served to the user, allowing us to give that page dynamic functionality and make it responsive to user input.
The first thing we will use Jinja templating for is to dynamically locate and load our index.html
file's stylesheet. Open the index.html
file in the templates
folder and find this line:
<link rel="stylesheet" href="dist/css/style.css">
Replace the value of href
with the string below.
<link rel="stylesheet" href="{{url_for('static',filename='css/style.css')}}">
In Jinja, anything between {{
and }}
is server-side code that will be evaluated before the page is served to users, i.e. when it is rendered. In this way, we can include the output of Python functions and the values of Python variables in our HTML. Jinja syntax is similar to Python, but not identical.
In the Jinja code above, we're calling the function url_for()
, which asks Flask to find the location of our style.css
file in our static
directory.
Speaking of Flask, we're almost ready to implement our functionality.
Setting Up the Virtual Environment
We'll create a virtual environment for our project. The virtual environment will be useful later on when we host our web application on Code Capsules, as it will ensure that the Python libraries we use for development are installed in the Capsule.
To create a virtual environment, navigate to the project
directory in a terminal and enter virtualenv env
.
Activate the virtual environment with:
Linux/MacOSX: source env/bin/activate
Windows: \env\Scripts\activate.bat
If the virtual environment has activated correctly, you'll notice (env)
to the left of your name in the terminal. Keep this terminal open – we'll install the project requirements next.
Installing the requirements
For our project, we'll use the following libraries:
Flask is a lightweight Python web development framework.
Gunicorn is the WSGI server we'll use to host our application on Code Capsules.
Requests is a Python library that allows us to send HTTP requests.
Stripe is another Python library that will help us interact with the Stripe API.
Install these by entering the command below in the virtual environment.
pip3 install flask gunicorn requests stripe
Now we can build the backend for our web application.
Creating the Flask Application
In the projects
folder, create a new file named app.py
. This file will contain all of our Flask code.
Open the app.py
file in a text editor and enter the following:
from flask import Flask, render_template, request
import requests, stripe
app = Flask(__name__)
@app.route("/",methods=["GET","POST"])
def index():
return render_template("index.html")
if __name__== '__main__':
app.run(debug=True)
We import the following functions from flask
:
-
Flask
, which provides the Flask application object. -
render_template()
, which will render ourindex.html
file. -
request
, an object which contains any information sent to our web application – later this will be used to retrieve the email address entered in our subscription box. Be careful not to confuse this with therequests
library.
The index
function has a route decorator which causes it to execute when Flask receives an HTTP GET
or POST
request for the "/" URL, i.e. when someone navigates to the website's domain or IP address in a browser.
When render_template("index.html")
runs, Flask will look in the templates
directory for a file named index.html
and render it by executing its Jinja template code and serving the resulting HTML. To see this, run app.py
with flask run
in your terminal. Open the provided IP address in your browser – the web application should look like this:
Let's make this web application useful and implement the first bit of functionality – the email list feature.
Signing up for Mailgun
We'll use Mailgun to handle our email subscriber list. Mailgun is free up to 5,000 emails per month. Register with Mailgun and continue.
With an account registered, create a mailing list by doing the following:
Log in to Mailgun.
-
Click "Sending" then "Mailing lists" on the dashboard.
At the top right, click "Create mailing list".
Enter whatever you'd like for the address prefix, name, and description – leave everything else default.
Click "Add mailing list".
Navigate to the mailing list we just created. You'll see something called an alias address – Mailgun provides every new mailing list with one. When you send an email to your alias address, Mailgun sends a copy of the email to everyone who is subscribed to your mailing list. Jot down your alias address, we'll use it soon.
Next, you'll need to retrieve the API key for your Mailgun account. We'll use this API key in our web application to validate your Mailgun account when people subscribe to your mailing list.
Find the API key by clicking on your account at the top right of the screen. Click "API keys", and make a note of your private API key.
Implementing the Subscribe Button
With the mailing list created, we can implement the subscribe button.
Re-open the index.html
file. At the bottom of the file, find the line:
<section class="newsletter section">
From the above line down to its corresponding </section>
tag, replace all of the markup with the following:
<section class="newsletter section">
<div class="container-sm">
<div class="newsletter-inner section-inner">
<div class="newsletter-header text-center">
<h2 class="section-title mt-0">Stay in the know</h2>
<p class="section-paragraph">Lorem ipsum is common placeholder text used to demonstrate the graphic elements of a document or visual presentation.</p>
</div>
<form method="POST">
<div class="footer-form newsletter-form field field-grouped">
<div class="control control-expanded">
<input class="input" type="email" name="email" placeholder="Your best email…">
</div>
<div class="control">
<button class="button button-primary button-block button-shadow" type="submit">Subscribe</a>
</div>
</div>
</form>
</div>
</div>
</section>
The important part of this HTML for our functionality is the form
tag. Let's take a closer look at it.
<form method="POST">
<div class="footer-form newsletter-form field field-grouped">
<div class="control control-expanded">
<input class="input" type="email" name="email" placeholder="Your best email…">
</div>
<div class="control">
<button class="button button-primary button-block button-shadow" type="submit">Subscribe</a>
</div>
</div>
</form>
This contains one input
, for the user's email address, and a button
for submitting that email address. When the user clicks on the button, an HTTP request will be sent from their browser to our Flask backend, containing the email address. As per the method
attribute of the form
tag, this will be a [POST request.
Recall that the Python code we entered in the last section provided for both GET
and POST
HTTP requests. As submitting this form will also send a request to "/", we can differentiate between a user browsing to our website (GET
) and subscribing to our mailing list (POST
) by looking at the HTTP method. We'll do that in the next section.
Subscribe functionality in Flask
Return to the app.py
file. Find this line:
@app.route("/",methods["GET","POST"])
Just above it, enter this code:
def subscribe(user_email, email_list, api_key):
return requests.post(
"https://api.mailgun.net/v3/lists/"+email_list+"/members",
auth=('api', api_key),
data={'subscribed': True,
'address': user_email,})
This function is called when a user clicks the "Subscribe" button. It takes three arguments:
user_email
: The email the user has entered.email_list
: Your Mailgun alias address.api_key
: Your Mailgun secret API key.
The real logic is contained in the return
line. Here, we use requests.post()
to add the user_email
to our email_list
, by sending (or "posting") all of the values in data
to Mailgun's email list API.
Next, modify the current index()
function like below, replacing MAILGUN_ALIAS
and YOUR-MAILGUN-PRIVATE-KEY
with the email alias and private API key we previously retrieved:
@app.route("/",methods["GET","POST"])
def index():
if request.method == "POST":
user_email = request.form.get('email')
response = subscribe(user_email,
'MAILGUN_ALIAS',
'YOUR-MAILGUN-PRIVATE-KEY')
return render_template("index.html")
In the previous section, we added the POST
method to the subscribe button. We will therefore know when someone has clicked the subscribe button with the line:
if request.method == "POST":
If they've clicked the subscribe button, we obtain the email they entered by referencing the relevant input
field's name
attribute in request.form.get
, and adding the email to the mailing list our subscribe
method.
Try it out: enter an email address and hit "Subscribe". Navigate back to the "Mailing lists" tab on Mailgun and click on the list we created. You will find the email address you just submitted under "Recipients".
All that's left is to add functionality to our "Buy Now" button.
Implementing "Buy Now" with Stripe Checkout
Stripe Checkout allows business owners to accept payments on their web applications. Let's create an account. After creating an account, log in and find your API keys by clicking "Developers" then "API keys" on the dashboard.
Here we'll see two API keys – a publishable API key, and a secret API key. You can think of these as a username and password. Stripe uses the publishable API key to identify your account, and the secret API key to ensure it's really you using it.
Open the app.py
file. Above the subscribe function, add the following lines, replacing YOUR PUBLISHABLE KEY HERE
and YOUR SECRET KEY HERE
appropriately:
app.config['STRIPE_PUBLISH_KEY'] = 'YOUR PUBLISHABLE KEY HERE'
app.config['STRIPE_SECRET_KEY'] = 'YOUR SECRET KEY HERE'
stripe.api_key = app.config['STRIPE_SECRET_KEY']
In third code, we place the two Stripe keys in our Flask app's configuration settings for ease of access, then set our Stripe secret API key.
These are test API keys that we'll use to check out our product. With these keys, no charges will be incurred when making payments. However, before we can make a payment, we need a product, so let's create one. Return to Stripe, log in, and navigate to the "Products" tab on the dashboard.
Creating a product
Create a product by doing the following:
- Click "Add product" on the top right.
- Name the product.
- Add a description and price.
- Choose "One time" payment.
- Click "Save product"
After the last step, save the API key found in the "Pricing" section. We'll use this API key to tell Stripe which product we want our customers to pay for.
Adding functionality in Flask
Time to create the "Buy Now" button logic. Open app.py
again and modify the index function accordingly. Replace "YOUR-PRICE-API-KEY" with the API key for your product, that we saved in the previous section.
@app.route("/",methods=["GET","POST"])
def index():
session = stripe.checkout.Session.create(
payment_method_types=['card'],
mode = 'payment',
success_url = 'https://example.com/success',
cancel_url = 'https://example.com/cancel',
line_items=[{'price':'YOUR-PRICE-API-KEY',
'quantity':1,
}]
)
if request.method == "POST":
user_email = request.form.get('email')
response = subscribe(user_email,
'MAILGUN_ALIAS',
'YOUR-MAILGUN-PRIVATE-KEY')
return render_template("index.html",
checkout_id=session['id'],
checkout_pk=app.config['STRIPE_PK'],
)
We use the stripe
library to create a new "Session" object. This object contains multiple variables affecting how our customers interact with the "Buy Now" button. For more information on these variables, see Stripe's documentation.
We also return two new variables – checkout_id
and checkout_pk
. checkout_id
, which we get from Stripe, stores information about the potential purchase (price, payment type, etc). checkout_pk
stores our private API key, which we added to the Flask app's configuration settings above. When a customer buys our product, their money is sent to the account associated with this private API key.
Let's see how our HTML will use these new variables and redirect us to the Stripe Checkout page.
"Buy Now" button functionality in the HTML file
With our Flask logic finished, we can implement the "Buy Now" button functionality in our index.html
file. Open the index.html
and find this section:
<div class="hero-copy">
<h1 class="hero-title mt-0">Landing template for startups</h1>
<p class="hero-paragraph">Our landing page template works on all devices, so you only have to set it up once, and get beautiful results forever.</p>
<div class="hero-cta"><a class="button button-shadow" href="#">Learn more</a><a id='checkout-button' class="button button-primary button-shadow" href="#">Buy Now</a></div>
</div>
Directly below the </div>
line, add:
<script src="https://js.stripe.com/v3/"></script>
<script>
const checkout_pk= '{{checkout_pk}}';
const checkout_id = '{{checkout_id}}';
var stripe = Stripe(checkout_pk)
const button = document.querySelector('#checkout-button')
button.addEventListener('click', event =>{
stripe.redirectToCheckout({
sessionId: checkout_id
}).then(function(result){
});
})
</script>
This code adds a JavaScript event which will trigger when the customer clicks the "Buy Now" button, and will redirect them to the Stripe Checkout page. The Stripe Checkout page changes according to the information stored in checkout_id
.
Also, take a look at the Jinja code in these lines:
const checkout_pk= '{{checkout_pk}}';
const checkout_id = '{{checkout_id}}';
When the index.html
template is rendered before being served to the user, Flask will substitute in the current values of the Python variables that we passed to render_template()
. While checkout_pk
will remain the same throughout, checkout_id
will be unique for each purchase.
The "Buy Now" button is good to go – run app.py
again. Try making a payment using the test credit card number 4242 4242 4242 4242
with any name, expiration date, and security code. No charges will be incurred.
Hosting the Application on Code Capsules
Now that we've added all the functionality, we need to create some files that Code Capsules will use when hosting our application. We'll also take a look at a security problem in our current application, and how to fix it before we go live.
Creating the "requirements.txt" file and procfile
To host this application on Code Capsules, we need to create a requirements.txt
file and a Procfile
.
In the
project
directory, ensure the virtual environment is activated and then enterpip3 freeze > requirements.txt
.Create another file named
Procfile
, containing the lineweb: gunicorn app:app
.
With the requirements.txt
file, Code Capsules will now know what libraries to install to run the application. The Procfile
tells Code Capsules to use the gunicorn
WSGI server to serve HTML rendered by Flask to end-users.
Now we can fix that security problem mentioned before, and host the application.
Removing secret keys
We need to send our application to GitHub so Code Capsules can host it. But currently this project's code contains all of our API keys. It is considered very poor practice to send personal API keys to GitHub, especially public repositories. Anyone could find them and incur charges on your debit card. Luckily, there is a workaround.
By working with environment variables, we can use our API keys on Code Capsules without exposing them on GitHub. We will alter our application to retrieve our API keys from environment variables, which we will set on Code Capsules.
To do this, change the code at the top of app.py
as follows:
from flask import Flask, render_template, request
import requests, stripe, os
app = Flask(__name__)
app.config['STRIPE_PK'] = os.getenv("STRIPE_PK")
app.config['STRIPE_SK'] = os.getenv("STRIPE_SK")
stripe.api_key = app.config['STRIPE_SK']
Notice that we've imported a new Python module, os, which allows us to retrieve with environment variables using the os.getenv()
method.
We've now done this with our Stripe publishable and secret API keys and our Mailgun secret key. On Code Capsules, we'll set environment variables named 'STRIPE_PK'
, 'STRIPE_SK'
, and 'MAILGUN_SK'
. This way, our API keys do not have to be stored on GitHub and will remain secret. Notice, we aren't adding environment variables for the Stripe product API key. This doesn't contain any sensitive information. It just displays a product's price.
Your final app.py
code should look like this:
from flask import Flask, render_template, request
import requests, stripe, os
app = Flask(__name__)
app.config['STRIPE_PK'] = os.getenv("STRIPE_PK")
app.config['STRIPE_SK'] = os.getenv("STRIPE_SK")
stripe.api_key = app.config['STRIPE_SK']
def subscribe(user_email, email_list, api_key):
return requests.post(
"https://api.mailgun.net/v3/lists/"+email_list+"/members",
auth=('api', api_key),
data={'subscribed': True,
'address': user_email,})
@app.route("/",methods=["GET","POST"])
def index():
session = stripe.checkout.Session.create(
payment_method_types=['card'],
mode = 'payment',
success_url = 'https://example.com/success',
cancel_url = 'https://example.com/cancel',
line_items=[{'price':'YOUR-PRICE-API-KEY',
'quantity':1,
}]
)
if request.method == "POST":
user_email = request.form.get('email')
response = subscribe(user_email,
'MAILGUN_ALIAS',
os.getenv("MAILGUN_SK"))
return render_template("index.html",
checkout_id=session['id'],
checkout_pk=app.config['STRIPE_PK'],
)
if __name__== '__main__':
app.run(debug=True)
We can now safely host our application.
Pushing to GitHub and hosting the application on Code Capsules
Before we create the Capsule that will host our code, take the following steps:
- Create a GitHub repository for the application.
- Send all code within the
project
directory to the repository on GitHub. - Log in to Code Capsules.
- Grant Code Capsules access to the repository.
- Create a Team and Space as necessary.
Now let's create the Capsule:
- Click "Create A New Capsule".
- Select your repository.
- Choose the Backend Capsule type.
- Create the Capsule.
All that's left is to set the environment variables. Navigate to the Capsule and click on the "Config" tab. Use the image below as a guide to properly add your environment variables. Replace each value with the appropriate API key.
After entering the API keys, make sure to click "Update".
All done – now anyone can view the web application and interact with the "Buy Now" and "Subscribe" buttons.
Further Reading
We covered a lot in this tutorial: how to use Flask to implement functionality for frontend code, how to set up an email subscriber list, and how to work with Stripe.
Earlier I mentioned more information on the url_for()
function. Check out Flask's documentation for more information.
For further information on how Jinja templates work and what can be done with them, check out this link to learn more about Flask templating and Jinja2.
Now that you have a functional email subscriber list, you may be interested in sending emails to your list.
Top comments (0)