Branded MMS Coupon Generation with Python and Twilio

In 2013 72.3% of customers reported using a coupon during a purchase. Yet consumers are increasingly digital and transient. MMS coupons instead of paper printouts provide an alternative way to engage with phone-wielding customers.

In this tutorial we’ll walk through how to build a service to distribute branded MMS coupons using Python and Flask. An MMS coupon will be a message with an image that combines the logo of your choice with a scannable barcode. The resulting text message will appear on the user’s phone as shown in the following image.

When the coupon receiver expands the message they’ll see a full image like this one below.

What You’ll Need

To build the branded coupons app we’ll use the three following technologies.

If you are anxious to see the results of this tutorial and want to deploy the code right now, try out the app with this Heroku deploy button. All of the code for this tutorial is also stored in an open source GitHub repository.

Deploy on Heroku

There are five sections to this coupon app tutorial.

  1. Create the structure of the coupon-creating Flask web app
  2. Write code to handle user input and HTML templates
  3. Use Pillow and PyBarcode to generate a branded coupon image
  4. Build the code to send the coupon image to a customer via MMS
  5. Deploy our coupon app to Heroku

Let’s get started incrementally building our coupon generator by creating the structure of our Flask app.

Flask web application structure

First set up the Flask app. If you don’t want to type up this code yourself there’s a tutorial-step-1 tag with these files already created.

In this section we’ll create the following list of files and directories.

  • requirements.txt – specifies necessary Python libraries
  • app.py – runs the dev server for our Flask app locally
  • coupons/ – contains the Flask app that generates and sends coupons

Let’s fill out the dependencies in the requirements.txt file. Each of these dependencies has several other libraries it relies upon which is why this list is shorter than the one you’ll see in the repository.

To install these dependencies run the following pip command.

Several directories are needed to store our app code. Create a coupons directory in the base project directory. Two subdirectories are required within coupons: templates and static. Under static we also need css and img subdirectories.

The mkdir command handles the creation subdirectories on Linux or Mac OS X. On Windows you can create the folders and subfolders via Windows File Explorer. The resulting directory structure should look like the following.

Note that Git will not propagate empty folders when pushing to a remote repository. Every folder we just created will have a file in it except for the img/ subdirectory. We need to remedy that for our app to work by placing a file in the folder. Download and save this Twilio logo picture or any .png formatted logo of your choice into the img/ directory.

Our app now has its dependencies in a requirements.txt file and the subdirectories for our Flask app.

Next create an app.py file for running our Flask dev server locally. This file will live in the base directory of our project and contain the following lines.

The coupons/ subdirectory needs to be made into a Python package. Create an empty file named __init__.py in coupons/.

We also need to create a second app.py file under our coupons/ subdirectory that bootstraps our Flask app. The following code with two stubbed out functions will live in this file.

This app.py file imports Flask and creates a new coupons web application. The app loads Flask’s configuration from a Python file named config.py.

The file also contains two stubbed functions we will fill out later in this tutorial. The first function create_coupon will display the coupon creation form page as well as handle the data from POST form submissions.

The second function coupon_confirmation will be a confirmation page after the coupon is sent properly.

app.py references a config.py file. Create the config.py file now under the coupons/ subdirectory and put the following code in it to load environment variables for our app.

The config.py file looks up Flask settings and Twilio credentials from environment variables set locally on Linux or Windows, so if you’re going to run the app locally go ahead and create those variables now.

COUPON_SAVE_DIR is where coupon images should be temporarily saved. Set COUPON_SAVE_DIR to your app’s coupons code directory plus “/static/img/”. For example, “/app/coupons/static/img/”. If you’re running the app locally, the directory is path to your Flask app plus “/coupons/static/img/”.

QUALIFIED_MEDIA_URL is the fully qualified path for saved coupons. For example, if your app is deployed to Heroku with an app named “mms-coupon-creator”, this value would be set to “http://mms-coupon-creator.herokuapp.com/static/img/”.

Next we need to build a web page that accepts user input so we know how to properly create the coupon.

Handling user input

One more Flask-specific Python file is required that will handle user input. Input fields on the webpage will allow a user to specify what phone number to send the coupon to and optionally the logo, barcode and message text to send with the coupon. The webpage’s input fields will look like the following screenshot.

Our app uses the Flask-WTF library to handle form input. Create a forms.py file under the coupons/ directory and put the following code inside.

In the above file we’re handling the required phone number and three optional fields, logo_image_url, coupon_text and serial_number. Some basic validation happens to ensure fields are correct but the validators could certainly be reinforced on an improved coupon creator.

That wraps up the Flask app structure but a few HTML templates are required under the templates directory to create the web user interface. These files come from the Bootstrap project’s Narrow Jumbotron theme customized with our input fields. Each of these files below links to the template file in the GitHub repo.

There are also two CSS files under the static/css directory.

We can run the app now using the python app.py command in the root project directory. However, the webpage will just display “stub” in the browser window. Let’s write the code for generating the coupon images so our app generates coupons and wire it up to our app’s views.

Branded Coupon Generation

We wrote code for the Flask app structure in the first two sections. If you want to start with that code already written, check out the tutorial-step-1 tag.

Code in coupons/app.py calls functions in a file named utils.py file that we’ll create now. While we could write all of the code in app.py, it’s good practice to separate logic out into other functions for easier testing. The following code should go into this utils.py file.

The above function opens an image file from a URL passed in as a parameter. open_image_file_from_url pulls in an external .png file that will be used to combine with a barcode for the branded coupon.

Within the same utils.py file, write a create_background_image function with the below code that return back a blank image canvas that will be used as a background for our coupon.

The third function to write in utils.py generates a barcode image. This function will be named generate_barcode_image. It wraps the PyBarcode library and returns back a rendered image. With the return value from this function we have a scannable barcode image that will be pasted by another function onto the blank background image.

Now we get to the meat of the utils.py file. The next function called combine_images_into_coupon takes in our logo image pulled down from a URL specified on our app’s web page form along with the rendered barcode. The output is a URL to our newly created coupon file with both the logo and barcode combined in the same image.

combine_images_into_coupon is where the logo and generated barcode come together in a single image. We ensure the logo or barcode is within the size of the background image. Then we paste the logo image followed by the barcode image onto the background. If there is transparency in the logo we need to add a mask but if that fails we simply do not add a mask to the image.

One more function for utils.py named save_image is shown below.

In save_image we save the generated coupon to a unique filename that is created via the uuid library within Python. The coupon image file for our app doesn’t need to be stored long term, it just needs to be saved and served up immediately via a URL for the MMS coupon sending phase.

Barcode generation is possible with the code we wrote above but we also need to wire it into the Flask app. Within the app.py file add the following highlighted code within the create_coupon function and remove the return 'stub' placeholder.

The above code retrieves user input from form data, downloads a logo image and generates the barcode with the assistance of the functions we wrote earlier.

For the coupon_confirmation function also within app.py, replace the return 'stub' placeholder under with the following two lines.

With the above code the user is allowed to create a coupon by submitting a form either on the landing page or the confirmation page.

If we run the app now using python app.py we’ll generate the coupon but now we need to deliver it directly to the user, which we can do using Twilio MMS.

Send coupon via MMS

We now have the code to create coupon images with barcodes. If you want to start with the code to this point in the blog post check out the tutorial-step-2 tag. Now let’s add the code for distributing those coupons via MMS.

Head back to the utils.py file. Add the following two imports at the top as well as an instantiation of the Twilio Python helper library.

A function is required to send the MMS based on user input. Add this code at the bottom of the utils.py file.

Finally, we’ll call this new function from the end of our create_image function in coupons/app.py. The additional line to send MMS coupon messages is highlighted below.

Congratulations! You now you have a fully functioning digital coupon creation and distribution service. Or almost anyways.

 

Give the app a test on your own machine and you’ll see that it will fail to send the MMS message. This is because Twilio needs a publicly accessibly URL from which it can grab the coupon images. You could use a great service like ngrok to test the app on your local machine, or even better, lets just deploy the app to Heroku.

Deploying the Coupon App

Our app should have the desired functionality, but we need to deploy it otherwise the coupons’ URLs will not be accessible to Twilio. To deploy the app to Heroku we need our code committed in a Git repository and to instruct the service how to use our app with a Procfile. For more information on deploying a Python app with Gunicorn on Heroku check out their official guide.

Create the Procfile in the root directory of our project with the following one-line instruction to run the app via Gunicorn.

web: gunicorn app:app

The master branch of our Git repo contains all the code at this point in our app development. Use the following commands to create a new Heroku app, push your code and set the environment variables.

Ensure you’ve set the QUALIFIED_MEDIA_URL with your own Heroku app name and trailing slash on the end of the URL. COUPON_SAVE_DIR also must have a trailing slash at the end of the path.

 

Head to the new Heroku instance and you’ll see the following input screen.

Now we can send branded coupons with text of the discount to a customer number!

Here’s one with the Twilio logo. This is also the default logo if you choose to not fill in a .png logo during the coupon generation process.

Wrapping it up

Awesome! We’ve combined Python, Flask and Twilio to create a web application that sends digital coupons to customers. Now you’ve got the ability to send MMS coupons to your phone-wielding customers as an alternative to paper coupons that can get lost or be forgotten at home.

This web application can be enhanced in many ways so feel free to fork it and contact me at makai@twilio.com with any questions you have on our new branded MMS coupon app.