tikhop / TPInAppReceipt

Reading and Validating In App Purchase Receipt Locally.
MIT License
635 stars 95 forks source link

Introductory Offer Support? #85

Closed Jerland2 closed 3 years ago

Jerland2 commented 3 years ago

Wondering if we can get introductory offer support by implementing the ability to check eligibility?

Each product returns whether it has an introductory offer tied to it. However in order to check eligibility we must parse the receipt. (Hence the feature request)

This would entail parsing the receipt for is_trial_period as well as is_in_intro_offer_period. If these values are equal to 1 somewhere, a user has already used free trial/introductory offer.

Helpful articles outlining this: https://www.revenuecat.com/blog/ios-introductory-prices https://developer.apple.com/documentation/storekit/in-app_purchase/subscriptions_and_offers/implementing_introductory_offers_in_your_app https://blog.apphud.com/introductory-offers-in-ios/

Helpful article snippet:

"As of iOS 12.0, SKProduct now includes subscriptionGroupIdentifier property, so it's now possible to compute Introductory Pricing eligibility locally. While this update is great, calculating intro eligibility locally still requires a lot of unnecessary dev work. To check intro eligibility on iOS >= 12.0, you need to:

Jerland2 commented 3 years ago

Upon closer investigation into the TPA codebase a variable similar to promotionalOfferIdentifier from receipt.purchases.first except for introductory would suffice. as described in the article snippet. As if we can get an identifier tied to a product signifying if the user ever used an intro price then we can compare against list of products. If a match is found we have determined the user in ineligible.

tikhop commented 3 years ago

Hi @Jerland2, sorry for the late response, I've been off the grid for the last couple weeks and going to be online next week.

I think we can do it, but I need some time.

Thanks, Pavel

tikhop commented 3 years ago

Hi @Jerland2, I'm back and about to start working on it. I think, I will have something to try later today.

tikhop commented 3 years ago

@Jerland2

I've pushed two new methods to check whether user is eligible for introductory offer into feature/introductory-offer-support branch.

Example:

...
let receipt = try InAppReceipt.localReceipt() 
var isEligible = receipt.isEligibleForIntroductoryOffer(for: "com.test.product.id")
// or 
isEligible = receipt.isEligibleForIntroductoryOffer(for: ["com.test.product.1", "com.test.product.2", "com.test.product.3"])
...

I think we can also check whether the user is not eligible for any products within the same subscription group. Let me know if you need it.

ungerc commented 3 years ago

brilliant idea, we need it 🥇

tikhop commented 3 years ago

We will definitely have it, just need to check whether current implementation works well and if so I will merge it to master and then, I will implement checking whether the user is eligible for any products within the same subscription group.

tikhop commented 3 years ago

Hi @Jerland2, @ungerc

I've push code into feature/introductory-offer-support branch with support for checking whether user is eligible for any products within the same subscription group.

Basically, you can retrieve a subscription group from SKProductsResponse using an extension provided by the library and then check whether user is eligible or not:


extension SKManager: SKProductsRequestDelegate 
{
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) 
    {
        let group = response.subscriptionGroup
        // let groups = response.subscriptionGroups // Use this if you have more than one group 

        let r = try InAppReceipt()
    let isEligible = r.isEligibleForIntroductoryOffer(for: group)

    // Do your stuff
    }
}

I think, I will merge it into master tomorrow.

ungerc commented 3 years ago

cool, I have a day off today, will check tomorrow.

ungerc commented 3 years ago

Hi @tikhop,
nice - looks good

tikhop commented 3 years ago

@ungerc thanks for checking it out. I'm going to release it later today.

tikhop commented 3 years ago

Just released it.

Thanks @Jerland2 and @ungerc.

ungerc commented 3 years ago

thanks @tikhop, forgive my ignorance but is there a necessity for you to update sth in the cocoapods repo in order for the version to appear there?

tikhop commented 3 years ago

@ungerc I'm sorry — updating cocoapods right now.

tikhop commented 3 years ago

@ungerc ✅

ungerc commented 3 years ago

me again @tikhop 👋🏽, this function func isEligibleForIntroductoryOffer(for group: SKSubscriptionGroup) -> Bool seems to be duplicated? - or am I holding it wrong? I see it's guarded by #if canImport(StoreKit) and an availability check, but I do not understand it.

tikhop commented 3 years ago

@ungerc

Well, there are two ways to check eligibility for a subscription group.

First, you need just to pass a set of product identifiers:

func isEligibleForIntroductoryOffer(for group: SubscriptionGroup) -> Bool //`SubscriptionGroup` is Set<String> 

Second, you must prepare SKSubscriptionGroup and pass it to the function.

func isEligibleForIntroductoryOffer(for group: SKSubscriptionGroup) -> Bool //`SKSubscriptionGroup ` class that contains `SKProducts`
ungerc commented 3 years ago

oh here: public typealias SubscriptionGroup = Set<String> ok, maybe hard to grasp that the typealias has the same name as the class

ungerc commented 3 years ago

oh and it doesn't, now I get it - my head was in a very dark place

tikhop commented 3 years ago

Yeah, It may seem like overengineering, but I tried to emphasize that the set of product identifiers must belong to the same subscription group.