Saltare al contenuto

Inizia

  1. Installare il pacchetto

    Finestra del terminale
    bun add @capgo/native-purchases
  2. Sincronizzare con progetti nativi

    Finestra del terminale
    bunx cap sync
  3. Verifica il supporto fatturazione

    import { NativePurchases } from '@capgo/native-purchases';
    const { isBillingSupported } = await NativePurchases.isBillingSupported();
    if (!isBillingSupported) {
    throw new Error('Billing is not available on this device');
    }
  4. Carica i prodotti direttamente dai negozi

    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. Implementa le flussi di acquisto e di ripristino

    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();
    • Crea prodotti e sottoscrizioni in-app in App Store Connect.
    • Utilizza StoreKit Local Testing o Sandbox tester per le QA.
    • Nessuna modifica del manifesto richiesta. Assicurati che i tuoi prodotti siano approvati.
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,
}),
});
}
}
OpzionePiattaformaDescrizione
productIdentifieriOS + AndroidCodice SKU/ID prodotto configurato in App Store Connect / Google Play Console.
productTypeSolo AndroidPURCHASE_TYPE.INAPP o PURCHASE_TYPE.SUBS. Predefinito a INAPP. Sempre impostato a SUBS per le sottoscrizioni.
planIdentifierSottoscrizioni AndroidID del piano base da Google Play Console. Richiesto per le sottoscrizioni, ignorato su iOS e acquisti in-app.
billingPlanTypeSottoscrizioni iOSPiano di fatturazione StoreKit per l'acquisto. Utilizzare 'monthly' per la fatturazione mensile con un impegno di 12 mesi quando product.pricingTerms esporre quella opzione.
quantityiOSSolo per gli acquisti in-app, predefinito a 1. L'Android acquista sempre un oggetto.
appAccountTokeniOS + AndroidUUID/string che collega l'acquisto al tuo utente. Richiesto essere UUID su iOS; Android accetta qualsiasi stringa obfuscatrice fino a 64 caratteri.
isConsumableAndroidImpostato su true per consumare automaticamente i token dopo aver concesso l'entitativa per i consumabili. Impostato su false.

Utilizza getPurchases() per una vista cross-platform di ogni transazione che i negozi segnalano:

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: Le sottoscrizioni includono isActive, expirationDate, willCancel, e supporto per l'ascolto di StoreKit 2. Le vendite in-app richiedono la validazione del ricevuto server.
  • Android: isActive/expirationDate non sono popolate; chiama il Google Play Developer API con il purchaseToken per lo stato autorizzativo. purchaseState deve essere PURCHASED e isAcknowledged deve essere true.
  • isBillingSupported() – controlla la disponibilità di StoreKit / Google Play.
  • getProduct() / getProducts() – estrapola il prezzo, titolo localizzato, descrizione, offerte introduttive e termini di prezzo iOS supportati.
  • purchaseProduct() – avvia il flusso di acquisto di StoreKit 2 o del client di fatturazione, compresi i piani mensili di fatturazione iOS.
  • restorePurchases() – riproduci le transazioni storiche e sincronizza con il dispositivo corrente.
  • getPurchases() – elenca tutte le transazioni iOS o le fatture di Play Billing.
  • manageSubscriptions() – apre l'interfaccia utente di gestione delle sottoscrizioni native.
  • addListener('transactionUpdated') – gestisci le transazioni pendenti di StoreKit 2 quando il tuo'app inizia (solo iOS).
  1. Mostra il prezzo della store – Apple richiede di visualizzare product.title e product.priceString; non hardcoded mai.
  2. Utilizza appAccountToken – generare deterministicamente un UUID (v5) dal tuo ID utente per collegare le acquisizioni agli account.
  3. Verifica server-side – invia receipt (iOS) / purchaseToken (Android) al tuo backend per la verifica.
  4. Gestisci gli errori con cortesia – controlla le cancellazioni degli utenti, le fallite di rete e gli ambienti di fatturazione non supportati.
  5. Testa accuratamente – segui il guida del sandbox iOS e Guida del sandbox Android.
  6. Ripristino e gestione offerte – aggiungi pulsanti UI collegati restorePurchases() e manageSubscriptions().

Dopo che il flusso di acquisto funziona, utilizza il Revenue Playbook per pianificare il primo canale di pagamento: ambito del prodotto, ottimizzazione della visibilità, prezzi, posizionamento del muro di pagamento, analisi e feedback sulla churn.

Prodotti non caricati

  • Assicurarsi che l'ID del bundle / ID dell'applicazione corrisponda alla configurazione del negozio.
  • Confermare che gli ID dei prodotti sono attivi e approvati (App Store) o attivati (Google Play).
  • Attendere alcuni ore dopo la creazione dei prodotti; la propagazione del negozio non è istantanea.

Acquisto annullato o bloccato

  • Gli utenti possono annullare durante il flusso; avvolgere le chiamate in try/catch e visualizzare messaggi di errore amichevoli.
  • Per Android, assicurarsi che gli account di test installino l'applicazione dal Play Store (track interno) affinché la fatturazione funzioni.
  • Controllare logcat/Xcode per gli errori di fatturazione quando si esegue l'applicazione sul dispositivo.

Lo stato della sottoscrizione è errato

  • Usare getPurchases() per confrontare i dati del negozio con il cache delle entrate locali.
  • Su Android, interrogare sempre il Google Play Developer API con purchaseToken per ottenere le date di scadenza o lo stato di rimborso.
  • Su iOS, controlla isActive/expirationDate e verifica le ricevute per rilevare i rimborsi o le revocazioni.