Passer au contenu

Commencer

  1. Installez le package

    Fenêtre de terminal
    npm i @capgo/native-purchases
  2. Synchronisation avec les projets natifs

    Fenêtre de terminal
    npx cap sync
  3. Vérifiez 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. Chargez les produits directement depuis les 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 des 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 intégrés à l’application dans App Store Connect.
    • Utilisez les tests locaux StoreKit ou les testeurs Sandbox pour le contrôle qualité.
    • Aucune modification du manifeste requise. Assurez-vous que vos produits sont 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,
}),
});
}
}
OptionsPlateformeDescriptif
productIdentifieriOS + AndroidSKU/ID de produit configuré dans la console App Store Connect / Google Play.
productTypeAndroid uniquementPURCHASE_TYPE.INAPP ou PURCHASE_TYPE.SUBS. La valeur par défaut est INAPP. Toujours défini sur SUBS pour les abonnements.
planIdentifierAndroid abonnementsID du plan de base de la console Google Play. Obligatoire pour les abonnements, ignoré sur iOS et les achats intégrés.
quantityiOSUniquement pour les achats intégrés, la valeur par défaut est 1. Android achète toujours un article.
appAccountTokeniOS + AndroidUUID/chaîne reliant l’achat à votre utilisateur. Il doit s’agir de l’UUID sur iOS ; Android accepte toute chaîne obscurcie jusqu’à 64 caractères.
isConsumableAndroidDéfinissez sur true pour consommer automatiquement les jetons après avoir accordé le droit aux consommables. La valeur par défaut est false.

Utilisez getPurchases() pour une vue multiplateforme de chaque transaction signalée par les magasins :

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 la prise en charge des écouteurs StoreKit 2. Les achats intégrés nécessitent une validation du reçu du serveur.
  • Android : isActive/expirationDate ne sont pas renseignés ; appelez le développeur Google Play API avec le purchaseToken pour obtenir le statut faisant autorité. purchaseState doit être PURCHASED et isAcknowledged doit être true.

API référence rapide- isBillingSupported() – vérifiez la disponibilité de StoreKit / Google Play.

Section titled “API référence rapide- isBillingSupported() – vérifiez la disponibilité de StoreKit / Google Play.”
  • getProduct() / getProducts() – prix de récupération, titre localisé, description, offres d’introduction.
  • purchaseProduct() – lancez le flux d’achat du client StoreKit 2 ou de facturation.
  • restorePurchases() - relire l’historique des achats et synchroniser avec l’appareil actuel.
  • getPurchases() – répertorie toutes les transactions iOS ou achats Play Billing.
  • manageSubscriptions() – ouvre l’interface utilisateur native de gestion des abonnements.
  • addListener('transactionUpdated') – gère les transactions StoreKit 2 en attente au démarrage de votre application (iOS uniquement).

## Bonnes pratiques

  1. Afficher les prix du magasin – Apple nécessite l’affichage de product.title et product.priceString ; jamais de code en dur.
  2. Utilisez appAccountToken – générez de manière déterministe un UUID (v5) à partir de votre identifiant utilisateur pour lier les achats aux comptes.
  3. Valider côté serveur – envoyez receipt (iOS) / purchaseToken (Android) à votre backend pour vérification.
  4. Gérez les erreurs avec élégance : vérifiez les annulations d’utilisateurs, les pannes de réseau et les environnements de facturation non pris en charge.
  5. Testez minutieusement – suivez le iOS guide du bac à sable et le Android guide du bac à sable.
  6. Offre de restauration et de gestion – ajoutez des boutons d’interface utilisateur connectés à restorePurchases() et manageSubscriptions().

Les produits ne se chargent pas

  • Assurez-vous que l’ID du bundle / l’ID de l’application correspond à la configuration du magasin.
  • Confirmez que les identifiants de produits sont actifs et approuvés (App Store) ou activés (Google Play).
  • Attendre plusieurs heures après la création des produits ; la propagation du magasin n’est pas instantanée.

Achat annulé ou bloqué

  • Les utilisateurs peuvent annuler le flux intermédiaire ; enveloppez les appels dans try/catch et des messages d’erreur faciles à afficher.
  • Pour Android, assurez-vous que les comptes de test installent l’application à partir de Play Store (piste interne) pour que la facturation fonctionne.
  • Vérifiez logcat/Xcode pour les erreurs de facturation lors de l’exécution sur l’appareil.

État d’abonnement incorrect

  • Utilisez getPurchases() pour comparer les données du magasin avec votre cache de droits local.
  • Le Android, interrogez toujours le développeur Google Play API avec le purchaseToken pour obtenir les dates d’expiration ou le statut du remboursement.
  • Le iOS, vérifiez isActive/expirationDate et validez les reçus pour détecter les remboursements ou les révocations.