Skip to main content
Question

Start of Free Trial period without automatic subscription


Forum|alt.badge.img+1

I am pretty new to Revenuecat. Sorry if this problem is answered a hundred times, but I could not find a solution. Here is my problem:

I do have an iOS app and have a paywall view. The user can either subscribe for a monthly or annual payment (I managed to set this up in App Store Connect and Revenuecat and this all works fine), or he can select a free trial for 14 days.
I know, that I can set up a free trial in app store connect, but as far as I understand, this automatically will start a chargeable subscription, if he does not cancel before the end of the trial period. So in fact the free trial is part of subscription and the user has to choose a subscription method at the paywall. This seems not very appealing to me.
I rather would like to say: “Click here and have a look”. When the user clicks ‘here’ then(!) the free trial period starts (without selecting a subscription method). During the free trial I can present a counter: ‘X days left’. After 14 days the user cannot access the content behind the paywall, but get’s the paywall and the subscription methods presented.
Of course, I could implement such free period tracker programmatically, but if the user deletes the app and makes a new install the free trial period would start again, which I do not want.

So here is my question: Is it possible to offer a free trial for 14 days without the need for the user to choose a subscription plan, to start that free trial when the user is ready to start (click/touch) and to remember the user ‘somewhere’ at Revenuecat to avoid, that the user can activate the free trial again? What are the steps to implement this?

This post has been closed for comments

16 replies

Forum|alt.badge.img+1
  • Author
  • Member
  • 9 replies
  • January 31, 2025

I am one step further: I found this article: 

which is pretty much the same problem. And Ryan Glanz is recommending Promotions. I followed the link and had a read through. As far as I understand, the talk is about ‘Granted entitlements’. The further description shows how to activate such Granted entitlement, BUT only for one specific customer, right? I want this feature for all new customers. Any idea, how this can be done?


jeffrey_bunn
RevenueCat Staff
Forum|alt.badge.img+6
  • RevenueCat Staff
  • 246 replies
  • February 6, 2025

Hi ​@Snoody! The link you found re: granted entitlements is relevant; here’s the flow you might consider building:

  1. When a user taps the “Give me 14 days free” button, call a function on your backend
  2. This backend function takes the app user ID and calls our grant an entitlement API endpoint (which gives the user access for the period of time specified in the API call)
  3. Your app fetches new customerInfo, which will show the entitlement they’ve just been granted and allows them to use your app
  4. After the 14 days is up, the entitlement will expire, and they’ll see your paywall again (depending on how you trigger your paywall)

Now, if you don’t identify your users with custom IDs (i.e., all users are anonymous), they’ll be able to delete your app and get the trial again. So this is a limitation of this method.

Another way of achieving this is, when users tap the “Give me 14 days free” button, you “sell” them a $0 non-renewing in-app purchase (that lasts 14 days). They still have to “pay” for this $0 purchase, but this method adds the purchase to their receipt (tied to their Apple ID). This means that if they delete your app, we’ll still see the purchase on their receipt, so they won’t be able to continually get another trial.

Hope this helps!


Forum|alt.badge.img+1
  • Author
  • Member
  • 9 replies
  • February 6, 2025

Hello Jeffrey,

Thank you for your reply. I read your statement several times and this sounds manageable for me. Some questions came up:

  1. This ‘app user ID’ could be ‘anything’, e.g. email-adress, right?
  2. How can I check, if the free trial has been used in the past with this ID to avoid a second activation?
  3. I guess (hope) that the check (via SDK: customerInfo?.entitlements.all["premium"]?.isActive == true) for a valid entitlement is the same during the trial period and in paid mode?
  4. So lets assume the app was reinstalled. As a first step the user has to enter the ID (= email). Which action will I take next? Do I call the same endpoint as for granting the first time?
  5. I have some more data, that I want to store as meta data in the user database (e.g. customer ID from office backend). Is this possible with RevenueCat or do I have to sign up the user on another system (e.g. my Wordpress system)?

Looking forward ...


Forum|alt.badge.img+1
  • Author
  • Member
  • 9 replies
  • February 7, 2025

… and one more question:

  1. (maybe this is part of question 2): How can the app get the startdate and enddate of the free trial to show the remaining days?

Forum|alt.badge.img+1
  • Author
  • Member
  • 9 replies
  • February 7, 2025

… uh, and:

  1. This app user ID (e.g. email) can be set at later time, not only when app start, right?
    Something like Purchases.configure(withAPIKey: "XYZ", appUserID: “personalemail@email.com”) when the user taps ‘Give me the 14 days trial’ ...

Forum|alt.badge.img+1
  • Author
  • Member
  • 9 replies
  • February 12, 2025

Hello ​@jeffrey_bunn,

Just wanted to let you know, that I managed to find some answers to these last 3 comments:
1. It is not recommended by RC to use the email address
7. This is done with the .login() method; got this already implemented
The other questions are still pending …

Thanx, Robert


jeffrey_bunn
RevenueCat Staff
Forum|alt.badge.img+6
  • RevenueCat Staff
  • 246 replies
  • February 18, 2025

Hi ​@Snoody 

How can I check, if the free trial has been used in the past with this ID to avoid a second activation?

Sorry, by ID do you mean app user ID? And what is the situation you’re describing - when a user deletes the app? 

I guess (hope) that the check (via SDK: customerInfo?.entitlements.all["premium"]?.isActive == true) for a valid entitlement is the same during the trial period and in paid mode?

If a user is on trial, they are entitled to your premium product. This is the same as if they had already paid for your product. So the entitlement check should work here.

So lets assume the app was reinstalled. As a first step the user has to enter the ID (= email). Which action will I take next? Do I call the same endpoint as for granting the first time?

The problem here is it’s unlikely that you can use the same app user ID after a reinstall (assuming you’re not using email addresses, which we don’t recommend using). Device identifiers aren’t necessarily stable nowadays, so it’s possible the ID could be different, making it difficult to track if they’ve already been given a trial. 

I have some more data, that I want to store as meta data in the user database (e.g. customer ID from office backend). Is this possible with RevenueCat or do I have to sign up the user on another system (e.g. my Wordpress system)?

You can set additional data as a customer attribute.

I would generally recommend using a typical trial or the $0 purchase workaround, as there’s a lot of complexity in this.


  • New Member
  • 1 reply
  • February 21, 2025

Hi ​@jeffrey_bunn 

I’m trying to implement your suggestion:

“sell” them a $0 non-renewing in-app purchase (that lasts 14 days). They still have to “pay” for this $0 purchase, but this method adds the purchase to their receipt (tied to their Apple ID)

Right now I’m using a .storekit file to test this locally, and I can easily add a $0 non-renewing subscription. I’m also able to add a $0 non-renewing subscription in RevenueCat. However, I do not believe it’s possible to add a $0 non-renewing subscription on App Store Connect. My concern is that this means that it’s not actually possible to use the $0 purchase option in production.

 

 


Forum|alt.badge.img+1
  • Author
  • Member
  • 9 replies
  • February 21, 2025

Hello,

I have managed to find a solution and want to share this with you. ​@jeffrey_bunn please let me know, if there is any misconception, I did.

When a user hits ‘give me the 14 days trial’ the RestAPI endpoint https://api.revenuecat.com/v1/subscribers/{User ID}/entitlements/premium/promotional is called with the calculated end date as POST body. Checking the customerInfo?.entitlements via SDK works as with paid subscriptions. After the free trial period, the granted entitlement is no longer valid. Great!
With customerInfo?.expirationDate(forProductIdentifier: "rc_promo_premium_custom") I can check, how many days of the free trial period are left. Furthermore, if the expirationDate is in the past, I do have the indicator, that the free trial was consumed and cannot be activated once again, thanks to the fact, that this free trial subscription stays in the customerInfo and is not deleted.


  • New Member
  • 4 replies
  • February 22, 2025

I have been researching this vary same use case for the the last week or so.  I’m new to in-app purchases in IOS apps.  I’ve been trying to set this up with just Storekit 2 and Apple Connect settings.  But as you point out, there is no way (that I have found) to setup a $0 non-renewing subscription in Apple Connect.  I’m surprises me that they don’t have a on-time purchase with a free trial option in Apple Connect. 

I’m also new to RevenueCat.  So my apologies for a question that I hope is not stupid.  If your able to implement a $0 non-renewing subscription in RevenueCat,  does that same subscription need to be in Apple Connect.  I thought that one of the features of RevenueCat is that you can have subscriptions that are unique to it and managed by it.  Like I said, maybe a stupid question.        

Anyway, have you tried to implement the solution you mention in your last post in swift code?  If so, would you be able to share that code?  I would be very appreciative.  


Forum|alt.badge.img+1
  • Author
  • Member
  • 9 replies
  • February 22, 2025

@hogman78 
To be honest, I’m pretty new to RC, too. So my solutions might not be the best. But here is my solution so far, which works (at least it looks like :-) ):
There is definitely no free trial available with App Store Connect. But with RC you can grant an entitlement, which is simply spoken telling your app, that the user can access a defined RC entitlement without billing. This is only possible using the RC Rest API, see the description here:
https://www.revenuecat.com/docs/dashboard-and-metrics/customer-history/promotionals
https://www.revenuecat.com/docs/api-v1#tag/entitlements

In my case, I have one entitlement named ‘premium’. On my paywall I offer the option to start a paid subscription or to start a free 14 days trial. This is the button for starting the trial:

Button("Let me in\nand start the 14 days free trial.") {

  Task {

    let data = try await RestAPI().call(

     urlString: "https://api.revenuecat.com/v1/subscribers/" + userVM.wpid + "/entitlements/premium/promotional",

     method: "POST",

     body: try? JSONEncoder().encode(["end_time_ms": 1000 * Int(Date().timeIntervalSince1970 + 14 * 24 * 60 * 60) ]),

     token: "Bearer " + RC_SECRET_API_KEY

                        )

    Purchases.shared.getCustomerInfo { customerInfo, error in

     userVM.isRCentitled = customerInfo?.entitlements.all["premium"]?.isActive == true

    }

  }

}

userVM.wpid is a custom App User ID, which was generated before. RestAPI.call() is a method I created to call a Rest API endpoint. And RC_SECRET_API_KEY is a secret api key, that you get from your RC settings. After granting the entitlement I want to set my flag userVM.isRCentitled. I could do this analyzing the Rest API response data, but I think it’s easier to use the RC SDK Purchases.shared.getCustomerInfo { …

Next step I want to check if the user has a valid entitlement and is within the 14 days trial period, when the app is startet. So I extend the init from the SDK in ContentView like this in my userVM:

Purchases.shared.getCustomerInfo { customerInfo, error in

init() {

  Purchases.shared.getCustomerInfo { customerInfo, error in

    self.isRCentitled = customerInfo?.entitlements.all["premium"]?.isActive == true

  // Check Free Trial: 0: Free Trial has been consumed, >0: Free Trial is running right now, -1: Free Trial not used

  if let freeTrialExpDate = customerInfo?.expirationDate(forProductIdentifier: "rc_promo_premium_custom") {

    self.freeTrialExpDate = freeTrialExpDate

    }

  }

}

OK, that’s it. With userVM.isRCentitled and userVM.freeTrialExpData I can work and show the proper infos to the user. The only drawback is, that the check for the trial is only at app start, but this is no problem for me. The only thing I do not know for sure is, if the granted entitle info in the RC customerInfo is removed anytimes? If so, I would loose the information, that the user had consumed the free trial before.

Hope this helps? Could someone from RC have a look through and give some suggestions, how to optimize this procedure? ;-) 


  • New Member
  • 4 replies
  • February 22, 2025

Thanks so much for taking the time for the info an code.  It looks like that will work. 

So is your “Premium” entitlement a non-consumable in-app purchase and the 14-day trial a non-renewing subscription? 

And I would also be concerned about if RC customerinfo record is removed,  We need to get a clarification from RC on this! 

You would think that RC would a simpler way to access the user info for these entitlements.  Bt maybe the REST APIs are the only way.   

BTW, I really like how you set up the static RESAPI method.  Makes it very reusable!  Would you mind sharing the code?  

Again thanks!!!


Forum|alt.badge.img+1
  • Author
  • Member
  • 9 replies
  • February 22, 2025

@hogman78 
Thanx, appreciate your reply.
The ‘premium’ entitlement is the same for the paid subscription as for the free trial, so nothing special about this. The difference if this renewable or not does not depend on the entitlement but the product/offer and when granted with Rest API no product is involved or activated.

If I get a response from RC stating, that the info about the free trial gets removed, then I have to persist the free trial information elsewhere. I think I could use the custom attributes for this, but did not try to read this in my app. I guess, this could be a solution.

RestAPI, here u r:

class RestAPI {

  func call(urlString: String, method: String = "POST", body: Data?, token: String) async throws -> Data {

    let url = URL(string: urlString)!

 

    var request = URLRequest(url: url)

    request.addValue(token, forHTTPHeaderField: "Authorization")

    request.httpMethod = method

    if let body = body {

      request.httpBody = body

    }

 

    let config = URLSessionConfiguration.default

    config.httpAdditionalHeaders = ["Content-Type": "application/json"]

    let session = URLSession(configuration: config)

 

    let (data, response) = try await session.data(for: request)

    if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {

      print("success")

    } else {

      print("fail")

      throw MyError.invalidResponse

    }

  return data

  }

}


  • New Member
  • 4 replies
  • February 22, 2025

Thanks so much for the quick response with info and the code.  

I think I’m getting a bit confused with the nomenclature.  What did you setup in Apple connect?  Was it just one non-consumable in app purchase?  And then set the entitlements for it in RC?  Or did you setup a non-renewing subscription?  Just want to be sure how it looks on the Apple Connect side.

Thanks!!


Forum|alt.badge.img+1
  • Author
  • Member
  • 9 replies
  • February 22, 2025

@hogman78 
For the setup I followed exactly the steps in this video:

https://www.youtube.com/watch?v=11A0hUjbCb4

I think, this should explain it very well. Especially when talking about products, offerings and entitlements in RC and how these are connected to subscriptions in App Store Connect.

Hope this helps.


  • New Member
  • 4 replies
  • February 22, 2025

That’s so funny!  I’ve reviewed the same video.  Thanks!!


Cookie policy

We use cookies to enhance and personalize your experience. If you accept you agree to our full cookie policy. Learn more about our cookies.

 
Cookie settings