One-time purchase with free trial

Dear all, This is my first post in this forum - and, in fact, my first app, too! I'm glad to be here, and thanks in advance for your help.

I'm looking to offer an app for a one-time payment. I'd also like people to be able to try the app for a week.

It seems that the "Pricing and Availability" section in App Store Connect is not the right place to configure this kind of offer. It does allow me to set a one-time price, but I cannot find a trial-period there (or am I missing something?)

Two different strategies seem possible here:

  1. Using in-app-purchases: make the actual app free, but ask users after a week to buy a non-consumable IAP. The problem with that: I need to verify that the app has been installed for seven days ... even if it has been uninstalled at some point.

  2. Using subscriptions: There is a "free trial" option for subscriptions. But after that free trial, subscriptions are being payed periodically. I'd rather have the user only pay once for lifetime-access.

Some apps seem to use strategy 1 - I believe the "Lap swim" app does. But still it seems like a bit of a hack - is there a more elegant way to achieve this?

Accepted Reply

Effectively you want a paid app (no access without buying it) but you want to provide a trial/temporary access. The way to do this on the App Store is to setup 2 non-consumable in-app purchase products.

IAP Setup: **1) Free Trial non-consumable - ** The first one will be for your trial - therefore priced at $0/free and will be named specifically to the trial you are offering. ie: “7-day Free Trial” or “30-day Free Trial” “1-month Free Trial”, etc. Your app must accurately provide free trial access based on the name of the IAP and will use the transaction purchaseDate to reliably start/end their trial experience. With a non-consumable the transaction will be consistent across all their devices with that Apple ID - from new devices, re-installs, etc. 2) 2nd non-consumable representing the paid 1-time purchase - This will be the 1-time purchase non-consumable that is priced accordingly.

**Logic - ** Your app will have some core logic to execute upon launch. See the “WWDC22 Proactive Restore” session for more info on checking status on launch seamlessly. But for the most part it was do proactive restore upon app launch and your logic on what transactions it finds or doesn’t find will be:

  1. Active Customer: First check should be to see if purchased the 1-time purchase non-consumable, if so, then grant access. If not go to -> #2
  2. Active Trial Customers: now check if they bought Free Trial non-consumable. If so, check if that trial is active or not. If active grant access, if not go to ->#3
  3. Inactive Trial Customer: They bought the trial but that trial period has ended, so you then merchandise your paid 1-time purchase non-consumable. But if no transaction is found, go to -> #4
  4. New Customers: this is when no IAP was found to be purchased, therefore they are “new” and you should merchandise the Free Trial non-consumable.
  • Brilliant, thanks a lot! Also thanks for the reference to the [WWDC22 Proactive Restore](Implement proactive in-app purchase restore - WWDC22 ...Apple Developer12.06.2022) video.

  • Sorry, bad copy-pasting on the link to the video. Here's the actual link

  • (1) What is the point of IAP No.1? The app will already be offered for free on the App Store (though it's Pricing and Availability settings). (2) this approach descried on the video is very much based on verifying transactions for existing products using a Transaction's currentEntitlements property. Unfortunately that's not available in ObjC - any workaround?

Replies

Note that buyer can redeem its purchase. But I don't how much time he can do so. That's aka free test period.

  • Thanks, I wasn't aware of that option. The process of getting a refund for a purchase seems a bit complicated, though. For reference, here's a link for how to request a refund

Add a Comment

I've looked into this myself. And after researching for hours, decided to go the subscription approach. This is because I'm not only almost positive this is something that's not supported by Apple, but also that if you try to implement yourself via logic within the app, if you lock the app up once the free trial is over then your app may be in violation of the Apple developer agreement and taken down. At least that's what I gleaned from the research.

  • Oh, this is very interesting. I guess the sections most relevant to IAP's in the Developer Agreement are: Attachment 2 "Additional Terms for Use of the In-App Purchase API" as well as Schedule 2. I couldn't find a paragraph dealing with a non-consumable IAP that locks up the app ... Have you found a particular place in the Agreement during your research?

Add a Comment

Effectively you want a paid app (no access without buying it) but you want to provide a trial/temporary access. The way to do this on the App Store is to setup 2 non-consumable in-app purchase products.

IAP Setup: **1) Free Trial non-consumable - ** The first one will be for your trial - therefore priced at $0/free and will be named specifically to the trial you are offering. ie: “7-day Free Trial” or “30-day Free Trial” “1-month Free Trial”, etc. Your app must accurately provide free trial access based on the name of the IAP and will use the transaction purchaseDate to reliably start/end their trial experience. With a non-consumable the transaction will be consistent across all their devices with that Apple ID - from new devices, re-installs, etc. 2) 2nd non-consumable representing the paid 1-time purchase - This will be the 1-time purchase non-consumable that is priced accordingly.

**Logic - ** Your app will have some core logic to execute upon launch. See the “WWDC22 Proactive Restore” session for more info on checking status on launch seamlessly. But for the most part it was do proactive restore upon app launch and your logic on what transactions it finds or doesn’t find will be:

  1. Active Customer: First check should be to see if purchased the 1-time purchase non-consumable, if so, then grant access. If not go to -> #2
  2. Active Trial Customers: now check if they bought Free Trial non-consumable. If so, check if that trial is active or not. If active grant access, if not go to ->#3
  3. Inactive Trial Customer: They bought the trial but that trial period has ended, so you then merchandise your paid 1-time purchase non-consumable. But if no transaction is found, go to -> #4
  4. New Customers: this is when no IAP was found to be purchased, therefore they are “new” and you should merchandise the Free Trial non-consumable.
  • Brilliant, thanks a lot! Also thanks for the reference to the [WWDC22 Proactive Restore](Implement proactive in-app purchase restore - WWDC22 ...Apple Developer12.06.2022) video.

  • Sorry, bad copy-pasting on the link to the video. Here's the actual link

  • (1) What is the point of IAP No.1? The app will already be offered for free on the App Store (though it's Pricing and Availability settings). (2) this approach descried on the video is very much based on verifying transactions for existing products using a Transaction's currentEntitlements property. Unfortunately that's not available in ObjC - any workaround?

Hello  @App Store Commerce Engineer ,

check if that trial is active or not

How can we get the date of the original purchase unaffected by refunds?

A Product has two properties that returns VerificationResult<Transaction>?: latestTransaction and currentEntitlement.

Testing it in Xcode, when I refund the free-trial non-consumable product and purchase it again, both of these properties' originalPurchaseDate and originalID refer to the latest purchase, not the original transaction that was refunded.

let latestTransactionVerificationResult = await freeTrialProduct.latestTransaction

switch latestTransactionVerificationResult {

case .verified(let signedType):
    // check whether originalPurchaseDate is within the free trial period
    let originalPurchaseDate = signedType.originalPurchaseDate
    // returns the latest active one, not the original refunded one
    logger.debug("freeTrial originalID: \(signedType.originalID)")
    logger.debug("freeTrial originalPurchaseDate: \(signedType.originalPurchaseDate)")
    logger.debug("freeTrial purchaseDate: \(signedType.purchaseDate)")
    logger.debug("freeTrial signedDate: \(signedType.signedDate)")

//...
}

The answer to my question is to use Transaction.all https://developer.apple.com/documentation/storekit/transaction/3851203-all

I go through that sequence and store the minimum originalPurchaseDate associated with a particular StoreKit product ID to be used in the app.