Receipt Validation on Android Store

Question about receipt validation on android store. I have some code for a non-consumable IAP to verify that it hasn’t been refunded or cancelled. If it has, the code will remove the benefits they got from buying it.

The question is: if they purchase the non-consumable, get refunded, then purchase it again, will it return two product receipts, one good one refunded? Or will it only return the most recent receipt? I’m not sure how I could test this

The code I’m using is the below:

        public bool ValidateVeteran()
        {
            if (!m_StoreController.products.WithID(veteran_productid).hasReceipt)
                return false;
            var validator = new CrossPlatformValidator(GooglePlayTangle.Data(), AppleTangle.Data(), Application.identifier);
            statusUpdate("Verifying Receipt");
            try
            {
                // On Google Play, result has a single product ID.
                // On Apple stores, receipts contain multiple products.
                var result = validator.Validate(m_StoreController.products.WithID(veteran_productid).receipt);
                // For informational purposes, we list the receipt(s)
                Debug.Log("Receipt is valid. Contents:");
                foreach (IPurchaseReceipt productReceipt in result)
                {
                    Debug.Log(productReceipt.productID);
                    Debug.Log(productReceipt.purchaseDate);
                    Debug.Log(productReceipt.transactionID);

                    if (productReceipt is GooglePlayReceipt google)
                    {
                        Debug.Log("Vet Purchase State: " + google.purchaseState);
                        if (google.purchaseState == GooglePurchaseState.Refunded || google.purchaseState == GooglePurchaseState.Cancelled)
                            return false;
                    }
                }
                return true;
            }
            catch (IAPSecurityException)
            {
                Debug.Log("Invalid receipt, not unlocking content");
                //validPurchase = false;
            }
            return false;
        }

Alternatively if there could be multiple receipts for the product return I would use this:

        public bool ValidateVeteran()
        {
            if (!m_StoreController.products.WithID(veteran_productid).hasReceipt)
                return false;
            var validator = new CrossPlatformValidator(GooglePlayTangle.Data(), AppleTangle.Data(), Application.identifier);
            statusUpdate("Verifying Receipt");
            try
            {
                // On Google Play, result has a single product ID.
                // On Apple stores, receipts contain multiple products.
                var result = validator.Validate(m_StoreController.products.WithID(veteran_productid).receipt);
                // For informational purposes, we list the receipt(s)
                bool goodreceipt = false;
                Debug.Log("Receipt is valid. Contents:");
                foreach (IPurchaseReceipt productReceipt in result)
                {
                    Debug.Log(productReceipt.productID);
                    Debug.Log(productReceipt.purchaseDate);
                    Debug.Log(productReceipt.transactionID);

                    if (productReceipt is GooglePlayReceipt google)
                    {
                        Debug.Log("Vet Purchase State: " + google.purchaseState);
                        if (google.purchaseState != GooglePurchaseState.Refunded && google.purchaseState != GooglePurchaseState.Cancelled)
                            goodreceipt = true;
                    }

                }
                return goodreceipt;
            }
            catch (IAPSecurityException)
            {
                Debug.Log("Invalid receipt, not unlocking content");
                //validPurchase = false;
            }
            return false;
        }

@Saishuuheiki I typically create multiple tester accounts when testing IAP for non-consumables and subscriptions. You should be able to test directly. But I don’t believe Google will allow a user to purchase again after a refund. Regardless, only a single receipt should be returned per product. Apple however is a bit different and may include multiple receipts.

The problem is we previously had a version up using the codeless-IAP that wasn’t properly acknowledging the purchase (e.g. PurchaseProcessingResult.Complete). Google published it however and didn’t notice the problem until 3 days later, when they started refunding these purchases because there’s a 3-day timeout.

While we have a new version up that shouldn’t have this problem in the future, if the user doesn’t run the updated app between now and timeout it will issue the refund.

Will these users be locked-out from purchasing the product when they were auto-rebated? That doesn’t seem right

Got it! No, they should be able to purchase. This wasn’t a requested refund, but automatic. Are you able to confirm? I don’t believe there would be a work around unfortunately, this is enforced by Google. You might need to create new products.