Crea tu propio Parity Pricing para SaaS con Polar y Astro
LaunchFast Logo LaunchFast
Blog
2091 palabras 11 min de lectura

Crea tu propio Parity Pricing para SaaS con Polar y Astro

Implementa precios de paridad de poder adquisitivo (PPP) con Polar y Astro: detecta el país del visitante en el servidor, mapéalo a un nivel de descuento y devuelve un % de descuento o un código de descuento de Polar para el checkout.

Rishi Raj Jain
Rishi Raj Jain Autor
Parity pricing con Polar y Astro

El parity pricing (a menudo basado en paridad de poder adquisitivo / PPP) es una de las formas más sencillas de hacer tu producto asequible a nivel global sin bajar permanentemente tu precio de referencia. La implementación también es directa si ya tienes:

Sponsored

Kits de inicio de alta calidad con flujo de autenticación integrada (Auth.js), carga de objetos (AWS, Clouflare R2, Firebase Storage, Supabase Storage), pagos integrados (Stripe, LemonSqueezy), flujo de verificación de correo electrónico (Resend, Postmark, Sendgrid) y mucho más . Compatible con cualquier base de datos (Redis, Postgres, MongoDB, SQLite, Firestore).

Get all 3 kits Bundle ↗

One-time license · Lifetime updates

  • Un proveedor de pagos que soporte códigos de descuento (Polar lo hace)
  • Una forma de detectar el país del visitante en el servidor (las rutas API de Astro en Vercel lo facilitan)
  • Un conjunto claro de niveles de descuento que estés dispuesto a ofrecer

Este tutorial recorre un sistema de parity listo para producción con Polar + Astro:

  • Detectar el país en el servidor
  • Mapear país → nivel (p. ej. 75 % / 65 % / 55 %…)
  • Devolver una respuesta JSON de «oferta» que contenga percentOff y, opcionalmente, un código de descuento de Polar
  • Renderizar un banner pequeño en el sitio de marketing y/o aplicar el descuento en el checkout

Tabla de contenidos

Requisitos previos

Necesitarás:

  • Node.js 20 o posterior
  • Una cuenta de Polar con al menos un producto
  • Astro ejecutándose en modo servidor (output: 'server') para que las rutas API se ejecuten en tiempo de ejecución
  • (Recomendado) Despliegue en Vercel si quieres geolocalización de solicitudes integrada vía headers

Visión general de la arquitectura

1. Navegador (sitio de marketing)
→ GET /api/parity-offer?product=astro (parámetro de producto opcional)
→ Recibe JSON: country, percentOff, eligible, código de cupón/descuento opcional
2. Ruta API de Astro (servidor)
→ Resuelve el país del visitante desde metadatos de la solicitud (p. ej. geolocalización de Vercel)
→ Busca el nivel PPP para ese país
→ Devuelve JSON de oferta para la UI + flujo de checkout
3. Polar (pagos)
→ Los códigos de descuento existen por nivel (o por producto, según tu estrategia)
→ El checkout aplica el código (prefijado o introducido por el usuario)

La decisión de diseño clave es: el navegador nunca debe decidir el descuento. La ruta del servidor debe calcular la oferta, porque el servidor tiene acceso a metadatos de solicitud confiables y puede aplicar salvaguardas.

Paso 1: Define tus niveles de parity (país → nivel de descuento)

Empieza decidiendo los niveles que quieres ofrecer. Un enfoque pragmático es usar un puñado de niveles porcentuales (10 %, 15 %, 25 %, 35 %, 45 %, 55 %, 65 %, 75 %) y asignar países a cada nivel.

En tu propia aplicación Astro, añade un archivo src/lib/parity.ts donde cada nivel tenga:

  • pppRange: puramente informativo (útil para docs/pantallas de admin)
  • percentOff: el descuento que anunciar
  • couponCode: un único código compartido por todos los países de ese nivel
  • countries: los códigos ISO 3166-1 alpha-2 que pertenecen a ese nivel

Tus tipos principales pueden verse así:

export type ParityTierMeta = {
pppRange: string
percentOff: number
couponCode: string
}
export type ParityOffer = {
country: string
countryName: string
product: string
percentOff: number
eligible: boolean
couponCode?: string
pppRange?: string
}

Luego construye un mapa de búsqueda rápido una vez al inicializar el módulo:

function buildCountryParityMap(): Record<string, { percentOff: number; couponCode: string; pppRange: string }> {
const map: Record<string, { percentOff: number; couponCode: string; pppRange: string }> = {}
for (const tier of PARITY_TIERS) {
for (const cc of tier.countries) {
map[cc] = { percentOff: tier.percentOff, couponCode: tier.couponCode, pppRange: tier.pppRange }
}
}
return map
}

Finalmente, expón un helper pequeño que devuelva una sola oferta:

export function getParityOffer({ country, product }: { country: string; product: string }): ParityOffer {
const upper = country.toUpperCase()
const rule = COUNTRY_PARITY[upper]
const percentOff = rule?.percentOff ?? 0
return {
country: upper,
countryName: new Intl.DisplayNames(['en'], { type: 'region' }).of(upper) ?? upper,
product,
percentOff,
eligible: percentOff > 0,
...(rule?.couponCode ? { couponCode: rule.couponCode } : {}),
...(rule?.pppRange ? { pppRange: rule.pppRange } : {}),
}
}

Elegir una fuente de mapeo

Tienes opciones:

  • Niveles hardcodeados (lo que hace este ejemplo): lo más simple, sin dependencias externas, fácil de razonar.
  • Niveles respaldados por base de datos: útil si necesitas una UI de admin o quieres actualizar mapeos sin redesplegar.
  • Índices PPP dinámicos: posible, pero rara vez vale la complejidad a menos que tu negocio dependa de ello.

Empieza con niveles hardcodeados; siempre puedes evolucionar más adelante.

Paso 2: Detecta el país del visitante en el servidor

Necesitas un código de país para aplicar parity pricing. En Vercel, puedes derivarlo de los metadatos de geolocalización de la solicitud.

La ruta API de este repositorio usa geolocation() de @vercel/functions dentro de un APIRoute de Astro:

src/pages/api/parity-offer.ts
import { geolocation } from '@vercel/functions'
import type { APIRoute } from 'astro'
import { getParityOffer } from '../../lib/parity'
export const GET: APIRoute = async ({ request, url }) => {
const product = url.searchParams.get('product') || 'default'
const geo = geolocation(request)
const country = geo.country || 'US'
const offer = getParityOffer({ country, product })
return new Response(JSON.stringify(offer), {
headers: { 'content-type': 'application/json' },
})
}

Si despliegas en otro lugar (o quieres más control), también puedes usar headers del proveedor (por ejemplo x-vercel-ip-country) o una búsqueda server-side de IP a país. Lo clave es que esto ocurre en el servidor y no puede falsificarse trivialmente desde el navegador.

Paso 3: Crea descuentos de parity en Polar (manual o vía API)

Tus niveles de parity referencian códigos como DRRBQY5P. Esos códigos deben existir como descuentos/promociones en Polar para que el checkout los aplique.

Puedes crearlos de dos formas:

  • Dashboard (manual): Crea un código de descuento por nivel (recomendado para empezar). Mantén el código corto, estable y reutilizable.
  • API de Polar (automatizado): Crea (y actualiza) descuentos programáticamente vía https://polar.sh/docs/api-reference/discounts/create para que tu tabla de niveles sea la fuente de verdad.

Estrategia: un código por nivel vs un código por país

  • Un código por nivel: lo más fácil de gestionar, lo que usa la mayoría de equipos.
  • Un código por país: te da control fino pero dispara la carga operativa.

En la mayoría de sistemas de parity, por nivel es el punto óptimo.

Automatizar la creación de descuentos (visión general)

Polar expone APIs para gestionar entidades de comercio. Los nombres exactos de los endpoints pueden evolucionar, pero la arquitectura se mantiene igual:

  1. Mantén una lista de definición de niveles en tu código (PARITY_TIERS)
  2. Añade una ruta admin solo interna o un script puntual que:
    • Liste descuentos existentes de Polar
    • Cree descuentos faltantes para cada nivel
    • Asegure que el percentOff de cada nivel coincida con lo esperado
  3. Almacena los identificadores/códigos de descuento resultantes de vuelta en tu mapa de niveles (o mantén códigos estables para no tener que escribir de vuelta)

Si quieres que el sitio devuelva un código de descuento, solo necesitas que el código exista en Polar. Si quieres que el sitio devuelva un porcentaje de descuento, puedes mostrarlo en la UI aunque el código se aplique manualmente en el checkout.

Paso 4: Devuelve «porcentaje de descuento» o «código de cupón» desde una ruta de servidor de Astro

La forma de tu respuesta API debe soportar ambos casos de uso:

  • UI de marketing: mostrar «(X)% de descuento para clientes en (Country)» + opcionalmente «Usa el código ABCD»
  • Checkout: aplicar el código automáticamente (si tu checkout soporta prefilling) o al menos mostrarlo de forma destacada

Este repositorio devuelve tanto percentOff como (cuando es elegible) couponCode desde /api/parity-offer.

Una mejora pequeña pero importante para producción es añadir caché:

  • Las ofertas basadas en geolocalización no necesitan frescura a nivel de milisegundos.
  • Cachea el JSON por un periodo corto en el edge (p. ej. 1 h–24 h) para reducir trabajo del servidor.

Por ejemplo:

return new Response(JSON.stringify(offer), {
headers: {
'content-type': 'application/json',
// Cache en el CDN; siempre revalidar en el servidor cuando no esté en caché.
'cache-control': 'max-age=0, s-maxage=86400',
},
})

Paso 5: Renderiza un banner de parity en Astro (lado del cliente)

Una vez tengas /api/parity-offer, puedes añadir un banner pequeño que muestre al usuario su descuento regional.

Un componente como src/components/ParityBanner.astro puede usar un script mínimo en el cliente:

  • Obtiene /api/parity-offer
  • Comprueba eligible y percentOff
  • Construye una frase corta y opcionalmente añade el código si se proporciona
src/components/ParityBanner.astro
<div id="parity-banner" class="hidden w-full border-b py-2.5 text-center" role="status" aria-live="polite">
<div class="mx-auto flex max-w-7xl flex-wrap items-center justify-center gap-x-2 gap-y-1 px-4 text-sm leading-snug">
<span id="parity-flag" class="shrink-0 text-lg leading-none" aria-hidden="true"></span>
<p id="parity-text" class="font-sans"></p>
</div>
</div>
<script>
function countryCodeToFlagEmoji(code) {
const upper = String(code).toUpperCase()
if (upper.length !== 2 || !/^[A-Z]{2}$/.test(upper)) return ''
return String.fromCodePoint(...[...upper].map((c) => 127397 + c.charCodeAt(0)))
}
const banner = document.getElementById('parity-banner')
const flagEl = document.getElementById('parity-flag')
const textEl = document.getElementById('parity-text')
if (!banner || !flagEl || !textEl) {
/* skip */
} else {
fetch('/api/parity-offer')
.then((r) => r.json())
.then((data) => {
if (!data || !data.eligible || !data.percentOff || data.percentOff <= 0) return
flagEl.textContent = countryCodeToFlagEmoji(data.country ?? '')
const pct = data.percentOff
const place = data.countryName || data.country || ''
const parts = []
parts.push(`<strong class="font-bold">${pct}%</strong>`)
parts.push(` discount for customers in `)
parts.push(`<strong class="font-bold">${place}</strong>!`)
if (data.couponCode) {
parts.push(` Use code `)
parts.push(`<strong class="font-bold">${data.couponCode}</strong>`)
}
textEl.innerHTML = parts.join('')
banner.classList.remove('hidden')
})
.catch(() => {})
}
</script>

Salvaguardas para prevenir abusos

El parity pricing es un sistema de descuentos, así que necesitas salvaguardas básicas:

  • Decisión solo en servidor: nunca calcules el nivel en el navegador.
  • Limitar y validar: solo devuelve códigos que existan en tu tabla de niveles.
  • Alcance por producto: si algunos productos no deben tener descuentos de parity, devuelve eligible: false para esos.

Si necesitas una aplicación más estricta (por ejemplo, evitar que los usuarios compartan un código de nivel alto), considera emitir mecanismos de descuento de corta duración y específicos por usuario en lugar de códigos compartidos. Es más complejo, pero es el siguiente paso cuando el parity se convierte en una palanca de ingresos material.

Conclusión

El parity pricing no requiere un gran esfuerzo de implementación cuando estás empezando con tu SaaS. Con Astro, Vercel y Polar, puedes implementarlo como:

  • Un mapa de niveles determinista (país → porcentaje + código)
  • Una única ruta API que resuelve el país en el servidor y devuelve un payload JSON de oferta
  • Una superficie de UI mínima que comunica el descuento con claridad

A partir de ahí, puedes automatizar la creación de descuentos vía la API de Polar, añadir reglas de elegibilidad por producto y reforzar la aplicación a medida que crece tu volumen.

Sigue leyendo