Aller au contenu
WebViewGold Du vibe coding à l'App Store
Français
Obtenir WebViewGold

ChatGPT & Codex

Achats intégrés pour une app web créée avec Codex : RevenueCat, WebViewGold et une pull request digne de confiance

C'est sur les paiements qu'un agent peut vous coûter un refus ou de l'argent bien réel : traitez donc cette tâche comme une tâche cloud soumise à une relecture stricte. Codex trouve chaque endroit où vous vendez des biens numériques, les fait passer par RevenueCat quand la page s'exécute dans l'app et écrit le webhook qui accorde l'accès sur votre serveur. Vous gardez la main sur les produits, les clés et le bouton de merge.

Dès $109 en paiement unique par plateforme, ou notre équipe s'occupe de tout dès $698.

  • Garantie d'approbation – ou remboursement sous 14 jours
  • Aucun codage requis
  • Paiement unique, mises à jour gratuites
  • Support humain 24/7
  • Conçu en Allemagne depuis 2014 · ISO 27001

L'objectif et le point de vue de l'examinateur

L'objectif : dans les apps iOS et Android, chaque produit numérique (abonnements, crédits, fonctionnalités premium, contenus à débloquer) s'achète via le store ; dans les navigateurs, Stripe continue de fonctionner ; et les deux parcours écrivent le même enregistrement de droits d'accès sur votre serveur.

L'examinateur vérifie trois choses. Un bouton d'achat qui ouvre un formulaire de carte ou un checkout externe pour des biens numériques entraîne un refus au titre de la guideline 3.1.1. Un paywall d'abonnement sans prix, période de facturation, conditions de renouvellement et liens vers les conditions d'utilisation et la politique de confidentialité entraîne un refus au titre de la 3.1.2. L'absence d'un bouton Restaurer les achats est également signalée. Les biens physiques et les services du monde réel relèvent d'une autre catégorie et peuvent garder Stripe dans l'app (comment faire la différence).

Les règles concernant les liens vers des achats externes ont évolué dans certaines régions, par exemple sur le storefront américain après la décision Epic de 2025 et dans l'UE avec le DMA. Si vous souhaitez utiliser ces options, lisez d'abord le texte actuel des guidelines dans App Store Connect et laissez-les de côté pour votre première soumission. La suite de cette page suppose le parcours standard, qui est aussi celui que recommande le workflow de publication avec Codex. Toutes les options d'achat proposées par WebViewGold sont résumées sur la page dédiée aux achats intégrés.

Tous les flux d'argent, cartographiés

SituationCe que fait l'app webRemarques
Un utilisateur du navigateur achèteLe Stripe Checkout existant, inchangéLe webhook Stripe écrit le droit d'accès
Un utilisateur de l'app achète un produitrevenuecat://purchase?external_id=UID&product=PRODUCT_IDiOS et Android ; UID est votre ID utilisateur stable, PRODUCT_ID tel qu'affiché dans RevenueCat
Un utilisateur de l'app ouvre un paywalliOS : revenuecat://launchPaywall?external_id=UID&offering=OFFERING (iOS 15 ou plus récent). Android : votre propre page de paywall qui appelle purchaseLa documentation ne décrit launchPaywall que pour iOS
Restaurerrestoreinapppurchases://Un bouton visible, attendu pour les abonnements et les produits non consommables
Gérer ou résiliercancelinapppurchase://Ouvre l'écran des abonnements du store
Afficher les achats passésgetpurchasehistory:// remplit window.purchaseHistoryPar appareil ; les noms de champs diffèrent entre iOS et Android
Accorder l'accèsRien côté clientLe webhook RevenueCat vers Express écrit le droit d'accès

WebViewGold nous a permis d'avoir le temps de mise sur le marché le plus court et le plus efficace jamais possible parmi toutes les autres plates-formes d'application que nous avons essayées au cours des 5 dernières années. Je ne sais pas pourquoi je ne vous ai pas découvert plus tôt, mais je suis vraiment, vraiment content de l'avoir fait. Nous approchons actuellement les 6000 téléchargements d'application sur l'App Store iOS et Google Play Store sans aucun problème!

Antonio Davila · CodeCanyon

Un AGENTS.md imbriqué pour le code de facturation

Codex combine les fichiers AGENTS.md depuis la racine du dépôt jusqu'au dossier dans lequel il travaille, et le fichier le plus proche du code l'emporte. Un petit server/billing/AGENTS.md garde les règles de paiement à côté du code de paiement, en plus de votre AGENTS.md racine, et sa section Review guidelines indique à la revue de code de Codex ce qu'elle doit signaler sur chaque pull request qui touche à la facturation. La revue Codex sur GitHub remonte les constats P0 et P1 : les règles utilisent donc ces niveaux.

markdown
# server/billing/AGENTS.md

## Invariants
- Entitlements live in Firestore at entitlements/{uid} and are written ONLY by
  webhooks: /webhooks/stripe (source "stripe") and /webhooks/revenuecat
  (source "revenuecat"). No client code, callable function or admin page sets premium.
- Every access check reads entitlements/{uid} on the server. Never trust a flag,
  cookie or localStorage value from the device.
- The RevenueCat app user ID is the Firebase uid, passed as external_id in
  revenuecat://purchase?external_id=...&product=... (never the email address).
- Webhooks: compare the Authorization header with REVENUECAT_WEBHOOK_AUTH, dedupe by
  event.id in billingEvents/{eventId}, answer 200 quickly. Tests use fixtures in
  server/billing/__fixtures__, never live RevenueCat or Stripe APIs.
- Inside the native app (isNativeApp()), digital goods use the bridge only:
  revenuecat://purchase, revenuecat://launchPaywall (iOS only),
  restoreinapppurchases://, cancelinapppurchase://.
  No Stripe links, no card forms, no "cheaper on the web" copy.
- Product IDs come from src/config/products.ts, never hardcoded in components.

## Review guidelines
- P0: any code path that grants or extends premium outside the two webhooks.
- P0: a webhook handler that does not check the Authorization header.
- P1: a Stripe checkout, price link or external payment URL for a digital product
  that is reachable when isNativeApp() is true.
- P1: a paywall without price, billing period, renewal terms, Terms and Privacy
  links, or without a Restore Purchases control.

La tâche cloud

Exécutez ceci en tâche cloud pour que le résultat arrive sous forme de diff que vous pouvez transformer en pull request, et non sous forme de modifications dans votre copie de travail. Lancez-la depuis la page Codex dans ChatGPT, depuis l'extension IDE ou avec codex cloud dans le terminal. L'environnement a besoin du dépôt et d'un script de configuration qui installe les dépendances (par exemple npm ci). Il n'a pas besoin d'accès réseau à RevenueCat ou à Stripe, et il ne doit jamais contenir de clés de production.

À coller dans une tâche cloud Codex (ou dans la CLI)
Lisez AGENTS.md et server/billing/AGENTS.md. Objectif : dans les apps natives iOS et Android (isNativeApp() est true), chaque produit numérique est vendu via les achats intégrés, par le bridge RevenueCat de WebViewGold. Les navigateurs gardent Stripe. Stack : front-end React, API Express, Firebase Auth, Firestore.

1. Inventaire : listez chaque endroit où un abonnement, un pack de crédits ou une fonctionnalité premium est vendu ou présenté avec un prix (composants, routes, e-mails ou bannières qui mènent au checkout). Mettez la liste dans la description de la pull request, avec le fichier correspondant à chaque entrée.
2. Client : créez src/lib/purchase.ts avec buy(productKey), openPaywall(), restorePurchases() et manageSubscription(). Dans l'app, buy navigue vers revenuecat://purchase?external_id=UID&product=PRODUCT_ID, avec UID = l'uid Firebase (encodé pour l'URL) et PRODUCT_ID issu de src/config/products.ts. restorePurchases navigue vers restoreinapppurchases://, manageSubscription vers cancelinapppurchase://. Dans les navigateurs, buy conserve le Stripe Checkout existant.
3. Paywall : sur iOS, openPaywall peut utiliser revenuecat://launchPaywall?external_id=UID&offering=default (documenté uniquement pour iOS 15 ou plus récent). Gardez notre propre composant de paywall pour Android et comme solution de repli, derrière un flag dans src/config/products.ts. Notre paywall affiche le prix, la période de facturation, les conditions de renouvellement et des liens vers /terms et /privacy.
4. Dans l'app, supprimez les boutons Stripe, les liens vers les tarifs web et les textes du type « moins cher sur le web » pour les biens numériques. Les biens physiques et les services restent sur Stripe.
5. Serveur : POST /webhooks/revenuecat dans server/billing/. Renvoyez 401 sauf si l'en-tête Authorization est égal à REVENUECAT_WEBHOOK_AUTH. Dédoublonnez par event.id dans billingEvents. Écrivez entitlements/{uid} avec premium, source, store, productId, expiresAt et sandbox. Le webhook Stripe écrit le même document avec source "stripe". Chaque vérification premium côté serveur lit uniquement ce document.
6. Une fois la feuille d'achat native fermée, le client n'accorde rien. Il interroge GET /api/entitlements pendant 20 secondes au maximum et affiche « Traitement de votre achat » en attendant.
7. Réglages : des lignes Restaurer les achats et Gérer l'abonnement, uniquement dans l'app native.
8. Tests avec des événements de fixture (INITIAL_PURCHASE, RENEWAL, CANCELLATION, EXPIRATION, TEST, un ID d'événement en double) et une requête avec un en-tête Authorization erroné. Aucun appel aux API réelles de RevenueCat ou de Stripe.
9. Description de la pull request : l'inventaire, les variables d'environnement que je dois définir, les étapes restantes dans le tableau de bord RevenueCat et un plan de test sandbox pour iOS et Android.

Le webhook que vous devriez trouver dans la pull request

Simplifié, mais avec les parties qui comptent lors de la relecture : l'autorisation, l'idempotence, l'ID utilisateur tiré de app_user_id (qui est votre external_id) et aucune intervention du client. Les remboursements, les transferts et les produits à vie demandent un traitement supplémentaire dans une vraie app.

javascript
// server/billing/revenuecat.js (Express + firebase-admin)
import express from "express";
import { getFirestore, FieldValue } from "firebase-admin/firestore";

const db = getFirestore();
export const revenuecatRouter = express.Router();
const GRANT = new Set(["INITIAL_PURCHASE", "RENEWAL", "UNCANCELLATION",
  "PRODUCT_CHANGE", "NON_RENEWING_PURCHASE", "SUBSCRIPTION_EXTENDED"]);

revenuecatRouter.post("/webhooks/revenuecat", express.json(), async (req, res) => {
  if (req.get("Authorization") !== process.env.REVENUECAT_WEBHOOK_AUTH) {
    return res.status(401).end();
  }
  const event = req.body && req.body.event;
  if (!event || !event.id) return res.status(400).end();
  if (event.type === "TEST") return res.status(200).end();

  const seen = db.collection("billingEvents").doc(event.id);
  if ((await seen.get()).exists) return res.status(200).end(); // duplicate delivery

  const uid = event.app_user_id; // = external_id from revenuecat://purchase
  if (uid) {
    const expiresAt = event.expiration_at_ms ? new Date(event.expiration_at_ms) : null;
    const ref = db.collection("entitlements").doc(uid);
    if (GRANT.has(event.type)) {
      await ref.set({ premium: true, source: "revenuecat", store: event.store,
        productId: event.product_id, expiresAt, sandbox: event.environment === "SANDBOX",
        updatedAt: FieldValue.serverTimestamp() }, { merge: true });
    } else if (event.type === "EXPIRATION") {
      await ref.set({ premium: false, expiresAt, updatedAt: FieldValue.serverTimestamp() }, { merge: true });
    }
    // CANCELLATION and BILLING_ISSUE: access usually runs until EXPIRATION.
  }
  await seen.set({ type: event.type, uid: uid || null, at: FieldValue.serverTimestamp() });
  res.status(200).end();
});

De la pull request à un achat en sandbox

  1. Relisez la pull request comme un auditeur

    Lisez d'abord l'inventaire : chaque point de vente doit y figurer, et chacun doit aboutir soit à Stripe (navigateur), soit au bridge (app). Commentez ensuite @codex review for payment bypasses sur la pull request pour obtenir un second avis ciblé qui applique les review guidelines imbriquées. Ne mergez que lorsque vous pouvez expliquer chaque parcours.

  2. Configurez le webhook

    Dans RevenueCat, ajoutez un webhook qui pointe vers https://api.example.com/webhooks/revenuecat et définissez une valeur d'en-tête d'autorisation ; mettez la même valeur dans REVENUECAT_WEBHOOK_AUTH sur votre serveur. Envoyez l'événement de test de RevenueCat et vérifiez que la réponse est un 200.

  3. Définissez les clés natives

    iOS, Config.swift : kRevenueCatEnable à true, revenueCatAPIKey, revenueCatProjectID ; laissez kRevenueCatShowDefaultOfferingPaywall à false, sauf si le paywall doit s'ouvrir à chaque lancement. Android, Config.java : REVENUECAT_API_KEY et REVENUECAT_PROJECT_ID. Le bundle ID et le nom de package doivent correspondre exactement aux apps RevenueCat (documentation IAP iOS, documentation IAP Android).

  4. Testez en sandbox

    iOS : un build TestFlight et un compte Apple sandbox. Android : des testeurs de licence et une piste de test interne. Achetez, restaurez sur un second appareil, laissez expirer un abonnement sandbox, et vérifiez que le droit d'accès ne change qu'à l'arrivée du webhook.

  5. Soumettez avec les produits associés

    Associez les premiers achats intégrés à la version de l'app dans App Store Connect, et indiquez à l'examinateur où se trouve le paywall et quel compte de démo utiliser (modèle de notes pour l'examen).

Checklist de test

  • Navigateur de bureau : Stripe fonctionne exactement comme avant, et aucun lien revenuecat:// n'est accessible.
  • App : aucun écran n'affiche de bouton Stripe, de formulaire de carte ni de lien vers les tarifs web pour des biens numériques.
  • Un achat sur iOS et sur Android débloque le premium en quelques secondes, et seulement après que le webhook a écrit le droit d'accès.
  • Fermer l'app de force pendant que la feuille d'achat est ouverte ne laisse pas d'état intermédiaire ; le webhook accorde quand même l'accès.
  • La restauration sur un second appareil (même compte store, même identifiant de connexion) rétablit le premium.
  • Gérer l'abonnement ouvre l'écran du store ; un abonnement sandbox résilié reste actif jusqu'à son expiration.
  • Le paywall affiche le prix, la période, les conditions de renouvellement et des liens vers les conditions d'utilisation et la politique de confidentialité.
  • Une requête webhook avec un en-tête Authorization erroné reçoit un 401.

Pièges

  • Utiliser l'adresse e-mail comme external_id. Les e-mails changent, et le client RevenueCat se retrouve scindé. Utilisez l'uid.
  • Vendre avant la connexion. Sans ID stable, l'achat atterrit chez un client que vous ne pourrez pas rattacher plus tard. Demandez d'abord la connexion, ou utilisez un ID d'appareil issu de get-uuid:// et fusionnez de manière délibérée.
  • Accorder le premium côté client à la fermeture de la feuille d'achat. C'est un déblocage gratuit pour quiconque modifie une requête ; interrogez plutôt votre serveur.
  • Se fier à l'ordre des événements. Les webhooks peuvent arriver en retard ou en double. Dédoublonnez par ID d'événement ; pour un état plus robuste, faites relire au handler le client depuis l'API REST de RevenueCat à chaque événement, au lieu d'interpréter chaque type d'événement.
  • Effacer les cookies à chaque démarrage (deletecache sur iOS, CLEAR_CACHE_ON_STARTUP sur Android). La documentation WebViewGold recommande de les laisser désactivés quand le parcours d'achat dépend de la session.
  • Supposer que window.purchaseHistory a la même forme partout : iOS renvoie purchaseDateMs sous forme de chaîne, Android purchaseTime et orderId. Demandez à Codex d'écrire une seule fonction de normalisation.

Questions fréquentes

Le paywall doit-il être natif ?

Non. Une page de paywall dans votre app web qui appelle revenuecat://purchase fonctionne sur les deux plateformes, à condition d'afficher le prix, la période, les conditions de renouvellement et les liens légaux. Sur iOS, vous pouvez aussi ouvrir un paywall conçu dans RevenueCat avec revenuecat://launchPaywall (iOS 15 ou plus récent).

Faut-il donner mes clés RevenueCat ou Stripe à la tâche cloud ?

Non. La tâche écrit du code et des tests basés sur des fixtures. Les clés ont leur place dans l'environnement de votre serveur, et c'est vous qui les définissez après le merge. Garder les secrets de production hors des environnements d'agents est la réduction de risque la moins coûteuse dont vous disposez.

Les abonnés web peuvent-ils utiliser le premium dans l'app ?

En général, oui. La règle multiplateforme d'Apple (guideline 3.1.3(b)) permet aux utilisateurs d'accéder à ce qu'ils ont acheté sur votre site web, à condition que les mêmes éléments soient également proposés en achat intégré dans l'app. Comme les deux webhooks écrivent le même droit d'accès, un abonné Stripe qui se connecte est aussi premium dans l'app.

Combien Apple et Google prélèvent-ils ?

La commission standard est de 30 %. Le Small Business Program d'Apple et le taux réduit de Google sur le premier million de dollars de revenus annuels la ramènent à 15 %, et RevenueCat prélève une part du chiffre d'affaires au-delà de son seuil gratuit.

Ai-je besoin de l'Extended License de WebViewGold ?

Oui. La documentation WebViewGold sur les achats intégrés exige l'Extended License pour utiliser les achats intégrés dans un produit final. Passez à cette licence avant de soumettre le build qui contient les achats.

Trois façons de mettre votre app dans les stores

Même résultat, charge de travail différente de votre côté. Les trois formules incluent la garantie d'approbation et le support humain 24/7.

Faites-le vous-même

$109 par plateforme, paiement unique

La licence WebViewGold : un projet Xcode ou Android Studio prêt à l'emploi. Collez votre URL, définissez le nom et l'icône, activez les fonctionnalités, compilez et soumettez. Mises à jour gratuites à vie.

Voir les offres sur la page principale

Full Service

$698 par plateforme $798

Envoyez-nous votre URL et votre icône. Notre équipe configure, compile et livre votre app sous 72 heures, en marque blanche, projets sources inclus. Les deux plateformes pour $1,158.

Détails du Full Service Propulsé par WeSetupYourWebViewApp

Prix Soldes d'hiver valables jusqu'au 31 décembre 2026.

Prêt à publier votre app ?

Garantie d'approbation App Store & Google Play – ou remboursement sous 14 jours

Vous préférez déléguer ? Full Service : nous configurons, compilons et livrons sous 72 heures, dès $698 par plateforme. Des questions ? Chattez avec nous 24/7.

Les remboursements sont accordés aux conditions indiquées dans notre FAQ : un refus non lié au contenu par Apple et/ou Google, demandé dans les 14 jours suivant l'achat.