iOS Question In-app Subscriptions and listing owned products

Discussion in 'iOS Questions' started by Paul Leischow, Apr 25, 2019.

  1. Paul Leischow

    Paul Leischow Member Licensed User

    In B4A we can initialize Billing Manager

    Dim manager As BillingManager3
    manager.Initialize("manager", key)

    and to get a list of owned products we can call
    manager.GetOwnedProducts which will trigger
    manager_OwnedProducts (Success As Boolean, purchases As Map)

    Are we not able to do something similar in B4I ?
    I can't seem to find something similar to GetOwnedProducts.
     
  2. Erel

    Erel Administrator Staff Member Licensed User

    There is the restore transactions method that you can call to get the owned products however it might require the user to asked to log in so it is not something that you should call automatically.
     
  3. Paul Leischow

    Paul Leischow Member Licensed User

    Yes, I saw the RestoreTransactions command but it doesn't seem very practical if the user must log in. Apple doesn't make things very easy or seamless when managing in-app purchases like subscriptions compared to Android.

    From what I've read it looks like the developer must keep their own database of users and subscription start & end dates and the app must query that on every start... and Apple seems to do nothing. Is this assumption correct?
    What are others doing to deal with stuff like monthly or yearly subscriptions?

    I'm kinda stumped here ;)
     
  4. Computersmith64

    Computersmith64 Well-Known Member Licensed User

    You can use receipt validation to determine whether products have been purchased.
    https://developer.apple.com/library...html#//apple_ref/doc/uid/TP40010573-CH105-SW1

    I have no idea how this is implemented in B4i though.

    What I do for subscriptions is keep a local record of the expiry date which I update with a background receipt validation call to App Store when the app starts. If for any reason the app can't connect to App Store I use my local record. The receipt validation is only really required for when a subscription has gone past its expiry date because if a user cancels a subscription it's still current until the original expiry date. For a user to get a refund on a subscription they have to contact Apple & it's apparently very rare for them to issue one - however if they did, then the receipt validation would show that the subscription had expired (actually I think you wouldn't get a receipt back).

    - Colin.
     
  5. Erel

    Erel Administrator Staff Member Licensed User

  6. Computersmith64

    Computersmith64 Well-Known Member Licensed User

    For subscriptions you can make a URLRequest to https://buy.itunes.apple.com/verifyReceipt & get the expiration date from the response. Here's the code in Swift:

    Code:
    func checkForReceipt(_ restoring: Bool) {

            let receiptUrl = Bundle.main.appStoreReceiptURL
            let fileExists = FileManager.default.fileExists(atPath: receiptUrl!.path)

            
    if fileExists {
                getReceipt(urlString: 
    "https://buy.itunes.apple.com/verifyReceipt") { (completion) in
                    
    if completion.value(forKey: "status"as! Int == 21007 {
                        print(
    "Going to sandbox for receipt validation")
                        self.getReceipt(urlString: 
    "https://sandbox.itunes.apple.com/verifyReceipt") { (completion) in
                            guard let expDate = self.expirationDateFromResponse(completion) 
    else {return}
                            print("SANDBOX: \(expDate)")
                        }
                    } else if completion.value(forKey: "status") as! Int == 0 {
                        guard let expDate = self.expirationDateFromResponse(completion) else {return}
                        print("PRODUCTION: \(expDate)")
                    }
                }

                if restoring {
                    updateSub()
                } else {
                    delegate?.setSubView()
                }
            } else {
                print("No Receipt")
                if restoring {
                    let alert = UIAlertView(title: "Oops!", message: "You haven't purchased a myPets subscription yet.", delegate: nil, cancelButtonTitle: "OK")
                    alert.show()
                }

            }

        }

      
        func getReceipt(urlString: String, completion: @escaping (NSDictionary) -> Void) {
            var jsonResponse: NSDictionary?
            let receiptURL = Bundle.main.appStoreReceiptURL
            let receiptData = try? Data(contentsOf: receiptURL!)

            guard let receiptToString = receiptData?.base64EncodedString(options: []) else {return}
            let dict = ["receipt-data" : receiptToString, "password" : [your shared secret code]] //**
            do {
                let request = try JSONSerialization.data(withJSONObject: dict, options: []) as Data
                var storeRequest = URLRequest(url: URL(string:urlString)!) //NSMutableURLRequest(url: storeURL)
                storeRequest.httpMethod = "POST"
                storeRequest.httpBody = request
                let session = URLSession(configuration: URLSessionConfiguration.default)
                let task = session.dataTask(with: storeRequest, completionHandler: { (data, response, error) in
                    if let error = error {
                        print(error.localizedDescription)
                        return
                    }
                    do {
                        jsonResponse = try (JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary)!
                        completion(jsonResponse!)
                    } catch {
                        //handle NSJSONSerialization errors
                        print(error.localizedDescription)
                    }
                })
                task.resume()
            } catch {
                print(error.localizedDescription)
                //handle NSJSONSerialization errors
            }

    func expirationDateFromResponse(_ jsonResponse: NSDictionary) -> Date? {

            if let receiptInfo: NSArray = jsonResponse["latest_receipt_info"] as? NSArray {
                guard let lastReceipt = receiptInfo.lastObject as? NSDictionary else {return nil}
                let formatter = DateFormatter()
                formatter.dateFormat = "yyyy-MM-dd HH:mm:ss VV"
                guard let expirationDate: Date = formatter.date(from: (lastReceipt["expires_date"] as! String)) else {return nil}
                currentSub = lastReceipt["product_id"] as! String
                currentSubExpiry = expirationDate
                saveSub()
                return expirationDate
            } else {
                return nil
            }
        }
    Here's the result I get on my test device with an expired subscription:
    & here's the result I get after I take out a test subscription:

    - Colin.
     
  7. Paul Leischow

    Paul Leischow Member Licensed User

    Thank you Colin, that code snippet was quite helpful !!
     
    Computersmith64 likes this.
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice