bizz84 / SwiftyStoreKit

Lightweight In App Purchases Swift framework for iOS 8.0+, tvOS 9.0+ and macOS 10.10+ ⛺
MIT License
6.58k stars 795 forks source link

Inconsistent purchase and restore purchases results on iOS 10/11 #287

Open TipToeTiger opened 7 years ago

TipToeTiger commented 7 years ago

Platform

In app purchase type

Environment

Version

0.10.5

Report

Issue summary

In production I am currently having some inconsistent issues.

When a user purchases the IAP in my app the purchases is successful and they get the successful purchase UIAlert. However, the effects of the purchase do not take place. I informed the user to use 'Restore purchases' to make sure that the effects of the purchase happen. However, when the user selects 'Restore Purchases' they receive nothing back, along with my UIAlert that tells them that there is nothing to restore.

This issue only seems to be occurring on iOS 10 devices. I have tried to replicate the issue on iOS 11 test and live devices but I cannot replicate it.

I tried to replicate the issue on an iOS 10 device twice and had one result where the IAP was successful an another where it failed.

Has anyone else come across an issue like this?

Any help would be greatly appreciated.

TipToeTiger commented 7 years ago

UPDATE: The issue has also been seen occurring in iOS 11 builds as well.

cubong commented 7 years ago

I also have the same problem. I think there is a problem with the Appstore

IamAliSufyan commented 6 years ago

I am facing the exact issue and it is hurting my user base badly. Did you find a way to fix it?

bizz84 commented 6 years ago

@TipToeTiger Apologies for the late reply.

If this is a production-only issue I could suggest adding some analytics or network-logging code to check if the callbacks are made.

Something along these lines this:

// PaymentQueueController.swift
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {

  let purchasedTransactions = transactions.filter { $0.transactionState == .purchased }
  let purchasedProductIds = purchasedTransactions.map { $0.payment.productIdentifier }
  logger.track(event: "purchasedTransactions", customData: ["productIds": purchasedProductIds, date: Date()])
  ...
}    

// in your app
SwiftyStoreKit.purchaseProduct("your-product-id") { result in
  logger.track(event: "purchaseProduct", customData: ["result": result, date: Date()])
}

You could then check your logs by date. You would expect to see "purchasedTransactions" and "purchaseProduct" events always showing up in pairs with almost identical dates.

Hope this helps.

TipToeTiger commented 6 years ago

Hi All,

I believe I fixed the issue by adding the following to my didfinshinglaunching function in my App Delegate:

SwiftyStoreKit.completeTransactions(atomically: true) { purchases in for purchase in purchases { if purchase.transaction.transactionState == .purchased || purchase.transaction.transactionState == .restored { if purchase.needsFinishTransaction { // Deliver content from server, then: SwiftyStoreKit.finishTransaction(purchase.transaction) } print("purchased: \(purchase)") } } }

After this the purchases worked correctly.

@bizz84 Thanks for the reply :) I will add the logging anyway 👍

Hopefully this helps!

TipToeTiger commented 6 years ago

Whoops sorry, didn't mean to close.

bizz84 commented 6 years ago

@TipToeTiger Not calling SwiftyStoreKit.completeTransactions() on startup can cause the purchase confirmation alert to show, but the callback to not be called.

Adding this in your AppDelegate should fix your problem - once you confirm this we can close this issue.

I'm going to add a console log to remind users to call SwiftyStoreKit.completeTransactions().

goloops commented 5 years ago

hi There,

I have the same problem, my app is an iMessage app extension app. If that's the case where should i insert the above code? *sorry i am only a beginner in programming. Thank you for the help :)

TipToeTiger commented 5 years ago

hi There,

I have the same problem, my app is an iMessage app extension app. If that's the case where should i insert the above code? *sorry i am only a beginner in programming. Thank you for the help :)

You will want to add it into your app delegate. In the didFinishLaunchingWithOptions function.

Hope this helps!

goloops commented 5 years ago
untitled untitled1 untitled3

These are what I have in my code. Mine is an iMessage extension, and i only have access to viewdidload function.

TipToeTiger commented 5 years ago

This is what I have in my appdelegate. No idea if it will work in your case, but give it a go anyway! Maybe try popping it in viewdidload or viewwillload?

EDIT: The layout keeps screwing up sorry!

SwiftyStoreKit.completeTransactions(atomically: true) { purchases in for purchase in purchases {

if purchase.transaction.transactionState == .purchased || purchase.transaction.transactionState == .restored { if purchase.needsFinishTransaction { // Deliver content from server, then: SwiftyStoreKit.finishTransaction(purchase.transaction)

                }
                print("purchased: \(purchase)")
            }
        }
    }
goloops commented 5 years ago

ok thank you, let me give it a try.

goloops commented 5 years ago

Hi there, it seems that the code can fix the purchase problem. But I still have another problem where every time there is an app update, all the previously bought stickers are locked again and restore button doesn’t do the job. User must press the buy button and grant payment before the “ you have bought it before, do you want to get it for free?” Message pops out. Can anyone help me with this pleaseeee?