web-dev-qa-db-ja.com

Androidアプリ内購入V3エラー:認証が必要です

私はGoogle In-app Purchase V3を実装しており、 ここ および公式ドキュメント ここ に記載されているすべての手順に従いました。 Alpha TestingのアプリをGooglePlaystoreにアップロードし、それをPlaystore URLから実際のデバイスにダウンロードしましたが、エラーが発生します

エラー

認証が必要です。 Googleアカウントにサインインする必要があります。

enter image description here

アプリ内購入の私のコードはここにあります:

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's 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's public key in MainActivity.Java. See README.");
            }
            if (getActivity().getPackageName().startsWith("com.myApp.activity")) {
                throw new RuntimeException(
                        "Please change the sample's 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's 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's 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's 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's 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's 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's 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's 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'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.");
            }
        }




    //In app billing method end here

私の製品は開発者アカウントManaged productsIn-app Productsです

enter image description here

編集:

Android.test.purchasedSKUとして使用すると、正常に機能し、skuproduct_idに変更すると、Error Authentication is required. You need to sign into your Google Account.が得られます。

22
Nak Android Dev

アプリ内購入用のプレイストアの商品IDが「test_product」であることを確認してください。コードでは同じSKUを使用する必要があります。また、アプリケーションからSKUを変更する場合は、考えられるすべてのSKU名がアプリ内製品としてPlayストアに存在する必要があります。私はかつてこの問題に遭遇しましたが、その理由は、SKUアイテムがGoogle Playstoreのアプリ内製品に存在しなかったためです。Playstoreに追加するだけで、解決されました。

7
arslan haktic

Android IABエラー-認証が必要です

APKを公開する必要があるようです。私もこの問題にこだわった。

2
Alexander

少し長めですが、「GooglePlayストア」アプリが誤動作している可能性があります。

アプリ内購入ができないというお客様からの報告があり、同じエラーが発生しています。また、まだ購入が進んでいることも知っています。ですから、必ずしも何か間違った設定をしたとは思いません。 IAB v3は、単に「GooglePlayストア」アプリで誤動作している可能性があります。

[設定]に移動して、アプリケーションマネージャーを開き、リストから[Google Playストア]アプリケーションを選択して、そこに表示される[アップデートのアンインストール]ボタンをクリックしてみてください。次に、もう一度購入してみてください。古いGooglePlayアプリケーションがアプリ内課金v3をサポートしている場合は、機能します。そうでない場合は、「Google Playストア」アプリをもう一度更新してみてください。おそらく、ストアアプリの新しいバージョンまたは異なるバージョンを入手して、購入できるようになります。

別の方法として、別のGoogleアカウントで2つ目のテストデバイスを設定してみるか、友人にベータテストグループに参加してテスト購入を依頼することができます。

IABv3にはあまり満足していません。少し不格好です。 V2は機能が少ないですが、しっかりしていると感じました。

1
omahena