Extending on this ============================================
My app also let’s people create an account. So...
The docs say:
The restorePurchases
method should not be triggered programmatically, since it may cause OS level sign-in prompts to appear, and should only be called from some user interaction (e.g. tapping a "Restore" button.) If you are trying to restore a purchase programmatically, use syncPurchases
instead.
So let’s consider two more scenarios:
Scenario 1 WITHOUT .login():
- A user downloads my app.
- RC creates an Anonymous User ID
- Then the user subscribes, and creates an account.
- I do NOT call the RevenueCat .login() method.
Then the user gets a new phone:
- The user signs into my app.
- RC generates a new anonymous ID.
- I do NOT call the RevenueCat .login() method.
- I call
syncPurchases
because the docs say I shouldn’t call restorePurchases
- Then I call getCustomerInfo()
My questions are:
- Will the active subscription from the first device be connected to the second device after calling
syncPurchases
? It doesn’t appear so. - What value will be in the
originalAppUserId
field of the CustomerInfo
object? The anonymous ID from the first device or the second device? - What values will be in the both the
app_user_id
and original_app_user_id
fields of future webhook events?
Scenario 2 WITH .login():
- A user downloads my app.
- RC creates an Anonymous User ID
- Then the user subscribes, and creates an account.
- I DO call the RevenueCat .login() method.
Then the user gets a new phone:
- The user signs into my app.
- RC generates a new anonymous ID
- I DO call the RevenueCat .login() method.
- I call
syncPurchases
because the docs say I shouldn’t call restorePurchases
- Then I call getCustomerInfo()
My questions are:
- Will the active subscription from the first device be connected to the second device after calling
syncPurchases
? It think so because I called .login() , but I’m not sure. - What value will be in the
originalAppUserId
field of the CustomerInfo
object? The anonymous ID from the first device or the second device? - What values will be in the both the
app_user_id
and original_app_user_id
fields of future webhook events?
I’m sorry that this has become such a long question, but the documentation is a bit light on these concepts.
Thank you for clarifying!
To help whoever answers this question at RevenueCat, I’ve explained what I “THINK” happens. Hopefully this saves you time.
Can you simply confirm or deny my assumptions and explain where I’m incorrect if I am?
Initial Scenario: Anonymous User Calling restorePurchases
:
originalAppUserId
in CustomerInfo
object = original anonymous ID. - Future Webhook events:
app_user_id
= new anonymous ID, original_app_user_id
= original anonymous ID.
- Same answers if trial was initiated and cancelled.
Account Scenario 1 (WITHOUT .login()
) and syncPurchases
:
- Subscription NOT connected to new device.
originalAppUserId
in CustomerInfo
object = new anonymous ID. - Future Webhook events:
app_user_id
= new anonymous ID, original_app_user_id
= new anonymous ID.
Account Scenario 2 (WITH .login()
) and syncPurchases
:
- Subscription IS connected to new device.
originalAppUserId
in CustomerInfo
object = original anonymous ID. - Future Webhook events:
app_user_id
= logged in user ID, original_app_user_id
= original anonymous ID.
Finally, for Account Scenario 2, is it necessary to call syncPurchases
after the user gets a new phone? Or will calling .login()
be enough to link the accounts and subscriptions?
Thanks,
Chris
Hi @Chris Thornham,
Thank you for the very detailed post! Let’s see if I can answer everything in one reply.
First of all, I want to put some definitions out here. `original_app_user_id` is the first app user id where we saw this specific user. If you don’t do any logIn or logOut it’ll be the same id as long as the user doesn’t delete the app. If they delete the app and re-install it again, a new anonymous id will be assigned and we’ll only know about their previous id if they do a restorePurchases or syncPurchases.
When that happens, we merge the users and `original_app_user_id` will be the first one and `app_user_id` the latest one.
The behavior of how the subscription works between original or latest user depends on your configuration, you can find all the information here including some examples that’ll help clarify some of your questions.
If you have a way to identify your users, then syncPurchases or restorePurchases is not needed, as soon as we identify the user we’ll provide them with the right access. You can find more information and things to consider here.
One thing to consider is when do you provide the custom app user id (or the logIn). If you do it after the configuration of the SDK, an anonymous alias will be provided. Once you do the logIn we’ll merge all the ids and aliases into a single customer.
Let me know if you have questions!
@joan-cardona
Thanks for taking the time to get back to me.
You’ve answered most of my questions, but I still need clarification on one scenario. I’ve read through the links you provided many times, and the answer is still not clear.
So you know, the reason I’m so concerned about the app_user_id
and original_app_user_id
values in the webhook events is because I’m building a mobile/web platform that uses my backend as a single source of truth for access privileges.
I need to do this because I use Stripe payments and webhooks to manage the web app, and RC payments and webhooks to manage the mobile app. Therefore, I need a way to link both accounts on my backend.
You have confirmed the following:
When an anonymous user deletes and reinstalls the app OR gets a new phone, calling restorePurchases
means:
- When I call
getCustomerInfo
that the originalAppUserId
value in the CustomerInfo
object = original anonymous ID from the first device. - Future Webhook events:
app_user_id
= new anonymous ID, original_app_user_id
= original anonymous ID.
- Same answers if trial was initiated and cancelled.
Next:
I’m going to avoid the second scenario where I do NOT call .login()
. So let’s take that off the table.
Finally: I still need to know what happens when an identified user who has an active subscription deletes and reinstalls the app OR gets a new phone and then Signs In to my app.
Here’s the flow:
- The user (who has an active subscription) opens my app and I configure RC.
- RC generates a new anonymous ID for the new device.
- The user signs into my app.
- I call the RevenueCat
.login()
method and pass it the Custom ID from my auth system. - I do NOT call
syncPurchases
because you said that is unnecessary.
Can you confirm:
- The subscription from the old device Will Be connected to new device.
- When I call
getCustomerInfo
that the originalAppUserId
value in the CustomerInfo
object = original anonymous ID from the first device. - Future Webhook events will contain:
app_user_id
= logged in Custom User ID, original_app_user_id
= original anonymous ID from the first device.
Thank you,
Chris
Hi Chris,
If you call logIn() from an anonymous user, they will be merged into a single user. When we merge a user, there will be only one App User ID within the original_app_user_id field in the webhook. You can still find the anonymous id in the field aliases. Find how they work here. This means that both the original and the app user id will be the same custom id - not the anonymous. We’ve recently expanded how logIn works when coming from an anonymous id with the table that you can find here.
Best,
@joan-cardona
Thanks for explaining things. I really appreciate it.
That said, I wanted to share a couple of thoughts about the docs because they either show the opposite of what you just said, or could benefit from some additional clarity.
Here are three examples:
1)
Unclear Sample Webhooks.
You said:
This means that both the original and the app user id will be the same custom id - not the anonymous.
But the sample webhooks on your website would suggest otherwise. The “Initial Purchase” event seen here shows a custom App User Id in app_user_id
while the original_app_user_id
value is the original anonymous value also shown in the alias field.
2)
Ambiguity in original_app_user_id
The docs say:
When a merge of customers occurs, there will be only one App User ID within the original_app_user_id
field.
But it’s not clear which App User ID will be in the original_app_user_id
field. Is it the original anonymous ID? The custom ID? How could I know from reading that sentence?
3)
Missing Details in the TableThe table you referenced doesn’t explain what values show up in the app_user_id
or original_app_user_id
fields for webhooks or the CustomerInfo
object. There’s no way to know what to expect from the table alone.
Just to clarify—I’m not trying to be difficult. I read the docs thoroughly before reaching out, and I’ve worked with plenty of SDKs before. This isn’t the first time I’ve done something like this, but I found these areas pretty confusing.
Maybe it’s just me, but I think adding a little more detail to these areas might really help.
Thanks again for your help,
Chris
Hi,
I've talked to the team and we did some test cases because as you mentioned it is not clear at all what those webhook fields mean. Also there are many edge cases that makes it work differently and we've also changed that behavior a couple of times already. We arrived to a definition for the 3 identity fields in webhooks that I think can clarify a lot of things:
original_app_user_id
: This is the first id we've ever seen from this specific customer. app_user_id
: This is the latest id we have from an specific customer. alisases
: It's an array containing all the known ids from an specific customer.
What does it mean in your case? As you correctly pointed out, original_app_user_id
will be the anonymous id first created and app_user_id
will be the id you've provided on logIn
.
The webhook examples are also not valid since aliases
don't contain all the ids, we are going to fix it. We are also going to change the text in the documentation because it's not clear.
Sorry for the confusion and for the error on my answer and please let me know if this clarifies it or if you have questions.
@joan-cardona
There's another important omission in the documentation.
Consider this scenario:
- An anonymous customer subscribes on Device #1. RevenueCat assigns an anonymous ID to that device—let’s call it RCAnonIdOne.
- The customer gets a new device, and RevenueCat assigns a new anonymous ID—let’s call it RCAnonIdTwo.
- The customer mistakenly subscribes again on the new device.
Here’s the key issue:
- Before they complete the purchase, the
CustomerInfo
object reports original_app_user_id
as RCAnonIdOne. - After they complete the purchase (when
purchasePackage()
is called from the SDK), the CustomerInfo
object updates, and original_app_user_id
now becomes RCAnonIdTwo.
This means that calling purchasePackage()
automatically syncs purchases with the App Stores and updates the CustomerInfo
object, even if you do not explicitly call .restorePurchases()
, .syncPurchases()
, or .login()
.
I’ve carefully reviewed the documentation and couldn’t find this behavior explained anywhere. I just spent five hours debugging errors caused by this, and I believe it should be documented.
Hi @Chris Thornham,
When you say “The customer mistakenly subscribes again on the new device.” do you mean it goes through purchasing but then it acts as a restore since they already own it, right?
We do update the `CustomerInfo` once we know the purchase history of a customer, which as you correctly pointed out it happens as well on purchase. I’ll make sure to note it and add it somewhere in the docs.
Thank you!