2012-03-19 14 views
7

Sto tentando di rendere un'app gratuita aggiornabile alla versione "a pagamento" utilizzando la fatturazione in-app. Ho utilizzato il codice da this tutorial per gestire la fatturazione poiché quello sul sito dello sviluppatore ufficiale è troppo complesso e disordinato da seguire per un flusso semplice come il mio.fatturazione in-app Android - restoreTransactionInformation

Il mio codice per eseguire l'aggiornamento funziona correttamente.

Il problema si presenta quando provo ad aggiungere qualcosa per verificare se l'utente ha già acquistato ma ha perso i dati di acquisto o reinstallando o cancellando i dati (non mi interessa quale).

All'avvio dell'app, controllo un flag impostato dopo la prima esecuzione. Se questo flag non è presente, all'utente viene visualizzata una finestra di dialogo che avverte che l'app controllerà gli acquisti precedenti e quando fa clic su OK, viene chiamato il metodo restoreTransactionInformation. Ciò quindi fa sì che l'applicazione forzatamente si chiuda.

Poiché la fatturazione in-app non funziona durante il debug o sull'emulatore, devo pubblicare una versione firmata dell'app ogni volta che voglio provare il codice. Non ho modo di sapere perché l'applicazione si chiude quando provo a effettuare la richiesta restoreTransactionInformation. Qualcuno ha la minima idea di come posso diagnosticarlo, o cosa potrebbe causare la morte della mia app? O un esempio funzionante di come utilizzare il metodo restoreTransactionInformation?

MODIFICA: Sembra che la richiesta RESTORE_TRANSACTIONS stia ricevendo una risposta corretta e che restituisca i dettagli del mio acquisto di prova. Sfortunatamente prima che possa fare qualcosa con esso, l'app è costretta a chiudere. Ecco un logcat (senza codice offuscato) di ciò che accade a destra dopo mercato risponde alla richiesta RESTORE_TRANSACTIONS:

I/BillingService(6484): confirmTransaction() 
D/Finsky (1884): [7] MarketBillingService.getPreferredAccount: com.hippypkg: Account from first account. 
I/BillingService(6484): current request is:********** 
I/BillingService(6484): RESTORE_TRANSACTIONS Sync Response code: RESULT_OK 
D/WindowManagerImpl(6484): finishRemoveViewLocked, mViews[0]: [email protected]********** 
W/InputManagerService(1381): [unbindCurrentClientLocked] Disable input method client. 
W/InputManagerService(1381): [startInputLocked] Enable input method client. 
D/NativeCrypto(1884): returned from sslSelect() with result 1, error code 2 
D/Finsky (1884): [1] MarketBillingService.sendResponseCode: Sending response RESULT_OK for request ********** to com.hippypkg. 
I/BillingService(6484): Received action: com.android.vending.billing.PURCHASE_STATE_CHANGED 
I/BillingService(6484): purchaseStateChanged got signedData: {"nonce":**********,"orders":[{"orderId":"**********","packageName":"com.hippypkg","productId":"hippy_upgrade_free_to_full","purchaseTime":1331476540000,"purchaseState":0}]} 
I/BillingService(6484): purchaseStateChanged got signature: **********== 
I/BillingService(6484): signedData: {"nonce":**********,"orders":[{"orderId":"**********","packageName":"com.hippypkg","productId":"hippy_upgrade_free_to_full","purchaseTime":1331476540000,"purchaseState":0}]} 
I/BillingService(6484): signature: **********== 
I/BillingService(6484): confirmTransaction() 
I/BillingService(6484): makerequestbundle success 
I/BillingService(6484): putstringarray success 
D/Finsky (1884): [24] MarketBillingService.getPreferredAccount: com.hippypkg: Account from first account. 
D/AndroidRuntime(6484): Shutting down VM 
W/dalvikvm(6484): threadid=1: thread exiting with uncaught exception (group=0x4001d5a0) 
E/AndroidRuntime(6484): FATAL EXCEPTION: main 
E/AndroidRuntime(6484): java.lang.RuntimeException: Unable to start receiver com.hippypkg.BillingReceiver: java.lang.NullPointerException 
E/AndroidRuntime(6484): at android.app.ActivityThread.handleReceiver(ActivityThread.java:2144) 
E/AndroidRuntime(6484): at android.app.ActivityThread.access$2400(ActivityThread.java:135) 
E/AndroidRuntime(6484): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1114) 
E/AndroidRuntime(6484): at android.os.Handler.dispatchMessage(Handler.java:99) 
E/AndroidRuntime(6484): at android.os.Looper.loop(Looper.java:150) 
E/AndroidRuntime(6484): at android.app.ActivityThread.main(ActivityThread.java:4385) 
E/AndroidRuntime(6484): at java.lang.reflect.Method.invokeNative(Native Method) 
E/AndroidRuntime(6484): at java.lang.reflect.Method.invoke(Method.java:507) 
E/AndroidRuntime(6484): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:849) 
E/AndroidRuntime(6484): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:607) 
E/AndroidRuntime(6484): at dalvik.system.NativeStart.main(Native Method) 
E/AndroidRuntime(6484): Caused by: java.lang.NullPointerException 
E/AndroidRuntime(6484): at android.os.Parcel.readException(Parcel.java:1328) 
E/AndroidRuntime(6484): at android.os.Parcel.readException(Parcel.java:1276) 
E/AndroidRuntime(6484): at com.android.vending.billing.IMarketBillingService$Stub$Proxy.sendBillingRequest(IMarketBillingService.java:100) 
E/AndroidRuntime(6484): at com.hippypkg.BillingHelper.confirmTransaction(BillingHelper.java:152) 
E/AndroidRuntime(6484): at com.hippypkg.BillingHelper.verifyPurchase(BillingHelper.java:250) 
E/AndroidRuntime(6484): at com.hippypkg.BillingReceiver.purchaseStateChanged(BillingReceiver.java:41) 
E/AndroidRuntime(6484): at com.hippypkg.BillingReceiver.onReceive(BillingReceiver.java:23) 
E/AndroidRuntime(6484): at android.app.ActivityThread.handleReceiver(ActivityThread.java:2103) 
E/AndroidRuntime(6484): ... 10 more 
W/ActivityManager(1381): Force finishing activity com.hippypkg/.Hippy 
+0

Hey there; Hai già trovato una soluzione? Ho appena scoperto questo problema e sono bloccato nello stesso identico posto in cui tu. – Sid

+0

Non così lontano - sfortunatamente mi sono commosso a lavorare su qualcos'altro e non ho avuto la possibilità di guardare. Segui la discussione sulla risposta di Nikolay per il miglior risultato che ho avuto fino ad ora. Fammi sapere se arrivi da qualche parte! – Hippyjim

+0

Risolto, 4 ore e 5 caffè più tardi :) Controlla la risposta. – Sid

risposta

4

Così finalmente sono riuscito a capirlo.

Se si guarda alla Google Documenti per l'Introduzione alla fatturazione In App, essa afferma che:

il tipo di richiesta RESTORE_TRANSACTIONS innesca anche una PURCHASE_STATE_CHANGED trasmesso intenti, che contiene lo stesso tipo di informazioni sulle transazioni che viene inviato nel corso di una richiesta di acquisto, anche se non è necessario rispondere a questo intento con un messaggio CONFIRM_NOTIFICATIONS.

In un normale ciclo di acquisto-conferma transazione, quando si richiede di acquistare un prodotto di fatturazione in app, google restituisce un JSON con una serie di campi. Uno di questi campi è "notification_id". Quando google invia un intento PURCHASE_STATE_CHANGED, si aspetta una risposta CONFIRM_NOTIFICATIONS dall'app, con un pacchetto contenente un gruppo di informazioni, incluse le notification_id. Tutto bene qui.

Il problema inizia quando ottieni un PURCHASE_STATE_CHANGED da Google per una richiesta RESTORE_TRANSACTIONS dall'app. Questo JSON non contiene i campi notificationion_id. La libreria risponde ancora con CONFIRM_NOTIFICATIONS, aggiungendo l'array notification_id al bundle, che, in questo caso, è null. Questo è ciò che causa NullPointerException.

Soluzione: ho modificato la classe BillingHelper.java con l'aggiunta di un valore booleano per tenere traccia quando l'utente effettua un acquisto normale, e quando vuole restoreTransactions. Se si tratta di una richiesta di restoreTransactions, invio un messaggio al gestore e salta il passaggio confirmNotifications.

EDIT: Il codice per la correzione di cui sopra è in BillingHelper.java Sto usando un flag booleano per monitorare se l'utente ha fatto una chiamata RESTORE_TRANSACTIONS (isRestoreTransactions).

Nel metodo 'verifyPurchase' di BillingHelper.java, ho cambiato il codice come segue:

protected static void verifyPurchase(String signedData, String signature) { 
     ArrayList<VerifiedPurchase> purchases = BillingSecurity.verifyPurchase(signedData, signature); 

     if(isRestoreTransaction) 
     { 
      /* 
      * 
      *Add some logic to retrieve the restored purchase product ID's from the 'purchases' array 
      * 
      */ 

      //Set the boolean to false 
      isRestoreTranscation = false; 

      //Send a message to the handler, informing it that purchases were restored 
      if(mCompletedHandler != null){ 
       mCompletedHandler.sendEmptyMessage(0); 
      } else { 
       Log.e(TAG, "verifyPurchase error. Handler not instantiated. Have you called setCompletedHandler()?"); 
      } 
     } 
     else 
     { 
      /* 
      *...... 
      *...... 
      *...... 
      *Original method body here 
      *...... 
      *...... 
      *...... 
      */ 
     } 
    } 
+0

Sid, grazie mille - questo lo ha spezzato. Dato che c'è solo 1 acquisto possibile dalla mia app, piuttosto che scherzare con i booleani ecc., Ho semplicemente controllato se l'ultimoPurchase.notificationId era nullo. In tal caso, non tenta di inviare la conferma (se è nullo non funzionerà). Se la tua soluzione è un po 'più completa, potrebbe essere utile per i futuri visitatori se potessi pubblicare le modifiche al codice qui, o forse al tutorial da cui abbiamo tratto questo. Grazie ancora! – Hippyjim

+0

puoi darmi le modifiche che hai apportato per RESTORE_TRANSACTIONS ...? Sono anche bloccato da quel problema per alcuni giorni. per favore aiutami fornendo i cambiamenti che hai fatto. –

+1

Rushabh, controlla la modifica – Sid

4

Non si può davvero fare una distinzione tra 'reinstallazione' e 'eliminato i dati app'. Sono essenzialmente gli stessi: le preferenze condivise sono vuote. Né avresti bisogno di.

Per quanto riguarda la diagnosi del problema, inserire un pulsante "ripristina transazioni" e fare semplicemente clic su di esso in stati diversi (solo installato, flag (s) impostato, ecc.). Quindi guarda il logcat.

BTW, potrebbe essere meglio attenersi al codice originale di Google in un primo momento, si otterrebbe più aiuto in questo modo. Ci sono anche alcuni progetti su Google Code che avvolgono il codice IAB per renderlo un po 'più facile da integrare.

+0

Grazie, ho chiarito la domanda in quanto non mi interessa davvero come hanno perso i loro dati - so che non è possibile dire la differenza tra un nuova installazione e una con dati cancellati. Come ho già detto, per poter utilizzare la fatturazione per verificare gli acquisti in-app gestiti, è necessario utilizzare un'app firmata non in modalità di debug, quindi deve essere eseguita su un dispositivo reale. Ciò significa che non ho logcat - a meno che mi manchi qualcosa? Per quanto riguarda l'utilizzo dell'enorme esempio di Google, è troppo complesso e non riesco a capire come far funzionare il mio semplice acquisto, quindi utilizzare il wrapper che ho menzionato. – Hippyjim

+0

Sembra che manchi qualcosa :) Hai logcat, puoi anche eseguire il debug dell'applicazione se imposti il ​​flag di debug nel manifest. Usare l'esempio "enorme" ha il vantaggio che in realtà dovrai capire come funziona internamente e che ti aiuterà a risolvere quando è rotto. Dovresti almeno leggere la parte sui test: http://developer.android.com/guide/market/billing/billing_testing.html –

+0

Grazie, purtroppo ho letto quella pagina e non è di grande aiuto. Parla solo dell'effettivo acquisto, che ho fatto senza problemi. Quando provo a guardare gli acquisti esistenti (facendo una richiesta RESTORE_TRANSACTIONS), ottengo RESULT_DEVELOPER_ERROR per ogni richiesta se l'app è in modalità di debug. Secondo la stessa guida a cui mi hai appena riferito, questo è il comportamento previsto.Quindi, l'unico modo per testare è con un'applicazione firmata - senza debug. C'è un modo per vedere il logcat su un dispositivo live? – Hippyjim

0

Sto anche lo sviluppo di un'applicazione gratuita aggiornabile, ma ho usato il campione oficial invece di tutorial di Blundell, perché questa esercitazione non salva le informazioni e entrate uso Managed articoli

Basta dare uno sguardo sul restoreDatabase() metodo nell'esempio di Dungeons, fa ciò che vuoi, controlla, usando SharedPreferences se è la prima esecuzione e se è chiamata il metodo restoreTransactions.

Per eseguire il debug, è sufficiente connettere il dispositivo a eclissi e controllare il logcat, non dimenticare di impostare il debug costante (in Consts.java) su true e nel manifest impostare il tag debuggable su true anche.

Per comprendere meglio il codice di esempio, ho aggiunto molto più debug e ora funziona.

Problemi correlati