web-dev-qa-db-fra.com

android: facturation Inapp: réponse d'erreur: 7: élément déjà possédé

J'apprends à implémenter une facturation in-app pour mon application de telle sorte que les gens peuvent par exemple faire un don en appuyant sur le bouton faire un don.

L'utilisateur est autorisé à donner plus d'une fois, c'est-à-dire que l'achat est consommable.

Les codes ci-dessous proviennent de l'exemple TrivalDrive et de quelques didacticiels sur le Web:

Code:

IabHelper mHelper;
static final String ITEM_SKU = "Android.test.purchased"; 

@Override
protected void onCreate(Bundle savedInstanceState) 
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_in_app_billing);

    buy10Button = (Button) findViewById(R.id.buy10Button); 
    buy15Button = (Button) findViewById(R.id.buy15Button); 
    buy20Button = (Button) findViewById(R.id.buy20Button);      

    String base64EncodedPublicKey = "keykeykey";

    mHelper = new IabHelper(this, base64EncodedPublicKey);


    mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() 
    {
          public void onIabSetupFinished(IabResult result) 
          {
            if (!result.isSuccess()) 
            {
               Log.d(TAG, "In-app Billing setup failed: " + result);
               return;
            } 
            if (mHelper == null) 
            {
                return;
            }          
            Log.d(TAG, "In-app Billing is set up OK");
          }
    });     
}

public void buy10Click(View view) 
{
    mHelper.launchPurchaseFlow(this, ITEM_SKU, 10001,  mPurchaseFinishedListener, "");
}

public void buy15Click(View view) 
{

}

public void buy20Click(View view) 
{

}   

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) 
{
    if (mHelper == null) return;  
    if (!mHelper.handleActivityResult(requestCode, resultCode, data)) 
    {     
        super.onActivityResult(requestCode, resultCode, data);
    }
}

IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() 
{
    public void onIabPurchaseFinished(IabResult result, Purchase purchase) 
    {
        if (mHelper == null) return;
        if (result.isFailure()) 
        {
           // Handle error
               return;
        }      
        else if ((purchase.getSku().equals(ITEM_SKU)))   
        {
           consumeItem();
        }              
    }
};

public void consumeItem() 
{
    mHelper.queryInventoryAsync(mReceivedInventoryListener);
}

IabHelper.QueryInventoryFinishedListener mReceivedInventoryListener = new IabHelper.QueryInventoryFinishedListener() 
{
    public void onQueryInventoryFinished(IabResult result, Inventory inventory) 
    {
        if (mHelper == null) return;
        if (result.isFailure()) 
        {
            // Handle failure
        } 
        else 
        {
            mHelper.consumeAsync(inventory.getPurchase(ITEM_SKU), mConsumeFinishedListener);
        }
    }
};

IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() 
{
    public void onConsumeFinished(Purchase purchase, IabResult result) 
    {
        if (mHelper == null) return;
        if (result.isSuccess()) 
        {
            Toast.makeText(InAppBillingActivity.this, "Thank you for your donation!!", Toast.LENGTH_LONG).show();   
        } 
        else 
        {
            // handle error
        }
    }
};

Question:

Pourtant, je continue de recevoir une erreur E/IabHelper(13392): In-app billing error: Unable to buy item, Error response: 7:Item Already Owned et la boîte de dialogue de paiement de Google Play ne s'ouvre tout simplement pas.

J'ai fait des recherches et découvert de nombreuses situations similaires, certaines ont suggéré d'attendre quelques minutes, puis l'achat sera réinitialisé par lui-même, mais j'ai attendu près d'une heure mais ça craint toujours.

J'ai également constaté que quelqu'un suggère de modifier la fonction IabResult public boolean isSuccess() { return mResponse == IabHelper.BILLING_RESPONSE_RESULT_OK; } pour renvoyer également le BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED Comme isSuccess = true, mais je ne sais pas comment modifier une telle ...

Comment résoudre le problème? Merci!!

26
pearmak

Vérifiez mon code ci-dessous ici:

Je ne comprends pas dans votre code pourquoi avez-vous utilisé l'inventaire des requêtes dans l'écouteur de fin d'achat. La méthode ConsumeAsync () doit être appelée pendant que vous obtenez le sku comme votre sku demandé.

// 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()) {
                complain("Error purchasing: " + result);
                return;
            }
            if (!verifyDeveloperPayload(purchase)) {
                complain("Error purchasing. Authenticity verification failed.");
                return;
            }

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

            if (purchase.getSku().equals(SKU_GAS)) {

                 // remove query inventory method from here and put consumeAsync() directly
                mHelper.consumeAsync(purchase, mConsumeFinishedListener);

            }

        }
    };

méthode startSetup

// vous avez oublié d'appeler la méthode d'inventaire de requête dans la méthode startSetup.

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

QueryInventoryFinishedListener

Et vérifiez également si l'achat de conditions est le même que celui qui vous est demandé n'est pas égal à null et que la charge utile du développeur est également la même dans votre écouteur de fin d'inventaire de requête.

if (gasPurchase != null && verifyDeveloperPayload(gasPurchase)){
    //code
}
// 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.");
                if (result.isFailure()) {
                    complain("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().
                 */

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

Explication pourquoi cela arrive:

Chaque fois que vous avez acheté un article consommable, Google Play Store ne sera pas géré, c'est le détail du produit acheté et d'autres choses dans la console Google Play. C'est pourquoi nous devons appeler la méthode consumeAsync (). lorsque nous avons acheté un article, Google Play Store garde un article enregistré a été acheté pour la première fois et vous permet de l'acheter une deuxième fois.

J'espère que cela résoudra votre problème.

20
Maulik

Vous avez acheté "Android.test.purchased" mais ne l'avez pas consommé. Cependant, si vous avez oublié de le consommer immédiatement, il n'est pas facile de le consommer à nouveau. Nous pouvons attendre 14 jours. Le faux achat sera effacé automatiquement. Mais ce n'est pas acceptable.

J'ai passé beaucoup de temps à trouver la solution:

Ajoutez cette ligne pour obtenir des informations de débogage.

_iabHelper.enableDebugLogging(true, "TAG");

Exécutez l'application. Dans LogCat, vous verrez une chaîne json comme

{"packageName":"com.example","orderId":"transactionId.Android.test.purchased","productId":"Android.test.purchased","developerPayload":"123","purchaseTime":0,"purchaseState":0,"purchaseToken":"inapp:com.example:Android.test.purchased"}

Consommez-le manuellement (remplacez THAT_JSON_STRING par votre chaîne json)

    Purchase purchase;
    try {
        purchase = new Purchase("inapp", THAT_JSON_STRING, "");
        _iabHelper.consumeAsync(purchase, new OnConsumeFinishedListener() {

            @Override
            public void onConsumeFinished(Purchase purchase, IabResult result) {
                Log.d("TAG", "Result: " + result);
            }
        });
    } catch (JSONException e) {
        e.printStackTrace();
    }

_iabHelper est mHelper.

38
Vince Yuan

J'ai réussi à "consommer l'achat" simplement en redémarrant l'appareil.

6
JohnK

Vous pouvez utiliser Google Play "RAPPORTS FINANCIERS" -> "Visitez votre compte marchand pour plus de détails" -> "Commandes" pour afficher et annuler toute commande pour "la consommer". Ensuite, vous devez redémarrer votre appareil. =)

5
Maksim