ddebilt / play.billing.v3

A C# Google Play client, for use with Xamarin.Android applications.
13 stars 11 forks source link

How to do Buy+Consume #1

Open HelmuthWcs opened 11 years ago

HelmuthWcs commented 11 years ago

Hi, I'm heaving troubling implementing a consumable IAP. Your sample shows how to do a purchase and a "consume" on two different events. I'm trying to chain a buy request with a consume request, but I'm getting errors saying that the product has not been bought even if that product was purchased successfully.

Even your demo doesn't work if I "buy Item_2" and then "consume item 2". I have to "query purchases" before consuming to make it work.

I'm also having trouble releasing the BillingService. If I call m_service.Dispose(); in Activity.Destroy I get an Java.Lang.IllegalArgumentException: Service not registered: play.billing.v3.BillingService@4052d918. I'm trying to do this because after leaving the store activity I'm getting leak warning messages.

Thanks, Helmuth

ddebilt commented 11 years ago

Hi @HelmuthWcs

I haven't fully tested the consume functionality yet, as I have just needed to use the regular purchase functionality in other apps that I am working on. I will take a look into it this weekend, as well as the disposal issue.

ddebilit

onomasticon commented 11 years ago

Did this ever get resolved? I've run into the same issue as the OP.

karios commented 11 years ago

Same issue here. I would love to know everybody's thoughts on that. I was thinking about setting a timed event to try and consume possible purchases by querying them at specific intervals, since chaining a purchase and consume has proven to be tricky. Any ideas?

ddebilt commented 11 years ago

Sorry, I haven't been able to spend any time on this, but I will take a look at this issue by the end of this week, since I will need this for an upcoming project. I will re-post with any additional findings.

ddebilt commented 11 years ago

The "Buy Item" code was not adding the purchased item to the inventory on success. Fixing that allows the chaining to work. I also changed the "Get Purchases" code to split up what is valid and what is not valid, rather than failing the entire request if something was not valid (didn't pass signature).

The service leak is also fixed. I was trying to unbind the play billing service from the MainActivity, rather than the BillingService object (IServiceConnection implementation).

Give it a try and let me know if I can close this out.

karios commented 11 years ago

I still have an issues with chaining the thing. When I use:

var req = new Buy(sku, m_requestId++); m_service.SendRequest(req).ContinueWith(t => gameActivity.RunOnUiThread(() => {
if (t.Result.Success) {

and the SendRequest is successful, the ContinueWith never runs (so it never goes to if (t.Result.Success). If it fails (for example, the item is already owned) it does run. This essentially means that the user has to click the "buy" button once to buy the item and then click it again, to make the task fail (because the item is already owned) and consume the item (since the ContinueWith works in that case).

ddebilt commented 11 years ago

@karios Are you able to confirm that OnActivityResult is getting invoked after you successfully purchase the item through the intent GUI? (perhaps put in a Toast or Log that can be seen).

Specifically, once OnActivityResult is invoked on your activity, the BillingService's HandleActivityResult method will get invoked, and the previously requested Buy's task will get its result set, which then should trigger the ContinueWith.

I won't be able to try this out again until later today, tomorrow at the latest, but will try as I suggested as well. I was able to do a sequential Buy - Consume - Buy - Consume, but it was not based on the Buy success tasks (rather it was just checking current inventory), so I will try your route as well.

HelmuthWcs commented 11 years ago

To successfully consume a purchase I was able to implement it into BillingService.cs in the HandleActivityResult method passing in my store activity to handle the successful purchase and consume it:

public bool HandleActivityResult(int requestCode, int resultCode, Intent data, Store store) { ... try { var item = new Purchase(buyReq.ItemType, purchaseData, dataSignature); string sku = item.Sku;

    // Verify signature
    if (!Security.VerifyPurchase(this.AppKey, purchaseData, dataSignature))
    {
        Utils.LogError("Purchase signature verification failed. SKU: " + sku);
        return true;
    }
    Utils.LogDebug("Purchase signature verification passed. SKU: " + sku);

            store.HandlePurchase(item);
}

... In Store.cs (Activity): public void HandlePurchase(Purchase purchaseID) { switch (purchaseID.Sku) { case "android.test.purchased": ConsumeProduct(purchaseID); ... private void ConsumeProduct(Purchase p) { System.Diagnostics.Debug.WriteLine("Attemping to consume: " + p.Sku); m_service.SendRequest(new ConsumePurchase(p, m_requestId++)).ContinueWith(t => this.RunOnUiThread(() => { if (t.Result.Success) { //p.Token? m_service.CurrentInventory.ErasePurchase(p.Sku); _status.Text = "Consume complete."; System.Diagnostics.Debug.WriteLine("Consume OK"); } else { System.Diagnostics.Debug.WriteLine("Consume failure. Error: " + t.Result.Message); _status.Text = "Consume failure. Error: " + t.Result.Message; } })); }

I hope this helps

karios commented 11 years ago

I think I got it in the end. I had a huge problem when I was calling m_service.SendRequest, with the OnActivityResult being called immediately with USER_CANCELLED. I think I fixed this by using LaunchMode = LaunchMode.SingleTask on the game Activity. Previously I was using LaunchMode = LaunchMode.SingleInstance and it seemed like it was causing this issue. Seems to work now. I am trying to buy and it waits until I complete or cancel the action before proceeding. The function that works for me (and launches a boolean Action if the purchase was successful) is the following:

public static void SendBuyRequest(string sku, bool consumable, Action result) { var req = new Buy(sku, m_requestId++); m_service.SendRequest(req).ContinueWith(t => { mainGameActivity.RunOnUiThread(() => { if (t.Result.Success) { System.Diagnostics.Debug.WriteLine("Purchase complete. Item: " + req.Sku); if (consumable) { consume(sku); } } else { System.Diagnostics.Debug.WriteLine("Purchase failure. Error: " + t.Result.Message); } result(t.Result.Success); }); }); }

I set the mainGameActivity at IAP initialization. The way I call it is like this:

SendBuyRequest(sku, consumable, success => { if (success) {

} }); I hope this helps someone. A big thanx to ddebilt for their efforts:)