How to Add PPP Pricing to Stripe (Step-by-Step Guide)
Set up purchasing power parity pricing on Stripe. How much to charge each country, three ways to build it, the code, and how to stop VPN abuse.
TL;DR Create a lower-priced Stripe Price for each group of countries, detect the buyer's country on your server, and send that Price ID to Checkout. Base the discounts on World Bank price levels (about 40–55% off in Brazil or Mexico, 65–75% off in India or Nigeria), and give VPN and proxy traffic your full price.
A $49 monthly plan is an easy yes for a developer in San Francisco. For a developer in Lagos it is several days of income. Purchasing power parity (PPP) pricing fixes that by charging each country a price that feels the same locally. This guide covers how much to charge where, the three ways to do it on Stripe, and working code for the approach that holds up best.
What is PPP pricing?
PPP pricing sets a different price in each country based on what money is worth there, not on the exchange rate. If everyday goods in India cost about a quarter of what they cost in the U.S., a PPP price in India is roughly a quarter of your U.S. price.
That is different from showing the price in local currency. Converting $49 to rupees at the bank rate gives you a number in rupees that is still set for the U.S. PPP pricing changes the amount itself.
Does PPP pricing increase revenue?
Yes, when most of the world can’t afford your current price. The clearest public numbers come from Twitch. In a test in Brazil, Twitch reported that lower sub prices “more than doubled creator revenue and total subscriber count.” After rolling local prices out in Mexico and Turkey, viewers gifted five times more subs than in the three months before.
The largest subscription companies have priced this way for years:
| Monthly price | U.S. | Brazil | India | Nigeria |
|---|---|---|---|---|
| Netflix Premium | $26.99 | $11.94 | $6.70 | $6.39 |
| YouTube Premium | $15.99 | $4.96 | $1.54 | $1.28 |
| Spotify Premium | $12.99 | $4.76 | $1.43 | $1.20 |
None of these companies gives up money in the U.S. to do it. Buyers there pay the same price as before. The lower prices bring in buyers who would otherwise pay nothing.
How much cheaper should each country be?
Start from the World Bank’s price level for each country: what the same basket of household goods costs there, as a share of the U.S. cost. Multiply your base price by it to get a starting point.
| Country | Price level (U.S. = 1.00) | $49 plan at local purchasing power |
|---|---|---|
| Switzerland | 1.28 | $63 |
| United States | 1.00 | $49 |
| UK | 0.92 | $45 |
| Canada | 0.90 | $44 |
| Germany | 0.81 | $40 |
| Japan | 0.69 | $34 |
| Mexico | 0.59 | $29 |
| Poland | 0.55 | $27 |
| Brazil | 0.46 | $23 |
| Turkey | 0.45 | $22 |
| South Africa | 0.43 | $21 |
| Philippines | 0.36 | $17 |
| Indonesia | 0.31 | $15 |
| India | 0.23 | $11 |
| Nigeria | 0.21 | $10 |
| Egypt | 0.16 | $7.69 |
PA.NUS.PRVT.PP) divided by the yearly average exchange rate. Latest available year per country, fetched 2026-09-26.Three adjustments turn those raw numbers into prices you can ship:
- Group countries into tiers. Four or five price points are much easier to manage, test and explain than 190. Countries within about 15% of your home market can simply pay full price.
- Set a floor. A raw price level of 0.16 (Egypt) would turn $49 into about $8. Most products stop around 25–35% of the base price so the cheapest tier still covers costs and support.
- Charge more where it costs more. A few markets, such as Switzerland, sit above U.S. prices. A higher tier there is optional, but it is consistent with the rest of the scheme.
Here is the tier scheme EasyPPP uses for a U.S. dollar base price, as a reference (how the tiers are built):
| Tier | Share of base price | Example countries |
|---|---|---|
| Base | 100% | United States, Canada, United Kingdom, Australia |
| Light | 70% | Germany, Japan, Spain, South Korea |
| Medium | 45% | Brazil, Mexico, Poland, South Africa |
| Deep | 35% | India, Indonesia, Nigeria, Vietnam |
| Premium | 130% | Switzerland, Iceland, Bermuda |
Refresh the data once a year. The World Bank updates its PPP figures annually, and exchange rates move constantly, so a tier list from three years ago will be wrong for fast-moving currencies.
What are the ways to do PPP pricing on Stripe?
There are three, and only one of them changes the price per country.
| Approach | How it works | The catch |
|---|---|---|
| Coupons or promotion codes | Show a banner with a country-specific code, or apply a coupon at checkout | Codes leak to forums and coupon sites, show as a discount on every invoice, stack with other promotions, and expire unless set to last forever |
| Multi-currency Prices or Adaptive Pricing | Set an amount per currency, or let Stripe convert at the exchange rate | One amount per currency, not per country. The euro covers both Germany and Greece; the U.S. dollar is paid in dozens of countries. Adaptive Pricing doesn’t change the value at all |
| A separate Price per tier, chosen by country | Create a lower-priced Price for each tier and send the right Price ID to Checkout | You have to detect the country and block VPNs yourself, or use a tool that does |
The third approach is the one to build. Each buyer gets a normal Stripe Price with no discount line, renewals stay at that price, and there is nothing to share.
How to set up PPP pricing on Stripe, step by step
The examples use Node.js and the official stripe package. The same calls exist in every Stripe SDK.
1. Create a Price for each tier
Create the tier Prices on the same Product as your base Price. A lookup_key lets your code find each one by
name instead of hard-coding IDs.
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const TIERS = { base: 1, light: 0.7, medium: 0.45, deep: 0.35 };
const BASE_CENTS = 4900; // $49 a month
for (const [tier, share] of Object.entries(TIERS)) {
await stripe.prices.create({
product: 'prod_YourProPlan',
currency: 'usd',
unit_amount: Math.round((BASE_CENTS * share) / 100) * 100, // round to whole dollars
recurring: { interval: 'month' },
lookup_key: `pro_monthly_${tier}`,
});
}
Round to prices that look intentional: $17 or $19 reads better than $17.15.
2. Map countries to tiers
Keep the mapping in one place and fall back to the base tier for anything missing.
const COUNTRY_TIER = {
DE: 'light', JP: 'light', ES: 'light', KR: 'light',
BR: 'medium', MX: 'medium', PL: 'medium', ZA: 'medium',
IN: 'deep', ID: 'deep', NG: 'deep', VN: 'deep',
// ...the rest of your list
};
const tierFor = (country) => COUNTRY_TIER[country] ?? 'base';
3. Detect the buyer’s country on the server
Most hosts already geolocate each request and pass the result as a header, so you don’t need a separate lookup:
| Host | Header with the two-letter country code |
|---|---|
| Cloudflare | CF-IPCountry |
| Vercel | x-vercel-ip-country |
| AWS CloudFront | CloudFront-Viewer-Country (add it to the origin request policy) |
Read the country on the server, never from the browser. Anything the browser sends, the buyer can change.
4. Show the price and send the same Price to Checkout
Look up the tier’s Price for the pricing page, then use that Price ID when creating the Checkout Session.
export async function createCheckout(req) {
const country = req.headers['cf-ipcountry'];
const tier = await isVpnOrProxy(req.ip) ? 'base' : tierFor(country);
const { data: [price] } = await stripe.prices.list({ lookup_keys: [`pro_monthly_${tier}`] });
return stripe.checkout.sessions.create({
mode: 'subscription',
line_items: [{ price: price.id, quantity: 1 }],
success_url: 'https://example.com/welcome',
cancel_url: 'https://example.com/pricing',
});
}
Decide the tier when the Checkout Session is created, not from a value the pricing page passes back. Otherwise anyone can swap in the cheapest Price ID.
5. Block VPN and proxy traffic
This is the step most homemade setups skip, and the one that decides whether PPP pricing costs you money. A buyer in the U.S. can connect through a VPN server in India in a few seconds.
- Check the IP before localizing. Look up the visitor’s IP in a VPN, proxy, Tor and hosting-provider database
(the
isVpnOrProxycall above) and show the base price when it matches. - Compare countries at payment. The card’s issuing country is on the payment method as
card.country. If it doesn’t match the IP country, you can flag the payment or move the customer to the base Price at renewal. Stripe Radar’s custom rules can do this comparison with:card_country:and:ip_country:.
Is there a faster way?
EasyPPP does all of the above as one API call. It creates the tier Prices on your Stripe Products, picks the right one for each visitor’s country, and returns your base price for VPN, proxy and hosting-provider traffic. Your pricing page calls one endpoint and shows the result:
const { data: [price] } = await fetch(
'https://api.easyppp.com/v1/price?key=ep_live_…&prices=price_1Pro',
).then((r) => r.json());
The response includes the Price ID to send to Checkout and a display amount in the visitor’s currency. EasyPPP charges 10% of the revenue it brings in at PPP prices and nothing on sales at your base price (pricing). It’s taking waitlist sign-ups ahead of launch, and a coding agent can set it up from this prompt.
Frequently asked questions
- How much should I discount for PPP pricing?
- Use the World Bank price level for each country as the guide. A country at 0.46 of U.S. prices, like Brazil, gets roughly half off. Most companies set a floor of 25–35% of the base price for the cheapest markets, and group countries into four or five tiers instead of pricing each one separately.
- Does Stripe support purchasing power parity pricing natively?
- Not directly. Stripe's Adaptive Pricing converts your price into the buyer's currency at the exchange rate, so the value doesn't change. Multi-currency Prices let you set one amount per currency, but a currency like the euro or the U.S. dollar covers countries with very different price levels. PPP pricing needs a price per country or per tier of countries.
- Should I use coupons or separate Stripe Prices for PPP?
- Separate Prices. Coupons can be shared and leaked, they show up as a discount on every invoice, and they can stack with other promotions. A separate Price is just the price, and it renews at that amount without any coupon duration to manage.
- How do I stop people using a VPN to get a cheaper price?
- Check the visitor's IP address against a VPN, proxy and hosting-provider database before showing a localized price, and show your base price when it matches. As a second check, compare the card's issuing country with the IP country at checkout.
- Will PPP pricing reduce my revenue from the U.S. and other rich markets?
- No, as long as buyers in those markets can't reach the lower prices. They see your base price as before. Only visitors whose country has a lower price level, and who aren't on a VPN or proxy, see a localized price.
- What data should PPP prices be based on?
- The World Bank's household consumption PPP conversion factor (indicator PA.NUS.PRVT.PP) divided by the market exchange rate. It compares what households actually buy, which suits software and digital products better than GDP-wide PPP figures or the Big Mac Index.
Charge every market a price it can pay.
EasyPPP sets purchasing power parity prices on your Stripe products, with VPN protection built in. 10% of the PPP revenue it brings in, nothing else.