Appstore SDK Plugin for Unity - GetPurchaseUdpates missing subscription IAP transactions

I’ve been integrating the Appstore SDK Plugin for Unity (v3.0.2) into a Unity app and testing it using the Live App Testing service. All of the IAP methods and responses are working as expected except for retrieving purchase transactions using GetPurchaseUpdates.

I’m trying to use GetPurchaseUpdates(true) to retrieve the entire transaction history so that I can check whether the user has an active subscription for any given subscription IAP. Calling GetPurchaseUpdates(true) is retrieving transactions as expected for consumable and entitlement IAPs, however, only some transactions are being retrieved for subscription IAPs. Notably, the transaction for any active subscription is always missing from the retrieved GetPurchaseUpdatesResponse object. The retrieved GetPurchaseUpdatesResponse object’s hasMore field is false so it doesn’t seem to be a result of missing transactions due to pagination.

As far as I can tell I’m following the purchasing flow correctly for the subscriptions - I’m getting email receipts for the purchases and the subscriptions are showing in my account’s active subscriptions - so I’m not sure why the transactions aren’t being retrieved successfully. It’s my understanding that GetPurchaseUpdates(true) should be retrieving a full transaction history. Any help with this would be greatly appreciated.

Here is the code that I’m using:

using System.Collections.Generic;
using com.amazon.device.iap.cpt;
using UnityEngine;

public class ExampleIAPManager : MonoBehaviour
{
    private IAmazonIapV2 iapService;

    public void Init()
    {
        iapService = AmazonIapV2Impl.Instance;

        iapService.AddGetUserDataResponseListener(GetUserDataCallback);
        iapService.AddGetProductDataResponseListener(GetProductDataCallback);
        iapService.AddPurchaseResponseListener(PurchaseCallback);
        iapService.AddGetPurchaseUpdatesResponseListener(GetPurchaseUpdatesCallback);
        iapService.AddLicenseResponseListener(VerifyLicenseCallback);
    }

    public void VerifyLicense()
    {
        iapService.VerifyLicense();
    }

    public void GetUserData()
    {
        var request = iapService.GetUserData();
    }

    public void GetProductDatas(List<string> skus)
    {
        var request = new SkusInput
        {
            Skus = skus
        };

        var response = iapService.GetProductData(request);
    }

    public void Purchase(string sku)
    {
        var request = new SkuInput
        {
            Sku = sku
        };

        var response = iapService.Purchase(request);
    }

    public void GetPurchaseUpdates(bool reset)
    {
        var request = new ResetInput
        {
            Reset = reset
        };

        var response = iapService.GetPurchaseUpdates(request);
    }

    private void GetUserDataCallback(GetUserDataResponse response)
    {
        if (response == null)
        {
            Debug.Log("[ExampleIAPManager] GetUserDataCallback Failed to get user data");
            return;
        }

        var requestId = response.RequestId;
        var userId = response.AmazonUserData.UserId;
        var marketplace = response.AmazonUserData.Marketplace;
        var status = response.Status;

        Debug.Log("[ExampleIAPManager] GetUserDataCallback response:" +
                  "\nRequest id: " + requestId +
                  "\nUser id: " + userId +
                  "\nMarketplace: " + marketplace +
                  "\nStatus: " + status);
    }

    private void GetProductDataCallback(GetProductDataResponse response)
    {
        if (response == null)
        {
            Debug.Log("[ExampleIAPManager] GetProductDataCallback Failed to get product data response");
            return;
        }

        var requestId = response.RequestId;
        var status = response.Status;

        Debug.Log("[ExampleIAPManager] GetProductDataCallback response:" +
                  "\nRequest id: " + requestId +
                  "\nStatus: " + status);

        var productDataMap = response.ProductDataMap ?? new Dictionary<string, ProductData>();
        var unavailableSkus = response.UnavailableSkus ?? new List<string>();

        foreach (var (key, productData) in productDataMap)
        {
            var sku = productData.Sku;
            var productType = productData.ProductType;
            var price = productData.Price;
            var title = productData.Title;
            var description = productData.Description;
            var smallIconUrl = productData.SmallIconUrl;

            Debug.Log("[ExampleIAPManager] GetProductDataCallback found product:" +
                      "\nSKU: " + sku +
                      "\nProduct type: " + productType +
                      "\nPrice: " + price +
                      "\nTitle: " + title +
                      "\nDescription: " + description +
                      "\nSmallIconUrl: " + smallIconUrl);
        }

        foreach (var sku in unavailableSkus)
        {
            Debug.Log("[ExampleIAPManager] GetProductDataCallback unable to find product with sku: " + sku);
        }
    }

    private void PurchaseCallback(PurchaseResponse response)
    {
        if (response == null)
        {
            Debug.Log("[ExampleIAPManager] PurchaseCallback Failed to get purchase response");
            return;
        }

        var requestId = response.RequestId;
        var userId = response.AmazonUserData?.UserId;
        var marketplace = response.AmazonUserData?.Marketplace;
        var receiptId = response.PurchaseReceipt?.ReceiptId;
        var cancelDate = response.PurchaseReceipt?.CancelDate;
        var purchaseDate = response.PurchaseReceipt?.PurchaseDate;
        var sku = response.PurchaseReceipt?.Sku;
        var productType = response.PurchaseReceipt?.ProductType;
        var status = response.Status;

        Debug.Log("[ExampleIAPManager] PurchaseResponseCallback response:" +
                  "\nRequest id: " + requestId +
                  "\nUser id: " + userId +
                  "\nMarketplace: " + marketplace +
                  "\nReceipt id: " + receiptId +
                  "\nCancel date: " + cancelDate +
                  "\nPurchase date: " + purchaseDate +
                  "\nSKU: " + sku +
                  "\nProduct type: " + productType +
                  "\nStatus: " + status);

        var success = status == "SUCCESSFUL";
        NotifyFulfillment(receiptId, success ? "FULFILLED" : "UNAVAILABLE");
    }

    private void NotifyFulfillment(string receiptID, string result)
    {
        Debug.Log("[ExampleIAPManager] NotifyFulfillment. Receipt:\n" + receiptID + "\nResult:\n" + result);

        var request = new NotifyFulfillmentInput
        {
            ReceiptId = receiptID,
            FulfillmentResult = result
        };

        iapService.NotifyFulfillment(request);
    }

    private void GetPurchaseUpdatesCallback(GetPurchaseUpdatesResponse response)
    {
        if (response == null)
        {
            Debug.Log("[ExampleIAPManager] GetPurchaseUpdatesCallback Failed to get purchase updates response");
            return;
        }

        var requestId = response.RequestId;
        var userId = response.AmazonUserData.UserId;
        var marketplace = response.AmazonUserData.Marketplace;
        var status = response.Status;
        var hasMore = response.HasMore;

        Debug.Log("[ExampleIAPManager] GetPurchaseUpdatesCallback response:" +
                  "\nRequest id: " + requestId +
                  "\nUser id: " + userId +
                  "\nMarketplace: " + marketplace +
                  "\nStatus: " + status +
                  "\nHasMore: " + hasMore);

        var receipts = response.Receipts;

        foreach (var receipt in receipts)
        {
            var receiptId = receipt.ReceiptId;
            var cancelDate = receipt.CancelDate;
            var purchaseDate = receipt.PurchaseDate;
            var sku = receipt.Sku;
            var productType = receipt.ProductType;
            var deferredDate = receipt.DeferredDate;
            var deferredSku = receipt.DeferredSku;
            var termSku = receipt.TermSku;

            Debug.Log("[ExampleIAPManager] GetPurchaseUpdatesCallback found purchase update:" +
                      "\nReceipt id: " + receiptId +
                      "\nCancel date: " + cancelDate +
                      "\nPurchase date: " + purchaseDate +
                      "\nSKU: " + sku +
                      "\nProduct type: " + productType +
                      "\nDeferred date: " + deferredDate +
                      "\nDeferred SKU: " + deferredSku +
                      "\nTerm SKU: " + termSku);
        }
    }

    private void VerifyLicenseCallback(LicenseResponse response)
    {
        if (response == null)
        {
            Debug.Log("[ExampleIAPManager] VerifyLicenseCallback Failed to get license response");
            return;
        }

        var requestId = response.RequestId;
        var status = response.Status;

        Debug.Log("[ExampleIAPManager] VerifyLicenseCallback response:" +
                  "\nRequest id: " + requestId +
                  "\nStatus: " + status);

        var output = iapService.GetAppstoreSDKMode();

        Debug.Log("[ExampleIAPManager] GetAppstoreSDKMode:" +
                  "\nAppstore SDK Mode: " + output.AppstoreSDKMode);
    }
}
1 Like

Hi @bz_hg ,

Thanks for posting. We have messaged the appropriate team for further investigation and will let you know once we receive an update.

Hi @Emma, that’s great. Thank you for your help!

Hi @Emma, is there any update on this issue?

Hello @bz_hg ,

The support team will need your App Name/ID, your Customer ID (which can be found here) and debug device logs when GetPurchaseUpdates call is being invoked. Please private message me this information. Thanks.

Hi @bz_hg ,

Thank you for providing additional details. This function may not work properly when the accelerated renewal resubscribe feature is enabled. Could you try turning off the accelerated renewal feature and re-initiating the test?

Hi @Emma, thanks for getting back to me. I’ve made a new LAT with accelerated subscription timelines disabled. Unfortunately I’m still experiencing the same issues with GetPurchaseUpdates in this new LAT.

Could you share the recent logs/ timestamps from the recent testing? This will help us investigate the issue further. Thanks!

1 Like