Solved

info.entitlements is nil even though user has a subscription

  • 24 January 2022
  • 16 replies
  • 915 views

Badge +4

Hi, I am new to programming and RevenueCat.  

My IOS app has a subscription purchase option and the user was able to make a purchase, which I think shows up in Revenue Cat but I am not 100% sure as I don’t know how to directly relate the RevenueCat app ID to the user.  Fortunately, at the present time I do not have a large number of users so I believe I have identified the user correctly.

 

However, when the user logs and the app queries  Purchases.shared.purchaserInfo, the entitlements comes back as nil.  

 

I have searched the documentation and can’t understand what I have done wrong and am at a loss of where to start troubleshooting.  Any help would be appreciated.

 

Thanks

icon

Best answer by KevBro 26 January 2022, 18:20

View original

16 replies

Badge +4

I have since tried the restore purchases feature and now the users app ID does not show up in the customers list in RevenueCat.  

However, when the user checks their purchases on their phone they are told they still have a valid subscription.

 

Now I am more confused… :(

Userlevel 5
Badge +10

Hey @KevBro 

Whenever a customer claims to have purchased a subscription and it does not show up in the purchaserInfo object, the recommended next steps is for them to trigger the restore purchases method. Note that the Customer List feature does have an hour cache, so it does take some time for updates to populate. Are you able to search for the app user ID in the ‘Find Customer’ card

Badge +4

Thanks Tina, I am certain the user has purchased the app.   They also previously showed up in the customer list as being an active subscriber.  When the app told them their subscription had expired they checked the status on their iPhone and was told the subscription was in fact in place.  They have attempted the restore purchases method to no affect.  They have also tried deleting the app and re-downloading it.

Userlevel 5
Badge +10

Hi @KevBro 

If you remember the app user ID, are you able to go to their individual customer page and check out what their entitlement says? 

Badge +4

Hi Tina, I can see the product they purchased and the expiry.  I even granted the user a lifetime entitlement to all access and they are still told their subscription has expired.    

Oddly, when I first open up the project the user ID shows as an active subscribe.  But when I go to customers they only show up in the sand box as having paid 0$.

 

I have another question for you, how do you relate the RevenueCat app ID to the user’s name and/or email?  Is that possible?

 

Thanks again for your help!

 

Badge +4

Also Tina, when I first open the project, the app ID for the user is different from the app ID for the user found in the sandbox I mention in my previous post.  However, they are the same user based on the log in history.  

 

I even assigned lifetime access to this user Id and it doesn’t help.

 

Is it possible the different user ID was created when I tried assigning a specific user ID to the user to see if that would solve the problem?

Userlevel 5
Badge +10

Hey @KevBro 

The app user ID needs to be the same within the app and what’s in the dashboard in order to pull the subscription status we have stored against the user. Does your app have a login system where customers create an account and get assigned a custom app user ID? If so, can you walk me through your app process. E.g: Customer first downloads the app and launches it, signs up for an account, you identify them with a custom app user ID, they purchase a product. Or is this all being done through RCAnonymousIDs? 

Does the app user ID have both sandbox and production purchases? Sandbox purchases shouldn’t show up for the customer or be allowed to purchase products in sandbox unless they’re added to a list of test users or currently on a TestFlight version of the app. If you want to share the app user ID(s), feel free to send me a private message so I can take a look. 

To provide some context around the restoring method. When this method is triggered, the receipt on the device will get sent to RevenueCat to validate and refresh the receipt. I’ve seen in cases where end customers would actually be logged in a different Apple ID they used to purchase the subscription. In production the receipt is tied directly to the Apple ID and in sandbox the receipt is generated against the sandbox testing account once a purchase is made. 

I have another question for you, how do you relate the RevenueCat app ID to the user’s name and/or email?  Is that possible?

Yes, this is possible through subscriber attributes! Docs for reference: https://docs.revenuecat.com/docs/subscriber-attributes Once an email attribute is set for an app user ID, you can search for them by email instead. This is also available through the REST API: https://docs.revenuecat.com/reference/update-subscriber-attributes 

 

 

Badge +4

Hi Tina the code uses RCAnonymousIDs.  Following is the code:

 

When the app opens:

Purchases.logLevel = .debug

Purchases.configure(withAPIKey: "the public key is entered here")

 

When the user purchases the subscription after signing up:

static func purchase(productID:String?, successfulPurchase:@escaping () -> Void) {

        

        guard productID != nil else {

            return

        }

        

        

        //get the SKProduct

        Purchases.shared.products([productID!]) { (products) in //1

            

            if !products.isEmpty {//2

                let skProduct = products[0]

                

                //Perform purchase

                

                Purchases.shared.purchaseProduct(skProduct) { (transaction, purchaseInfo, error, userCancelled) in //3

                    

                    //if successful purchase

                    

                    if error == nil && !userCancelled {//4

                        successfulPurchase()

                        

                    }//4

                }//3

            }//2

        }//1

 

When the user logs in:

//check if the user has an active subscription.

       

        Purchases.shared.purchaserInfo { (info, error) in //1

            

            if error != nil {

                return

            }

            //check the info parameter for entitlements

            

 

            if info?.entitlements["challengeraccess"]?.isActive == true  || info?.entitlements["rc_promo_challengeraccess_lifetime"]?.isActive == true {//2

“Once confirmed I set certain properties that give the user access”

}

}

 

Question2 - The user has test privileges.  I have sent you a separate message with the app Ids.

Thanks for the information on subscriber information, I will review.

 

 

 

Userlevel 5
Badge +10

Hey @KevBro 

Thank you for sharing the app user IDs. A few things to note:

  • Since you do have a login system, it’s recommended to also associate them with a custom app user ID. Docs for reference: https://docs.revenuecat.com/docs/user-ids#provide-app-user-id-after-configuration $RCAnonymousIDs are technically anonymous and as the app grows, it would be difficult to associate customers to their anonymous ID. I mention this because I noticed that both the production and sandbox app user IDs you shared do not have non-anonymous aliases associated with them. We also have docs on using Firebase to help keep app user IDs in sync that’s worth checking out. 
  • When you’re assigning the Promotional Entitlements to the two app user IDs you reported, it doesn’t look like it’s being associated to the correct app user ID within the app. For example, I took a look at the sandbox user you sent over and the Promotional Entitlement was granted on January 25, 2022 UTC, but the last time that app user ID was seen using the app was January 24, 2022 UTC. As for the production user, the Promotional Entitlement was granted on January 24, 2022 at 5:04PM UTC and last opened the app on January 24th, 2022 at 12:58am UTC. 
  • When a customer deletes the app and reinstalls it, this ends up clearing the cache and they are assigned a new RCAnonymousID. When triggering a restore, this will alias the two anonymous IDs together as long as they share the same underlying receipt. 
  • Is the user a tester on TestFlight?

How I would troubleshoot:

  • You should make a purchase in your development mode (sandbox) with a fresh sandbox account and place some print statements inside your code. You can check if certain code blocks are executed by placing print statements inside of them and printing the PurchaserInfo object.
  • While testing, store the debug logs in a text file for your own reference. You can find the app user ID within the debug logs and find that associated user in RevenueCat. Check if the PurchaserInfo and RevenueCat customer profile information lines up. Note that sandbox transactions renewal times are accelerated. For example, a monthly subscription will renew every 5 minutes. 
  • Since you mentioned the user has testing privileges. You should ask for their reproduction step and what actions they took in the app. Are you able to reproduce their issue? 
Badge +4

Thank you Tina, I will implement the changes you recommend and then try and restore purchases to see if the app IDs are aligned.

 

This may take me a couple of days due to some other issues I am working on.

 

Thanks again and I will get back to you.

Badge +4

Hi Tina, I was able to find some time to implement the changes last night.  I think it is now working, at least in sandbox mode.  

 

To  prevent a user from creating multiple user IDs and then providing these to friends I set the restore behaviour in project settings to Block Restores.

 

However, this then prevents me from creating multiple users to test the app function.  Am I misunderstanding the use of ‘Block Restores’.  If so, what setting do I invoke to prevent a user from creating multiple sign iDs using the same app ID and then passing them onto friends?

 

Thanks

Userlevel 5
Badge +10

Hey @KevBro 

The ‘block restore’ option is documented here: https://docs.revenuecat.com/docs/restoring-purchases#block-restores Can you let me know your reproduction steps? 

It sounds like you implemented the logIn method to the app. When customers trigger that button and input their account credentials, your backend should map their account to the app user ID associated and pass in that app user ID into logIn

Badge +4

Hi Tina, I have gone through the Block restore documentation and that is why I implemented it.  But I am not sure how I can create multiple users to test the app when this is in place.  

I am not sure what you mean by reproduction steps.  

Yes the user logs in and a Firebase userId is obtained.  This userId is then set as the RevenueCat using the logIn method (I actually use identify which is now deprecated with logIn).

 

Userlevel 5
Badge +10

Hey @KevBro 

I am not sure what you mean by reproduction steps.  

Some steps I was wondering about is if you’re creating these accounts and passing the credentials to your friends? You should still be able to create multiple users with the ‘Block’ restore option on. Do you have a “log out” button on the app that calls the logOut / reset (older SDKS) method? Note that with block restores on, your support team (or yourself) should be prepared to guide customers through an account recovery process if needed. The transfer purchases is the best option here to reduce future customer issue. Testers will also need to create new sandbox accounts to go through the entire payment flow as a new user. 

Badge +4

Hi Tina, thanks for all the time you are spending on this!

 

Yes I have a logout button on the app that calls the logOut method.  

 

If I understand you correctly I should be able to:

  1. Create a userID in Firebase and then sign up for a subscription in the sandbox using my Apple App Store testing ID.  The logIn method then assigns the Firebase userId to the RevenueCat userId.
  2. Create a second userId in Firebase and sign up for a subscription in the sandbox using the same Apple App Store testing ID  

However, when I try and sign up for a subscription using the second userId with the same Apple App Store testing ID I am told I already have a subscription, even though that subscription is for the first userId.

 

You mention that the Transfer Purchases method is what I should be using. The documentation states:

“ if UserA buys a subscription, then UserB logs into your app on the same device and restores transactions, UserB would now have access to the subscription and it would be revoked from UserA.”  

 

So my question is, what prevents two users from using the app from the same device and only paying for one subscription?

 

Thanks

 

 

Userlevel 5
Badge +10

Hey @KevBro 

I’m happy to help! I understand your scenario now and I hope this response clears up the confusion and elaborates how receipts work. 

In #2:

sign up for a subscription in the sandbox using the same Apple App Store testing ID  

This is the issue here. You’ll need to sign up for a subscription using a different testing account. If you’re testing in TestFlight, this means a new Apple ID. If you’re testing in sandbox, this means testing in a different sandbox account. When testing purchasing, I recommend going the sandbox route. It is much much easier creating a new sandbox account versus creating a new Apple ID. 

However, when I try and sign up for a subscription using the second userId with the same Apple App Store testing ID I am told I already have a subscription, even though that subscription is for the first userId.

You’re correct that the subscription belongs to the first app user ID. However, the underlying receipt is the same. In sandbox when a user purchases a product, this will create an Apple receipt and is saved directly on the Apple sandbox account. So when you’re attempting to sign up for a subscription on the second app user ID (with the same Apple sandbox account logged in as your first app user ID), RevenueCat notices that we already have the same receipt saved for another user. As a result, the ‘Block’ restore option will return to you that error. 

 

So my question is, what prevents two users from using the app from the same device and only paying for one subscription?

The ‘transfer’ option will pull the Apple receipt from one user ID to another after the second user restores.

For example, the following would happen:

1. User 1 makes a purchase and unlocks an entitlement

2. User 2 restores using the same Apple ID / Apple sandbox account, unlocks the entitlement and claims the receipt 

3. User 1 has their entitlement removed, as the receipt is no longer associated with them

Essentially the subscription is only tied to 1 app user ID, so only 1 person has access at a time. I wouldn’t stress too much about 2 people sharing the same device. Majority of end customers are not malicious and would rather not share their Apple ID credentials or device with another person, especially if it means their own account will lose access to a subscription they paid for.

The biggest issue is if 2 different app user IDs are sharing the same subscription on different devices. This is actually the reason why RevenueCat revamped our restoring behavior to include ‘Transfer’ and ‘Block’, which prevents this from happening.

If this transferring option is not ideal to you, you can stick with the ‘Block’ restore option. However, you should be prepared for support cases when a customer forgets their account credentials. 

Reply