RevenueCat

Build With RevenueCat

Build a customized mobile subscription business with RevenueCat. We do the heavy lifting of normalizing subscribers from any source and maintain a single source of truth for subscription status, so you can get back to building your app.

RevenueCat is a powerful, secure, reliable, and free to use in-app purchase server with global support. All you need to get started is an API key.

Ask A Question

Questions

2
ANSWERED

purchaserInfo received in viewDidLoad is not the latest purchaserInfo (Mac Catalyst)

Starting a new question to help clarify the issue in my testing (Mac Catalyst): On the first load of the app after the subscription has expired, I am still getting isActive = true for the app's only entitlement. If I then load the app again, it correctly shows isActive = false, but on the first load of the app after expiration, isActive still shows as true. Similarly, on the first load of the app after an auto-renewal of a subscription, the expiration date on the purchaserInfo received in viewDidLoad still shows as the expiration date from the initial purchase. Only on the next load of the app does it correctly show the updated expiration date for the renewal period. Each time I run the app, the 'purchaserInfo' that is loaded is the previous purchaserInfo as opposed to the most updated one, so it shows as still active when it has expired, or it shows a past expiration date when the expiration date was recently updated due to renewal. Are you seeing the same in your testing of Mac Catalyst apps? Any help would be greatly appreciated! // Get the latest purchaserInfo to see if we have a full version user or not Purchases.shared.purchaserInfo { (purchaserInfo, error) in if let e = error { print(e.localizedDescription) } if let purchaserInfo = purchaserInfo { if purchaserInfo.entitlements["FullVersion"]?.isActive == true { let dateFormatter = DateFormatter() dateFormatter.dateStyle = .full dateFormatter.timeStyle = .long if let purchaseDate = purchaserInfo.purchaseDate(forEntitlement: "FullVersion") { print("Purchase Date: \(dateFormatter.string(from: purchaseDate))") } if let expirationDate = purchaserInfo.expirationDate(forEntitlement: "FullVersion") { print("Expiration Date: \(dateFormatter.string(from: expirationDate))") } } } }

Posted by Ran 5 months ago

1
ANSWERED

My offerings hierarchy and some notes

I'm working on a Mac App Store app, available as a Standard and Pro subscription, with a monthly or yearly renewal interval, with 2-week trials. Here's what I have for my new offerings hierarchy: PRODUCTS (which map directly to the MAS product identifiers) com.mycompany.myapp.pro_1month com.mycompany.myapp.pro_1year com.mycompany.myapp.standard_1month com.mycompany.myapp.standard_1year OFFERINGS default 1. pro_monthly = Professional monthly package = com.mycompany.myapp.pro_1month 2. pro_yearly = Professional yearly package = com.mycompany.myapp.pro_1year 3. standard_monthly = Standard monthly package = com.mycompany.myapp.standard_1month 4. standard_yearly = Standard yearly package = com.mycompany.myapp.pro_1year ENTITLEMENTS pro = Professional features 1. com.mycompany.myapp.pro_1month 2. com.mycompany.myapp.pro_1year standard = Standard features 1. com.mycompany.myapp.standard_1month 2. com.mycompany.myapp.standard_1year On launch, I use purchaserInfoWithCompletionBlock set the delegate which means I also get an immediate didReceiveUpdatedPurchaserInfo call with the latest purchaserInfo. This also means that same delegate method will be called with updated purchaserInfo if any future changes happen to the subscription. That delegate stores the current purchaserInfo then broadcasts out a notification on the main thread so any UI elements can refresh, hide, or appear to reflect the current purchased entitlement, which is simply purchaserInfo.entitlements.active.allValues.firstObject since I'm only looking for "pro" or "standard". I use offeringsWithCompletionBlock to grab the current offering (aka the "default" one listed above) and, while I'm at it, I iterate through offerings.current.availablePackages to grab all the package.product.productIdentifier's so I have a dynamic array of MAS product identifiers instead of hardcoding anything. I then feed that into checkTrialOrIntroductoryPriceEligibility to see which identifiers are eligible for the 2-week trial. If receiveEligibility[productIdentifier].status != RCIntroEligibilityStatusIneligible then I assume a trial is possible so I add that product identifier to a Set for quick lookups. I fill a popup of available packages by iterating over the current offering's availablePackages: [self.plansPopUp addItemWithTitle:[NSString stringWithFormat:NSLocalizedString(@"%@ %@", nil), package.product.localizedDescription, package.localizedPriceString] representedObject:package]; So I have popup entries like "Professional (1 Month) $6.99", for example. The current active plan in the popup is where package.product.productIdentifier equals activeEntitlementInfo.productIdentifier. And when they make the purchase I just grab the popup item's representedObject, which is that item's package, to send to purchasePackage. There's some quickly logic to form the various fine-print text based on what they choose to show trial period (if eligible), rate, interval, boilerplate text, etc. But, long story short, this is working beautifully! I can start with a monthly Standard, let it auto-renew for a bit, switch to a monthly Pro, auto-renew, self-expire after a bit (due to sandbox), which switches the app instantly to a read-only mode, make a new purchase to get going again, etc. It's all working brilliantly! I'll continue testing but I'm loving how straightforward this has been! Congrats to the RC team!

Posted by George Browning 5 months ago

1
ANSWERED

iOS: Unknown Error when switching subscriptions

Hi, I already emailed you support a few times, but I takes about 1 whole day to respond and we're on a tight schedule to release our new update; maybe someone else knows? I am testing the App and I have 3 different subscriptions in 1 group at Apple and I placed them in 1 entitlement in RevenueCat. Now everytime I switch between subscription plans in that Group/Entitlement, the Apple default AlertControllers will be shown with firs: * if I am sure that I want to switch between subscriptions and than; * (after confirming) I get the message that my purchase has been succesfull. But the problem is that I get an error back from RevenueCat each time this happens, while I do see that the transaction was succesful in the Sandbox data. And no I am not getting the Boolean for userCancelled back, I am just getting these errors: Error: Optional(UNKNOWN) Message: Unknown error. Underlying Error: Optional(Error Domain=SSServerErrorDomain Code=3720 "Klaar" UserInfo={NSLocalizedDescription=Klaar}) Sidenote 1) The word "Klaar" in the errors means "Done" in Dutch, so perhaps that something goes wrong on that end, like that RevenueCat maybe expects an English response from Apple? Sidenote 2) I did look at the docs but I couldn't find anything about switching subscriptions in a Group or Entitlement, but maybe I just missed the explanation Anyway, I have no idea what went wrong but I am hoping somebody could perhaps help me who had a similar problem. Thanks in advance for any help!

Posted by Vasco 6 months ago