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

Я попытался включить в свое приложение выставление счетов в приложении и в целях тестирования основал всю процедуру на примере TrivialDrive для версии 3 выставления счетов в приложении (и реализовал неизмененные версии файлов IAB, предоставленные в "util" в демо-версии), но у меня это не работает - на LogCat непосредственно перед завершением работы приложения с ошибкой выдается сообщение "Ошибка выставления счетов в приложении: Недопустимое состояние для операции (launchPurchaseFlow ): IAB Helper не настроен." (сразу после того, как функция 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'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;
    }
}

}

Вот еще строки из 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)

person richey    schedule 19.12.2012    source источник
comment
Это может быть связано с тем, что установка не завершена или не удалась, поэтому в IabHelper служба при подключении проверила, поддерживается ли выставление счетов, и это не так, или служба вообще не подключалась. Какой у вас весь логкэт (или хотя бы еще несколько строк)   -  person AndroidPenguin    schedule 19.12.2012
comment
спасибо, я только что добавил их выше.   -  person richey    schedule 19.12.2012
comment
Вы тестируете на устройстве 4.0? У меня такая же проблема, но она отлично работает на более низких API.   -  person    schedule 19.12.2012
comment
@richey, не могли бы вы добавить свой лог-кот из того места, где настраивается iabhelper   -  person AndroidPenguin    schedule 19.12.2012
comment
Я сделал, спасибо за попытку помочь.   -  person richey    schedule 20.12.2012
comment
На каком уровне API вы это тестируете? Я обнаружил, что bindService возвращает false, и на этом он останавливается. В IabHelper у меня есть эта проверка, где происходит bindService: booleanbound = mContext.bindService(new Intent(com.android.vending.billing.InAppBillingService.BIND), mServiceConn, Context.BIND_AUTO_CREATE); logDebug (bindService + привязка);   -  person    schedule 20.12.2012
comment
Попробуйте очистить данные/кеш в магазине Google Play, затем один раз запустите магазин Play, а затем повторите попытку покупки в приложении. Это сработало для меня, хотя это не подходящее решение для пользователей.   -  person    schedule 20.12.2012


Ответы (5)


Была такая же проблема при выполнении функции 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.");
    }
}

РЕДАКТИРОВАТЬ: Кроме того, проблема также существует, когда у вас есть неправильный пароль, связанный с вашей учетной записью gmail на вашем телефоне (это случилось со мной сегодня). Конечно, все функции биллинга Inapp должны быть протестированы на телефоне, но я думаю, что это очевидно.

person Eric    schedule 20.12.2012
comment
Нет, на самом деле я реализовал это, если вы посмотрите на мой фрагмент кода выше. Спасибо. - person richey; 23.12.2012
comment
О, я этого не заметил. Тем не менее, из прикрепленного вами стекового вызова кажется, что вы пытаетесь вызвать метод PurchaseFlow, когда startSetup все еще выполняется (не завершен) или что-то в этой функции пошло не так. Я бы посоветовал проверить, что происходит в функции startSetup (отладить ее), прежде чем запускать покупку. - person Eric; 23.12.2012
comment
Я не вижу вывод журнала из Log.d(TAG, установка завершена.); заявление в вашем onIabSetupListener. Так что не похоже, что вы подключаетесь. Я видел похожую проблему на некоторых из моих старых тестовых устройств. На более новых работает. На каком устройстве / версии Android вы используете это? - person Bryant Harris; 05.01.2013
comment
У меня такая же проблема, когда я проверил упомянутую функцию startSetup(), она показывает, что mSetupDone имеет ложное значение, но на планшетах она работает нормально. я не знаю почему - person Abdul Waheed; 03.12.2015
comment
Я столкнулся с той же проблемой, если не был добавлен аккаунт Google. в моем случае я добавил учетную запись Google на устройство, и проблема была решена. - person shaby; 05.10.2016

Фундаментальная проблема заключается в том, что 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), ненадежно, потому что оно неявно полагается на то, что ваше приложение остается в памяти, когда на самом деле оно может быть переработано, или потому что оно полагается на один этап состояния гонки (например, установка) до завершения. другой этап (например, запуск покупки) делает и так далее.

person Carl    schedule 01.04.2013
comment
Я столкнулся с той же проблемой, но в другом контексте. Мой процесс покупки инициируется нажатием кнопки и работает отлично. Но только после комментирования строки в onCreate() mHelper.queryInventoryAsync(mGotInventoryListener); что не удается, потому что var mSetupDone в классе IabHelper по-прежнему имеет значение false. Но result.isSuccess() возвращает true. - person Sanjay Singh; 02.08.2013
comment
@Sanjay Singh: см. этот ответ: stackoverflow.com/questions/17944522/ - person Carl; 06.08.2013
comment
Привет Карл, я столкнулся с той же проблемой. Я получаю много отчетов об этом приложении. Хотя устройства, на которых я тестировал, работают отлично. Если у пользователя нет подключения к Интернету, произойдет ли такой же сбой? Я подозреваю, что реальной проблемой является Интернет, потому что после установки пользователю требуется около 30 секунд, чтобы запустить процесс покупки. Не имеет смысла, что установка не будет завершена за 30 секунд, не так ли? - person idish; 09.10.2013
comment
Привет Карл, я столкнулся с той же проблемой. Я получаю много отчетов об этом приложении. Хотя устройства, на которых я тестировал, работают отлично. Если у пользователя нет подключения к Интернету, произойдет ли такой же сбой? Если да, я подозреваю, что реальной проблемой может быть Интернет, потому что после того, как произойдет установка, пользователь нажмет кнопку примерно через 30 секунд, чтобы запустить поток покупки. Не имеет смысла, что установка не будет завершена за 30 секунд, не так ли? - person idish; 09.10.2013
comment
@idish: Поскольку вам не удалось воспроизвести ошибку, я предлагаю вам попробовать протестировать устройство с опцией разработчика «Не сохранять действия», которая приводит к уничтожению вашего приложения каждый раз, когда пользователь покидает его, чтобы имитировать то, что ОС будет делать это, когда ей нужно освободить память, чтобы вы могли убедиться, что ваше приложение работает в этих условиях. Если у вас более старая версия Android, в которой нет этой опции, вы можете получить аналогичную функциональность, установив приложение Developers Tools от PandaDev (без подключения) здесь: play.google.com/store/apps/details?id=com.ggb.development - person Carl; 10.10.2013
comment
@idish: я предполагаю, что если вы полагаетесь на 30-секундную задержку пользователя, вы не ждете подтверждения установки перед использованием IabHelper. Это не удается, если пользователь перешел к диалоговому окну покупки GP, а ОС нуждается в памяти для этого и сбрасывает ваше собственное приложение из памяти. Затем пользователь подтверждает покупку, и ваше приложение перезапускается, а onCreate() запускает процесс повторного создания объекта IabHelper, но до его завершения вызывается ваш onActivityResult() и пытается использовать объект IabHelper. См. это: stackoverflow.com /вопросы/17944522/ - person Carl; 10.10.2013
comment
Я думаю, что у вас есть основная проблема во втором комментарии, я использую IabHelper как синглтон. Я почти уверен, что это происходит из-за того, что вы только что объяснили. Большое спасибо за информацию, сейчас попробую решить. - person idish; 10.10.2013
comment
Я использовал IabHelper как синглтон и startSetup только один раз, в моем первом действии. Я предполагаю, что решение будет состоять в том, чтобы прекратить использование шаблона singletone и начать настройку на каждом экземпляре IabHelper, который я создаю. Есть много действий, с помощью которых пользователи могут покупать продукты, я пытался облегчить себе жизнь, я думаю, это вызвало эти проблемы. - person idish; 10.10.2013
comment
У меня никогда не было мысли, что ОС может перезапустить мое приложение, чтобы запустить процесс покупки, сейчас я все исправляю. Вы молодец, большое вам спасибо. - person idish; 10.10.2013
comment
Да, это часто случается на старых устройствах; не так много на новых телефонах и планшетах с большим количеством доступной памяти. Поэтому, если вы тестируете Nexus7, вы можете не столкнуться с этим, если не протестируете с помощью функции «Не сохранять действия», но ваши пользователи со своими старыми устройствами Froyo или Gingerbread столкнутся с этим. - person Carl; 10.10.2013

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

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

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

person NerdyTherapist    schedule 12.02.2013
comment
Документация (developer.android.com/google/play/billing/billing_testing.html) четко указать, что вы не можете использовать эмулятор для тестирования In-app Billing; вы должны установить приложение на устройство, чтобы протестировать In-app Billing. Кроме того, на вашем телефоне должна быть установлена ​​следующая версия Google Play (для API версии 3): Для покупки и запроса управляемых элементов в приложении требуется клиент Google Play версии 3.9.16 или выше. Для покупки и запроса элементов подписки требуется клиент Google Play версии 3.10.10 или выше. - person britzl; 20.02.2013
comment
Позор мне, я явно не читал это. Но что касается исходного вопроса, у меня был тот же журнал ошибок, так что, возможно, ОП допустил ту же ошибку. - person NerdyTherapist; 27.02.2013

Еще одна вещь, с которой я столкнулся; хотя на вашем устройстве может быть установлена ​​последняя версия Google Play, которая поддерживает последнюю версию биллинга в приложении, другие пользователи могут этого не делать. И хотя сбои, вызванные этим, теоретически должны появляться в консоли разработчика, я не мог видеть эти сбои, пока не реализовал firebase... а потом я видел их много. В итоге я использовал функцию try catch и связывал пользователей, у которых не было последней версии Google Play или которые столкнулись с проблемой в магазине Google Play, на эту страницу https://support.google.com/googleplay/answer/1050566?hl=ru

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 — это просто диалоговое окно со ссылкой на указанную выше веб-страницу.

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

person mattbauer_QueueDo    schedule 12.02.2017

Если у вас установлен Lucky Patcher и модифицированный playstore, вы получите сообщение "iab helper is not setup". Решение: установите эмулятор Android на свой ПК или Lucky Patcher, и модифицированный игровой магазин не будет установлен. У вас больше не будет сообщения об ошибке, и вы сможете произвести оплату, у меня это сработало идеально :)

person xang feng    schedule 24.02.2020