💰 Bonus — Monétiser une application mobile avec Ionic
⚠️ Chapitre hors scope du module, proposé en bonus pour les plus curieux. Il ne fait pas partie des objectifs évalués, mais reflète la réalité du marché et vous permettra de mettre en place une monétisation dans vos projets personnels.
Dans ce chapitre bonus, vous allez découvrir comment gagner de l'argent avec une application mobile, de la théorie jusqu'à l'implémentation concrète dans un projet Ionic-Vue.
La monétisation est une réalité du marché : même les applications gratuites doivent générer des revenus pour être viables. Comprendre comment ça fonctionne — et comment l'intégrer proprement dans un projet — est une compétence précieuse, que vous soyez futur développeur freelance, employé, ou entrepreneur.
🎯 Objectifs d'apprentissage
À la fin de ce chapitre, vous serez capables de :
- comprendre les différents modèles de monétisation d'une application mobile ;
- distinguer les formats publicitaires (bannière, interstitiel, rewarded) et savoir quand les utiliser ;
- appliquer les guidelines officielles AdMob par format ;
- intégrer le plugin
@capacitor-community/admobdans un projet Ionic-Vue ; - implémenter une bannière, une pub interstitielle et une pub récompensée ;
- comprendre le fonctionnement des achats intégrés (IAP) et leur intégration avec RevenueCat ;
- comprendre ce que la monétisation implique pour la publication sur les stores.
💰 B.1 — Les modèles de monétisation mobiles
💡 B.1.1 Vue d'ensemble
Il existe plusieurs façons de gagner de l'argent avec une application mobile. Elles ne s'excluent pas et peuvent souvent être combinées.
| Modèle | Description | Exemples |
|---|---|---|
| Freemium | Gratuit avec fonctionnalités premium payantes | Spotify, Duolingo |
| Publicité (ads) | Affichage de pubs, revenus via impressions/clics | Jeux mobiles gratuits |
| Abonnement | Paiement récurrent mensuel ou annuel | Netflix, Strava |
| Achat unique (paid app) | L'utilisateur achète l'app une seule fois | Certains jeux/outils |
| Achats intégrés (IAP) | Contenu ou fonctionnalités achetables dans l'app | V-Bucks Fortnite, skins |
| Affiliate / partenariats | Liens sponsorisés, recommandations rémunérées | Apps de voyage, finance |
💬 Exemple : Clash Royale est gratuit, mais génère des centaines de millions via les achats intégrés (gemmes, coffres). Candy Crush, lui, se finance surtout via les pubs et quelques IAP.
📊 B.1.2 Lequel choisir ?
Le choix du modèle dépend du type d'app, de l'audience cible et de la valeur perçue.
Règle générale :
- App utilitaire ou pro → abonnement ou achat unique
- Jeu casual ou app grand public → publicité + IAP
- App avec forte valeur ajoutée → freemium (base gratuite + premium payant)
⚠️ Attention à l'UX :
Un mauvais modèle de monétisation peut détruire une bonne application.
- Pubs intrusives
- Paywalls agressifs
- Dark patterns (faux boutons "Fermer", comptes à rebours trompeurs)
… → avis négatifs et désinstallations massives.
Dans ce chapitre, nous nous concentrons sur deux approches pratiques :
- la publicité avec Google AdMob ;
- les achats intégrés (IAP) avec RevenueCat.
📢 B.2 — La publicité mobile avec Google AdMob
📱 B.2.1 Qu'est-ce qu'AdMob ?
Google AdMob est la régie publicitaire mobile de Google, et la plus utilisée dans le monde.
Elle sert d'intermédiaire entre :
- les annonceurs (qui veulent diffuser leurs pubs)
- les développeurs (qui veulent monétiser leur app).
En tant que développeur, vous :
- créez une unité publicitaire dans la console AdMob ;
- intégrez le SDK AdMob dans votre app ;
- recevez des revenus à chaque impression ou clic (CPM / CPC).
💬 Exemple : pour 1 000 impressions d'une bannière, vous touchez en moyenne entre 1€ et 3€ selon la région et la thématique de l'app. Les pubs vidéo rewarded rapportent généralement beaucoup plus.
🎨 B.2.2 Les formats publicitaires
Il existe plusieurs formats, chacun adapté à un contexte précis.
🔲 Bannière (Banner)
- Petite bande publicitaire, affichée de manière permanente en haut ou en bas de l'écran.
- Non-intrusive, revenus modestes mais constants.
- Idéale pour les apps à usage prolongé (lecture, outils, utilitaires).
💬 Exemple : une app de calculatrice affiche une bannière en bas en permanence.
⬛ Interstitiel (Interstitial)
- Publicité plein écran qui s'affiche entre deux écrans ou moments de transition naturels.
- Plus intrusive, mais plus rémunératrice.
- Doit être utilisée aux bons moments — jamais en plein milieu d'une action.
- L'utilisateur peut la fermer après quelques secondes.
💬 Exemple : dans un jeu mobile, une interstitielle s'affiche entre deux niveaux ou après un "Game Over".
🎬 Rewarded (RewardedAd)
- Publicité vidéo volontaire : l'utilisateur choisit de la regarder en échange d'une récompense.
- Meilleur taux d'engagement, non-intrusive car volontaire.
- Très utilisée dans les jeux.
💬 Exemple : "Regardez une vidéo pour obtenir 50 pièces d'or !" dans un jeu mobile.
📋 B.2.2 Tableau récapitulatif des formats
| Format | Intrusivité | Revenus | Déclenchement recommandé |
|---|---|---|---|
| Bannière | Faible | Faibles | Permanent, contenu statique |
| Interstitiel | Élevée | Moyens | Transitions naturelles (fin de niveau, changement de page) |
| Rewarded | Nulle (volontaire) | Élevés | Sur action explicite de l'utilisateur |
⏱️ B.2.3 Guidelines officielles et bonnes pratiques UX
Google publie des guidelines officielles pour chaque format. Ne pas les respecter peut entraîner la suspension de votre compte AdMob ou le refus de votre app sur le store.
Nous allons voir les règles essentielles, format par format.
🔲 Guidelines — Bannière
- Ne jamais placer une bannière de façon à provoquer des clics accidentels — par exemple juste sous un bouton de l'app.
- Ne pas empiler plusieurs bannières sur le même écran.
- Ne pas masquer la bannière sous d'autres éléments de l'interface.
- La bannière doit rester visible sans jamais gêner l'usage principal.
- Utiliser de préférence le format
ADAPTIVE_BANNER: il s'adapte à la largeur de l'écran et est le format recommandé par Google depuis 2023.
⬛ Guidelines — Interstitiel
Ces règles sont issues de la documentation officielle AdMob.
À propos du format
- Conçues pour des apps à expérience linéaire, avec des points de départ/arrêt clairs.
- Si votre app n'a pas cette structure (lampe torche, calculatrice), préférez une bannière.
Certaines interstitielles peuvent avoir un délai jusqu'à 5 secondes avant d'afficher "Fermer". Avec les high-engagement ads, ce délai peut monter à 12 secondes (voire ~30 secondes via certains réseaux).
⬛ Guidelines — Interstitiel (bonnes pratiques)
✅ Implémentations recommandées :
-
Afficher après une transition naturelle (fin de niveau, changement de section).
-
Laisser l'utilisateur terminer son action avant la pub.
-
Pré-charger la pub en arrière-plan avant d'en avoir besoin (
prepareInterstitial()). -
Se poser ces questions avant chaque affichage :
- Comment l'utilisateur interagit-il avec l'app à cet instant ?
- L'interstitielle va-t-elle le surprendre ?
- Est-ce vraiment le bon moment ?
⬛ Guidelines — Interstitiel (interdictions)
❌ Implémentations interdites :
- Afficher une interstitielle au lancement de l'application.
- Déclencher une pub en plein milieu d'une action (formulaire, lecture, gameplay actif).
- Afficher plusieurs interstitielles à la suite sans pause.
- Cacher ou rendre difficile d'accès le bouton "Fermer".
- Déclencher une pub suite à un clic accidentel.
💬 Règle des intervalles : implémentez toujours un cooldown (par ex. 3 à 5 minutes) entre deux interstitielles.
🎬 Guidelines — Rewarded
- La pub doit toujours être volontaire : l'utilisateur choisit explicitement de la regarder.
- La récompense doit être clairement annoncée avant le visionnage ("Regardez 30 secondes pour obtenir 50 pièces").
- Ne jamais forcer un utilisateur à regarder une rewarded pour continuer à utiliser l'app normalement.
- La récompense doit être délivrée uniquement après visionnage complet (événement
Rewarded). - Pour les grosses récompenses (argent réel, contenu premium), utiliser la vérification côté serveur (SSV).
💬 Les pubs rewarded ont les meilleurs eCPM et un engagement maximal.
📊 B.2.4 Estimations de revenus publicitaires (2024–2025)
| Format | eCPM moyen (UE/US) | Engagement | Risque UX |
|---|---|---|---|
| Bannière | 1€ – 3€ / 1 000 impressions | Faible | Très faible |
| Interstitiel | 5€ – 15€ / 1 000 impressions | Moyen | Élevé si mal placé |
| Rewarded | 10€ – 30€ / 1 000 impressions | Très élevé | Nul (volontaire) |
💬 Ces valeurs sont indicatives et varient selon la région, la thématique et la qualité de l’audience.
🛒 B.3 — Les achats intégrés (In-App Purchases)
🛒 B.3.1 Principe général
Les achats intégrés (IAP) permettent à l'utilisateur d'acheter du contenu ou des fonctionnalités directement dans l'app.
Types principaux :
| Type | Description | Exemple |
|---|---|---|
| Consommable | Acheté et "consommé" (disparaît après usage) | 100 pièces d'or, 5 vies |
| Non-consommable | Acheté une fois, disponible à vie | Supprimer les pubs, déverrouiller un niveau |
| Abonnement | Accès récurrent, renouvelable automatiquement | Accès premium mensuel |
🏪 B.3.2 Le rôle des stores
Les stores (Google Play, App Store) sont obligatoirement impliqués :
- hébergent et valident les produits achetables ;
- gèrent le paiement (sécurisé, l'app ne voit jamais les données bancaires) ;
- prélèvent une commission de 15% à 30% sur chaque achat.
💬 Sur un achat à 1.00 CHF, vous recevez 0.70 à 0.85 CHF selon les politiques en vigueur.
🔄 B.3.3 Le flux d'un achat
Quand l'utilisateur achète :
- L'utilisateur appuie sur "Acheter" dans l'app.
- Le store natif (Google Play / App Store) affiche sa fenêtre de paiement.
- L'utilisateur confirme (empreinte, Face ID, mot de passe).
- Le store envoie un receipt (reçu cryptographique) à l'app.
- L'app valide ce receipt (idéalement côté serveur) et délivre le contenu.
- Le store verse les revenus mensuellement (commission déduite).
⚠️ Validation côté serveur : recommandée pour les achats sensibles (abonnements, monnaie premium). RevenueCat gère cela automatiquement.
⚙️ B.3.4 RevenueCat — pourquoi et comment ça marche
Pour Ionic + Capacitor, la solution la plus fiable en production est RevenueCat.
| Sans RevenueCat | Avec RevenueCat |
|---|---|
| Deux APIs différentes (StoreKit / Billing) | Une seule API unifiée |
| Receipts à valider soi-même | Validation côté serveur automatique |
| Pas de dashboard | Dashboard revenus / rétention / MRR / LTV |
| Gestion complexe des abonnements | Gestion automatique |
| Gratuit | Gratuit jusqu'à 2 500$/mois gérés |
Concepts :
-
Product : produit store (ex :
premium_monthly) -
Entitlement : accès débloqué (ex :
premium) - Offering : ensemble d’offres à présenter (mensuel, annuel…)
💬 RevenueCat écoute les stores, valide les reçus, expose une API unifiée.
⚠️ B.3.5 Prérequis concrets pour tester les IAP
Contrairement à AdMob, les IAP :
- ne fonctionnent pas en local pur ;
- nécessitent une app sur piste de test interne (Google / Apple).
Prérequis :
- compte RevenueCat configuré avec votre app ;
- produits créés dans Google Play / App Store ;
- app publiée sur une piste de test interne.
👉 Dans cet exercice, concentrez-vous sur AdMob. Les IAP sont là pour vos projets personnels quand vous aurez les comptes et stores configurés.
📋 B.4 — Implications pour la publication
📋 B.4.1 Ce qu'il faut prévoir à l'avance
| Élément | Obligatoire ? | Description |
|---|---|---|
| Politique de confidentialité | ✅ Oui | Obligatoire dès que vous collectez des données (AdMob le fait) |
| Consentement RGPD (UMP) | ✅ Oui (UE) | Formulaire de consentement pour les pubs personnalisées |
| Déclaration des pubs dans le store | ✅ Oui | Google / Apple demandent de déclarer l'usage d'AdMob |
| Compte AdMob créé et vérifié | ✅ Oui | Doit être lié au projet avant publication |
| Compte développeur Google Play | ✅ Oui (Android) | 25$ unique |
| Compte développeur Apple | ✅ Oui (iOS) | 99$/an |
| Informations fiscales | ✅ Oui | Obligatoires pour recevoir les revenus |
| Déclaration COPPA (enfants) | ✅ Si <13 ans | Pubs non-personnalisées obligatoires |
| Bouton "Restaurer les achats" | ✅ Oui (iOS + IAP) | Exigé par Apple |
🔐 B.4.2 Consentement RGPD et UMP
Depuis 2024, Google exige pour les pubs en UE :
- un formulaire de consentement via UMP (User Messaging Platform) ;
- l'utilisateur choisit pubs personnalisées ou non.
Si l’utilisateur refuse :
- pubs non-personnalisées (
npa: true) ; - revenus plus faibles, mais conformité légale.
L’implémentation du formulaire est intégrée dans useAdMob.ts via requestConsentInfo() et showConsentForm().
📊 B.4.3 IDs de test vs production
AdMob fournit des IDs de test officiels :
| Plateforme | ID de test Banner | ID de test Interstitiel | ID de test Rewarded |
|---|---|---|---|
| Android | ca-app-pub-3940256099942544/6300978111 |
ca-app-pub-3940256099942544/1033173712 |
ca-app-pub-3940256099942544/5224354917 |
| iOS | ca-app-pub-3940256099942544/2934735716 |
ca-app-pub-3940256099942544/4411468910 |
ca-app-pub-3940256099942544/1712485313 |
⛔ Ne jamais utiliser vos IDs de prod pour tester :
- faux clics = suspension immédiate du compte AdMob.
Toujours utiliser les IDs de test en développement.
🔨 B.5 — Mise en pratique : projet AdMob de A à Z
B.5.1 — Créer et préparer le projet
ionic start quizflash-admob tabs --type=vue --capacitor
cd quizflash-admob
Vérifiez votre version de Capacitor :
npx cap --version
Installez les plugins :
npm install @capacitor-community/admob@6
npm install @revenuecat/purchases-capacitor
Ajoutez Android et synchronisez :
npx cap add android
ionic build
npx cap sync
⚠️
npx cap add androidune seule fois ; ensuiteionic build+npx cap sync.
B.5.2 — Configuration Android
Dans android/app/src/main/AndroidManifest.xml, à l'intérieur de <application> :
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="@string/admob_app_id"/>
Assurez-vous que launchMode est compatible avec les IAP :
<activity
android:name="com.yourapp.MainActivity"
android:launchMode="singleTop" />
Dans android/app/src/main/res/values/strings.xml :
<string name="admob_app_id">ca-app-pub-3940256099942544~3347511713</string>
💬 App ID de test à remplacer par le vôtre en prod.
B.5.3 — Composable useAdMob.ts (1/3)
// src/composables/useAdMob.ts
import {
AdMob,
BannerAdOptions, BannerAdSize, BannerAdPosition,
AdmobConsentStatus,
} from '@capacitor-community/admob'
// ─── IDs publicitaires ──────────────────────────────────────────
const IS_TESTING = true
const AD_IDS = {
banner: IS_TESTING ? 'ca-app-pub-3940256099942544/6300978111' : 'VOTRE_ID_BANNER',
interstitial: IS_TESTING ? 'ca-app-pub-3940256099942544/1033173712' : 'VOTRE_ID_INTERSTITIAL',
rewarded: IS_TESTING ? 'ca-app-pub-3940256099942544/5224354917' : 'VOTRE_ID_REWARDED',
}
// ─── Cooldown interstitiel ──────────────────────────────────────
let lastInterstitialTime = 0
const INTERSTITIAL_COOLDOWN_MS = 3 * 60 * 1000 // 3 minutes
export function useAdMob() {
B.5.3 — Composable useAdMob.ts (2/3)
async function initialize(): Promise<void> {
await AdMob.initialize()
const consentInfo = await AdMob.requestConsentInfo()
if (consentInfo.isConsentFormAvailable && consentInfo.status === AdmobConsentStatus.REQUIRED) {
await AdMob.showConsentForm()
}
const trackingInfo = await AdMob.trackingAuthorizationStatus()
if (trackingInfo.status === 'notDetermined') {
await AdMob.requestTrackingAuthorization()
}
}
async function showBanner(): Promise<void> {
const options: BannerAdOptions = {
adId: AD_IDS.banner,
adSize: BannerAdSize.ADAPTIVE_BANNER,
position: BannerAdPosition.BOTTOM_CENTER,
}
await AdMob.showBanner(options)
}
async function hideBanner(): Promise<void> { await AdMob.hideBanner() }
async function removeBanner(): Promise<void> { await AdMob.removeBanner() }
B.5.3 — Composable useAdMob.ts (3/3)
async function prepareInterstitial(): Promise<void> {
await AdMob.prepareInterstitial({ adId: AD_IDS.interstitial })
}
async function showInterstitial(): Promise<boolean> {
const now = Date.now()
if (now - lastInterstitialTime < INTERSTITIAL_COOLDOWN_MS) {
console.log('[AdMob] Cooldown actif — interstitielle ignorée.')
return false
}
try {
await AdMob.showInterstitial()
lastInterstitialTime = now
return true
} catch (e) {
console.warn('[AdMob] Interstitiale non disponible :', e)
return false
}
}
async function showRewardedAd(): Promise<{ type: string; amount: number } | null> {
try {
await AdMob.prepareRewardVideoAd({ adId: AD_IDS.rewarded })
const reward = await AdMob.showRewardVideoAd()
return { type: reward.type, amount: reward.amount }
} catch (e) {
console.warn('[AdMob] Rewarded non disponible :', e)
return null
}
}
return {
initialize,
showBanner, hideBanner, removeBanner,
prepareInterstitial, showInterstitial,
showRewardedAd,
}
}
B.5.4 — Initialiser AdMob dans main.ts
// src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { IonicVue } from '@ionic/vue'
import { useAdMob } from '@/composables/useAdMob'
async function bootstrap() {
const app = createApp(App).use(IonicVue).use(router)
const { initialize } = useAdMob()
await initialize()
router.isReady().then(() => {
app.mount('#app')
})
}
bootstrap()
B.5.5 — Tab 1 : Quiz + Ads (1/2)
<!-- src/views/Tab1Page.vue -->
<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>QuizFlash 🧠</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-card>
<ion-card-header>
<ion-card-title>
Question {{ currentQuestion + 1 }} / {{ questions.length }}
</ion-card-title>
</ion-card-header>
<ion-card-content>
<p>{{ questions[currentQuestion].text }}</p>
<ion-button expand="block" @click="nextQuestion">
Question suivante →
</ion-button>
<ion-button
expand="block"
fill="outline"
color="warning"
@click="getHint"
>
💡 Obtenir un indice (regarder une vidéo)
</ion-button>
</ion-card-content>
</ion-card>
<ion-toast
:is-open="toastOpen"
:message="toastMessage"
:duration="3000"
@didDismiss="toastOpen = false"
/>
</ion-content>
</ion-page>
</template>
B.5.5 — Tab 1 : Quiz + Ads (2/2)
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import {
IonPage, IonHeader, IonToolbar, IonTitle, IonContent,
IonCard, IonCardHeader, IonCardTitle, IonCardContent,
IonButton, IonToast
} from '@ionic/vue'
import { useAdMob } from '@/composables/useAdMob'
const { showBanner, removeBanner, prepareInterstitial, showInterstitial, showRewardedAd } = useAdMob()
const questions = [
{ text: 'Quelle est la capitale de la Suisse ?' },
{ text: 'Combien font 7 × 8 ?' },
{ text: 'En quelle année a eu lieu la Révolution française ?' },
{ text: 'Quel est le symbole chimique de l\'or ?' },
{ text: 'Combien de côtés a un hexagone ?' },
]
const currentQuestion = ref(0)
const toastOpen = ref(false)
const toastMessage = ref('')
function showToast(msg: string) {
toastMessage.value = msg
toastOpen.value = true
}
async function nextQuestion() {
currentQuestion.value = (currentQuestion.value + 1) % questions.length
const shown = await showInterstitial()
if (!shown) await prepareInterstitial()
}
async function getHint() {
showToast('⏳ Chargement de la vidéo...')
const reward = await showRewardedAd()
if (reward) {
showToast(`🎉 Indice débloqué ! (récompense : ${reward.amount} ${reward.type})`)
} else {
showToast('❌ Vidéo non disponible, réessayez plus tard.')
}
}
onMounted(async () => {
await showBanner()
await prepareInterstitial()
})
onUnmounted(async () => {
await removeBanner()
})
</script>
⚙️ B.5.6 — Composable useIAP.ts (1/2)
// src/composables/useIAP.ts
import { Purchases, PurchasesOffering } from '@revenuecat/purchases-capacitor'
import { ref, toRaw } from 'vue'
export function useIAP() {
const offering = ref<PurchasesOffering | null>(null)
const isPremium = ref(false)
const isLoading = ref(false)
const error = ref<string | null>(null)
async function loadOffering(): Promise<void> {
isLoading.value = true
error.value = null
try {
const result = await Purchases.getOfferings()
offering.value = result.current ?? null
} catch (e: any) {
error.value = e?.message ?? 'Impossible de charger les offres'
} finally {
isLoading.value = false
}
}
async function checkPremiumStatus(): Promise<void> {
try {
const { customerInfo } = await Purchases.getCustomerInfo()
isPremium.value = customerInfo.entitlements.active['premium'] !== undefined
} catch (e: any) {
console.warn('[IAP] Impossible de vérifier le statut premium :', e)
}
}
⚙️ B.5.6 — Composable useIAP.ts (2/2)
async function purchasePackage(packageToPurchase: any): Promise<boolean> {
isLoading.value = true
error.value = null
try {
const { customerInfo } = await Purchases.purchasePackage({
aPackage: toRaw(packageToPurchase)
})
isPremium.value = customerInfo.entitlements.active['premium'] !== undefined
return isPremium.value
} catch (e: any) {
if (e?.code !== 'PURCHASE_CANCELLED') {
error.value = e?.message ?? 'Erreur lors de l\'achat'
}
return false
} finally {
isLoading.value = false
}
}
async function restorePurchases(): Promise<void> {
isLoading.value = true
error.value = null
try {
const { customerInfo } = await Purchases.restorePurchases()
isPremium.value = customerInfo.entitlements.active['premium'] !== undefined
} catch (e: any) {
error.value = e?.message ?? 'Impossible de restaurer les achats'
} finally {
isLoading.value = false
}
}
return {
offering,
isPremium,
isLoading,
error,
loadOffering,
checkPremiumStatus,
purchasePackage,
restorePurchases,
}
}
B.5.7 — Tab 2 : Écran Paywall (IAP)
<!-- src/views/Tab2Page.vue -->
<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Premium ⭐</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<div v-if="isLoading" class="ion-text-center ion-padding">
<ion-spinner />
<p>Chargement des offres...</p>
</div>
<ion-card v-else-if="error" color="danger">
<ion-card-content>{{ error }}</ion-card-content>
</ion-card>
<ion-card v-else-if="isPremium" color="success">
<ion-card-header>
<ion-card-title>⭐ Vous êtes Premium !</ion-card-title>
</ion-card-header>
<ion-card-content>
Vous avez accès à toutes les fonctionnalités.
</ion-card-content>
</ion-card>
<template v-else-if="offering">
<ion-card>
<ion-card-header>
<ion-card-title>Passer Premium</ion-card-title>
</ion-card-header>
<ion-card-content>
<p>✅ Sans publicité</p>
<p>✅ Accès illimité au contenu</p>
<p>✅ Fonctionnalités exclusives</p>
</ion-card-content>
</ion-card>
<ion-list>
<ion-item
v-for="pkg in offering.availablePackages"
:key="pkg.identifier"
button
@click="purchasePackage(pkg)"
>
<ion-label>
<h2>{{ pkg.product.title }}</h2>
<p>{{ pkg.product.description }}</p>
</ion-label>
<ion-note slot="end">{{ pkg.product.priceString }}</ion-note>
</ion-item>
</ion-list>
<ion-button expand="block" fill="clear" @click="restorePurchases">
Restaurer mes achats
</ion-button>
</template>
<ion-card v-else>
<ion-card-content class="ion-text-center">
<p>Aucune offre disponible.</p>
<p><small>RevenueCat doit être configuré avec un vrai compte et des produits publiés sur le store.</small></p>
</ion-card-content>
</ion-card>
</ion-content>
</ion-page>
</template>
🤝 B.6 — Bonnes pratiques et éthique
✅ À faire
- Afficher le consentement RGPD (UMP) pour les utilisateurs UE.
- Respecter les cooldowns et guidelines par format.
- Proposer une option "Supprimer les pubs" (IAP) si possible.
- Ajouter un bouton "Restaurer les achats" (obligatoire sur iOS).
- Tester l'expérience en se mettant dans la peau d'un vrai utilisateur.
❌ À ne jamais faire
- Afficher des pubs au lancement de l’app.
- Cacher le bouton de fermeture d’une interstitielle.
- Générer de faux clics (suspension AdMob).
- Diffuser des pubs personnalisées dans une app ciblant < 13 ans.
- Utiliser des dark patterns pour pousser à l’achat.
💬 Les conséquences peuvent aller jusqu'à la dépublication de l'app et clôture des comptes.
🧩 B.7 — Activité : Ajouter une bannière
🎯 Objectif : intégrer une bannière de test dans le projet Ionic développé durant l’atelier.
- Installez
@capacitor-community/admob@6dans votre projet existant. - Lancez
npx cap add androidsi nécessaire, puisionic buildetnpx cap sync. - Configurez
AndroidManifest.xmletstrings.xmlavec l’App ID de test. - Créez le composable
useAdMob.tsen vous basant sur celui du cours. - Ajoutez une bannière sur la page principale — pensez au
marginsi votre app a des tabs. - Vérifiez son affichage sur émulateur ou appareil.
🏆 Bonus : ajoutez une interstitielle déclenchée après une action, avec un cooldown de 2 minutes.
🔗 B.8 — Références et ressources
- @capacitor-community/admob — GitHub
- Google AdMob — Console
- Google AdMob — IDs de test officiels
- Google AdMob — Guidelines interstitielles
- Google AdMob — Guidelines bannières
- Google UMP — Consentement RGPD
- RevenueCat — Achats intégrés Capacitor
- RevenueCat — Quickstart
- Politiques AdMob
- Apple — In-App Purchase
- Google Play — Facturation in-app
💰 Bonus — Monétiser une application mobile avec Ionic
By tirtho
💰 Bonus — Monétiser une application mobile avec Ionic
- 18