In-App-Käufe für eine Claude Code Web-App: RevenueCat in WebViewGold
In den iOS- und Android-Apps laufen digitale Abos über die Stores; auf deiner Website kann Stripe bleiben. WebViewGold startet den Store-Kauf über RevenueCat mit einem einzigen URL-Schema, die Arbeit in deiner Codebasis besteht also aus einer sauberen Plattform-Weiche, einem Webhook und einer Entitlement-Prüfung. Dieser Guide zeigt, wie du genau das von Claude Code bauen lässt und wie du vor dem Review beweist, dass es funktioniert.
Ab $109 einmalig pro Plattform, oder unser Team erledigt alles ab $698.
Genehmigungsgarantie – oder Geld zurück innerhalb von 14 Tagen
Kein Programmieren nötig
Einmalzahlung, kostenlose Updates
24/7 Support von echten Menschen
Made in Germany seit 2014 · ISO 27001
9:41
Deine App
Deine AppJetzt auf dem Smartphone
App Store · Google Play Freigegeben
Die Regel und wie die Teile zusammenpassen
Apples Richtlinie 3.1.1 besagt, dass digitale Inhalte, Funktionen und Abos, die in einer iOS-App freigeschaltet werden, über In-App-Kauf laufen müssen; Googles Zahlungsrichtlinie verlangt dasselbe für Play Billing. Physische Waren und Dienstleistungen, die außerhalb der App genutzt werden, sind ausgenommen. Die regionalen Ausnahmen haben sich seit 2025 stark verändert: Apples aktuelle Richtlinien erlauben in Apps im US-Storefront Buttons und Links zu anderen Zahlungsmethoden, und Google hat seine US-Regeln nach dem Urteil Epic v. Google geändert. Lies beide in ihrer aktuellen Fassung, bevor du dich darauf verlässt; der einfachste Weg, der überall durchgeht, bleibt In-App-Kauf in der App und Stripe im Web. Der Guide Stripe vs. In-App-Kauf führt dich durch die Entscheidung.
Auf der WebViewGold-Seite ist es wenig Arbeit. RevenueCat ist die Standard-Integration: Dein Button navigiert zu revenuecat://purchase?external_id=USER_ID&product=PRODUCT_ID, das native Store-Sheet erscheint, RevenueCat erfasst die Transaktion und schickt einen Webhook an deinen Server, und dein Server schreibt das Entitlement, das deine Web-App prüft. restoreinapppurchases:// stellt Käufe wieder her, und cancelinapppurchase:// öffnet die Abo-Einstellungen des Stores. Ab iOS 15 zeigt revenuecat://launchPaywall?external_id=USER_ID&offering=OFFERING_ID die RevenueCat-Paywall, die du in deren Dashboard gestaltet hast; die Doku führt dieses Schema nur für iOS, Android nutzt also revenuecat://purchase pro Produkt. Beachte, dass In-App-Käufe in einem Endprodukt die WebViewGold Extended License voraussetzen (siehe die Doku zu In-App-Käufen).
Was Claude Code übernimmt und was du machst
Bereich
Claude Code
Du
Store-Konsolen
Schreibt die Produktliste und die Paywall-Texte, die du dort einfügst
Vereinbarung für kostenpflichtige Apps, Bank- und Steuerdaten, Abo-Produkte in App Store Connect und der Play Console
RevenueCat
Erklärt, welche Felder du ausfüllen musst, mehr nicht
Projekt mit beiden Apps, Produkte, Entitlement, Offering, Webhook-URL und der Wert des Authorization-Headers
WebViewGold
Kann auf Wunsch die RevenueCat-Keys in Config.swift / Config.java setzen
Bundle-ID und Package-Name passend zu RevenueCat, Builds, Sandbox-Tester
Web-App
Paywall-Weiche, Restore- und Verwalten-Links, Webhook-Route, Entitlement-Tabelle, Polling nach dem Kauf, ein Prüfbefehl gegen Stripe in der App
Den Plan freigeben, Umgebungsvariablen setzen, den Kauf auf Geräten testen
Einrichtung in der Reihenfolge, die Wartezeit spart
Store-Vereinbarungen und die Prüfung der Produkte dauern am längsten; fang damit an.
Produkte in beiden Stores
Unterzeichne in App Store Connect die Vereinbarung für kostenpflichtige Apps (Paid Apps) und lege dann eine Abo-Gruppe mit Produkten wie pro_monthly und pro_yearly an. Lege in der Play Console dieselben Produkt-IDs als Abos mit einem Basis-Abo an; Play verlangt, dass die App in mindestens einem Test-Track liegt. Der RevenueCat-Setup-Guide geht jedes Feld durch.
RevenueCat-Projekt
Füge die App-Store-App und die Play-Store-App hinzu, importiere die Produkte, hänge sie an ein Entitlement namens pro und pack sie in ein Offering. Trag unter Integrations → Webhooks https://app.example.com/api/webhooks/revenuecat und einen selbst generierten Wert für den Authorization-Header ein.
WebViewGold-Keys
iOS: Setze in Config.swift kRevenueCatEnable auf true und fülle revenueCatAPIKey und revenueCatProjectID aus; lass kRevenueCatShowDefaultOfferingPaywall auf false, außer du willst wirklich bei jedem Start eine Paywall. Android: Fülle REVENUECAT_API_KEY und REVENUECAT_PROJECT_ID in Config.java aus. Verwende den öffentlichen SDK-Key, den RevenueCat für die App der jeweiligen Plattform anzeigt.
Den Prompt im Plan-Modus ausführen
Der Plan ist hier der wertvollste Teil: Er sollte jede Stelle auflisten, an der deine App etwas verkauft oder einen Preis nennt. Gib ihn frei, lass den Agenten implementieren, trag dann die Umgebungsvariable REVENUECAT_WEBHOOK_AUTH und deine Datenbank-Zugangsdaten beim Hoster ein und deploye.
In der Sandbox bauen und testen
TestFlight auf iOS, interner Test auf Android, mit den Checks unten. Wenn du die nativen Builds und das Store-Setup lieber abgibst, konfiguriert und baut unser Full Service-Team beide Apps.
Ersetze die Produkt-IDs und Routennamen. Der letzte Punkt legt einen wiederverwendbaren Slash-Command an, den du vor jedem Release ausführen kannst.
In Claude Code einfügen (zuerst im Plan-Modus)
In unseren iOS- und Android-Apps (gebaut mit WebViewGold, erkennbar am Token WebViewGoldApp im User-Agent) müssen digitale Käufe über den App Store und Google Play laufen. WebViewGold integriert RevenueCat: Die Navigation zu revenuecat://purchase?external_id=USER_ID&product=PRODUCT_ID startet einen nativen Kauf, restoreinapppurchases:// stellt Käufe wieder her und cancelinapppurchase:// öffnet die Abo-Einstellungen des Stores. Ab iOS 15 zeigt revenuecat://launchPaywall?external_id=USER_ID&offering=OFFERING_ID die RevenueCat-Paywall; die Doku führt das nur für iOS, Android nutzt also revenuecat://purchase pro Produkt. Lies die Abschnitte zu In-App Purchases in https://www.webviewgold.com/llms-full.txt und verwende nichts, was dort nicht dokumentiert ist. Unsere Produkt-IDs sind pro_monthly und pro_yearly, das Entitlement heißt pro.
Erstelle zuerst einen Plan und warte auf meine Freigabe. Der Plan muss jede Stelle auflisten, an der wir etwas Digitales verkaufen, bepreisen oder bewerben (Stripe-Checkout, Preisseite, Upgrade-Banner, E-Mails mit Zahlungslinks), und wie sich jede davon in der App verhält.
Implementiere dann:
1. src/lib/native.ts: Verwende isNativeApp() weiter und ergänze isIOSApp() auf Basis des User-Agents.
2. Paywall-Aktionen: buyPro(productId) navigiert zu revenuecat://purchase mit der ID des angemeldeten Nutzers (URL-kodiert) als external_id. Starte nie einen Kauf für einen abgemeldeten Nutzer; schick ihn zuerst zum Login. In Browsern bleibt der bestehende Stripe-Ablauf unverändert.
3. Blende in der App jeden Stripe-Button, jeden Web-Preis und jeden externen Zahlungslink aus. Erwähne keine günstigeren Web-Preise.
4. Die Paywall zeigt Tarifname, Laufzeit, Preis pro Zeitraum, den Hinweis, dass sich das Abo bis zur Kündigung automatisch verlängert, und Links zu /terms und /privacy. Bevorzuge auf iOS launchPaywall, das die Store-Preise anzeigt. Wo wir eine eigene Paywall rendern, lies das Storefront mit getstorelocation:// (window.storeLocation) aus und nimm die Preise aus einer Tabelle pro Storefront in src/config/prices.ts.
5. Ein Button „Käufe wiederherstellen“ (restoreinapppurchases://) und ein Link „Abo verwalten“ (cancelinapppurchase://) in den Einstellungen und auf der Paywall, nur in der App.
6. POST /api/webhooks/revenuecat: Vergleiche den Authorization-Header mit REVENUECAT_WEBHOOK_AUTH, lies event.type, event.app_user_id, event.product_id, event.expiration_at_ms, event.environment und event.store und schreibe per Upsert eine Zeile in entitlements (user_id, entitlement, product_id, store, expires_at, will_renew, sandbox). CANCELLATION bedeutet nur, dass die automatische Verlängerung aus ist; der Zugang gilt bis expires_at. Leg die Migration an.
7. GET /api/me/entitlement liefert, ob der Nutzer gerade pro hat. Frag den Endpunkt nach dem Start eines Kaufs bis zu einer Minute lang alle 4 Sekunden ab und zusätzlich bei visibilitychange, und schalte dann die UI frei. Der Webhook ist die einzige verlässliche Quelle; schalte nie auf Basis von Client-State frei.
8. Lege .claude/commands/iap-check.md an: einen Slash-Command, der Komponenten, die in der App gerendert werden, nach Stripe-Aufrufen, Checkout-Links und fest kodierten Preisen durchsucht und alles meldet, was mit Apple-Richtlinie 3.1.1 kollidieren würde.
Schließe mit den Umgebungsvariablen, den Einstellungen, die ich im RevenueCat-Dashboard eintragen muss, und einem Testplan ab.
Die Webhook-Route, die deine Entitlements verwaltet
RevenueCat schickt bei jedem Event den Wert des Authorization-Headers mit, den du im Dashboard eingetragen hast. app_user_id ist die external_id, die dein Button übergeben hat, deshalb muss das deine stabile Nutzer-ID sein.
typescript
// app/api/webhooks/revenuecat/route.ts
import { NextResponse } from "next/server";
import { db } from "@/lib/server";
const GRANTS = new Set(["INITIAL_PURCHASE", "RENEWAL", "PRODUCT_CHANGE", "UNCANCELLATION", "NON_RENEWING_PURCHASE"]);
export async function POST(req: Request) {
// Derselbe Wert, den du in RevenueCat -> Integrations -> Webhooks -> Authorization header eingetragen hast
if (req.headers.get("authorization") !== process.env.REVENUECAT_WEBHOOK_AUTH) {
return NextResponse.json({ error: "unauthorized" }, { status: 401 });
}
const { event } = await req.json();
const userId: string | undefined = event?.app_user_id;
if (!userId || userId.startsWith("$RCAnonymousID")) {
return NextResponse.json({ ok: true, skipped: "no user id" }); // Kauf ohne external_id
}
const base = {
userId,
entitlement: "pro",
productId: event.product_id,
store: event.store, // APP_STORE oder PLAY_STORE
sandbox: event.environment === "SANDBOX",
};
const expiresAt = event.expiration_at_ms ? new Date(event.expiration_at_ms) : null;
if (GRANTS.has(event.type)) {
await db.entitlements.upsert({ ...base, expiresAt, willRenew: true });
} else if (event.type === "CANCELLATION" || event.type === "BILLING_ISSUE") {
// Verlängerung aus oder Zahlung fehlgeschlagen: Zugang behalten, bis der Store den Ablauf meldet
await db.entitlements.upsert({ ...base, expiresAt, willRenew: false });
} else if (event.type === "EXPIRATION") {
await db.entitlements.upsert({ ...base, expiresAt: new Date(), willRenew: false });
}
// TRANSFER-Events enthalten stattdessen transferred_from / transferred_to; behandle sie, wenn du Kontowechsel erlaubst
return NextResponse.json({ ok: true });
}
Checkliste (Sandbox, echte Geräte)
iOS: Installiere den TestFlight-Build und kaufe. TestFlight-Käufe laufen in der Sandbox und werden nicht berechnet; Abos verlängern sich dort einmal pro Tag, bis zu sechsmal. Um Verlängerung und Ablauf schneller zu testen, melde dich mit einem Sandbox-Apple-Account an (App Store Connect → Benutzer und Zugriff → Sandbox), bei dem sich die Verlängerungsrate anpassen lässt.
Android: Füge dein Gmail-Konto in der Play Console unter Einrichtung → Lizenztests hinzu, installiere aus dem internen Test-Track (keine per Sideload installierte APK) und kaufe; Testkäufe werden nicht berechnet.
Stell das RevenueCat-Dashboard auf Sandbox-Daten um und such den Kunden über deine Nutzer-ID. Taucht der Kauf stattdessen unter einer anonymen ID auf, war external_id leer.
Deine Tabelle entitlements hat eine Zeile mit sandbox = true, und die App schaltet pro innerhalb weniger Sekunden frei, ohne manuelles Neuladen.
Lösch die App, installiere sie neu, melde dich an und tippe auf „Käufe wiederherstellen“: Der Zugang ist wieder da. Tippe auf „Abo verwalten“: Der Abo-Screen des Stores öffnet sich.
Öffne die Website im Desktop-Browser mit demselben Konto: Auch dort ist pro freigeschaltet, und Web-Besuchern wird weiterhin Stripe angeboten.
Führe /iap-check in Claude Code aus: In der App ist kein Stripe-Button, kein Checkout-Link und kein Web-Preis erreichbar.
Stolperfallen
Instabile external_id. Übergib deine Nutzer-ID aus der Datenbank, keine E-Mail-Adresse, die sich ändern kann. Verlange vor der Paywall einen Login; so bleiben RevenueCat-Kunden und deine Konten eins zu eins zugeordnet.
Fest kodierte Preise. Store-Preise unterscheiden sich je nach Land und ändern sich mit den Steuern. Bevorzuge auf iOS die RevenueCat-Paywall und halte jede Preistabelle pro Storefront synchron mit den Stores.
CANCELLATION als Ende behandeln. Das Event schaltet nur die automatische Verlängerung ab. Entzieh den Zugang bei EXPIRATION oder wenn expires_at überschritten ist.
Gelöschte Sessions. Steht deletecache (iOS) oder CLEAR_CACHE_ON_STARTUP (Android) auf true, werden Nutzer bei jedem Start abgemeldet, und dein Button hat keine Nutzer-ID zum Mitschicken.
Stripe, das in die App durchsickert. Eine Preisseite, die über einen Footer-Link erreichbar ist, eine Upgrade-E-Mail, die sich in der App öffnet, eine Checkout-Erfolgsseite: Jede davon ist ein Risiko unter 3.1.1. Genau dafür ist der Slash-Command da.
Häufig gestellte Fragen
Muss ich RevenueCat verwenden?
Nein. WebViewGold hat auch eine manuelle API (inapppurchase:// und inappsubscription:// mit Erfolgs- und Ablauf-URLs, dazu die injizierten Variablen planID, transactionIdentifier und subreceipts). RevenueCat ist der Standard, weil du damit ein Entitlement und einen Webhook für beide Stores bekommst.
Kann ich Stripe für meine Website behalten?
Ja. Besucher im Browser behalten den Stripe-Checkout. Beide Quellen schreiben in dieselbe Entitlement-Tabelle, ein Nutzer, der im Web bezahlt hat, ist also auch in der App pro, solange die App nicht für den Kauf im Web wirbt.
Was ist mit den US-Regeln für externe Zahlungslinks?
Apples aktuelle Richtlinien erlauben für Apps im US-Storefront Buttons und Links zu anderen Zahlungsmethoden, und Google hat seine US-Richtlinie nach dem Epic-Urteil geändert. Die Regeln unterscheiden sich je nach Region und ändern sich laufend, lies also den aktuellen Richtlinientext, bevor du darauf aufbaust.
Braucht Claude Code meine RevenueCat-Secrets?
Nein. Die öffentlichen SDK-Keys in Config.swift und Config.java sind dafür gedacht, mit der App ausgeliefert zu werden. Der Authorization-Wert des Webhooks und die Datenbank-Keys liegen in den Umgebungsvariablen deines Hosters; gib dem Agenten nur ihre Namen.
Woher weiß die Web-App, dass ein Kauf geklappt hat?
Die Doku definiert keinen Erfolgs-Callback für revenuecat://purchase, behandle den Webhook also als verlässliche Quelle: Die App fragt nach dem Start eines Kaufs kurz deinen Entitlement-Endpunkt ab und schaltet frei, sobald die Zeile auftaucht.
Drei Wege, deine App in die Stores zu bringen
Gleiches Ergebnis, unterschiedlich viel Arbeit auf deiner Seite. Alle drei kommen mit Genehmigungsgarantie und 24/7 Support von echten Menschen.
Selbst machen
$109 pro Plattform, einmalig
Die WebViewGold-Lizenz: ein fertiges Xcode- oder Android-Studio-Projekt. URL einfügen, Name und Icon setzen, Features einschalten, bauen und einreichen. Kostenlose Updates für immer.
Kein Mac, kein Xcode, kein Android Studio: Du konfigurierst im Browser, wir bauen beide Apps in der Cloud und übergeben sie an App Store Connect und die Play Console.
Schick uns deine URL und dein Icon. Unser Team konfiguriert, baut und liefert deine App innerhalb von 72 Stunden, White-Label, inklusive Quellprojekten. Beide Plattformen für $1,158.
Erstattungen erfolgen zu den in unseren FAQ genannten Bedingungen: eine nicht inhaltsbedingte Ablehnung durch Apple und/oder Google, geltend gemacht innerhalb von 14 Tagen nach dem Kauf.