una studio sistema programar programa para movil gratis facturar facturacion desde crear como cero celular app aplicaciones aplicacion java android in-app-billing

java - studio - ¿Cómo implementar la facturación en la aplicación en una aplicación de Android?



programa para crear aplicaciones android (6)

Parece que es bastante complicado implementar la facturación en la aplicación en una aplicación de Android. ¿Cómo podría hacer esto? La aplicación de muestra del SDK solo tiene una actividad, que simplifica en exceso para una aplicación como la mía que tiene múltiples actividades.


Bueno, intentaré explicar lo que experimenté. No me considero un experto en esto pero me rompí la cabeza varios días.

Para empezar, pasé un mal momento tratando de entender el flujo de trabajo del ejemplo y la aplicación. Pensé que debería ser mejor comenzar con un ejemplo simple, sin embargo es muy difícil separar el código en pedazos pequeños y no saber si está rompiendo algo. Te diré lo que tengo y lo que cambié del ejemplo para que funcione.

Tengo una sola Actividad de donde provienen todas mis compras. Se llama Pro.

En primer lugar, debe actualizar la variable base64EncodedPublicKey en su clase Security con su clave pública de desarrollador de Market o verá una buena excepción.

Bueno, ato mi actividad a mi BillingService así:

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(); }

De esta forma, todas las compras se comunican con este servicio, que luego enviará las solicitudes JSON al mercado. Puede pensar que las compras se realizan en el mismo instante, pero no. Envía la solicitud y la compra puede llegar minutos u horas más tarde. Creo que esto se debe principalmente a la sobrecarga del servidor y la aprobación de las tarjetas de crédito.

Luego tengo un ListView con mis artículos, y abro un AlertDialog en cada uno, invitándolos a comprar el artículo. Cuando hacen clic en un elemento, hago esto:

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(); } } }

De acuerdo, debería ver que Market se abre y el usuario finaliza o cancela la compra.

Lo que entonces es importante es mi PurChaseObserver, que maneja todos los eventos que envía el mercado. Esta es una versión eliminada, pero debe entender el punto (vea mis comentarios a través del código):

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 } } }

Y creo que no deberías necesitar editar nada más. El resto del código "funciona". Puedes intentar usar el SKU de muestra al principio en tus propios elementos "android.test.purchased". Hasta ahora lo he probado y funciona, sin embargo, todavía tengo que cubrir todo, como el estado de reembolso. En este caso, le permito al usuario mantener las funciones, pero quiero asegurarme de que funciona perfecto antes de modificarlo.

Espero que te ayude a ti y a los demás.


De acuerdo, esta es una de esas cosas que no tienen mucha documentación disponible en línea, así que haré todo lo posible para explicar todo paso a paso. Tomado de mi publicación en el blog, que es una versión más detallada de esto (con capturas de pantalla), aquí en The Millibit . Sin más preámbulos,

Primer paso: Permisos Este es el paso más fácil. Navegue a su archivo manifest.xml y agregue la siguiente línea debajo de su etiqueta:

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

Esto le otorgará a su aplicación los permisos para acceder a la facturación en la aplicación. Si está orientando versiones por encima de API 22, deberá asegurarse de que este permiso se otorgue en tiempo de ejecución.

Paso dos: Play Console Ahora necesitas subir tu aplicación a Google Play Console. Todavía no publicamos nuestra aplicación al público (no se preocupe), solo la estamos cargando en la sección BETA RELEASE, lo que nos permitirá probar compras en la aplicación. La razón por la que necesitamos hacer esto es que Google necesita tener alguna versión de su APK cargada para que los procesos de facturación realmente funcionen.

  1. Vaya a https://play.google.com/apps/publish/

  2. Crea la aplicación

  3. Sigue los pasos para configurar tu aplicación

  4. Ir a Versiones de la aplicación

  5. Navega a Beta

  6. Cree una APK de su aplicación en el estudio de Android y súbala a la producción Beta en Play Console

(antes de liberar asegúrese de que ya ha completado el Listado de la tienda, Clasificación del contenido y Fijación de precios y distribución)

  1. Presiona el botón mágico (¡publícalo!)

Paso tres: Proyecto de configuración Está bien, esta es la parte donde tienes que copiar y pegar un montón de archivos.

Primero, toma this archivo, descárgalo y colócalo en src/main Debe compilarse en una carpeta. A continuación, toma esta carpeta completa de utilidades y pégala en la src/java folder. A continuación, reconstruya su proyecto para resolver los errores. La carpeta Util contiene las siguientes clases:

  • IabBroadcastReceiver
  • IabException
  • IabHelper
  • IabResult
  • Inventario
  • Compra
  • Seguridad
  • SkuDetalles

Cuarto paso: crear productos

  1. Crear producto administrado

  2. Haz clic en guardar y crea una "plantilla de precios"

Aquí, seleccionará el precio de este producto. Puede elegir el precio para diferentes países o hacer que se ajuste automáticamente si selecciona todos los países a su precio:

  1. Asegúrese de que el producto integrado en la aplicación esté activado y vinculado con la aplicación correcta en la consola por última vez.

Por último, tenga en cuenta la identificación de su producto. Utilizaremos esta identificación en los próximos pasos.

  1. Obtenga su Base64EncodedString

Dirígete a "Servicios y API" y toma tu Base64EncodedString. Copie y pegue esto en un bloc de notas en algún lugar para que tenga acceso a él. No comparta esto con nadie, ellos podrán hacer cosas maliciosas con él.

Paso cinco: ¡Finalmente! Podemos comenzar a codificar: primero enlazaremos con la biblioteca de facturación integrada en la aplicación y consultaremos qué ha comprado / no ha comprado el usuario. Luego, compraremos el producto que configuramos anteriormente.

Primero, importa todo lo que configuramos anteriormente:

import util.*;

Ahora usaremos un objeto IabHelper llamado mHelper, y haremos todo con esto.

base64EncodedPublicKey = ""; //PUT YOUR BASE64KEY HERE mHelper = new IabHelper(this, base64EncodedPublicKey); mHelper.enableDebugLogging(false); //set to false in real app mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() { public void onIabSetupFinished(IabResult result) { if (!result.isSuccess()) { // Oh no, there was a problem. if (result.getResponse() == 3) { new AlertDialog.Builder(MainActivity.this) .setTitle("In app billing") .setMessage("This device is not compatible with In App Billing, so" + " you may not be able to buy the premium version on your phone. ") .setPositiveButton("Okay", null) .show(); } Log.v(TAG, "Problem setting up In-app Billing: " + result); } else { Log.v(TAG, "YAY, in app billing set up! " + result); try { mHelper.queryInventoryAsync(mGotInventoryListener); //Getting inventory of purchases and assigning listener } catch (IabHelper.IabAsyncInProgressException e) { e.printStackTrace(); } } } });

De acuerdo, déjame analizar lo que está pasando aquí. Básicamente, llamamos "startSetup" para inicializar nuestro "IabHelper". Si la configuración es exitosa, consultamos qué compras tiene el usuario y almacenamos las respuestas en mGotInventoryListener , que codificaremos a continuación:

IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() { public void onQueryInventoryFinished(IabResult result, Inventory inventory) { i = inventory; if (result.isFailure()) { // handle error here Log.v(TAG, "failure in checking if user has purchases"); } else { // does the user have the premium upgrade? if (inventory.hasPurchase("premium_version")) { premiumEditor.putBoolean("hasPremium", true); premiumEditor.commit(); Log.v(TAG, "Has purchase, saving in storage"); } else { premiumEditor.putBoolean("hasPremium", false); premiumEditor.commit(); Log.v(TAG, "Doesn''t have purchase, saving in storage"); } } } };

El código anterior es bastante autoexplicativo. Básicamente, solo comprueba qué compras tiene el usuario. Ahora que sabemos si el usuario ya ha comprado nuestro producto o no, ¡sabemos si le pedimos o no que compre nuestro producto! Si nunca compraron nuestro producto antes, iniciemos una solicitud de compra:

public void buyPremium() { try { mHelper.flagEndAsync();//If any async is going, make sure we have it stop eventually mHelper.launchPurchaseFlow(this, "premium_version", 9, mPurchaseFinishedListener, "SECURITYSTRING"); //Making purchase request and attaching listener } catch (Exception e) { e.printStackTrace(); mHelper.flagEndAsync();//If any async is going, make sure we have it stop eventually new AlertDialog.Builder(MainActivity.this) .setTitle("Error") .setMessage("An error occurred in buying the premium version. Please try again.") .setPositiveButton("Okay", null) .show(); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data); // Pass on the activity result to the helper for handling if (!mHelper.handleActivityResult(requestCode, resultCode, data)) { } else Log.d(TAG, "onActivityResult handled by IABUtil."); } } IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() { public void onIabPurchaseFinished(IabResult result, Purchase purchase) { Log.v(TAG, "purchase finished"); if (purchase != null) { if (purchase.getSku().equals("premium_version")) { Toast.makeText(MainActivity.this, "Purchase successful!", Toast.LENGTH_SHORT).show(); premiumEditor.putBoolean("hasPremium", true); premiumEditor.commit(); } } else { return; } if (result.isFailure()) { return; } } };

Aquí compramos el artículo (con la ID que generamos en la consola de reproducción anteriormente) con lo siguiente:

mHelper.launchPurchaseFlow(this, "premium_version", 9, mPurchaseFinishedListener, "SECURITYSTRING"); //Making purchase request and attaching listener

Tenga en cuenta que mPurchaseFinishedListener en los parámetros. Esto significa que el resultado de la compra será devuelto a este oyente. Luego, simplemente verificamos si la compra es nula, y si no, otorguemos al usuario la característica que compraron.

No dejes que los oyentes tengan fugas! Debemos destruirlos cuando la aplicación destruya.

@Override public void onDestroy() { super.onDestroy(); if (mHelper != null) try { mHelper.dispose(); mHelper = null; } catch (IabHelper.IabAsyncInProgressException e) { e.printStackTrace(); } }

Finalmente, si desea consumir su compra y volver a comprarla, puede hacerlo fácilmente. Un ejemplo de esto es si un usuario compró gas para un automóvil virtual y se agotó. Necesitan comprar el mismo producto nuevamente, y puedes hacerlo disponible para una segunda compra al consumirlo:

public void consume(){ //MAKING A QUERY TO GET AN ACCURATE INVENTORY try { mHelper.flagEndAsync(); //If any async is going, make sure we have it stop eventually mHelper.queryInventoryAsync(mGotInventoryListener); //Getting inventory of purchases and assigning listener if(i.getPurchase("gas")==null){ Toast.makeText(this, "Already consumed!", Toast.LENGTH_SHORT).show(); } } catch (IabHelper.IabAsyncInProgressException e) { e.printStackTrace(); Toast.makeText(this, "Error, try again", Toast.LENGTH_SHORT).show(); mHelper.flagEndAsync();//If any async is going, make sure we have it stop eventually } //ACTUALLY CONSUMING try { mHelper.flagEndAsync();//If any async is going, make sure we have it stop eventually this.mHelper.consumeAsync(this.i.getPurchase("gas"), new IabHelper.OnConsumeFinishedListener() { public void onConsumeFinished(Purchase paramAnonymousPurchase, IabResult paramAnonymousIabResult) { //resell the gas to them } }); return; } catch (IabHelper.IabAsyncInProgressException localIabAsyncInProgressException) { localIabAsyncInProgressException.printStackTrace(); Toast.makeText(this, "ASYNC IN PROGRESS ALREADY!!!!" +localIabAsyncInProgressException, Toast.LENGTH_LONG).show(); Log.v("myTag", "ASYNC IN PROGRESS ALREADY!!!"); mHelper.flagEndAsync(); } }

¡Eso es! Ahora puede comenzar a ganar dinero. ¡Es así de simple!

De nuevo, si desea una versión más detallada de este tutorial, con capturas de pantalla e imágenes, visite la publicación original aquí . Déjame saber en los comentarios si tienes más preguntas.


Hay un ejemplo completo de Android In-App Billing v3 paso a paso se da aquí con una captura de pantalla. Consulte el tutorial: Android In-App Billing v3 utilizando ServiceConnection Class

Espero que ayude

Para obtener más información, consulte este tutorial: Implementación de facturación en la aplicación en la API de la versión 3

Pasos a seguir para integrar la biblioteca de facturación en la aplicación en nuestro proyecto

Actualice su archivo AndroidManifest.xml.

Cree una ServiceConnection y conéctela a IInAppBillingService.

Envíe las solicitudes de facturación en la aplicación desde su aplicación a IInAppBillingService.

Maneje las respuestas de Facturación en la aplicación desde Google Play.

Actualiza AndroidManifest.xml

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

Agregue los permisos en el archivo Manifest.xml

Agregar el archivo AIDL a su proyecto

Construye tu aplicación. Debería ver un archivo generado llamado IInAppBillingService.java en el directorio / gen de su proyecto.

Actualización de dependencias en el archivo 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 y activity_in_app_purchase.xml

Aquí es donde ofreceremos a nuestros usuarios de aplicaciones la oportunidad de realizar compras en la aplicación. En el archivo de diseño, le daremos al usuario la oportunidad de realizar compras en diferentes denominaciones.

InAppPurchaseActivity.java

Nota: los métodos getAllUserPurchase () y itemPurchaseAvailability () deben invocarse en el subproceso No UI para evitar el bloqueo de la aplicación.

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); } } }

Crear directorio de paquetes de ayuda

Crea una nueva carpeta de paquete y nómbrala ayudantes. Dentro del paquete, crea un nuevo archivo 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(); } }

Prueba de compra de facturación en la aplicación

  1. Crea una cuenta de Google+ (no uses la cuenta principal)
  2. Agregue los usuarios que probarán la aplicación en su grupo o comunidad.

Errores que puede encontrar durante la prueba de compra en la aplicación

el artículo que solicitaste no está disponible para comprar

Solución: según AndreiBogdan en ,

Todo el crédito va a Inducesmile por su tutorial

Android Developer Blog también recomienda una clase de capacitación sobre la venta de productos integrados en la aplicación. Para ver una implementación completa y aprender cómo probar la aplicación, consulte este tutorial: Venta de productos integrados en la aplicación



Si desea utilizar una biblioteca sencilla para publicar en Google Play y en la RoboBillingLibrary Amazon, puede ir con RoboBillingLibrary . Resume los detalles de ambos en una biblioteca fácil de usar. Las instrucciones detalladas están en la página de Github.