Guida agli acquisti in app (IAP) su Instant Developer Cloud

Buongiorno a tutti,

Scrivo questo post perchè di recente mi sono dovuto scontrare con la gestione degli acquisti in app da parte di Google e Apple, e visto che l’argomento è ostico e la documentazione scarseggia ho pensato potesse essere utile una “guida” per chi si dovesse cimentare nella stessa impresa su InDe Cloud.

Per prima cosa bisogna scegliere un plugin di cordova da implementare.
La nostra scelta è ricaduta su Cordova Plugin Purchase (trovate la documentazione su In App Purchase 2 - Awesome Cordova Plugins) perchè è un progetto nella documentazione ufficiale di ionic, che viene mantenuto aggiornato e che ci sembrava avesse tutto ciò che faceva al caso nostro.
Ulteriore documentazione con esempi si può trovare al link https://purchase.cordova.fovea.cc

Come seconda cosa bisogna implementare il plugin, quindi nel progetto InDe di riferimento bisognerà :

  • Aggiungere una libreria di tipo applicazione
  • Aggiungere una classe a questa libreria : questa classe verrà chiamata dal codice lato client
  • Aggiungere una risorsa di tipo Plugin
  • Tornando nell’app, dentro AppObjects->device, sarà apparso un plugin con il nome della classe che abbiamo inserito prima : nel campo parametri andrà inserito cordova-plugin-purchase, o più in generale il comando che dirà a cordova che plugin aggiungere.
  • Ora il server genererà i file IPA e APK con il plugin aggiornato e con i permessi richiesti (nel nostro caso quello degli acquisti in app)
  • Nella classe che abbiamo creato in precedenza dobbiamo aggiungere una serie di funzioni alias : queste verranno chiamate dall’app e a loro volta chiameranno i metodi del plugin
  • Nel caso la funzione importata debba ritornare qualche valore possiamo usare req.setResult(x) per ritornare il valore, mentre se ha delle callback dobbiamo impostarle noi dentro al codice della funzione alias e gestirne il comportamento di conseguenza.

Bene, questa è la guida generica di come implementare un plugin Cordova partendo da zero, ma per implementare gli acquisti in app abbiamo bisogno di un altro paio di passaggi :sweat_smile:

  • Bisogna avere un’applicazione caricata sulla google developer console e su app store connect, anche solo su Testflight o come test interno : in questo modo possiamo generare degli acquisti in app legati alla firma dell’applicazione e questi verranno riconosciuti in fase di test
  • Per evitare di spendere soldi veri, bisogna generare degli account di test dove possiamo testare i vari casi per gli acquisti in app (carta approvata, declinata, acquisto immediato o in ritardo…), Anche per questo vi lascio due link utili : Test your Google Play Billing Library integration  |  Google Play's billing system  |  Android Developers e Testing in-app purchases with sandbox | Apple Developer Documentation
  • Ora dobbiamo solamente compilare i dati bancari relativi a entrambi gli store e pubblicare degli acquisti in app nell’apposita sezione di entrambi gli store : una volta che questi saranno in uno stato “pronto per l’invio” o con bollino verde, potremo vederli in app (ci vogliono un pò di ore prima che vengano abilitati)
  • La documentazione del plugin (linkata a inizio post) specifica bene le differenze tra i vari tipi di IAP ed è più dettagliata a questo riguardo, quindi se avete dubbi vi rimando a quella

Bene, ora abbiamo finito i preparativi! :partying_face:
Passando al codice in sè, la nostra libreria usa un sistema simile alle promise per gestire il flusso degli acquisti in app, i miei consigli per lo sviluppo sono i seguenti :

  • Come prima funzione da implementare, se usate un launcher con update Live, consiglio una funzione che controlla se app.device.nomeplugin è disponibile : se c’è allora sono un file che ha il plugin e può usare le relative funzioni, altrimenti no e quindi posso disattivare le relative funzioni nel codice per evitare errori.
  • Subito dopo, bisogna registrare i prodotti usando l’ID dell’acquisto in app inserito su entrambi gli store, io consiglio di tenere lo stesso su tutti gli store per comodità e una manutenzione più agevole.
    Per farlo bisogna impostare una funzione alias che al suo interno chiama store.register, come ad esempio

store.register({
type: store.CONSUMABLE,
id: req.params.inappPurchaseID
});

  • In questo modo abbiamo creato un consumabile a cui abbiamo associato un ID, e stiamo dicendo all’app che il prodotto ha quell’ID quindi può prendersi i relativi valori dal server.
  • Dopodichè ci servono dei listener da associare ai vari stati della transazione del prodotto : sempre tramite una funzione alias, implementiamo questa funzione :

store.when(req.params.inappPurchaseID)
.updated(function () {
console.log(“Prodotto aggiornato”);
})

  • La funzione fa uso delle promise per monitorare lo stato dell’acquisto , come esempio banale ho usato un console log quando cambia lo stato del prodotto, ma ovviamente vanno implementate soluzioni diverse in base allo stato in cui il prodotto si trova e dettagli ulteriori si possono trovare nella documentazione del plugin.
  • Una volta fatto questo dobbiamo chiamare store,refresh tramite un’altra funzione alias, per tenere l’app aggiornata con gli stati degli acquisti in app e iniziare effettivamente ad ascoltare gli IAP registrati in precedenza
    *Quasi finito! Nella pagina di acquisto del prodotto dobbiamo sempre chiamare tramite alias il metodo store.get(req.params.inappPurchaseID) così da avere i dati dell’acquisto in app come li vede il server e permetterci di usarli nella schermata (ad esempio per fare vedere il prezzo sul bottone di acquisto)
  • Sul bottone di acquisto, bisogna chiamare tramite alias store.order(req.params.inappPurchaseID); e basta! In questo modo l’acquisto verrà processato in automatico, e una volta che il suo stato cambierà in acquistato o in errore verrà aggiornata la funzione di ascolto che abbiamo impostato prima, che eseguirà il codice a sua volta

Le ultime considerazioni :

  1. Gli acquisti in app possono essere testati SOLO su un apk o ipa installati sul dispositivo fisico, emulatori o instalauncher non funzioneranno purtroppo.
  2. Può essere utile bloccare la videata in fase di acquisto, a me piace usare un app.popup di tipo “loading” da cancellare in seguito una volta risolto il flusso dell’acquisto in app.
  3. A tal proposito nei commenti metto un modo per far comunicare tra di loro le promise con la videata dove si trova il bottone, per gestirne il click e i vari comportamenti

Questo è quanto! Spero di essere stato utile, se qualcuno ha domande può farle qui sotto e cercherò di rispondere al meglio!

5 Mi Piace

Per far comunicare tra di loro la schermata dove si trova il bottone e il plugin, bisogna impostare i listener come segue :

store.when(req.params.IDprodotto)
.updated(function () {
console.log(“Update interno plugin”);
})
.approved(function (product) {
PlugMan.sendEvent(req, “AcquistoApprovato”);
product.finish();
})
.cancelled(function () {
PlugMan.sendEvent(req, “AcquistoFallito”);
});

Tramite Plugman possiamo invocare eventi sulla classe che a sua volta ha invocato il plugin : ad esempio, nel caso di acquisto approvato, verrà chiamata la funzione onAcquistoApprovato, dentro alla quale possiamo scrivere il codice relativo alla videata (come ad esempio associare prodotto e utente, togliere la schermata di caricamento che bloccava la videata sino alla fine del pagamento, refreshare i bottoni della schermata e via dicendo :smile: )
Ovviamente questo comportamento va implementato in modo identico in fase di fallimento del pagamento, e per farlo ci basta aggiungere l’evento sul plugin tramite interfaccia o aggiungere l’evento al plugin via codice il prima possibile (tipo nel metodo onStart) per essere pronti subito a intercettare eventi relativi agli IAP

1 Mi Piace

Mi scuso inoltre sia per eventuali errori di sintassi/formato (è il mio primo post) sia se l’esempio non è chiarissimo : purtroppo gli acquisti in app solitamente vanno in progetti di dimensioni medio grandi e ho cercato di astrarre quanto più possibile per far capire come implementare tutto il possibile.

Feedback costruttivi sono sempre ben accetti e anzi vi invito a farmene :grinning_face_with_smiling_eyes:

Grazie del tuo contributo @gabriele.lesignoli e benvenuto nella Community di Instant Developer.

1 Mi Piace

@gabriele.lesignoli grazie per la guida. Stiamo valutando anche noi di integrare un sistema per acquisti in app. Posso chiederti per quale motivo non hai utilizzato il pacchetto stripe del framework già integrato? Perchè noi pensavamo di partire proprio da lì essendo già integrato e con un esempio pubblico sulla console.

1 Mi Piace

Ciao Francesco, ci mancherebbe!

Abbiamo valutato di utilizzare Stripe sia per il portale web che per l’app, ma Apple ci ha bloccato la release dicendo che dovevamo utilizzare il loro sistema proprietario per gli acquisti IAP, e di conseguenza ci siamo dovuti adattare.
Da parte di Google, invece, non ci sono stati problemi per nessuna release o modifica.
Se avete un sito web o non utilizzate Apple Store come canale di pubblicazione, ovviamente, potete fare come vi è più comodo.

Se siete ancora alle prime fasi dello sviluppo proverei a chiedere al supporto Apple, magari hanno cambiato il regolamento interno e ora permettono di utilizzare anche plugin esterni per gli IAP

2 Mi Piace

Grazie mille per i suggerimenti :+1:

Temevo proprio il blocco apple

1 Mi Piace