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

Android In-App Purchase V3 Ошибка: требуется аутентификация

Я реализую Google In-app Purchase V3 и выполнял все шаги, указанные выше здесь, а также в официальной документации . Я загрузил свое приложение в Google Playstore для Alpha Testing, и я загрузил его с URL-адреса playstore на свое реальное устройство, но это дало мне ошибку.

Ошибка

Требуется аутентификация. Вам нужно войти в свою учетную запись Google.

enter image description here

Мой код для покупки In-app находится здесь:

public class BuyPointsFragment extends Fragment
//In app Billing variable start

    // Debug tag, for logging
        static final String TAG = "com.myApp";

        // Does the user have the premium upgrade?
        boolean mIsPremium = false;

        // Does the user have an active subscription to the infinite gas plan?
        boolean mSubscribedToInfiniteGas = false;

        // SKUs for our products: the premium upgrade (non-consumable) and gas
        // (consumable)
        static final String SKU_PREMIUM = "premium";
        static final String SKU_GAS = "gas";

        // SKU for our subscription (infinite gas)
        static final String SKU_INFINITE_GAS = "infinite_gas";

        // (arbitrary) request code for the purchase flow
        static final int RC_REQUEST = 10001;

        // Graphics for the gas gauge
        static int[] TANK_RES_IDS = {};

        // How many units (1/4 tank is our unit) fill in the tank.
        static final int TANK_MAX = 4;

        // Current amount of gas in tank, in units
        int mTank;

        // The helper object
        IabHelper mHelper;



    //In app billing variable end



@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
    //inapp  load game data
            loadData();

            String base64EncodedPublicKey = "Base64Key from publisher account";

            // Some sanity checks to see if the developer (that you!) really
            // followed the
            // instructions to run this sample (don't put these checks on your app!)
            if (base64EncodedPublicKey.contains("CONSTRUCT_YOUR")) {
                throw new RuntimeException(
                        "Please put your app public key in MainActivity.java. See README.");
            }
            if (getActivity().getPackageName().startsWith("com.myApp.activity")) {
                throw new RuntimeException(
                        "Please change the sample package name! See README.");
            }

            // Create the helper, passing it our context and the public key to
            // verify signatures with
            Log.d(TAG, "Creating IAB helper.");
            mHelper = new IabHelper(getActivity(), 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()) {
                        // Oh noes, there was a problem.
                        complain(getString(R.string.problem_setting_inapp_billing) + result);
                        return;
                    }

                    // Have we been disposed of in the meantime? If so, quit.
                    if (mHelper == null)
                        return;

                    // IAB is fully set up. Now, let get an inventory of stuff we
                    // own.
                    Log.d(TAG, "Setup successful. Querying inventory.");
                    mHelper.queryInventoryAsync(mGotInventoryListener);



                }
            });

            //In app billing code end here
    }

//В методах выставления счетов в приложении начинаются здесь

public void inappCall(){

    setWaitScreen(true);
    Log.d(TAG, "Launching purchase flow for gas.");

    /*
     * TODO: for security, generate your payload here for verification. See
     * the comments on verifyDeveloperPayload() for more info. Since this is
     * a SAMPLE, we just use an empty string, but on a production app you
     * should carefully generate this.
     */
    String payload = "";

    mHelper.launchPurchaseFlow(getActivity(), SKU_GAS, RC_REQUEST,
            mPurchaseFinishedListener, payload);
}

    // updates UI to reflect model
        public void updateUi() {
            // update the car color to reflect premium status or lack thereof
            // ((ImageView)findViewById(R.id.free_or_premium)).setImageResource(mIsPremium
            // ? R.drawable.premium : R.drawable.free);

            // "Upgrade" button is only visible if the user is not premium
            // findViewById(R.id.upgrade_button).setVisibility(mIsPremium ?
            // View.GONE : View.VISIBLE);

            // "Get infinite gas" button is only visible if the user is not
            // subscribed yet
            // (R.id.infinite_gas_button).setVisibility(mSubscribedToInfiniteGas ?
            // View.GONE : View.VISIBLE);

            // update gas gauge to reflect tank status
            if (mSubscribedToInfiniteGas) {
                // ((ImageView)findViewById(R.id.gas_gauge)).setImageResource(R.drawable.gas_inf);
            } else {
                int index = mTank >= TANK_RES_IDS.length ? TANK_RES_IDS.length - 1
                        : mTank;
                // ((ImageView)findViewById(R.id.gas_gauge)).setImageResource(TANK_RES_IDS[index]);
            }
        }

        // Enables or disables the "please wait" screen.
        void setWaitScreen(boolean set) {
            // findViewById(R.id.screen_main).setVisibility(set ? View.GONE :
            // View.VISIBLE);
            // findViewById(R.id.screen_wait).setVisibility(set ? View.VISIBLE :
            // View.GONE);
        }

        void complain(String message) {
            Log.e(TAG, "**** TrivialDrive Error: " + message);
            alert("Error: " + message);
        }

        void alert(String message) {
            AlertDialog.Builder bld = new AlertDialog.Builder(getActivity());
            bld.setMessage(message);
            bld.setNeutralButton("OK", null);
            Log.d(TAG, "Showing alert dialog: " + message);
            bld.create().show();
        }

        void saveData() {

            /*
             * WARNING: on a real application, we recommend you save data in a
             * secure way to prevent tampering. For simplicity in this sample, we
             * simply store the data using a SharedPreferences.
             */

            SharedPreferences.Editor spe = getActivity().getPreferences(getActivity().MODE_PRIVATE).edit();
            spe.putInt("tank", mTank);
            spe.commit();
            Log.d(TAG, "Saved data: tank = " + String.valueOf(mTank));
        }

        void loadData() {
            SharedPreferences sp = getActivity().getPreferences(getActivity().MODE_PRIVATE);
            mTank = sp.getInt("tank", 2);
            Log.d(TAG, "Loaded data: tank = " + String.valueOf(mTank));
        }

        // We're being destroyed. It important to dispose of the helper here!
        @Override
        public void onDestroy() {
            super.onDestroy();

            // very important:
            Log.d(TAG, "Destroying helper.");
            if (mHelper != null) {
                mHelper.dispose();
                mHelper = null;
            }
        }

        // Listener that called when we finish querying the items and
        // subscriptions we own
        IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
            public void onQueryInventoryFinished(IabResult result,
                    Inventory inventory) {
                Log.d(TAG, "Query inventory finished.");

                // Have we been disposed of in the meantime? If so, quit.
                if (mHelper == null)
                    return;

                // Is it a failure?
                if (result.isFailure()) {
                    complain(getString(R.string.failed_to_query_inventory) + result);
                    return;
                }

                Log.d(TAG, "Query inventory was successful.");

                /*
                 * Check for items we own. Notice that for each purchase, we check
                 * the developer payload to see if it correct! See
                 * verifyDeveloperPayload().
                 */

                // Do we have the premium upgrade?
                Purchase premiumPurchase = inventory.getPurchase(SKU_PREMIUM);
                mIsPremium = (premiumPurchase != null && verifyDeveloperPayload(premiumPurchase));
                Log.d(TAG, "User is " + (mIsPremium ? "PREMIUM" : "NOT PREMIUM"));

                // Do we have the infinite gas plan?
                Purchase infiniteGasPurchase = inventory
                        .getPurchase(SKU_INFINITE_GAS);
                mSubscribedToInfiniteGas = (infiniteGasPurchase != null && verifyDeveloperPayload(infiniteGasPurchase));
                Log.d(TAG, "User "
                        + (mSubscribedToInfiniteGas ? "HAS" : "DOES NOT HAVE")
                        + " infinite gas subscription.");
                if (mSubscribedToInfiniteGas)
                    mTank = TANK_MAX;

                // Check for gas delivery -- if we own gas, we should fill up the
                // tank immediately
                Purchase gasPurchase = inventory.getPurchase(SKU_GAS);
                if (gasPurchase != null && verifyDeveloperPayload(gasPurchase)) {
                    Log.d(TAG, "We have gas. Consuming it.");
                    mHelper.consumeAsync(inventory.getPurchase(SKU_GAS),
                            mConsumeFinishedListener);
                    return;
                }

                updateUi();
                setWaitScreen(false);
                Log.d(TAG, "Initial inventory query finished; enabling main UI.");
            }
        };

        /** Verifies the developer payload of a purchase. */
        boolean verifyDeveloperPayload(Purchase p) {
            String payload = p.getDeveloperPayload();

            /*
             * TODO: verify that the developer payload of the purchase is correct.
             * It will be the same one that you sent when initiating the purchase.
             * 
             * WARNING: Locally generating a random string when starting a purchase
             * and verifying it here might seem like a good approach, but this will
             * fail in the case where the user purchases an item on one device and
             * then uses your app on a different device, because on the other device
             * you will not have access to the random string you originally
             * generated.
             * 
             * So a good developer payload has these characteristics:
             * 
             * 1. If two different users purchase an item, the payload is different
             * between them, so that one user purchase can't be replayed to
             * another user.
             * 
             * 2. The payload must be such that you can verify it even when the app
             * wasn't the one who initiated the purchase flow (so that items
             * purchased by the user on one device work on other devices owned by
             * the user).
             * 
             * Using your own server to store and verify developer payloads across
             * app installations is recommended.
             */

            return true;
        }

        // 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 we were disposed of in the meantime, quit.
                if (mHelper == null)
                    return;

                if (result.isFailure()) {
                    complain(getString(R.string.error_purchase) + result);
                    setWaitScreen(false);
                    return;
                }
                if (!verifyDeveloperPayload(purchase)) {
                    complain(getString(R.string.error_purchase_authenitcity_failed));
                    setWaitScreen(false);
                    return;
                }

                Log.d(TAG, "Purchase successful.");

                if (purchase.getSku().equals(SKU_GAS)) {
                    // bought 1/4 tank of gas. So consume it.
                    Log.d(TAG, "Purchase is gas. Starting gas consumption.");
                    mHelper.consumeAsync(purchase, mConsumeFinishedListener);
                } else if (purchase.getSku().equals(SKU_PREMIUM)) {
                    // bought the premium upgrade!
                    Log.d(TAG, "Purchase is premium upgrade. Congratulating user.");
                    alert(getString(R.string.thank_you_updgraing_premium));
                    mIsPremium = true;
                    updateUi();
                    setWaitScreen(false);
                } else if (purchase.getSku().equals(SKU_INFINITE_GAS)) {
                    // bought the infinite gas subscription
                    Log.d(TAG, "Infinite gas subscription purchased.");
                    alert("Thank you for subscribing to infinite gas!");
                    mSubscribedToInfiniteGas = true;
                    mTank = TANK_MAX;
                    updateUi();
                    setWaitScreen(false);
                }
            }
        };

        // Called when consumption is complete
        IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
            public void onConsumeFinished(Purchase purchase, IabResult result) {
                Log.d(TAG, "Consumption finished. Purchase: " + purchase
                        + ", result: " + result);

                // if we were disposed of in the meantime, quit.
                if (mHelper == null)
                    return;

                // We know this is the "gas" sku because it the only one we
                // consume,
                // so we don't check which sku was consumed. If you have more than
                // one
                // sku, you probably should check...
                if (result.isSuccess()) {
                    // successfully consumed, so we apply the effects of the item in
                    // our
                    // game world logic, which in our case means filling the gas
                    // tank a bit
                    Log.d(TAG, "Consumption successful. Provisioning.");
                    mTank = mTank == TANK_MAX ? TANK_MAX : mTank + 1;
                    saveData();
                    alert("You filled 1/4 tank. Your tank is now "
                            + String.valueOf(mTank) + "/4 full!");
                } else {
                    complain("Error while consuming: " + result);
                }
                updateUi();
                setWaitScreen(false);
                Log.d(TAG, "End consumption flow.");
            }
        };

        @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data) {
            Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + ","
                    + data);
            if (mHelper == null)
                return;

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




    //In app billing method end here

Мои продукты Managed products в аккаунте разработчика In-app Products

enter image description here

EDIT:

Когда я использую android.test.purchased как SKU, тогда он отлично работает, и когда я меняю свой SKU на мой product_id, то он дает мне Error Authentication is required. You need to sign into your Google Account.

4b9b3361

Ответ 1

Пожалуйста, убедитесь, что ваш идентификатор продукта в магазине для покупки приложения, который является "test_product", вы должны использовать тот же sku в своем коде. И если вы измените sku из своего приложения, тогда все возможные имена sku должны существовать в PlayStore как в приложениях. Я как-то столкнулся с этой проблемой, и причина в том, что мой элемент SKU не существовал в продуктах Google Playstore в приложении. Я просто добавляю их в Playstore, и это было разрешено.

Ответ 3

Немного длинный, но ваше приложение "Google Play Store" может быть неисправно.

У меня есть отчет от клиента, что он не может сделать покупку In-App, и он получает ту же ошибку. Я также знаю, что мы все еще получаем покупки. Поэтому я не думаю, что вы обязательно настроили что-то неправильно. IAB v3 может быть просто неисправен с вашим приложением "Google Play Store" .

Попробуйте настроить Диспетчер приложений и выберите приложение "Магазин Google Play" в списке и нажмите кнопку "Удалить обновления", которая появляется там. Затем попробуйте снова сделать покупку. Если ваше старое приложение Google Play поддерживает In-App billing v3, оно будет работать. Если этого не произойдет, попробуйте снова обновить приложение "Google Play Store" , возможно, вы получите новую/новую версию приложения магазина, и вы сможете совершить покупку.

В качестве альтернативы вы можете попробовать и настроить второе тестовое устройство с другой учетной записью Google... или попросить друга присоединиться к группе бета-тестирования и сделать пробную покупку.

Я не очень доволен IAB v3. Он чувствует себя немного неуклюжим. У V2 было меньше функций, но они были тверды.