---
title: "Eigene Parity Pricing für SaaS mit Polar und Astro erstellen"
description: "Kaufkraftparität (PPP) mit Polar und Astro umsetzen: Land des Besuchers serverseitig erkennen, Rabattstufe zuordnen und entweder % Rabatt oder einen Polar-Rabattcode für den Checkout zurückgeben."
source: "https://www.launchfa.st/de/blog/parity-pricing-polar-astro"
author: "Rishi Raj Jain"
created_at: 2026-04-07T12:00:00.000Z
keywords: "astro, polar, parity pricing, ppp pricing, purchasing power parity, regional pricing, discount codes, vercel, api routes, typescript"
---

> **Site index for agents**
> Fetch https://www.launchfa.st/llms.txt to discover every page on this site.
> Every page is also available as Markdown by appending `.md` to its URL.

<img width="1600" height="900" alt="Parity pricing with Polar and Astro" decoding="async" loading="eager" class="mt-4 border rounded-sm bg-cover bg-center bg-no-repeat transform will-change-auto" src="https://ik.imagekit.io/vjeqenuhn/launchfast-website/parity-pricing-polar-astro.png" />

Parity Pricing (oft basierend auf Kaufkraftparität / PPP) ist eine der einfachsten Möglichkeiten, Ihr Produkt weltweit erschwinglich zu machen, ohne Ihren Listenpreis dauerhaft zu senken. Die Umsetzung ist ebenfalls unkompliziert, wenn Sie bereits Folgendes haben:

- Einen Zahlungsanbieter, der **Rabattcodes** unterstützt (Polar tut das)
- Eine Möglichkeit, das **Land** eines Besuchers serverseitig zu erkennen (Astro-API-Routen auf Vercel machen das einfach)
- Ein klares Set an **Rabattstufen**, die Sie anbieten möchten

Dieses Tutorial führt durch ein produktionsreifes Parity-System mit **Polar + Astro**:

- Land **serverseitig** erkennen
- Land → Stufe zuordnen (z. B. 75 % / 65 % / 55 % …)
- JSON-Antwort mit Angebot zurückgeben, die **`percentOff`** und optional einen **Polar-Rabattcode** enthält
- Ein kleines Banner auf der Marketing-Website rendern und/oder den Rabatt beim Checkout anwenden

## Inhaltsverzeichnis

- [Schritt 1: Parity-Stufen definieren (Land → Rabattstufe)](#step-1-define-your-parity-tiers-country--discount-tier)
  - [Auswahl einer Mapping-Quelle](#picking-a-mapping-source)
- [Schritt 2: Land des Besuchers auf dem Server erkennen](#step-2-detect-the-visitors-country-on-the-server)
- [Schritt 3: Parity-Rabatte in Polar erstellen (manuell oder per API)](#step-3-create-parity-discounts-in-polar-manual-or-via-api)
  - [Strategie: ein Code pro Stufe vs. ein Code pro Land](#strategy-one-code-per-tier-vs-one-code-per-country)
  - [Rabatterstellung automatisieren (Überblick)](#automating-discount-creation-high-level)
- [Schritt 4: Entweder „Prozent Rabatt“ oder „Gutscheincode“ über eine Astro-Server-Route zurückgeben](#step-4-return-either-percent-off-or-coupon-code-from-an-astro-server-route)
- [Schritt 5: Parity-Banner in Astro rendern (clientseitig)](#step-5-render-a-parity-banner-in-astro-client-side)
- [Schutzmaßnahmen gegen Missbrauch](#guardrails-to-prevent-abuse)


## Voraussetzungen

Sie benötigen:

- [Node.js 20](https://nodejs.org/en/blog/announcements/v20-release-announce) oder höher
- Ein [Polar](https://polar.sh/)-Konto mit mindestens einem Produkt
- Astro im **Server-Modus** (`output: 'server'`), damit API-Routen zur Laufzeit ausgeführt werden
- (Empfohlen) Vercel-Deployment, wenn Sie integrierte Request-Geolokalisierung über Header nutzen möchten

## Architekturüberblick

```
1. Browser (marketing site)
   → GET /api/parity-offer?product=astro (optional product parameter)
   → Receives JSON: country, percentOff, eligible, optional coupon/discount code

2. Astro API route (server)
   → Resolves visitor country from request metadata (e.g. Vercel geolocation)
   → Looks up PPP tier for that country
   → Returns offer JSON for the UI + checkout flow

3. Polar (payments)
   → Discount codes exist per tier (or per product, depending on your strategy)
   → Checkout applies the code (either prefilled or user-entered)
```

Die zentrale Designentscheidung lautet: **Der Browser darf den Rabatt niemals selbst festlegen**. Die Server-Route sollte das Angebot berechnen, weil der Server Zugriff auf vertrauenswürdige Request-Metadaten hat und Schutzmaßnahmen durchsetzen kann.

## Schritt 1: Parity-Stufen definieren (Land → Rabattstufe)

Beginnen Sie damit, die Stufen festzulegen, die Sie anbieten möchten. Ein pragmatischer Ansatz ist, eine Handvoll Prozentstufen (10 %, 15 %, 25 %, 35 %, 45 %, 55 %, 65 %, 75 %) zu verwenden und Länder jeweils einer Stufe zuzuordnen.

In Ihrer eigenen Astro-Anwendung fügen Sie eine Datei `src/lib/parity.ts` hinzu, in der jede Stufe Folgendes enthält:

- `pppRange`: rein informativ (nützlich für Docs/Admin-Oberflächen)
- `percentOff`: der zu kommunizierende Rabatt
- `couponCode`: ein **einzelner** Code, den alle Länder in dieser Stufe teilen
- `countries`: die ISO-3166-1-alpha-2-Codes, die zu dieser Stufe gehören

Ihre Kerntypen können so aussehen:

```ts
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
}
```

Erstellen Sie anschließend einmal beim Modul-Init eine schnelle Lookup-Map:

```ts
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
}
```

Stellen Sie schließlich einen kleinen Helper bereit, der ein einzelnes Angebot zurückgibt:

```ts
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 } : {}),
  }
}
```

### Auswahl einer Mapping-Quelle

Sie haben mehrere Optionen:

- **Hardcodierte Stufen** (wie in diesem Beispiel): am einfachsten, keine externen Abhängigkeiten, leicht nachvollziehbar.
- **Datenbankgestützte Stufen**: sinnvoll, wenn Sie eine Admin-Oberfläche brauchen oder Zuordnungen ohne Redeploy aktualisieren möchten.
- **Dynamische PPP-Indizes**: möglich, aber selten die Komplexität wert, es sei denn, Ihr Geschäft hängt davon ab.

Starten Sie mit hardcodierten Stufen, Sie können später jederzeit weiterentwickeln.

## Schritt 2: Land des Besuchers auf dem Server erkennen

Sie benötigen einen Ländercode für Parity Pricing. Auf Vercel können Sie ihn aus Geolokalisierungs-Metadaten des Requests ableiten.

Die API-Route in diesem Repository nutzt `geolocation()` aus `@vercel/functions` innerhalb einer Astro-`APIRoute`:

```ts title="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' },
  })
}
```

Wenn Sie woanders deployen (oder mehr Kontrolle wünschen), können Sie auch Provider-Header nutzen (z. B. `x-vercel-ip-country`) oder eine serverseitige IP-zu-Land-Abfrage. Entscheidend ist, dass dies serverseitig passiert und vom Browser nicht trivial gefälscht werden kann.

## Schritt 3: Parity-Rabatte in Polar erstellen (manuell oder per API)

Ihre Parity-Stufen referenzieren Codes wie `DRRBQY5P`. Diese Codes müssen in Polar als Rabatte/Promotions existieren, damit der Checkout sie anwenden kann.

Sie können sie auf zwei Wegen erstellen:

- **Dashboard (manuell):** Pro Stufe einen Rabattcode anlegen (empfohlen zum Start). Halten Sie den Code kurz, stabil und wiederverwendbar.
- **Polar API (automatisiert):** Rabatte programmatisch erstellen (und aktualisieren) über https://polar.sh/docs/api-reference/discounts/create, sodass Ihre Stufen-Tabelle die Source of Truth ist.

### Strategie: ein Code pro Stufe vs. ein Code pro Land

- **Ein Code pro Stufe**: am einfachsten zu verwalten, das nutzen die meisten Teams.
- **Ein Code pro Land**: feine Kontrolle, aber hoher operativer Aufwand.

In den meisten Parity-Systemen ist **stufenbasiert** der Sweet Spot.

### Rabatterstellung automatisieren (Überblick)

Polar stellt APIs zur Verwaltung von Commerce-Entitäten bereit. Die genauen Endpoint-Namen können sich ändern, die Architektur bleibt gleich:

1. Halten Sie eine Stufen-Definitionsliste im Code (`PARITY_TIERS`)
2. Fügen Sie eine interne Admin-Route oder ein Einmal-Skript hinzu, das:
   - Bestehende Rabatte aus Polar auflistet
   - Fehlende Rabatte pro Stufe erstellt
   - Sicherstellt, dass `percentOff` jeder Stufe Ihren Erwartungen entspricht
3. Speichern Sie die resultierenden Rabatt-IDs/Codes zurück in Ihrer Stufen-Map (oder halten Sie Codes stabil, sodass kein Zurückschreiben nötig ist)

Wenn die Website einen **Rabattcode** zurückgeben soll, muss der Code nur in Polar existieren.
Wenn die Website einen **Rabattprozentsatz** zurückgeben soll, können Sie ihn in der UI anzeigen, auch wenn der Code manuell beim Checkout eingegeben wird.

## Schritt 4: Entweder „Prozent Rabatt“ oder „Gutscheincode“ über eine Astro-Server-Route zurückgeben

Ihre API-Antwort sollte beide Anwendungsfälle abdecken:

- **Marketing-UI**: „\(X\) % Rabatt für Kunden in \(Country\)“ anzeigen + optional „Code ABCD verwenden“
- **Checkout**: Code automatisch anwenden (wenn Ihr Checkout Prefill unterstützt) oder zumindest prominent anzeigen

Dieses Repository gibt sowohl `percentOff` als auch (bei Berechtigung) `couponCode` von `/api/parity-offer` zurück.

Eine kleine, aber wichtige Verbesserung für Production ist Caching:

- Geolokalisierungsbasierte Angebote brauchen keine millisekundengenaue Aktualität.
- JSON kurz am Edge cachen (z. B. 1 h–24 h), um Serverarbeit zu reduzieren.

Zum Beispiel:

```ts
return new Response(JSON.stringify(offer), {
  headers: {
    'content-type': 'application/json',
    // Cache on the CDN; always revalidate on the server when not cached.
    'cache-control': 'max-age=0, s-maxage=86400',
  },
})
```

## Schritt 5: Parity-Banner in Astro rendern (clientseitig)

Sobald Sie `/api/parity-offer` haben, können Sie ein kleines Banner hinzufügen, das den regionalen Rabatt anzeigt.

Eine Komponente wie `src/components/ParityBanner.astro` kann ein minimales Client-Skript nutzen:

- Ruft `/api/parity-offer` ab
- Prüft `eligible` und `percentOff`
- Baut einen kurzen Satz und hängt optional den Code an, falls vorhanden

```astro title="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>
```

## Schutzmaßnahmen gegen Missbrauch

Parity Pricing ist ein Rabattsystem, Sie brauchen daher grundlegende Schutzmaßnahmen:

- **Nur-Server-Entscheidung:** Stufe niemals im Browser berechnen.
- **Begrenzen und validieren:** Nur Codes zurückgeben, die in Ihrer Stufen-Tabelle existieren.
- **Produkt-Scoping:** Wenn manche Produkte keine Parity-Rabatte haben sollen, für diese `eligible: false` zurückgeben.

Wenn Sie stärkere Durchsetzung brauchen (z. B. um das Teilen eines High-Tier-Codes zu verhindern), erwägen Sie **kurzlebige, benutzerspezifische** Rabattmechanismen statt geteilter Codes. Das ist komplexer, aber der nächste Schritt, wenn Parity ein wesentlicher Umsatzhebel wird.

## Abschluss

Parity Pricing braucht beim Start Ihres SaaS keinen großen Implementierungsaufwand. Mit Astro, Vercel und Polar können Sie es so umsetzen:

- Eine deterministische Stufen-Map (Land → Prozent + Code)
- Eine einzelne API-Route, die das Land serverseitig auflöst und ein Angebots-JSON zurückgibt
- Eine kleine UI-Oberfläche, die den Rabatt klar kommuniziert

Von dort aus können Sie die Rabatterstellung per Polar API automatisieren, produktspezifische Berechtigungsregeln hinzufügen und die Durchsetzung verschärfen, wenn Ihr Volumen wächst.
