Skip to main content

Hello!

We are planning to use the "block restore" configuration on Revenue Cat for our iOS application and wanted to make sure (1) it's the right option for us given our business logic and (2) if we use it, can/should we avoid RC anonymous IDs altogether since we require a user to have an account and thus internal UUID (app user id) in our application before subscribing? We want to have a single source of truth for identifying a user, and to avoid having lots of anonymous IDs and the network calls to RC API that would unnecessarily create them (if possible).

1. Our application requires users to create an account in order to subscribe, and we want to prevent the sharing of a subscription linked to a single Apple ID across multiple user accounts / internal app_user_ids that live in our application database. This makes it seem like the "block restore" option is the way for us to go since that should prevent both (a) the sharing of an Apple ID’s subscription from User A to User B (where both users are associated with the same Apple ID), as well as (b) the revocation of that subscription from User A in case User B tries to subscribe while logged into the same Apple iD – this is what the "transfer purchases" configuration would enable, which could open a can of worms for us.

We were somewhat hesitant to run with the "block restore" approach given the "Use with caution 🚧" disclaimer associated with it in the documentation: https://docs.revenuecat.com/docs/restoring-purchases#block-restores – but is it still the right option for us given the above? We understand we will need to have special flows / messaging for users who are blocked in order to help them get access to their subscription entitlements.

If I understand correctly, when someone installs and opens our application for the first time, the Revenue Cat SDK will create an anonymous ID for that person so long as we configure/initialize RC SDK on app launch, i.e. before a user signs up. If the person then creates an account on our application as User A, we will pass new User A's UUID (let's say RevCatA) to the Revenue Cat SDK through the login() method described here: https://docs.revenuecat.com/docs/user-ids#login-behavior 

This will merge our internal app_user_id “RevCatA” with the anonymous RC ID for the same subscriber record synced via Revenue Cat API, making the anonymous ID an alias.

Scenario 1: User A never subscribes, but we create a RC subscriber record with a RC anonymous ID and our provided RecCatA app_user_id in RevenueCat’s database via the RC SDK. User A signs out, and User B creates a new account on our system while logged into the same Apple ID.

Will Revenue Cat block a subscription attempt by a User B associated with the same Apple ID because User A exists in Revenue Cat’s DB associated with the same Apple ID, even though User A never subscribed?

Scenario 2: User A subscribes, creating a record of their subscription on Apple and Revenue Cat. They delete their account through our application while their subscription is active, which triggers a DELETE subscriber call from our API to Revenue Cat, deleting the record of the subscriber and all subscription history on RC. User B then creates a new account in our system while logged into the same Apple ID as User A – because there is no record of the active Apple ID subscription on Revenue Cat, but there is a record of the active subscription on Apple, Revenue Cat will allow User B to use the active subscription recognized by Apple.

Is this correct?

Scenario 3: User A subscribes, creating a record of their subscription on Apple and Revenue Cat. They do not delete their account. User B signs up while logged into the same Apple ID as User A and attempts to subscribe. Revenue Cat blocks the subscription request because a record of the subscription exists for User A with the same Apple iD in the RC database.

Is this correct?

2) Given that we will always need a user account and internal app_user_id in our database for the user to subscribe, is there any reason to use RC’s anonymous IDs? While there is a state in our application with no internal user ID available (no user is logged in), there’s no possibility that the person can subscribe until they create a user account in our system, and thus not a clear need to have an anonymous ID / record on Revenue Cat for the anonymous user.

Should we instead defer the instantiation of the RC SDK until after the user logs in and we have their internal app_user_id? This would avoid the creation of the anonymous ID, but is it recommended even if we don’t want to create a RC subscriber until a user can actually subscribe according to our application logic? Based on this, it seems we should instantiate immediately (generate anonymous ID ) and logIn() the user when we have their internal app_user_id, but that doesn’t let us avoid the creation of the RC anonymous ID.

Is there a proper way to create a subscriber on RC with the app_user_id we provide while preventing RC from generating an anonymous ID before our user authenticates?

Additionally, the logout() method makes another network request to create a new anonymous user, which again is redundant in our case. We don’t see why we would need a new anonymous ID generated upon logout() – is it possible/advisable to log the user out without creating an anonymous user?

 

Thank you!

For your second question (if we use it, can/should we avoid RC anonymous IDs altogether since we require a user to have an account and thus internal UUID (app user id) in our application before subscribing?) I posted a response to a similar question about avoiding anonymous user IDs. It’s not really possible to avoid anonymous IDs and we recommend not to try to avoid them actually.

Assuming you use the block restores setting in RevenueCat:

 

Will Revenue Cat block a subscription attempt by a User B associated with the same Apple ID because User A exists in Revenue Cat’s DB associated with the same Apple ID, even though User A never subscribed?

No, User B should be able to subscribe with the same Apple ID since User A never subscribed. Really what’s going on here is User A has no purchases associated with them, so when User B makes a purchase, it’s considered a “new” purchase in RevenueCat and is attached to User B. RevenueCat doesn’t really have a concept of attaching an Apple ID to a user, just purchases.

 

Scenario 2: User A subscribes, creating a record of their subscription on Apple and Revenue Cat. They delete their account through our application while their subscription is active, which triggers a DELETE subscriber call from our API to Revenue Cat, deleting the record of the subscriber and all subscription history on RC. User B then creates a new account in our system while logged into the same Apple ID as User A – because there is no record of the active Apple ID subscription on Revenue Cat, but there is a record of the active subscription on Apple, Revenue Cat will allow User B to use the active subscription recognized by Apple.

Correct! Deleting a user completely deletes all of their data from RevenueCat, including past purchases they made. This allows the same purchase to then be restored by a different user.

 

Scenario 3: User A subscribes, creating a record of their subscription on Apple and Revenue Cat. They do not delete their account. User B signs up while logged into the same Apple ID as User A and attempts to subscribe. Revenue Cat blocks the subscription request because a record of the subscription exists for User A with the same Apple iD in the RC database.

Correct! What’s going on here is RevenueCat will not attach the subscription to User B because it’s already attached to User A.

 

Is there a proper way to create a subscriber on RC with the app_user_id we provide while preventing RC from generating an anonymous ID before our user authenticates?

You can delay configuring the SDK, but it’s much better to configure as soon as the app launches. That’s because there’s a lot of initial setup that the SDK needs to do in order to start processing purchases, and Apple recommends to do that when the app launches (more info in the post I linked above.) Besides, delaying configure won’t prevent an anonymous user ID from being created when you call logOut. As long as you make sure to identify the user before they make a purchase, the anonymous user IDs shouldn’t cause issues with the block restores setting.

 

Additionally, the logout() method makes another network request to create a new anonymous user, which again is redundant in our case. We don’t see why we would need a new anonymous ID generated upon logout() – is it possible/advisable to log the user out without creating an anonymous user?

I answered this question in the post linked above but the short answer is no, logOut always creates an anonymous user ID. However, RevenueCat has new rules to limit the number of anonymous aliases which you can find in our guide on identifying users.


@sharif thank you for your quick and detailed response! You answered our questions so hopefully this helps someone else out down the road.


@sharif , we are attempting to implement the same as @po_  in our product. Your answers above are very helpful.

We are writing our App in Flutter using the Revenue Cat Flutter Package. Our Restore Behavior = Block Restores.

Scenario 3 is where I have a question. 

A customer creates User_A on our App then subscribes to Package_A. This sends a webhook call to our server passing app_user_id = User_A. Our server marks the user as having the subscription.

The same customer (same AppleID or GoogleAccount) then logs out of our App and creates User_B on our App. As a result of this we call Purchases.logIn(User_B). They then attempt to subscribe to Package_B. When we call Purchases.purchasePackage( Package_B) we are expecting it to fail, but it works and subscribes to Package_B. 

Questions:

  1. Is the purchase of Package_B working only because we’re testing in the sandbox, or will it work in production. 
  2. If it fails in production, what error will we receive at the client? I’m expecting that we’ll get a PlatformException back after calling Purchases.purchasePackage(Package_B).
  3. Is there any way at the client App level prior to calling purchasePackage() that we can tell that User_B should not be allowed to subscribe? I have tried the following code which works except for the rare occasions when originalAppUserId = $RCAnonymousID. 
    var currentPurchaserInfo = await Purchases.getPurchaserInfo();

if (currentPurchaserInfo?.originalAppUserId?.isNotEmpty == true &&
currentPurchaserInfo.originalAppUserId != userId) {
throw PlatformException(
code: 'ONLY_ONE_ACCOUNT_PER_CUSTOMER',
message:
"Cannot subscribe with a different user account...",
);
}
  1. The only accounts we have where originalAppUserId = $RCAnonymousID are older accounts from before setting  Restore Behavior = Block Restores. However, I’m reading from your response above that there could still be cases where originalAppUserId = $RCAnonymousID. Correct?

 

Thanks for your assistance,

KeithB

 

 

 


Reply