Solved

After Deleting a Transaction from StoreKit, why does RevenueCat still think the Transaction Exists?


Badge +3

I’m new to RevenueCat, read the setup docs, watched some YouTube videos, and I got everything working so far. But I ran into a problem after making a test purchase on a real device.

A- In viewDidLoad I run fetchCustomerInfo() to check if the user has subscribed or not. When the user hasn’t, the subscribe and restore buttons are shown. I press the subscribeButton and the transaction is successful. I stop the app, and when I restart, the buttons don’t show, which is correct.

B- In Xcode I go to Debug > StoreKit > ManageTransactions > I delete the Transaction, and it says No Transaction.

C- I stop the app, open the app back up, and the buttons don’t show, but they should show because there aren't any transactions. I check the flow in step 2 again and it still says No Transaction.

D- I go to the RevenueCat console, Projects > My Project > iOS > Customers > Active/Sandbox/Non-Subscription/Expired, there aren't any customers in either of them. There is no data whatsoever.

E- At the top of the page, I tried to toggle Sandbox data, but it’s grayed out, and I can't interact with it in any way. The little check box doesn’t do anything when I click it.

 

 

My Questions:

1- Using the fetchCustomerInfo() code, why does info.entitlements.all["myRevenueCatEntitlementID"]?.isActive still think there is an active subscription?

2- If there aren't any customers in the RevenueCat console, why does it think that there are, or better yet, why does it think this particular app has an active Subscription?

3- If there aren't any customers, how does it associate this app with the initial subscription that I made, which is no longer valid?

 

func fetchCustomerInfo() {

    Purchases.shared.getCustomerInfo { (info, error)in

        if let error = error as? RevenueCat.ErrorCode { return }

        guard let info = info else { return }

        let isActive = info.entitlements.all["myRevenueCatEntitlementID"]?.isActive ?? false

        if isActive { // after deleting a transaction in Xcode, this is always true even though there aren't any customers in the RevenueCat console

            hideSubscribeAndRestoreButtons()

        } else {

            showSubscribeAndRestoreButtons()

        }

    }

}

 

 

icon

Best answer by Michael Fogel 11 July 2023, 18:12

View original

10 replies

Badge +3

According to this answer by @jazmine, Data can take up to an hour to be viewable in Customer Lists.

 

I’m not sure why, but it took way longer than a hour for the transaction to show up in my Customer list. I just checked and the transaction is now there.

 

I haven't had a chance to delete the customer and check what happens on the iOS side. I’m assuming once I delete the customer from the RevenueCat console, my question should be resolved.

Btw, I can now interact with the Sandbox data check box at the top of the page.

Userlevel 4
Badge +6

Hey @lance-samaria ,

 

1- Using the fetchCustomerInfo() code, why does info.entitlements.all["myRevenueCatEntitlementID"]?.isActive still think there is an active subscription?

This returns true if we are seeing an active entitlement. I noticed that you are calling .all which might change what you are looking for here. I recommend using the following code to make sure that only the entitlement you are checking is the value that you are going for. 

if customerInfo.entitlements["myRevenueCatEntitlementID"]?.isActive == true {
// user has access to "your_entitlement_id"
}

2- If there aren't any customers in the RevenueCat console, why does it think that there are, or better yet, why does it think this particular app has an active Subscription?

This likely has to do with how we cache lists. In Customer Lists, the data will always cache and refresh every two hours. So you might have been seeing the old cache before the list was updated which was causing it to look like there was never a customer. 

3- If there aren't any customers, how does it associate this app with the initial subscription that I made, which is no longer valid?

This might also be due to the cache, the result that you get from the SDK when calling getCustomerInfo is more up-to-date, so when testing I would go by what the SDK is reporting before looking at your customer list because the dashboard metrics are cached. 

 

More information on how we cache can be found here: https://www.revenuecat.com/docs/caching#dashboard

Badge +3

@Michael Fogel thanks for the very informative answer!!! Cheers!!! :-)

Badge +3

@Michael Fogel I just though about something you said

In Customer Lists, the data will always cache and refresh every two hours. So you might have been seeing the old cache before the list was updated which was causing it to look like there was never a customer. 

 

Maybe I’m misunderstanding the 2 cache. Does this 2 hour delay apply for first time purchases and/or cancelled purchases in actual production? For example a user makes their first purchase, updates a previously cancelled purchase, or cancels a currently active purchase, wouldn’t everything in the parse function be nil (first ever purchase) or have stale info (updating/cancelling) because of the 2 hour cache refresh? On a side note I hooked up AppStore and PlayStore Notifications with RevCat.

Purchases.shared.purchase(package: aPackage) { (transaction, customerInfo, error, userCancelled) in

    guard let entitlementsInfo = customerInfo?.entitlements["myRevenueCatEntitlementID"] else { return }

         parse(entitlementsInfo)

} )

fun parse(entitlementsInfo: EntitlementInfo) {
    if entitlementsInfo.isActive == false { return }

let originalPurchaseDate = entitlementsInfo.originalPurchaseDate
    let latestPurchaseDate = entitlementsInfo.latestPurchaseDate
    let expirationDate = entitlementsInfo.expirationDate

let unsubscribedDate = entitlementsInfo.unsubscribeDetectedAt

  let currentDate = Date()

    // check the above constants against currentDate ...

}

 

Userlevel 4
Badge +6

Hey @lance-samaria 

 

The Cache issue mainly applies to the metrics seen on the dashboard. The purchase will go through the SDK as soon as it is triggered, you just might not be able to see it on the dashboard initially. 

Badge +3

I’m running into the same issue. I delete the transaction from storekit in xcode, and the transaction was deleted (I can resubscribe to it), but the SDK still keeps telling me that I am entitled even though I am not. For some reason it does not realize that the transaction was refunded or deleted. Any ideas?

Badge +3

@gshenar-75dd34 You should post your code.

 

Quick question, are you using .all as in all["myRevenueCatEntitlementID"]?.isActive? I posted this question some time ago but from what I remember @Michael Fogel answer is what helped me:

 

“I noticed that you are calling .all which might change what you are looking for here. I recommend using the following code to make sure that only the entitlement you are checking is the value that you are going for.”

if customerInfo.entitlements["myRevenueCatEntitlementID"]?.isActive == true {
// user has access to "your_entitlement_id"
}
Badge +3

My entitlements object doesn’t have the entitlement directly on it. It has two subobjects `all` and `active`

After I buy the `addCustomerInfoUpdateListener` gets triggered and the entitlements get updated to look like this:

 "entitlements": {
    "active": {
      "Pro Access Individual": "[Object]"
    },
    "all": {
      "Pro Access Individual": "[Object]"
    }
  }


After I delete the transaction, or refund it, no listeners are called, and even though the transaction no longer exists, and the device knows this (I can buy again), RevenueCat still shows that I am entitled to the product

Badge +3

@gshenar-75dd34 I had another issue that I ran into here. I got very good help from RevenueCat. What I found out was I needed to setup my files along these lines:

 

Step 1.

Purchases.configure(withAPIKey: yourRevenueCatAPIKey, appUserID: userId) // this goes 1st. You can also use RevenueCat’s ‘async-await’ if you prefer.

Purchases.logLevel = .debug // this goes 2nd

Purchases.shared.delegate = self // assuming you’re using this, this goes 3rd

 

Step 2.

You definitely need to call Purchases.logLevel = .debug because it will print out the RevenueCat log files. Once you make your RevenueCat calls, you need to look at all of the debug files. Make sure that Purchases.configure isn't getting called twice. If does you’ll see it in the logs and that can cause an issue. That’s what was causing my other issue. The log file was:

ℹ️ Purchases instance already set. Did you mean to configure two Purchases objects? // THIS IS NOT GOOD

 

Step 3.

After you set it up like above and you are 100% sure that Purchases.configure isn't getting called more than once, you need to start fresh. What I did was (1) use a real device, (2) used a different device from the one that was causing the issues, (3) freshly installed a new version of the app, (4) created a brand new Sandbox-Test-ID in AppStore Connect, (5) on that new device with the fresh install and the new sandbox-test-id, make a purchase, and read the log files.

 

As for your code above, I could be wrong but you're still calling all:

 

"all": { <--- this is still .all

    "Pro Access Individual": "[Object]"    

}

 

If you read the accepted answer above from @Michael Fogel he said don’t use .all

 

 

 

Badge +3

@gshenar-75dd34 are you using iOS or Android? It looks like iOS. To get an individual entitlement read my answer here: 

 

Reply