Aller directement au contenu

Démarrage

  1. Installez le package

    Fenêtre de terminal
    bun add @capgo/native-purchases
  2. Synchroniser avec les projets natifs

    Fenêtre de terminal
    bunx cap sync
  3. Vérifier le support de facturation

    import { NativePurchases } from '@capgo/native-purchases';
    const { isBillingSupported } = await NativePurchases.isBillingSupported();
    if (!isBillingSupported) {
    throw new Error('Billing is not available on this device');
    }
  4. Charger les produits directement des magasins

    import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
    const { products } = await NativePurchases.getProducts({
    productIdentifiers: [
    'com.example.premium.monthly',
    'com.example.premium.yearly',
    'com.example.one_time_unlock'
    ],
    productType: PURCHASE_TYPE.SUBS, // Use PURCHASE_TYPE.INAPP for one‑time products
    });
    products.forEach((product) => {
    console.log(product.title, product.priceString);
    });
  5. Mettre en œuvre les flux d'achat et de restauration

    import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
    const monthlyPlanId = 'monthly-plan'; // Base Plan ID from Google Play Console
    const transaction = await NativePurchases.purchaseProduct({
    productIdentifier: 'com.example.premium.monthly',
    planIdentifier: monthlyPlanId, // REQUIRED for Android subscriptions, ignored on iOS
    productType: PURCHASE_TYPE.SUBS,
    quantity: 1,
    });
    console.log('Transaction ID', transaction.transactionId);
    await NativePurchases.restorePurchases();
    • Créez des produits et des abonnements en application dans App Store Connect.
    • Utilisez StoreKit Local Testing ou les testeurs de sandbox pour la QA.
    • Aucune modification du manifeste requise. Assurez-vous que vos produits soient approuvés.
import { NativePurchases, PURCHASE_TYPE, Transaction } from '@capgo/native-purchases';
import { Capacitor } from '@capacitor/core';
class PurchaseService {
private premiumProduct = 'com.example.premium.unlock';
private monthlySubId = 'com.example.premium.monthly';
private monthlyPlanId = 'monthly-plan'; // Base Plan ID (Android only)
async initialize() {
const { isBillingSupported } = await NativePurchases.isBillingSupported();
if (!isBillingSupported) throw new Error('Billing unavailable');
const { products } = await NativePurchases.getProducts({
productIdentifiers: [this.premiumProduct, this.monthlySubId],
productType: PURCHASE_TYPE.SUBS,
});
console.log('Loaded products', products);
if (Capacitor.getPlatform() === 'ios') {
NativePurchases.addListener('transactionUpdated', (transaction) => {
this.handleTransaction(transaction);
});
}
}
async buyPremium(appAccountToken?: string) {
const transaction = await NativePurchases.purchaseProduct({
productIdentifier: this.premiumProduct,
productType: PURCHASE_TYPE.INAPP,
appAccountToken,
});
await this.processTransaction(transaction);
}
async buyMonthly(appAccountToken?: string) {
const transaction = await NativePurchases.purchaseProduct({
productIdentifier: this.monthlySubId,
planIdentifier: this.monthlyPlanId, // REQUIRED for Android subscriptions
productType: PURCHASE_TYPE.SUBS,
appAccountToken,
});
await this.processTransaction(transaction);
}
async restore() {
await NativePurchases.restorePurchases();
await this.refreshEntitlements();
}
async openManageSubscriptions() {
await NativePurchases.manageSubscriptions();
}
private async processTransaction(transaction: Transaction) {
this.unlockContent(transaction.productIdentifier);
this.validateOnServer(transaction).catch(console.error);
}
private unlockContent(productIdentifier: string) {
// persist entitlement locally
console.log('Unlocked', productIdentifier);
}
private async refreshEntitlements() {
const { purchases } = await NativePurchases.getPurchases({
productType: PURCHASE_TYPE.SUBS,
});
console.log('Current purchases', purchases);
}
private async handleTransaction(transaction: Transaction) {
console.log('StoreKit transaction update:', transaction);
await this.processTransaction(transaction);
}
private async validateOnServer(transaction: Transaction) {
await fetch('/api/validate-purchase', {
method: 'POST',
body: JSON.stringify({
transactionId: transaction.transactionId,
receipt: transaction.receipt,
purchaseToken: transaction.purchaseToken,
}),
});
}
}
OptionPlateformeDescription
productIdentifieriOS + AndroidSKU/ID de produit configuré dans App Store Connect / Google Play Console.
productTypeSeulement AndroidPURCHASE_TYPE.INAPP ou PURCHASE_TYPE.SUBS. Par défaut INAPP. Toujours défini sur SUBS pour les abonnements.
planIdentifierAbonnements AndroidID du plan de base du Google Play Console. Obligatoire pour les abonnements, ignoré sur iOS et pour les achats en application.
billingPlanTypeAbonnements iOSPlan de facturation StoreKit pour l'achat. Utilisez 'monthly' pour un facturation mensuelle avec un engagement de 12 mois lorsque product.pricingTerms expose cette option.
quantityiOSSeulement pour les achats en application, par défaut sur 1. Les appareils Android achètent toujours un article.
appAccountTokeniOS + Android__CAPGO_KEEP_0__ est un identifiant UUID liant l'achat à votre utilisateur. Obligatoire d'être UUID sur iOS ; Android accepte toute chaîne obfusquée jusqu'à 64 caractères.
isConsumableAndroidDéfini sur true pour consommer automatiquement les jetons après avoir accordé un droit pour les consommables. Défaut à false.

Utilisez getPurchases() pour une vue croisée de chaque transaction que les magasins signalent :

import { NativePurchases, PURCHASE_TYPE } from '@capgo/native-purchases';
const { purchases } = await NativePurchases.getPurchases({
productType: PURCHASE_TYPE.SUBS,
});
purchases.forEach((purchase) => {
if (purchase.isActive && purchase.expirationDate) {
console.log('iOS sub active until', purchase.expirationDate);
}
const isAndroidIapValid =
['PURCHASED', '1'].includes(purchase.purchaseState ?? '') && purchase.isAcknowledged;
if (isAndroidIapValid) {
console.log('Grant in-app entitlement for', purchase.productIdentifier);
}
});
  • iOS: Les abonnements incluent isActive, expirationDate, willCancel, et le support de l'écouteur StoreKit 2. Les achats en application nécessitent la validation du reçu de serveur.
  • Android: isActive/expirationDate ne sont pas remplis; appelez le développeur Google Play API avec le purchaseToken pour le statut autoritaire. purchaseState doit être PURCHASED et isAcknowledged doit être true.
  • isBillingSupported() – vérifiez la disponibilité de StoreKit / Google Play.
  • getProduct() / getProducts() – récupérer le prix, le titre localisé, la description, les offres d'introduction et les termes de tarification iOS pris en charge.
  • purchaseProduct() – initier la mise en œuvre de l'achat de StoreKit 2 ou du client de facturation, y compris les plans mensuels de facturation iOS.
  • restorePurchases() – relier les achats historiques et synchroniser avec le dispositif actuel.
  • getPurchases() – afficher toutes les transactions iOS ou les achats de facturation Play.
  • manageSubscriptions() – ouvrir l'interface utilisateur de gestion des abonnements native.
  • addListener('transactionUpdated') – gérer les transactions StoreKit 2 en attente lorsque votre application démarre (iOS uniquement).
  1. Afficher le prix de la boutique – Apple exige de montrer product.title et product.priceString; ne jamais les coder en dur.
  2. Utilisez appAccountToken – générer de manière déterministe un UUID (v5) à partir de votre ID d'utilisateur pour lier les achats aux comptes.
  3. Valider côté serveur – envoyer receipt (iOS) / purchaseToken (Android) à votre backend pour la vérification.
  4. Gérer les erreurs avec souplesse – vérifier les annulations de l'utilisateur, les échecs de réseau et les environnements de facturation non pris en charge.
  5. Tester soigneusement – suivre le guide du sandbox iOS et – suivre le guide du sandbox Android Guide de sandbox Android.
  6. Rétablissement et gestion des offres – ajouter des boutons d'interface utilisateur branchés à restorePurchases() et manageSubscriptions().

Après que le flux de vente fonctionne, utilisez le Playbook des revenus pour planifier votre premier canal payant : portée du produit, ASO, tarification, placement de la barrière de paiement, analytics et feedback sur le taux de dérive.

Produits non chargés

  • Vérifiez que l'ID de l'ensemble / de l'ID de l'application correspond à la configuration de la boutique.
  • Confirmez que les IDs de produits sont actifs et approuvés (App Store) ou activés (Google Play).
  • Attendez plusieurs heures après avoir créé des produits ; la propagation de la boutique n'est pas instantanée.

Achat annulé ou bloqué

  • Les utilisateurs peuvent annuler en cours de flux ; enveloppez les appels dans try/catch et affichez des messages d'erreur amicaux.
  • Pour Android, assurez-vous que les comptes de test installent l'application depuis la boutique Play (piste interne) afin que la facturation fonctionne.
  • Vérifiez logcat/Xcode pour les erreurs de facturation lors de l'exécution sur appareil.

État de l'abonnement incorrect

  • Utilisez getPurchases() pour comparer les données de la boutique avec votre cache de droits locaux.
  • Sur Android, interrogez toujours le développeur Google Play API avec purchaseToken obtenir les dates d'expiration ou l'état de remboursement.
  • Sur iOS, vérifiez isActive/expirationDate et validez les reçus pour détecter les remboursements ou les révocations.