Подтвердить что ты не робот

Биллинг в приложении не работает: "IAB Helper не настроен"

Я попытался включить оплату в приложении в моем приложении и для тестирования на основе всей процедуры на примере "TrivialDrive" для версии 3 биллинга в приложении (и реализации немодифицированных версий IAB файлов как поставляемый в подкаталоге "util" демонстрации), но он не работает для меня - на LogCat, незадолго до того, как приложение завершит с ошибкой, оно выдает сообщение "Ошибка биллинга в приложении: незаконное состояние для работы (launchPurchaseFlow ): Помощник IAB не настроен". (сразу после запуска функции startRegistered() был запущен, и мне было отправлено сообщение LOG "Кнопка регистрации нажата, запуск потока покупок для обновления".)...

Любая идея, что здесь не так?

Вот соответствующие части моего кода:

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

}

Здесь больше строк из 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)
4b9b3361

Ответ 1

Имел ту же проблему при выполнении функции PurchaseFlow. Взгляните на класс Activity в примере Google и, в частности, на метод protected void onActivityResult(int requestCode, int resultCode, Intent data). Вероятно, вы забыли реализовать это. Эта функция жизненно важна для работы всего механизма без сбоев.

@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.");
    }
}

EDIT: Кроме того, проблема также возникает, когда у вас неправильный пароль, связанный с вашей учетной записью gmail на вашем телефоне (это случилось сегодня со мной). Конечно, все функции биллинга Inapp должны быть протестированы по телефону, но я думаю, что это очевидно.

Ответ 2

Основная проблема заключается в том, что startRegistered() вызывается в прямом ответе на пользовательский клик пользовательского интерфейса, тогда как настройка вашего объекта IabHelper запускается асинхронно и, как известно, не может быть завершена до тех пор, пока асинхронный ответ не будет получен через onIabSetupFinished().

Ваш метод startRegistered() запускается щелчком пользователя и вызывает вызовы launchPurchaseFlow(), что, в свою очередь, требует, чтобы объект IabHelper уже завершил настройку, но если пользователь нажимает кнопку для запуска покупки до получения этого подтверждения ( либо потому, что установка завершилась неудачно, либо потому, что пользователь исключительно быстро выполнил ничью), то настройка не будет завершена, и launchPurchaseFlow() сообщит об ошибке, которую вы видите. В случае вашего логарифма задержка составляет 14 секунд, чего обычно достаточно, но... возможно, не в этом случае. Или, может, что-то пошло не так, и вы никогда бы не подключились независимо от того, как долго вы ждали.

В вашем логарифме нет сообщения с сообщением о соединении биллинга, которое является одной из первых вещей, которые должны произойти, если ваша установка будет завершена. Так как этого не происходит, вы также не видите сообщения (либо успеха, либо отказа) от onIabSetupFinished().

Это сложный материал из-за требуемых асинхронных ответов. Один из подходов - отключить кнопку, используемую для запуска покупки, до тех пор, пока ваш onIabSetupFinished() не вернется с успехом. Это предотвратит запуск покупки до тех пор, пока объект IabHelper не будет успешно настроен. Конечно, если установка завершится неудачно, у вас будет неработающая кнопка, но, по крайней мере, вы можете сказать пользователю, что делать (путем размещения сообщения, которое указывает, что вы ожидаете завершения установки), например, как часть текст кнопки).

Даже тогда, как только ваша покупка будет инициирована, и пользователю появится диалоговое окно платежа, вы сможете использовать приложение через цикл onStop(), который сбрасывает ваше приложение из памяти, когда пользователь размышляет о ее покупке (с момента покупки диалог является частью Google Play, а не частью вашего приложения, и для ОС может потребоваться память для ее запуска, и эта память может быть получена путем остановки вашего приложения). Это уничтожит ваш объект IabHelper(), который затем должен быть создан и асинхронно настроен снова. И снова, поскольку это вызвано асинхронно в вашем методе onCreate(), функция onActivityResult() может быть вызвана службой Google Play, чтобы сообщить о действии покупки пользователя до завершения настройки объекта IabHelper, а так как в onActivityResult() вам понадобится для использования экземпляра IabHelper это может привести к ошибке. Кажется, что вы должны быть готовы ко всему.

Это должно дать вам вкус того, с чем вы имеете дело. IAB затруднена именно по этим причинам - несколько потоков асинхронного вещания (например, установка против покупок против действий ОС Android, которые останавливают ваше приложение для захвата памяти для использования, вполне возможно, самой операцией покупки приложения в Google Play, для которой ваше приложение ждет получения результатов покупки). Многое из того, что внедряется (в том числе по образцу TrivialDrive), является шероховатым, потому что оно неявно полагается на ваше приложение, остающееся в памяти, когда на самом деле оно может быть переработано или потому, что оно зависит от одной ступени состояния гонки (например, установки) другая нога (например, покупка), и т.д.

Ответ 3

Я только что закончил обволакивать ту же проблему. Запускается IabHelper-Setup, но после этого больше ничего не происходит. И нажатие на приложение-Purchase возвращает ту же самую ошибку.

Вот что я понял: я использовал эмуляторы только от затмения. Как только я прочитал, что требуется определенная версия Google Play, я заметил, что Google Play полностью отсутствует на моих тестовых эмуляционных дисках.

Когда я использовал настоящий телефон, он работал безупречно! Поэтому, если вы все еще застряли в этой проблеме, попробуйте использовать реальное устройство (если у вас есть один доступный). Это должно делать свое дело.

Ответ 4

Еще одна вещь, с которой я столкнулся; в то время как у вас может быть последняя версия игры Google на вашем устройстве, которая поддерживает самую последнюю версию биллинга приложений, другие пользователи могут этого не делать. И в то время как сбои, вызванные этим теоретически, должны появиться в консоли разработчика, я не мог видеть эти сбои, пока я не реализовал firebase..., а затем я увидел их много. То, что я закончил, это использовать попытку поймать и связать пользователей, у которых не было последней версии игры Google, или возникла проблема в конце игрового магазина google на этой странице 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 - это просто диалоговое окно со ссылкой на веб-страницу выше.

Но сначала я бы рекомендовал тестировать покупки приложений на нескольких телефонах друзей, чтобы убедиться, что это проблема обновления магазина, а не проблема с кодом.