play - in app purchase android studio
la facturación en la aplicación no funciona: "IAB Helper no está configurado" (4)
Traté de incluir la facturación en la aplicación en mi aplicación y con el propósito de probar, basé todo el procedimiento en el ejemplo "TrivialDrive" para la versión 3 de la facturación en la aplicación (e implementé las versiones no modificadas de los archivos IAB tal como se suministran en subdirectorio "util" de la demostración), pero no funciona para mí: en LogCat, justo antes de que la aplicación finalice con un error, aparece el mensaje "Error de facturación en la aplicación: estado ilegal para la operación (launchPurchaseFlow): IAB El ayudante no está configurado. "(Justo después de que se haya activado la función startRegistered () y me haya enviado el mensaje de REGISTRO" Se hizo clic en el botón Registrar; se inició el flujo de compra para la actualización. ") ...
¿Alguna idea de lo que va mal aquí?
Aquí están las partes relevantes de mi código:
package com.mytest;
(..)
import com.mytest.iab.IabHelper; // the originals from the demo example, unmodified
import com.mytest.iab.IabResult;
import com.mytest.iab.Inventory;
import com.mytest.iab.Purchase;
public class Result3 extends Activity implements OnClickListener {
private static final String TAG = "BillingService";
private Context mContext;
boolean mIsRegistered = false;
// this has already been set up for my app at the publisher''s console
static final String IS_REGISTERED = "myregistered";
static final int RC_REQUEST = 10001;
// The helper object
IabHelper mHelper;
/** Call when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.result3);
mContext = this;
String base64EncodedPublicKey = "[my public key]"; // (from publisher''s console for my app)
// Create the helper, passing it our context and the public key to verify signatures with
Log.d(TAG, "Creating IAB helper.");
mHelper = new IabHelper(this, base64EncodedPublicKey);
// enable debug logging (for a production application, you should set this to false).
mHelper.enableDebugLogging(true);
// Start setup. This is asynchronous and the specified listener
// will be called once setup completes.
Log.d(TAG, "Starting setup.");
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
Log.d(TAG, "Setup finished.");
if (!result.isSuccess()) {
complain("Problem setting up in-app billing: " + result);
return;
}
// Hooray, IAB is fully set up. Now, let''s get an inventory of stuff we own.
Log.d(TAG, "Setup successful. Querying inventory.");
mHelper.queryInventoryAsync(mGotInventoryListener);
}
});
// Set the onClick listeners
findViewById(R.id.btnPurchase).setOnClickListener(this);
}
// Listener that''s called when we finish querying the items we own
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
Log.d(TAG, "Query inventory finished.");
if (result.isFailure()) {
complain("Failed to query inventory: " + result);
return;
}
Log.d(TAG, "Query inventory was successful.");
// Do we have the premium upgrade?
mIsRegistered = inventory.hasPurchase(IS_REGISTERED);
Log.d(TAG, "User is " + (mIsRegistered ? "REGISTERED" : "NOT REGISTERED"));
setWaitScreen(false);
Log.d(TAG, "Initial inventory query finished; enabling main UI.");
}
};
// User clicked the "Register" button.
private void startRegistered() {
Log.d(TAG, "Register button clicked; launching purchase flow for upgrade.");
setWaitScreen(true);
mHelper.launchPurchaseFlow(this, IS_REGISTERED, RC_REQUEST, mPurchaseFinishedListener);
}
@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)) {
// not handled, so handle it ourselves (here''s where you''d
// perform any handling of activity results not related to in-app billing..
super.onActivityResult(requestCode, resultCode, data);
}
else {
Log.d(TAG, "onActivityResult handled by IABUtil.");
}
}
// Callback for when a purchase is finished
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Log.d(TAG, "Purchase finished: " + result + ", purchase: " + purchase);
if (result.isFailure()) {
// Oh noes!
complain("Error purchasing: " + result);
setWaitScreen(false);
return;
}
Log.d(TAG, "Purchase successful.");
if (purchase.getSku().equals(IS_REGISTERED)) {
Log.d(TAG, "User has registered..");
alert("Thank you.");
mIsRegistered = true;
setWaitScreen(false);
}
}
};
// We''re being destroyed. It''s important to dispose of the helper here!
@Override
public void onDestroy() {
// very important:
Log.d(TAG, "Destroying helper.");
if (mHelper != null) mHelper.dispose();
mHelper = null;
}
void complain(String message) {
Log.e(TAG, "**** Register Error: " + message);
alert("Error: " + message);
}
void setWaitScreen(boolean set) {
// just a dummy for now
}
void alert(String message) {
AlertDialog.Builder bld = new AlertDialog.Builder(this);
bld.setMessage(message);
bld.setNeutralButton("OK", null);
Log.d(TAG, "Showing alert dialog: " + message);
bld.create().show();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnPurchase:
startRegistered();
break;
default:
break;
}
}
}
Aquí hay más líneas de Logcat:
12-20 01:06:36.701: D/dalvikvm(299): GC_FOR_MALLOC freed 4262 objects / 308592 bytes in 84ms
12-20 01:06:36.701: D/webviewglue(299): nativeDestroy view: 0x2ea718
12-20 01:06:36.771: W/webcore(299): Can''t get the viewWidth after the first layout
12-20 01:07:07.111: W/webcore(299): Can''t get the viewWidth after the first layout
12-20 01:07:18.510: D/webviewglue(299): nativeDestroy view: 0x2dd458
12-20 01:07:18.510: D/dalvikvm(299): GC_FOR_MALLOC freed 6042 objects / 544504 bytes in 50ms
12-20 01:07:18.530: D/webviewglue(299): nativeDestroy view: 0x2ea8d0
12-20 01:07:18.660: D/BillingService(299): Creating IAB helper.
12-20 01:07:18.660: D/BillingService(299): Starting setup.
12-20 01:07:18.660: D/IabHelper(299): Starting in-app billing setup.
12-20 01:07:19.621: W/webcore(299): Can''t get the viewWidth after the first layout
12-20 01:07:20.160: W/webcore(299): Can''t get the viewWidth after the first layout
12-20 01:07:32.481: D/webviewglue(299): nativeDestroy view: 0x3f88e8
12-20 01:07:32.491: D/dalvikvm(299): GC_FOR_MALLOC freed 5798 objects / 513640 bytes in 50ms
12-20 01:07:32.511: D/BillingService(299): Register button clicked; launching purchase flow for upgrade.
12-20 01:07:32.511: E/IabHelper(299): In-app billing error: Illegal state for operation (launchPurchaseFlow): IAB helper is not set up.
12-20 01:07:32.521: D/AndroidRuntime(299): Shutting down VM
12-20 01:07:32.521: W/dalvikvm(299): threadid=1: thread exiting with uncaught exception (group=0x4001d800)
12-20 01:07:32.541: E/AndroidRuntime(299): FATAL EXCEPTION: main
12-20 01:07:32.541: E/AndroidRuntime(299): java.lang.IllegalStateException: IAB helper is not set up. Can''t perform operation: launchPurchaseFlow
12-20 01:07:32.541: E/AndroidRuntime(299): at com.test_ed.iab.IabHelper.checkSetupDone(IabHelper.java:673)
12-20 01:07:32.541: E/AndroidRuntime(299): at com.test_ed.iab.IabHelper.launchPurchaseFlow(IabHelper.java:315)
12-20 01:07:32.541: E/AndroidRuntime(299): at com.test_ed.iab.IabHelper.launchPurchaseFlow(IabHelper.java:294)
12-20 01:07:32.541: E/AndroidRuntime(299): at com.test_ed.Result3.startRegistered(Result3.java:157)
12-20 01:07:32.541: E/AndroidRuntime(299): at com.test_ed.Result3.onClick(Result3.java:248)
12-20 01:07:32.541: E/AndroidRuntime(299): at android.view.View.performClick(View.java:2408)
12-20 01:07:32.541: E/AndroidRuntime(299): at android.view.View$PerformClick.run(View.java:8816)
12-20 01:07:32.541: E/AndroidRuntime(299): at android.os.Handler.handleCallback(Handler.java:587)
12-20 01:07:32.541: E/AndroidRuntime(299): at android.os.Handler.dispatchMessage(Handler.java:92)
12-20 01:07:32.541: E/AndroidRuntime(299): at android.os.Looper.loop(Looper.java:123)
12-20 01:07:32.541: E/AndroidRuntime(299): at android.app.ActivityThread.main(ActivityThread.java:4627)
12-20 01:07:32.541: E/AndroidRuntime(299): at java.lang.reflect.Method.invokeNative(Native Method)
12-20 01:07:32.541: E/AndroidRuntime(299): at java.lang.reflect.Method.invoke(Method.java:521)
12-20 01:07:32.541: E/AndroidRuntime(299): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
12-20 01:07:32.541: E/AndroidRuntime(299): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
12-20 01:07:32.541: E/AndroidRuntime(299): at dalvik.system.NativeStart.main(Native Method)
Acabo de terminar de envolver mi cabeza alrededor del mismo problema. Se inicia IabHelper-Setup, pero después de eso, no sucede nada más. Y al hacer clic en una compra dentro de la aplicación, aparece exactamente el mismo error que tuvo usted.
Esto es lo que descubrí: solo usé emuladores de eclipse. Una vez que leí que se requiere una determinada versión de Google Play, noté que faltaba Google Play por completo en mis unidades de prueba de emulación.
Cuando utilicé un teléfono real, ¡funcionó perfectamente! Por lo tanto, si aún tiene problemas con ese problema, intente usar un dispositivo real (si tiene uno disponible). Eso debería hacer el truco.
El problema fundamental es que startRegistered () se invoca en respuesta directa a un clic de usuario de UI, mientras que la configuración de su objeto IabHelper se activa de forma asíncrona, por lo que no se puede saber que se haya completado hasta que se reciba una respuesta asíncrona a través de onIabSetupFinished ().
Su método startRegistered () es activado por un clic del usuario, y eso llama launchPurchaseFlow (), que a su vez requiere que el objeto IabHelper ya haya completado la configuración, pero si el usuario hace clic para activar una compra antes de que se reciba la confirmación (ya sea porque la configuración fallado o porque el usuario es excepcionalmente rápido en el sorteo), entonces la configuración no se habrá completado, y launchPurchaseFlow () informará el error que estás viendo. En el caso de su logcat, el retraso es de 14 segundos, lo que suele ser suficiente, pero ... tal vez no en este caso. O, tal vez algo salió mal y nunca te habrías conectado, sin importar cuánto tiempo hubieras esperado.
En su logcat, no hay ningún mensaje que indique "Servicio de facturación conectado", que es una de las primeras cosas que deben suceder para que su configuración se complete. Como eso no ocurre, tampoco está viendo ningún mensaje (ya sea de éxito o de falla) de onIabSetupFinished ().
Esto es algo complicado debido a las respuestas asíncronas requeridas. Un enfoque sería deshabilitar el botón utilizado para activar una compra hasta que su onIabSetupFinished () regrese con éxito. Esto evitaría la activación de la compra hasta que el objeto IabHelper se haya configurado correctamente. Por supuesto, si falla la configuración, tendrá un botón que no funciona, pero al menos puede decirle al usuario lo que está pasando (poniendo un mensaje que indica que está esperando que se complete la configuración, por ejemplo, como parte de la botón de texto).
Incluso entonces, una vez que se inicia su compra y aparece el cuadro de diálogo de pago para el usuario, existe la posibilidad de que su aplicación pase por un ciclo onStop () que borra su aplicación de la memoria mientras el usuario está considerando su compra (ya que el cuadro de diálogo de compra es parte de Google Play, no es parte de su aplicación, y el sistema operativo puede requerir memoria para ejecutarlo, y esa memoria se puede obtener al detener su aplicación). Eso destruiría su objeto IabHelper (), que luego tendría que crearse y configurarse de forma asíncrona nuevamente . Y nuevamente, dado que se activa de forma asíncrona en su método onCreate (), el servicio Google Play puede invocar a onActivityResult () para informar la acción de compra del usuario antes de que se complete la configuración del objeto IabHelper, y desde onActivityResult () necesitará para usar su instancia de IabHelper, esto podría resultar en un error. Parece que tienes que estar preparado para cualquier cosa.
Esto debería darle el sabor de lo que está tratando. IAB es difícil por exactamente estas razones: múltiples secuencias de elementos asíncronos (p. Ej., Configuración frente a compras frente a las acciones del sistema operativo Android que impiden que la aplicación agote la memoria para que la utilice la misma operación de compra de la aplicación Google Play para la que funciona. está a la espera de obtener los resultados de la compra). Mucho de lo que se implementa (incluido por la muestra TrivialDrive) es inusual porque implícitamente se basa en que la aplicación permanezca en la memoria cuando en realidad podría ser reciclada, o porque se basa en una condición de carrera (por ejemplo, configuración) completada antes otra pierna (por ejemplo, lanzamiento de compra) lo hace, y así sucesivamente.
Otra cosa que me encontré; Si bien es posible que tenga la última versión de Google Play en su dispositivo que admite la última versión de facturación de la aplicación, es posible que otros usuarios no lo hagan. Y mientras que los bloqueos causados por esto en teoría deberían aparecer en la consola del desarrollador, no pude verlos hasta que implementé Firebase ... y luego vi muchos de ellos. Lo que terminé haciendo fue usar un retén de prueba y vincular a los usuarios que no tenían la última versión de Google Play o que tenían un problema en el final de la tienda de Google Play en esta página https://support.google.com/googleplay/answer/1050566?hl=en
try {
mHelper.launchPurchaseFlow(this, SKU_PRO_LT, RC_REQUEST,
mPurchaseFinishedListener, payload);
} catch (Exception e) { //with IabHelper.IabAsyncInProgressException the code still fatally crashes for some reason
//complain("Error launching purchase flow. Another async operation in progress.");
alert2("[error msg]");
setWaitScreen(false);
}
alert2 es solo un cuadro de diálogo con un enlace a la página web anterior.
Pero en primer lugar, recomendaría probar las compras de aplicaciones en algunos teléfonos de amigos solo para asegurarse de que sea un problema de actualización de Play Store y no un problema de código.
Tuvo el mismo problema al ejecutar la función purchaseFlow. Eche un vistazo a la Clase de actividad en el ejemplo de Google y, específicamente, en el método protected void onActivityResult(int requestCode, int resultCode, Intent data)
. Probablemente te olvidaste de implementar este. Esta función es vital para que todo el mecanismo funcione sin problemas.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.i(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
// Pass on the activity result to the helper for handling
if (!inappBillingHelper.handleActivityResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
else {
Log.i(TAG, "onActivityResult handled by IABUtil.");
}
}
EDITAR: Además, el problema también existe cuando tiene una contraseña incorrecta asociada con su cuenta de Gmail en su teléfono (esto me sucedió hoy). Por supuesto, todas las funciones de facturación de Inapp deben probarse en el teléfono, pero creo que eso es obvio.