benjaminmayo / merchantkit

A modern In-App Purchases management framework for iOS.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Question about the auto-renewable subscription.

hellbit opened this issue · comments

When I check the state of the subscription it returns case .isPurchased(_): But my subscription was expired and I don't have any subscriptions right now.

The question is: Do I need to check the info.expiryDate from case .isPurchased(_):? I am asking it because I see this one in the code - Note: Do not use this value to decide whether to allow access to the product.

And another question is: why do I see two different dates? From iTunes I see 11 Jul 2020 but info.expiryDate is equal 2020-06-26 10:39:48 +0000

This's my code:

private init() {
        configurateStoreKit()
    }
    
    private func configurateStoreKit() {
        self.merchant = Merchant.init(configuration: .default, delegate: self)
        self.merchant.register(ProductDatabase.allProducts)
        self.merchant.setup()
        self.checkAvailablePurchases()
    }
    
    private  func checkAvailablePurchases() {
        self.task = merchant.availablePurchasesTask(for: ProductDatabase.allProducts)
        self.task?.onCompletion = { [weak self] result in
            switch result {
            case .success(let value):
                DispatchQueue.main.async {
                    self?.products = value.sortedByPrice(ascending: true)
                    self?.checkSubscription()
                }
            case .failure(_): break
            }
        }
        self.task?.start()
    }
 func checkSubscription() {
        let monthState = self.merchant.state(for: ProductDatabase.monthlySubscription)
        let sixmonthlyState = self.merchant.state(for: ProductDatabase.sixmonthlySubscription)
        let yearState = self.merchant.state(for: ProductDatabase.yearlySubscription)
                
        switch monthState {
            case .isPurchased(_):
                self.setPurchase(true)
                return;
            default: break
        }
        
        switch sixmonthlyState {
        case .isPurchased(_):
            self.setPurchase(true)
            return;
        default: break
        }
        
        switch yearState {
        case .isPurchased(_):
            self.setPurchase(true)
            return;
        default:
            break
        }
        
        self.setPurchase(false)
    }

extension SubscriptionServices: MerchantDelegate {
    
    func merchant(_ merchant: Merchant, didChangeStatesFor products: Set<Product>) {
        checkSubscription()
    }
    
    /// Called when the `isLoading` property on the `Merchant` changes. You may want to update UI in response to loading state changes, e.g. show/hide the status bar network activity indicator, or you may want to do nothing. The default implementation of this delegate method does nothing.
    func merchantDidChangeLoadingState(_ merchant: Merchant) {
        
    }
    
    /// Called when a user activates a Promoted In-App Purchase in the App Store, with the intent to buy the `Product`. The default implementation of this delegate method returns `StoreIntentResponse.default` (equal to `StoreIntentResponse.automaticallyCommit`) which begins the purchase flow immediately. You may want to defer the commit until later, in which case your application logic should keep hold of the `Purchase` to use later, and return `StoreIntentResponse.defer`.
    func merchant(_ merchant: Merchant, didReceiveStoreIntentToCommit purchase: Purchase) -> StoreIntentResponse {
        return .default
    }
    
}

Thank you

There is sometime a lag between StoreKit updating renewals and the actual state of subscriptions. MerchantKit offers a grace period to avoid these issues and prevents users being told they need to pay, when they are actually paying. The info represents the last known value, which is why it will sometimes be out of sync. You should only use isPurchased to determine whether to give access to the user's product.

If you want to, you can customize the duration of the grace period by providing a custom configuration, but I wouldn't recommend it.

My canceled subscriptions returning as isPurcahsed true up to 1 months delay. What can I do about it?

As explained above, you can supply a custom configuration to the Merchant(...) initialiser, which customises the timeout via the subscriptionRenewalLeeway on the LocalReceiptValidator. However, I don't recommend it as it penalises legitimate users.

subscriptionRenewalLeeway is which value in default configuration?