Localize Stripe Prices to the Visitor's Currency in 25 Lines

Published: (May 4, 2026 at 03:20 PM EDT)
3 min read
Source: Dev.to

Source: Dev.to

Overview

Stripe’s Price object is tied to a single currency (e.g., $79 USD).
For conversion‑rate optimization you can display the price in the visitor’s local currency (ARS, BRL, EUR, …) while still charging the canonical USD amount on the backend.

How it works

  1. Store the canonical price in USD – never modify the Stripe Price.
  2. Detect the visitor’s country on the server (IP‑based).
  3. Map the country to its local currency.
  4. Fetch the live USD → local‑currency rate from an FX API.
  5. Render the localized amount (e.g., “≈ AR$ 7 900”) next to the USD price.
  6. When the user clicks Buy, Stripe processes the payment in USD; the conversion is display‑only.

This approach gives visitors familiar numbers while keeping Stripe’s accounting simple and chargeback‑safe.

Implementation

Helper: localizedPrice

// lib/localized-price.ts
import { headers } from 'next/headers';

interface LocalizedPrice {
  usd: number;
  display: string;
  currency: string;
  rate: number;
}

const APOGEO = 'https://api.apogeoapi.com/v1';

export async function localizedPrice(usdAmount: number): Promise {
  const ip =
    headers().get('x-forwarded-for')?.split(',')[0] ??
    headers().get('x-real-ip') ??
    '';

  if (!ip) return fallback(usdAmount, 'USD', 1);

  // 1️⃣ Detect visitor country
  const ipRes = await fetch(`${APOGEO}/ip/${ip}`, {
    headers: { 'X-API-Key': process.env.APOGEOAPI_KEY! },
    next: { revalidate: 3600 }, // 1 h
  });
  if (!ipRes.ok) return fallback(usdAmount, 'USD', 1);
  const { country } = await ipRes.json();
  const currency: string = country.currency;
  if (currency === 'USD') return fallback(usdAmount, 'USD', 1);

  // 2️⃣ Fetch live FX rate
  const fxRes = await fetch(`${APOGEO}/exchange-rates/${currency}`, {
    headers: { 'X-API-Key': process.env.APOGEOAPI_KEY! },
    next: { revalidate: 14400 }, // 4 h
  });
  if (!fxRes.ok) return fallback(usdAmount, 'USD', 1);
  const { usdRate }: { usdRate: number } = await fxRes.json();

  const localAmount = Math.round(usdAmount * usdRate);
  const display = new Intl.NumberFormat(undefined, {
    style: 'currency',
    currency,
    maximumFractionDigits: 0,
  }).format(localAmount);

  return { usd: usdAmount, display, currency, rate: usdRate };
}

function fallback(usdAmount: number, currency: string, rate: number): LocalizedPrice {
  return {
    usd: usdAmount,
    display: `$${usdAmount} ${currency}`,
    currency,
    rate,
  };
}

Using the helper in a component

// components/PricingCard.tsx
import { localizedPrice } from '@/lib/localized-price';

export default async function PricingCard() {
  const price = await localizedPrice(79); // Stripe price in USD

  return (
    <>
      <h2>Professional Plan</h2>

      <p>$79 USD</p>

      {price.currency !== 'USD' && (
        <p>≈ {price.display} at today&apos;s rate</p>
      )}

      <button>Buy now</button>
    </>
  );
}

Practical Tips

  • Never change the Stripe Price object per currency; keep a single USD price and use display‑only localization.
  • Round appropriately – e.g., ARS has no useful cents, so maximumFractionDigits: 0 removes fractions.
  • Graceful fallback – if the FX API is unavailable, show the USD amount instead of “$undefined”.
  • Cache rates for 4 hours (matching the API’s refresh cadence) to avoid excess requests.
  • Adaptive Pricing (Stripe‑provided per‑currency Prices) incurs a ~2 % FX spread that Stripe keeps and hides the visitor’s original display price. The display‑only method is free, transparent, and simpler.

Optional: Currency Switcher

If you want users to override auto‑detection:

  1. Store the chosen currency in a cookie.
  2. Pass that value to localizedPrice (add an optional overrideCurrency parameter).
  3. The same helper will return the appropriate display amount.

Cost & Limits

  • ApogeoAPI free tier: 1 000 requests/month.
  • With a 4‑hour cache, this covers roughly 5 000 page views per month.
  • Get a key at .

Original Publication

Stripe Localized Pricing with FX – ApogeoAPI Blog

0 views
Back to Blog

Related posts

Read more »

Making my own framework. Any tips?

!Cover image for Making my own framework. Any tips?https://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fde...