Come implementare la fatturazione in-app in un'applicazione Android?

Sembra che sia molto complicato implementare la fatturazione in-app in un'applicazione Android. Come posso fare questo? L'applicazione di esempio dell'SDK ha solo un'attività, il cui tipo di sovra-semplifica per un'applicazione come la mia che ha più attività.

  • Come evitare di scrivere il codice duplicato di boilerplate per richiedere le autorizzazioni?
  • Distinguere tra Tablet e Smart phone su ICS
  • Riempire due sovrapposizioni di immagini come la barra di ricerca nell'evento touch di visualizzazione image
  • Copertura del codice di Jacoco nello studio android
  • Dopo aver rimosso l'apk, each volta che avvia Debug, mi dice che il pacchetto non è installato
  • Videochiamate Android usando il sorso di android
  • Come effettuare query ricorsive in SQLite?
  • Come avviare un'attività senza un interface utente?
  • come get informazioni sul negozio google play nella mia applicazione android?
  • Reverse engineering da un file APK a un progetto
  • Applicazione Android che lavora sull'emulatore ma non sul dispositivo reale
  • android youtube carica video con username statico e password
  • 5 Solutions collect form web for “Come implementare la fatturazione in-app in un'applicazione Android?”

    Bene, cercherò di spiegare quello che ho sperimentato. Non mi considero un esperto di questo ma ho rotto la testa per diversi giorni.

    Per cominciare, ho avuto un brutto momento cercando di capire il stream di lavoro dell'esempio e dell'applicazione. Ho pensato che sarebbe meglio iniziare con un semplice esempio, tuttavia è molto difficile separare il codice in piccoli pezzi e non sapere se stai rompendo qualcosa. Ti dirò quello che ho e cosa ho cambiato dall'esempio per farlo funzionare.

    Ho una sola attività in cui provengono tutti i miei acquisti. Si chiama Pro.

    Innanzitutto, dovresti aggiornare la base64EncodedPublicKey variabile nella class di sicurezza con il tuo chiave pubblica del pubblico Market o vedrai una bella exception.

    Beh, vinco la mia attività al mio BillingService come così:

    public class Pro extends TrackedActivity implements OnItemClickListener { private BillingService mBillingService; private BillingPurchaseObserver mBillingPurchaseObserver; private Handler mHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.pro); //Do my stuff mBillingService = new BillingService(); mBillingService.setContext(getApplicationContext()); mHandler = new Handler(); mBillingPurchaseObserver = new BillingPurchaseObserver(mHandler); } } @Override protected void onStart() { //Register the observer to the service super.onStart(); ResponseHandler.register(mBillingPurchaseObserver); } @Override protected void onStop() { //Unregister the observer since you dont need anymore super.onStop(); ResponseHandler.unregister(mBillingPurchaseObserver); } @Override protected void onDestroy() { //Unbind the service super.onDestroy(); mBillingService.unbind(); } 

    In questo modo, tutti gli acquisti parlano con questo servizio, che quindi invieranno le richieste JSON al mercato. Potresti pensare che gli acquisti siano fatti nello stesso istante ma no. Invia la richiesta e l'acquisto potrebbe venire minuti o ore più tardi. Penso che questo sia soprattutto al sovraccarico del server e all'approvazione delle carte di credito.

    Poi ho un ListView con i miei oggetti e apro un AlertDialog su ciascuno, invitandoli a comprare l'articolo. Quando fanno clic su un elemento, faccio questo:

      private class BuyButton implements DialogInterface.OnClickListener { private BillingItem item = null; private String developerPayload; public BuyButton(BillingItem item, String developerPayload) { this.item = item; this.developerPayload = developerPayload; } @Override public void onClick(DialogInterface dialog, int which) { if (GeneralHelper.isOnline(getApplicationContext())){ //I track the buy here with GA SDK. mBillingService.requestPurchase(this.item.getSku(), this.developerPayload); } else { Toast.makeText(getApplicationContext(), R.string.msg_not_online, Toast.LENGTH_SHORT).show(); } } } 

    Va bene, dovresti vedere che il Market si apre e l'utente finisce o annulla l'acquisto.

    Quanto poi importnte è il mio PurChaseObserver, che gestisce tutti gli events che il mercato invia. Questa è una versione strappata di esso, ma dovresti get il punto (vedi i miei commenti attraverso il codice):

     private class BillingPurchaseObserver extends PurchaseObserver { public BillingPurchaseObserver(Handler handler) { super(Pro.this, handler); } @Override public void onBillingSupported(boolean supported) { if (supported) { //Enable buy functions. Not required, but you can do stuff here. The market first checks if billing is supported. Maybe your country is not supported, for example. } else { Toast.makeText(getApplicationContext(), R.string.billing_not_supported, Toast.LENGTH_LONG).show(); } } @Override public void onPurchaseStateChange(PurchaseState purchaseState, String itemId, int quantity, long purchaseTime, String developerPayload) { //This is the method that is called when the buy is completed or refunded I believe. // Here you can do something with the developerPayload. Its basically a Tag you can use to follow your transactions. i dont use it. BillingItem item = BillingItem.getBySku(getApplicationContext(), itemId); if (purchaseState == PurchaseState.PURCHASED) { if (item != null){ //This is my own implementation that sets the item purchased in my database. BillingHelper is a class with methods I use to check if the user bought an option and update the UI. You should also check for refunded. You can see the Consts class to find what you need to check for. boolean resu = item.makePurchased(getApplicationContext()); if (resu){ Toast.makeText(getApplicationContext(), R.string.billing_item_purchased, Toast.LENGTH_LONG).show(); } } } } private void trackPurchase(BillingItem item, long purchaseTime) { //My code to track the purchase in GA } @Override public void onRequestPurchaseResponse(RequestPurchase request, ResponseCode responseCode) { //This is the callback that happens when you sent the request. It doesnt mean you bought something. Just that the Market received it. if (responseCode == ResponseCode.RESULT_OK) { Toast.makeText(getApplicationContext(), R.string.billing_item_request_sent, Toast.LENGTH_SHORT).show(); } else if (responseCode == ResponseCode.RESULT_USER_CANCELED) { //The user canceled the item. } else { //If it got here, the Market had an unexpected problem. } } @Override public void onRestoreTransactionsResponse(RestoreTransactions request, ResponseCode responseCode) { if (responseCode == ResponseCode.RESULT_OK) { //Restore transactions should only be run once in the lifecycle of your application unless you reinstalled the app or wipe the data. SharedPreferences.Editor edit = PreferencesHelper.getInstance().getDefaultSettings(getApplicationContext()).edit(); edit.putBoolean(Consts.DB_INITIALIZED, true); edit.commit(); } else { //Something went wrong } } } 

    E credo che non dovresti modificare nient'altro. Il resto del codice "funziona". Puoi provare ad utilizzare il codice SKU inizialmente nei tuoi articoli "android.test.purchased". Finora ho provato questo e funziona comunque ho ancora bisogno di coprire tutto come lo stato rimborsato. In questo caso, lascio che l'utente conserva le funzionalità ma voglio assicurarsi che sia perfetta prima di modificarlo.

    Spero che aiuta voi e gli altri.

    V3: qui è un tutorial per un rapido avvio .. Sta utilizzando le classi di helper dall'esempio google (Trivial Drive) … Buono come primo "Hello Filling" ..

    http://www.techotopia.com/index.php/Integrating_Google_Play_In-app_Billing_into_an_Android_Application_%E2%80%93_A_Tutorial

    Per la fatturazione in-app v3, ho trovato questo davvero utile.
    http://blog.blundellapps.com/simple-inapp-billing-payment-v3/

    Se desideri utilizzare una libreria facile da pubblicare su Google Play e Amazon Appstore, puoi andare con RoboBillingLibrary . Essa estrae i dettagli di entrambi in una libreria facile da usare. Le istruzioni dettagliate sono riportte nella pagina Github.

    C'è un esempio completo di Android In-App fatturazione v3 passo dopo passo è dato qui con screenshot. Controlla il tutorial: Android In-App Billing v3 utilizzando la class ServiceConnection

    Spero che aiuterà.

    Per ulteriori chiarimenti, passare attraverso questo tutorial: Implementazione di fatturazione in-app nella versione 3 API

    Passaggi da seguire per integrare la biblioteca di fatturazione in-app nel nostro progetto

    Aggiorna il tuo file AndroidManifest.xml.

    Creare un ServiceConnection e legarlo a IInAppBillingService.

    Invia le richieste di fatturazione in-app dalla tua applicazione a IInAppBillingService.

    Gestire le risposte di fatturazione in-app da Google Play.

    Aggiorna AndroidManifest.xml

     <uses-permission android:name="com.android.vending.BILLING" /> 

    Aggiungi le autorizzazioni nel file Manifest.xml

    Aggiunta del file AIDL al progetto

    Crea la tua applicazione. Dovresti vedere un file generato denominato IInAppBillingService.java nella directory / gen del tuo progetto.

    Aggiorna le dependencies nel file build.gradle

     apply plugin: 'com.android.application' android { compileSdkVersion 24 buildToolsVersion "24.0.0" defaultConfig { applicationId "com.inducesmile.androidinapppurchase" minSdkVersion 14 targetSdkVersion 24 versionCode 2 versionName "1.1" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:24.1.1' compile 'com.intuit.sdp:sdp-android:1.0.3' compile 'com.android.support:support-annotations:24.1.1' compile 'org.jetbrains:annotations-java5:15.0' } 

    InAppPurchaseActivity.java e attività_in_app_purchase.xml

    Questo è where offrirà ai nostri utenti l'opportunità di effettuare acquisti in-app. Nel file di layout, daremo all'utente l'opportunità di effettuare acquisti in diverse denominazioni.

    InAppPurchaseActivity.java

    Nota: i methods getAllUserPurchase () e itemPurchaseAvailability () devono essere chiamati in thread non UI per evitare l'arresto di un'applicazione.

     public class InAppPurchaseActivity extends AppCompatActivity { private static final String TAG = InAppPurchaseActivity.class.getSimpleName(); private IInAppBillingService mService; private CustomSharedPreference customSharedPreference; String[] productIds = new String[]{Helper.ITEM_ONE_ID, Helper.ITEM_TWO_ID, Helper.ITEM_THREE_ID}; private ImageView buyOneButton, buyTwoButton, buyThreeButton; private static final char[] symbols = new char[36]; static { for (int idx = 0; idx < 10; ++idx) symbols[idx] = (char) ('0' + idx); for (int idx = 10; idx < 36; ++idx) symbols[idx] = (char) ('a' + idx - 10); } private String appPackageName; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_in_app_purchase); appPackageName = this.getPackageName(); Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND"); serviceIntent.setPackage("com.android.vending"); bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE); customSharedPreference = new CustomSharedPreference(InAppPurchaseActivity.this); buyOneButton = (ImageView)findViewById(R.id.buy_one); buyOneButton.setVisibility(View.GONE); assert buyOneButton != null; buyOneButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(!isBillingSupported()){ Helper.displayMessage(InAppPurchaseActivity.this, getString(R.string.in_app_support)); return; } purchaseItem(Helper.ITEM_ONE_ID); } }); buyTwoButton = (ImageView)findViewById(R.id.buy_two); buyTwoButton.setVisibility(View.GONE); assert buyTwoButton != null; buyTwoButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(!isBillingSupported()){ Helper.displayMessage(InAppPurchaseActivity.this, getString(R.string.in_app_support)); return; } purchaseItem(Helper.ITEM_TWO_ID); } }); buyThreeButton = (ImageView)findViewById(R.id.buy_three); buyThreeButton.setVisibility(View.GONE); assert buyThreeButton != null; buyThreeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(!isBillingSupported()){ Helper.displayMessage(InAppPurchaseActivity.this, getString(R.string.in_app_support)); return; } purchaseItem(Helper.ITEM_THREE_ID); } }); } ServiceConnection mServiceConn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { mService = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = IInAppBillingService.Stub.asInterface(service); AvailablePurchaseAsyncTask mAsyncTask = new AvailablePurchaseAsyncTask(appPackageName); mAsyncTask.execute(); } }; private void purchaseItem(String sku){ String generatedPayload = getPayLoad(); customSharedPreference.setDeveloperPayLoad(generatedPayload); try { Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(), sku, "inapp", generatedPayload); PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT"); try { startIntentSenderForResult(pendingIntent.getIntentSender(), Helper.RESPONSE_CODE, new Intent(), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0)); } catch (IntentSender.SendIntentException e) { e.printStackTrace(); } } catch (RemoteException e) { e.printStackTrace(); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == Helper.RESPONSE_CODE) { int responseCode = data.getIntExtra("RESPONSE_CODE", 0); String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA"); String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE"); if (resultCode == RESULT_OK) { try { JSONObject purchaseJsonObject = new JSONObject(purchaseData); String sku = purchaseJsonObject.getString("productId"); String developerPayload = purchaseJsonObject.getString("developerPayload"); String purchaseToken = purchaseJsonObject.getString("purchaseToken"); //the developerPayload value is better stored in remote database but in this tutorial //we will use a shared preference for(int i = 0; i < productIds.length; i++){ if(productIds[i].equals(sku) && developerPayload.equals(customSharedPreference.getDeveloperPayload())){ customSharedPreference.setPurchaseToken(purchaseToken); //access to private content Intent contentIntent = new Intent(InAppPurchaseActivity.this, PrivateContentActivity.class); startActivity(contentIntent); } } } catch (JSONException e) { e.printStackTrace(); } } } } private String getPayLoad(){ RandomString randomString = new RandomString(36); String payload = randomString.nextString(); return payload; } public class RandomString { private final Random random = new Random(); private final char[] buf; public RandomString(int length) { if (length < 1) throw new IllegalArgumentException("length < 1: " + length); buf = new char[length]; } public String nextString() { for (int idx = 0; idx < buf.length; ++idx) buf[idx] = symbols[random.nextInt(symbols.length)]; return new String(buf); } } public final class SessionIdentifierGenerator { private SecureRandom random = new SecureRandom(); public String nextSessionId() { return new BigInteger(130, random).toString(32); } } private class AvailablePurchaseAsyncTask extends AsyncTask<Void, Void, Bundle> { String packageName; public AvailablePurchaseAsyncTask(String packageName){ this.packageName = packageName; } @Override protected Bundle doInBackground(Void... voids) { ArrayList<String> skuList = new ArrayList<String>(); skuList.add(Helper.ITEM_ONE_ID); skuList.add(Helper.ITEM_TWO_ID); skuList.add(Helper.ITEM_THREE_ID); Bundle query = new Bundle(); query.putStringArrayList(Helper.ITEM_ID_LIST, skuList); Bundle skuDetails = null; try { skuDetails = mService.getSkuDetails(3, packageName, "inapp", query); } catch (RemoteException e) { e.printStackTrace(); } return skuDetails; } @Override protected void onPostExecute(Bundle skuDetails) { List<AvailablePurchase> canPurchase = new ArrayList<AvailablePurchase>(); int response = skuDetails.getInt("RESPONSE_CODE"); if (response == 0) { ArrayList<String> responseList = skuDetails.getStringArrayList("DETAILS_LIST"); if(responseList != null){ for (String thisResponse : responseList) { JSONObject object = null; try { object = new JSONObject(thisResponse); String sku = object.getString("productId"); String price = object.getString("price"); canPurchase.add(new AvailablePurchase(sku, price)); } catch (JSONException e) { e.printStackTrace(); } } } } if(checkIfPurchaseIsAvailable(canPurchase, productIds[0])){ buyOneButton.setVisibility(View.VISIBLE); }else{ buyOneButton.setVisibility(View.GONE); } if(checkIfPurchaseIsAvailable(canPurchase, productIds[1])){ buyTwoButton.setVisibility(View.VISIBLE); }else{ buyTwoButton.setVisibility(View.GONE); } if(checkIfPurchaseIsAvailable(canPurchase, productIds[2])){ buyThreeButton.setVisibility(View.VISIBLE); }else{ buyThreeButton.setVisibility(View.GONE); } } } @org.jetbrains.annotations.Contract("null, _ -> false") private boolean checkIfPurchaseIsAvailable(List<AvailablePurchase> all, String productId){ if(all == null){ return false;} for(int i = 0; i < all.size(); i++){ if(all.get(i).getSku().equals(productId)){ return true; } } return false; } public boolean isBillingSupported(){ int response = 1; try { response = mService.isBillingSupported(3, getPackageName(), "inapp"); } catch (RemoteException e) { e.printStackTrace(); } if(response > 0){ return false; } return true; } public void consumePurchaseItem(String purchaseToken){ try { int response = mService.consumePurchase(3, getPackageName(), purchaseToken); if(response != 0){ return; } } catch (RemoteException e) { e.printStackTrace(); } } public Bundle getAllUserPurchase(){ Bundle ownedItems = null; try { ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null); } catch (RemoteException e) { e.printStackTrace(); } return ownedItems; } public List<UserPurchaseItems> extractAllUserPurchase(Bundle ownedItems){ List<UserPurchaseItems> mUserItems = new ArrayList<UserPurchaseItems>(); int response = ownedItems.getInt("RESPONSE_CODE"); if (response == 0) { ArrayList<String> ownedSkus = ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST"); ArrayList<String> purchaseDataList = ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST"); ArrayList<String> signatureList = ownedItems.getStringArrayList("INAPP_DATA_SIGNATURE_LIST"); String continuationToken = ownedItems.getString("INAPP_CONTINUATION_TOKEN"); if(purchaseDataList != null){ for (int i = 0; i < purchaseDataList.size(); ++i) { String purchaseData = purchaseDataList.get(i); assert signatureList != null; String signature = signatureList.get(i); assert ownedSkus != null; String sku = ownedSkus.get(i); UserPurchaseItems allItems = new UserPurchaseItems(sku, purchaseData, signature); mUserItems.add(allItems); } } } return mUserItems; } @Override public void onDestroy() { super.onDestroy(); if (mService != null) { unbindService(mServiceConn); } } } 

    Creare l'elenco dei pacchetti di helper

    Creare una nuova cartella dei pacchetti e denominarli aiutanti. All'interno del pacchetto, creare un nuovo file Java Helper.java.

    Helper.java

     public class Helper { public static final String ITEM_ID_LIST = "ITEM_ID_LIST"; public static final String ITEM_ONE_ID = "productone"; public static final String ITEM_TWO_ID = "producttwo"; public static final String ITEM_THREE_ID = "productthree"; public static final int RESPONSE_CODE = 1001; public static final String SHARED_PREF = "shared_pref"; public static final String DEVELOPER_PAYLOAD = "developer_payload"; public static final String PURCHASE_TOKEN = "purchase_token"; public static void displayMessage(Context context, String message){ Toast.makeText(context.getApplicationContext(), message, Toast.LENGTH_LONG).show(); } } 

    Testare l'acquisto di fatturazione in-app

    1. Crea un account Google+ (non utilizza l'account principale)
    2. Aggiungi gli utenti che provano l'applicazione nel tuo gruppo o nella tua comunità.

    Gli errori che potrebbero verificarsi durante il test di acquisto in-app

    l'articolo richiesto non è disponibile per l'acquisto

    Soluzione – Secondo AndreiBogdan in Stackoverflow ,

    Tutto il merito va a Inducesmile per il suo tutorial

    Android Developer Blog raccomanda inoltre una class di formazione sulla vendita di prodotti in-app. Per vedere una implementazione completa e imparare a testare l'applicazione, consulta questo tutorial: Vendita di prodotti in-app

    L'Android è un fan Android di Google, tutto su telefoni Android, Android Wear, Android Dev e applicazioni Android Games e così via.