The Guide to Making Your Django SaaS Business Worldwide (for free)

The Prelude

Warning: This is a very necessary and overdramatic prelude, in which I explain why and how you can make your digital business global. Right after that, I continue with the regular Django tutorial. You can go ahead and skip this introduction, but then don’t whine when you feel all lost in the deep forest at night later on in the tutorial, okay?

Let’s say you have a Django SaaS website where you sell some stuff online. Actually, it does not matter what you are selling—whether it’s SaaS, or maybe some other kind of software, or some online services, or online courses, or maybe e-learning - the main point is that you sell something digital on your website.

Now, open up your sales report (if you have one, obviously), Google Analytics, or whatever. Check which countries your customers are in and who you are selling your product to. Write down the top 10 countries where your customers are located. Yes, I am serious—do it right now (or don't; who am I to tell you?). For me, my top-selling countries are:

  1. United States
  2. United Kingdom
  3. Canada
  4. Germany
  5. Australia
  6. Sweden
  7. France
  8. Netherlands
  9. Denmark
  10. Belgium

Nearly 95% of the revenue from my SaaS project comes from these 10 countries. Now, if I open up the traffic report to see what people visit my website and select the top 10 countries once again (you should do it, too), here is what this report looks like:

  1. United States
  2. United Kingdom
  3. Canada
  4. India
  5. Poland
  6. China
  7. Turkey
  8. Brazil
  9. Mexico
  10. Germany

As you can see, my website traffic does not align with my sales report. Six out of ten countries that have sent high traffic to my website do not generate sales.

Despite being the top-selling country in Europe, Germany is in 10th place in my traffic report. On the other hand, India has ~four times more traffic than Germany, but it ranks around 24th in sales and hardly makes any money.

I get huge loads of traffic from nearly 90 countries, but only 10 generate real sales. Those 80 countries are left untouched, and I miss 88% of my potential revenue. And I am sure that if you have actually opened up your sales and traffic reports, you will see a similar picture.

But why does it happen?

As you might have noticed, the top-selling countries have one thing in common.

They are rich as heck.

At the same time, the countries in the traffic report are not particularly known for being “rich”. What happens is that people in those countries usually have a lower level of salary and, consequently, much lower buying power.

So, you have probably already guessed the largest obstacle here. It’s the price of your products.

And this is quite obvious—just compare the average salary in Germany, which is about 51k Euro per year, with India, which has an average salary of 5k Euro. This is just about 10 times lower. 

So, yes, India definitely has much lower buying power when compared to Germany, but India has buying power. People in India can also be quite interested in your digital products—it's just that they can be too unaffordable for them—but if they could buy them, they would do it. 

So, this is when we finally come to a solution, which is called “geographical pricing”. Now, until you get scared of those weird marketing terms, I will quickly explain what that bad boy is:

Basically, “geographical pricing” is when you give different prices to your products for each country.

So, for example, in the United States, you sell your digital product for $20. In India, you sell it for $7. And finally, in Brazil, you sell it for $8. This is what those fancy economists and marketologists call “geographic pricing”. As you can see, it's a very basic concept.

And again, until you all start screaming, “Wah, this is discriminating towards those people,” or “Aaa, this will only bring losses and reduce income,” or anything else, let me just say that Spotify, Netflix, Google, Apple, Amazon, and thousands of other companies already use geographical pricing. I don’t think that all those S&P 500 companies are so stupid that they would use a pricing method that brings losses—probably not, right?

So, to address this issue when you have a lot of international traffic but don’t have enough sales in all those countries, you can add geographic pricing to your website and (hopefully) start reclaiming your global sales. And this is exactly what I did. We can now move on to the actual tutorial part of this article, in which I explain how I made my SaaS global by implementing geographical pricing. 

Finally, you have made it to the tutorial part

Now, let’s talk about how to implement geographical pricing (finally!).

So, how do you implement it, girl?

I already know the basic idea of geographical pricing, and now I only need to implement it on my Django website. Specifically, I aim to implement a form of geographical pricing that operates discreetly, similar to how S&P 500 companies do. My goal is to dynamically display adapted prices to visitors from, for example, South Africa, without explicitly indicating that these prices may differ for others. So, I don’t want to say “Hey, you are from South Africa, here is a discount for you!”. Instead, I want to adjust the prices “silently”.

Roughly, what I need to do is:

  1. On my pricing page, (where I display the prices for my product, duh) I want to show the adapted prices, based on the visitor’s location. For this, I need to:
    1. Identify the visitor’s country.
    2. Check what the adapted price is for this country (according to its buying power)
    3. Display adapted prices
  2. The next step is the actual payment. For this, I will do:
    1. Identify the visitor’s country (again)
    2. Check what the adapted price is for this country (again).
    3. Create a payment session with those adapted prices.

At the same time, I only want to do this to the countries with lower buying power: for example, only for India, Poland, Turkey, etc. But not for the countries with high buying power, where I already make profits, like US, UK, Germany, etc. So, for the “rich” countries, I don’t want to make any changes to my pricing process—it will only be different for the countries with lower buying power.

As a software engineer, I quickly saw a major flaw in this thing. What if some smart ass from San Francisco tries to use a VPN to circumvent my geographical pricing and pretend he is from Venezuela to get, like, a 60% discount? This is when I realized that implementing geographical pricing might not be as easy as I thought it would be. And when I think about it again, I wonder how I could have missed such a major issue and not seen it coming. 

I opened up DuckDuckGo (because go to hell, Google) and searched for how to implement geographical pricing for SaaS. I found several solutions, called “ParityVend” and “ParityDeals”. I compared them and decided to go with ParityVend because:

  1. has a much more generous free plan with more monthly requests and features
  2. It includes API in its free plan, which is a requirement for any SaaS (the most important point here)
  3. It also has good documentation

So, I decided to sign up for a free plan with ParityVend. Then, I created a test project and grabbed my API keys. After this, I was ready to implement geographical pricing for my Django SaaS. FYI, for this whole process, I followed the service's docs

before y'all start screaming "bah, shame on you! that's an advertising! you-marketing-bitch!!!!", I will tell you: no, this is not an advertising or a promotion. My blog is proudly ad-free, and it's going to stay that way (because I am a fucking loser and brands don't take me seriously). I'm just here to share some interesting tools that I've come across and genuinely enjoy, I don't get anything for this, except for the internet points on Reddit, obviously.

Finally, here goes the coding part

If you are reading this tutorial, it means that you already have a SaaS website built with Django. This means you are likely to know how to create a new Django project. So, I will skip the basic things. Let’s assume that your SaaS has at least two parts: a pricing page and the checkout code. For me, this is how my pricing page looks (“dope”, right?):

My wonderful pricing page
My wonderful pricing page

When you click on “Buy now”, the pricing page will send a POST request. Then, my view will create a Stripe checkout session for “My Amazing Product”, and redirect the customer to it. Nothing new here. I am using Stripe, but you can use any other payment processor.

Here is a view code for the pricing page:

def pricing(request):
    if request.method == "POST":
        discounts = []

        checkout_session = stripe.checkout.Session.create(
            success_url="http://127.0.0.1:8000/",
            cancel_url="http://127.0.0.1:8000/",
            mode="subscription",
            discounts=discounts,
            line_items=[
                {
                    "price": PRICE_ID,
                    "quantity": 1,
                }
            ],
        )

        return redirect(checkout_session.url, code=303)
    return render(
        request,
        "main/pricing.html",
    )

Let’s run this. When I click on the “buy now” button, I am redirected to the Stripe checkout page. It works great!

Stripe works!

So, now that we have a basic “SaaS”, let’s add geographical pricing to it. Based on my plan, I need to make changes to the pricing page and then to my checkout code. Before we proceed, let me briefly explain the project setup I completed in Parityvend: in short, I created a new project, entered an example domain, skipped the design step, added coupon codes for each country group I wanted to offer discounts in, and finally, copied my API keys. Then, I went to Stripe and added the coupons for the ones I created in ParityVend. After this, I was good to go. I don't want to explain these boring steps as it was easy enough, so you probably can do it, too. 

Let’s continue with the pricing page. I used the docs as a reference (meaning that I copied the whole code from there). For the API keys part, I simply decided to go with Django’s template engine and render my API keys that way.

So, I added the API to my pricing page and added some code to change the price of “My Amazing Product” based on the discount that the API returns for the visitor of my page. Here is how my updated pricing page code looks:

<script>
    window.addEventListener('DOMContentLoaded', async () => {
        let url = "https://api.parityvend.com/frontend/testing-mode-get-discount/";
        url += '{{PARITYVEND_KEY}}';
        url += '?testing-secret={{PARITYVEND_TESTING_KEY}}';
        url += '&testing-country=BD'; // Emulate a visit from Bangladesh

        const response = await fetch(url);
        const jsonData = await response.json();

        if (jsonData.status !== "ok") {
            console.error(
                "ParityVend API returned an error:",
                JSON.stringify(jsonData)
            );
            return;
        }

        if (jsonData.discount) {
            const discount = jsonData.discount;
            const elm = document.getElementById('product-price');
            const originalPrice = elm.dataset.price;
            const newPrice = originalPrice - (originalPrice * discount);
            elm.innerHTML = `$${newPrice.toFixed(2)}`;
        }
    });
</script>

That’s it! It works! I refreshed the page and changed the “testing-country” parameter to emulate visitors from different countries. Here are screenshots emulating India, Brazil, and the United States, respectively:

Yay, the first part of my plan is already implemented! Now, I need to continue with the backend part. Now, when a visitor from India opens my "SaaS", he will see $8 instead of my original price of $20. Similarly, my price will change to $12 for people in Brazil. But, in countries with good buying power, such as US, UK, Canada, Germany, etc, my price remains unchanged. 

This is the end of part 1. To be continued in Part 2.

Subscribe to my email newsletter to be the first to receive Part 2 when I release it!

Also, follow me on Medium, if you want to: medium.com/@catnotfoundnear

I will post the GitHub of this project soon. Stay tuned for more!