Skip to main content
Question

Migrating from glassfy


I originally went with glassfy 2 years ago because their docs and setup for capacitor were simpler to understand.  Unfortunately, they’ve now gone defunct, so I need to migrate to RC.

 

 have two apps that I need to migrate. 

One app is very seasonal, so glassfy may not exist any more when the users finally open their app. 

The other, I had issues with android not sending the subscription into to glassfy anyway, and their support refused to respond.

 

My main question:

  • can I simply call syncPurchases once for all users the first time they run the updated app version that has RC integrated?

If I can simply call syncPurchases once on update, and each user who does this will automatically be given their subscription access, that would be great. 

 

Can anyone confirm this would work?  Or do I need to do something more complicated?

@MarcosC if you’re still offering a look-over, I’d love to take you up on it. How do I get you my account details?


Though things feel a bit disjointed and like you have to jump all around, and the code samples are really lacking in how to actually interpret the data, I managed to get both my apps converted over. 


@MarcosC if you’re still offering a look-over, I’d love to take you up on it. How do I get you my account details?

if you have a lot of receipts, definitely see if they will import.  It took hours and hours to do 12,000 receipts here, it seemed to rate limit to about 2 seconds per receipt.  I had a network failure at some point about 4,500 in.  I then amended my import script to take a --start-index param to resume.

This version has --start-index as an optional param: https://gist.github.com/foerster/9b2b93b571791d9293c78747f1c2cee7


@snapdown you rock -- thank you for sharing.

The export I got from Glassfy had slightly different columns, so it required a few edits, but the script worked well!

What sucks has nothing to do w/ the script, but the export Glassfy gave me -- I have custom user ids as well as anonymous users for when they purchase without login, and Glassfy unfortunately did not export the anonymous app_user_ids (the “Subscriber ID” they use in their system) and left them as blank.

Which meant a lot of data was skipped on import because blank ids are understandably not allowed.

I’m not sure what the best practice is here to be honest, or whether I was never supposed to have both anonymous and custom IDs at the same time. 😞


I have the same issue with the Glassfy export csv file.
The  app_user_id (e.g. “Subscriber ID”) field is empty.
I’m not sure how to handle the import in Revenuecat.


>if you’re still offering a look-over, I’d love to take you up on it. How do I get you my account details?

I’ve checked your app and at a first glance, all looks good. I see both android and apple apps configured, both with notifications configured and we are getting them and I can see production purchases.

 

> I’m not sure what the best practice is here to be honest, or whether I was never supposed to have both anonymous and custom IDs at the same time. 

You can create an anonymous ID for those users and import them. When those users open their app, they could restore purchases which should merge both accounts

 

If you want anything in particular you want me to review, send me a DM 


This is how we create anonymous IDs in our iOS SDK → https://github.com/RevenueCat/purchases-ios/blob/26eed6c80abf6e8bef69b7f7db75bdb07c725d24/Sources/Identity/IdentityManager.swift#L99-L101


> I’m not sure what the best practice is here to be honest, or whether I was never supposed to have both anonymous and custom IDs at the same time. 

You can create an anonymous ID for those users and import them. When those users open their app, they could restore purchases which should merge both accounts

 

Hi, what would be the difference between importing from Glassfy and then the user still needing to restore purchases, and the user just restoring their purchases?

I also need to migrate from Glassfy to RevenueCat and also got an export from Glassfy without any user id:s.. wondering if I should use that or syncPurchases..

What would be the best way to only run syncPurchases once? Saving the syncPurchases run in local storage? But then it would be run again if the user re-installs the app (or maybe that wouldn’t be a big issue?)..

Thanks!


> Hi, what would be the difference between importing from Glassfy and then the user still needing to restore purchases, and the user just restoring their purchases?

The only difference is that if that user never opens the app, you won’t have that data and that the user would need to restore (unless you force a sync purchase)

 

>What would be the best way to only run syncPurchases once? Saving the syncPurchases run in local storage? But then it would be run again if the user re-installs the app (or maybe that wouldn’t be a big issue?)..

As long as you don’t call it on every launch, it should be fine. You can also force a sync only if the user does not have a purchases


> I’m not sure what the best practice is here to be honest, or whether I was never supposed to have both anonymous and custom IDs at the same time. 

You can create an anonymous ID for those users and import them. When those users open their app, they could restore purchases which should merge both accounts

 

Hi, what would be the difference between importing from Glassfy and then the user still needing to restore purchases, and the user just restoring their purchases?

I also need to migrate from Glassfy to RevenueCat and also got an export from Glassfy without any user id:s.. wondering if I should use that or syncPurchases..

What would be the best way to only run syncPurchases once? Saving the syncPurchases run in local storage? But then it would be run again if the user re-installs the app (or maybe that wouldn’t be a big issue?)..

Thanks!

I don’t have real answers as I’m navigating this as well.

I load a didSyncPurchases flag from my storage, defaults to false.  If they did not sync yet, I call syncPurchases once per user in the app and then save the didSyncPurchases to storage locally so the next time it doesn’t sync again.


Thanks for the answers, I just tried syncPurchases (with the capacitor library)..

So I first call configure(), then syncPurchases() if I haven’t already done it in a previous app init, and then getCustomerInfo() to check if the user has an active subscription for a specific entitlement.

But I noticed that when I call syncPurchases() and then getCustomerInfo(), it doesn’t return that the entitlement is active, but it does so the next time I open the app and getCustomerInfo() is called..

Is it possible for the getCustomerInfo() to return that the entitlement is active directly after running syncPurchases()? Or do I need to do something else in between? I noticed that syncPurchases() should return a CustomerInfo object but it doesn’t for the capacitor library?


This is what I have.  I have people upgraded and using it, haven’t heard any issues -- I think it all worked seamlessly.

I have a customer info update listener -- maybe you need this?

 

      await Purchases.setLogLevel({ level: LOG_LEVEL.DEBUG }); // Enable to get debug logs
await Purchases.configure({
apiKey: this.getRevenueCatKey(),
});

const hasSynced = await this.storage.getPref('RC_PURCHASES_SYNCED');
if (!hasSynced) {
await Purchases.syncPurchases();
await this.storage.setPref('RC_PURCHASES_SYNCED', 'true');
console.log('RevenueCat purchases synced');
}

await this.getRcOfferings();

const ci = await Purchases.getCustomerInfo();
this.handleRcPermissions(ci.customerInfo);

await Purchases.addCustomerInfoUpdateListener((customerInfo) => {
// console.log('customerInfoUpdateListener', customerInfo);
this.handleRcPermissions(customerInfo);
});

 


This is what I have.  I have people upgraded and using it, haven’t heard any issues -- I think it all worked seamlessly.

I have a customer info update listener -- maybe you need this?

 

      await Purchases.setLogLevel({ level: LOG_LEVEL.DEBUG }); // Enable to get debug logs
await Purchases.configure({
apiKey: this.getRevenueCatKey(),
});

const hasSynced = await this.storage.getPref('RC_PURCHASES_SYNCED');
if (!hasSynced) {
await Purchases.syncPurchases();
await this.storage.setPref('RC_PURCHASES_SYNCED', 'true');
console.log('RevenueCat purchases synced');
}

await this.getRcOfferings();

const ci = await Purchases.getCustomerInfo();
this.handleRcPermissions(ci.customerInfo);

await Purchases.addCustomerInfoUpdateListener((customerInfo) => {
// console.log('customerInfoUpdateListener', customerInfo);
this.handleRcPermissions(customerInfo);
});

 

Thanks, that did it. I thought CustomerInfo would already be updated if waiting for the syncPurchases function to resolve, but it looks like it takes a little time after syncPurchases for CustomerInfo to be updated.


@trabdin great, glad it worked!