Google Play IAP Problem

Hi everyone!

We encountered a big problem since we upgraded our In-app purchase from old “AIDL” to “Unity In App Purchasing”. The problem is simple and critical:

“Some users somehow manage a way to never pay their bill but the game charges their inventory!”

We do not know how some users manage to do this issue but due to some unknown reason and conditions (at least unknown to us!), a user starts in app purchase order, even though the corresponding order is in “Payment pending” state in the Google Play Console, the “ProcessPurchase” is called in our game and this transaction is treated as a valid transaction. Then after a while, the transaction status will change to either “Canceled” or “Payment declined”.

I think that these users make this on purpose and it is not accidental, because these users only buys our most expensive IAP Items rapidly several times a row. Also we know that our IAP system is working correctly because we have checked it using our test users and test devices.

Has anybody encountered the same problem or know the solution?

We had the same issue and solved it by checking the purchaseState flag before confirming the IAP: Google payments are being automatically refunded.

We implemented SetDeferredPurchaseListener (as you said) but the problem still exists.

You need to ensure to return Pending instead of Complete from ProcessPurchase until the purchaseState is not 2 and not 4. You’ll need to parse the receipt to do so.

2 Likes

I am a little confused here, I checked IAP examples at this thread:
https://discussions.unity.com/t/700293
but I did not find any thing related to the purchaseState or how to parse receipt. How may I parse receipt?

In ProcessPurchase, you can obtain the receipt via the passed argument args.purchasedProduct.receipt. We will be exposing the purchaseState as a property in a future release. For now, you would need to parse the receipt yourself.

Hi @bardia68 another solution would be to use the GooglePlayExtension.IsPurchasedProductDeferred API.
So in your ProcessPurchase function, you would use GooglePlayExtension.IsPurchasedProductDeferred on your product and if the function returns true then you should return Pending.

1 Like

I just find out that I can use GooglePlayReceipt.purchaseState instead of manually parse the receipt:

var result = validator.Validate(receipt);
foreach (var productReceipt in result)
{
       if (productReceipt is GooglePlayReceipt google)
       {
                    // This is Google's Order ID.
                    // Note that it is null when testing in the sandbox
                    // because Google's sandbox does not provide Order IDs.
                    orderId = google.orderID;
                    token = google.purchaseToken;
                    purchaseState = google.purchaseState;
                    if ((int) purchaseState == 4)
                    {
                        ParsisDebug.Log("Deferred IAP, Not bought yet!");
                        return PurchaseProcessingResult.Pending;
                    }
}

Thank you for the hint, I just ended up using aforementioned piece of code!

By the way I think this is a BIG problem that every Android game developer WOULD DEFINITELY encounter, so I think that it would be wise to mention this problem clearly in Unity Documents and every IAP Sample projects.

7 Likes

Totally agree! This is a new feature from Google, documentation updates are on the way. And thanks for the code!

1 Like

That is very strange. UnityProcessPurchase API should only be called when the purchase completes as mentioned in their documents here. Also, as shown in Unity’s docs below, ProcessPurchase is only notified after a purchase succeeded step when the transaction is complete from the stores, not before or while it’s pending or deferring:

At the point it gets back to the App in the ProcessPurchase() call, the purchase should be complete as mentioned in the docs.

Thus, this sounds like a bug on the Unity IAP Plugins due to the recent google change @JeffDUnity3D
If so, is there going to be a fix for this that we can track?

The diagram and the documentation refers to the Google Play Billing Library v2, we are now using v3 as required by Google. And v4 is on the way! The behavior is expected and is how our major studios are handling it.

Hi, I use in app package v4 and implemented the deferred purchase as in the exemple:

if( m_GoogleExtensions.IsPurchasedProductDeferred( args.purchasedProduct ) )
{
    return PurchaseProcessingResult.Pending;
}

But I still get purchasesState = 4 going to my server validation process. Doesn’t this IsPurchasedProductDeferred function suposed to check this purchaseState ?

We are not sending it to your server, you are. It’s up to you if you want to validate the receipt. The code you provided is correct, continue to return Pending if it’s a deferred purchase with purchaseState = 4. I don’t think you need to check the receipt until purchaseState = 1 (when you actually award the product to the user), but it’s up to you.

to be more clear here is my code in ProcessPurchase

if( m_GoogleExtensions.IsPurchasedProductDeferred( args.purchasedProduct ) )
{
    return PurchaseProcessingResult.Pending;
}

// HERE I DO THE SERVER SIDE VALIDATION

// then
return  PurchaseProcessingResult.Complete;

It seems that m_GoogleExtensions.IsPurchasedProductDeferred( args.purchasedProduct ) doesn’t returns true if the purchaseState = 4 as on my server side validation I receive some receipt with purchaseState = 4.

Is it a bug in IsPurchasedProductDeferred function or do I have to handle it myself ?

I’m not familiar with that method myself, I have been recommending this code https://discussions.unity.com/t/848244/8

1 Like

I think the GooglePurchaseState enum needs to be updated in the docs if we’re expected to handle 4, as currently only 0-2 are documented:
https://docs.unity3d.com/Packages/com.unity.purchasing@4.0/api/UnityEngine.Purchasing.Security.GooglePurchaseState.html

I understand that C++ won’t cause an exception for enum’s that are out of bounds, but it definitely would help in finding a solution to this problem to know what values we can expect. Also I’m curious now what 3 equals to.

Those are Google values, not Unity. And yes, they need to update their docs (then we can). There is not a 3 value to my knowledge.

Ah, so this is related to https://developer.android.com/reference/com/android/billingclient/api/Purchase.PurchaseState right?

I guess I was confused because of the enum mismatch. Does this look correct to you?
7478818--919664--upload_2021-9-8_1-23-53.png

1 Like

Correct

I see now where the confusion comes from. The 4 is being returned from their API in the json, whereas the 2 is returned from their library, which has this implementation :

public int getPurchaseState() {
    switch(this.zzc.optInt("purchaseState", 1)) {
    case 4:
        return 2;
    default:
        return 1;
    }
}

So we’re basically working with the raw json return, vs with the documented Purchase.PurchaseState.PENDING.
But I think that would mean they have a documentation for the pending state, right. So would be good to have this also mentioned in the Unity IAP docs.