Stripe SMS Notifications via Twilio, Heroku, and Python

February 24, 2017
Written by
Chris Hranj
Contributor
Opinions expressed by Twilio contributors are their own

image07

I recently started a small business using the online platform Storenvy. Storenvy integrates with Stripe, a popular online payments platform. As a small business owner, receiving notifications as soon as orders come in is critical.

Emails are easy to miss, forget about, or overlook, but one thing I never fail to read is a text message. What if it were possible to receive a text message every time my store makes a sale?

Neither Storenvy nor Stripe have SMS notifications built into their platforms, so let’s build a custom SMS notification system using Twilio, Heroku, and Python.

Note: If you’re only interested in the code, it can be found on Github here, along with a “Deploy to Heroku” button.

What You’ll Need
  • git – Git will handle source control and make Heroku deployments easier.
  • Python 3.6 – Heroku’s supported Python runtimes are python-2.7.13 and python-3.6.0. I’ll be using Python 3, although the Python 2 code should be the same.
  • ngrok – ngrok will let us create secure tunnels to our local machine. (See download instructions)
  • A Heroku account. We’ll only be needing the Free tier.
  • A Twilio account. It’s free to sign up and the Free Trial will be sufficient.
  • A Stripe account. Also free, other than transaction fees.
  • (Recommended) virtualenv and virtualenvwrapper – These tools will allow us to create virtual Python environments to avoid cluttering our default environment. You can install these tools by running the following in your terminal (you may need to use sudo):

 

pip install virtualenv virtualenvwrapper

 

Getting Started

Here is an outline of what we’ll accomplish:

  • Build a simple app to handle incoming POST requests using Flask and send outgoing text messages with Twilio.
  • Use ngrok to securely tunnel to the Flask app.
  • Configure a webhook on your Stripe account to point to your app.
  • Deploy the app to Heroku and reconfigure the webhook to point the app in the cloud.

If you are are using virtualenvwrapper you should first set up a new Python environment using the following commands:

mkvirtualenv stripe-sms-notifications
workon stripe-sms-notifications

Your terminal prompt will prepend (stripe-sms-notifications) to every line to indicate the virtual environment you are currently in. If at any point you want to leave this virtual environment and return to your default Python environment, you can use the command $ deactivate and use the $ workon stripe-sms-notifications command to re-enter it.

With the environment ready, run the following command to install the Python packages we need:

$ pip install flask twilio

The above command will install Flask, the micro-framework we are using to handle incoming requests, as well as the Twilio Python helper library and all of its dependencies.

Now in the editor of your choice create a new file named app.py. Add the following code:

import os
from flask import Flask, request

app = Flask(__name__)


@app.route("/", methods=['POST'])
def receive_order():
    return '', 200


if __name__ == "__main__":
    port = int(os.environ.get("PORT", 5000))
    app.run(host='0.0.0.0', port=port)

At the top of the file we are importing the os module, and two flask modules, Flask and request. The os module is needed to access environment variables, which you can see being used towards the bottom of the file and which we will also need when we import Twilio.

The app = Flask(__name__) and app.run(host='0.0.0.0', port=port) lines are creating and starting our Flask app, respectively. The most important lines are the ones in the middle.

  • @app.route("/", methods=['POST']) represents a rule we are defining for our app’s routing system. Here we are handling POST requests sent to the / route.
  • def receive_order(): defines the function that will handle requests sent to the route above. It does not take any arguments because we are not handling any URL parameters.
  • return '', 200 simply returns a 200 response with an empty body.

Save this file and run python app.py in the same directory that app.py is located in. Your terminal should hang and the following line should be displayed:

Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

This means your Flask app was able to start without errors. We can test out our app by making some requests to it. With your app still running, open another, separate terminal and run the following command:

curl localhost:5000

You’ll notice that you receive a 405 Method Not Allowed response. This is fine because we set up our route to only accept POST requests and we sent it a GET request. Use the following command to send a POST request instead:

curl -d '' localhost:5000

It will look like nothing happened because our app returned an empty response body. If you switch back to your other terminal you’ll notice that a POST request was received and a 200 OK response was returned. Our Flask app is ready to go.

Here Comes Twilio

The next step is to import Twilio into our app so that we can send outgoing SMS messages. If you have never worked with Twilio before it might help to first check out their Python Quickstart as we will borrow most of our code from there.

Open up app.py again and add the following lines:

import os
from flask import Flask, request
from twilio.rest import TwilioRestClient

app = Flask(__name__)

# Find these values at https://twilio.com/user/account
account_sid = os.environ['TWILIO_ACCOUNT_SID']
auth_token = os.environ['TWILIO_AUTH_TOKEN']
client = TwilioRestClient(account_sid, auth_token)


@app.route("/", methods=['POST'])
def receive_order():
    message = client.messages.create(
        to=os.environ['PHONE_NUMBER'],
        from_=os.environ['TWILIO_NUMBER'],
        body="Hello, World!")
    return '', 200


if __name__ == "__main__":
    port = int(os.environ.get("PORT", 5000))
    app.run(host='0.0.0.0', port=port)

At the top of the file we are importing the TwilioRestClient and building a client object using our Twilio account credentials and phone numbers. Your account sid and auth token can be found in your Twilio console.

I’ve set my credentials as environment variables using the $ export command. If you decide to hard-code your credentials, do not push your source code to a public github repo as it will expose them to the world.

The message = client.messages.create(...) line uses the client object to send an outgoing SMS with the given parameters.

Save the file again, restart the app, run your curl command ($ curl -d '' localhost:5000), and check your phone. You should have received a text message from your Twilio number that says “Hello, World!” If you did not receive a text or your app displays an error, double check that you copy and pasted your Twilio credentials properly.

Handling Stripe Webhooks

The next step is to configure an endpoint in our Stripe account to send data to our application.

If you want to know more about Stripe’s webhooks and how they work, check out their documentation or this blog post. Basically, webhooks allow you to send data to a specific URL whenever events involving your Stripe account occur.

But first we need to make our app publicly accessible so that it can be reached by Stripe. We are going to use ngrok to accomplish this. In a separate terminal run the following command:

ngrok http 5000

Your terminal will transform into a window that looks similar to this:

Copy the first Forwarding URL to your clipboard (http://7dbabaa4.ngrok.io in this case, but yours will be different). This will be the URL we use to configure our Stripe webhook.

Go to your Stripe dashboard and select Webhooks -> Settings (or click here). You should see a menu that looks like this:

Select “+ Add endpoint…” and configure it like this (using the ngrok URL you copied):

We want to be notified whenever we receive a Stripe payment successfully, so the event we are interested in is charge.succeeded. We’re also setting the “Mode” to “Test” so we don’t have to wait for actual payments to come in to test it.

In app.py modify receive_order() to look like this:

@app.route("/", methods=['POST'])
def receive_order():
    event_json = request.get_json()
    amount = event_json['data']['object']['amount'] / 100
    message_body = "Hey! Your shop just received an order for $" + \
        '{:,.2f}'.format(amount) + "."
    message = client.messages.create(
        to=os.environ['PHONE_NUMBER'],
        from_=os.environ['TWILIO_NUMBER'],
        body=message_body)
    return '', 200

Here we are parsing the incoming request object as JSON and extracting Stripe’s amount value from it. We have to divide amount by 100 to convert from cents to dollars. This value gets concatenated to the string message_body, which is then used as the body of our outgoing SMS. Save this file one more time and restart your app.

Testing Our Finished App Locally

All the Python code for our app is now finished and we can test it with Stripe. In your Stripe webhook settings select the “Send test webhook…” button. Use the endpoint we just created to send a charge.succeeded event. You should see the message “Test webhook sent successfully” and receive a text message like in the image below:

Excellent! Now it’s time to deploy our app.

Deploying to Heroku

We are using Heroku in order to avoid the large hassle of renting, configuring, and securing our own server. If you’d like to use your own server, you can skip to the “Wrapping Up” section below.

If you are unfamiliar with the Heroku platform it might be a good idea to at least read through their Getting Started on Heroku with Python guide or check out other Twilio articles that feature Heroku.

However, you’ll find a list of all the commands you’ll need to run below followed by a brief explanation of what the commands are doing.

Make sure you have the Heroku CLI installed and then run the following commands:

echo -n "web: python app.py" > Procfile
pip freeze > requirements.txt
git init
git add app.py requirements.txt Procfile
git commit -m "Initial commit"
heroku create
heroku apps:rename your_project_name
git push heroku master

Replace your_project_name in the command above with whatever you want to name your app. You’ll have to come up with your own name because “stripe-sms-notifications” is already taken by me. Sorry!

The commands above create a Procfile, write our Python dependencies to requirements.txt, initialize and commit all our files to a new Git repo, and create/deploy our app to Heroku.

Once the deployment finishes, run heroku ps:scale web=1 to assign a worker process for your app. (Read more about this step here.)

Heroku Config Vars

Finally, before we can send text messages we need to set a number of config vars for the app. Config vars are Heroku’s equivalent to environment variables. If you previously hard-coded your Twilio credentials you can skip this step.

Run the following commands to set all the necessary config vars, making sure to use your actual Twilio credentials.

heroku config:set TWILIO_ACCOUNT_SID=ACXXXXXXXXXXXXXXXXX
heroku config:set TWILIO_AUTH_TOKEN=YYYYYYYYYYYYYYYYYY
heroku config:set TWILIO_NUMBER=+15555555555
heroku config:set PHONE_NUMBER=+15555555555

Heroku will take care of restarting the app once the config vars are set.

Testing Our Heroku App

Now that your app is configured and deployed, head back to your Stripe webhook settings and edit the URL of your webhook to point to your Heroku app instead of your ngrok tunnel.

Send another test request using the “Send test webhook…” button as we did earlier. You should receive the same text message as before.

Wrapping Up

Now that your app is working and deployed to Heroku, we need to go back and tell Stripe to use live data instead of the test endpoints we’ve been working with.

We’ll need to create a new endpoint as Stripe will not allow to you edit an endpoint’s Mode after you set it. In your Stripe webhooks select “Add endpoint…”.

As seen below, set the URL to the URL of your Heroku app (remember that yours will be different than mine), set Mode to Live and select the charge.succeeded event.

Note: If you are using your own server instead of Heroku, the URL field should point to your publicly accessible server.

Congratulations! You will now receive an SMS message every time you receive a payment via Stripe. Check your phone for those orders and start makin’ money!

I hope you enjoyed following along with this post. If you run into any issues with your app or you have questions, please reach out to me on Twitter or on Github.