-
Notifications
You must be signed in to change notification settings - Fork 796
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PaymentQueueController's shouldAddStorePayment method Implementation #240
Comments
@zntfdr thanks for reporting this. Note that this method is optional: @available(iOS 11.0, *)
optional public func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, for product: SKProduct) -> Bool From Apple Docs:
We need to verify that if this method is implemented and returns true, then this delegate method in the payment queue is called: func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) If this is the case, SwiftyStoreKit will then handle this by calling the completion block of Questions
public struct Purchase {
public let productId: String
public let quantity: Int
public let transaction: PaymentTransaction
public let originalTransaction: PaymentTransaction?
public let needsFinishTransaction: Bool
public let isAppStoreInitiated: Bool
} |
Hi Andrea, You're correct: it just calls the method you mentioned. func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) Regarding the other questions:
|
Sounds like the user should be able to register a closure to be called by SwiftyStoreKit when var shouldAddStorePaymentHandler: (_ payment: SKPayment, _ product: SKProduct) -> Bool I'd like to open this for contributions from the community. This is an outline that may be useful to get things started: Summary of requirements
class SwiftyStoreKit {
typealias ShouldAddStorePaymentHandler = (_ payment: SKPayment, _ product: SKProduct) -> Bool
class var shouldAddStorePaymentHandler: ShouldAddStorePaymentHandler? {
didSet {
sharedInstance.paymentQueueController.shouldAddStorePaymentHandler = shouldAddStorePaymentHandler
}
}
}
public func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, for product: SKProduct) -> Bool {
return shouldAddStorePaymentHandler?(payment, product) ?? false
}
|
Was the |
I deleted it because the project now compiles fine on Xcode 9 beta 3. You can point your pod to |
Hey, whats the expected time for this feature to be ready? Also a concern I have after watching the WWDC video: I think App Store IAP is looking if SKPaymentTransactionObserver is implemented in AppDelegate. If it is, its showing the IAPs that can be bought from the store, if not I think its hiding those IAPs. Of course I have no way of testing or confirming this at the moment but something to keep on mind. |
Don't mind that, it's just for simplicity and demo's sake.
This is an open source project! 😀 If you'd like, please feel free to implement it and send a PR 😊 |
Thanks for the answer. I'd love to contribute but I don't understand Apple's crappy StoreKit frameworks to implement them in anyway, thats why I'm using this great library 😄 |
Looks like no willing contributors are to be seen on this one. ;) I’ll try to implement it this week. |
@bizz84 Awesome! I would help but haven't really worked with StoreKit on a low level in a while. Especially because of this package. Thanks for this amazing package!! |
This has now been implemented and is available on version 0.10.6. I have not tested it on device - please let me know if it works ok for you. Once this is confirmed, I will close the issue. |
Great, thanks! Follow up: works for me! 💯 💯 Thanks 🤗 |
Awesome! |
@bizz84 Is documentation going to be updated for this? |
@fishcharlie You're right, I forgot! Will do tomorrow. |
Done. |
@bizz84 So is there a best practice for where to implement |
Hi @fishcharlie , please refer to the dedicated WWDC session to know everything about the new handler. In short, the method is called whenever the user tries to buy an in-app purchase from the App Store (possible only from iOS 11). If we return AFAIK the link in safari is used for testing purposes only (to imitate the in-app purchase from the App Store). I hope this answer clears things up :) |
@zntfdr And with this latest version of SwiftyStoreKit and that video will I be able to implement it all so users can subscribe to my auto-renewal subscription from the app store? |
Yup 😄 . The implementation requires very little effort from your side. Just update your pod and add something like SwiftyStoreKit.shouldAddStorePaymentHandler = { (_ payment: SKPayment, _ product: SKProduct) in
// check if user can purchase the product
return true // or false if user shall not purchase the product yet
} in your codebase. Cheers! |
@zntfdr Ok, I'm just going to have to figure out how to actually handle unlocking everything and the process after going in my app. |
If you already process in-app purchases inside your app, there's nothing else that you must do: Best of luck 💪 💪 |
@zntfdr But it's all linked to a button. The |
Charlie, the |
@zntfdr Is there a best practice for where to set |
Hi Charlie! |
@zntfdr Awesome. Thanks so much. Trying it now. After looking into it more it seems easier than I thought it was. I'm honestly shocked at how easy it looks... kinda crazy |
Haha, sure! |
Standing on the shoulders of giants... 🍏 |
Thanks for the addition of the new feature. In fact, I saw this message in the transcript
To my understanding, I check whether Onboarding is done, and return false if the user is going through it. Do I need to add another code piece to keep the SKPayment object here and submit later? |
@harryworld: |
@zntfdr Thanks for quick reply, I know the implication for returning true. My question is about returning false, which is true in cases like Onboarding. |
@harryworld: Yes, you can keep track of the user wish and trigger a new payment later (suggestion: show something to the user before launching a new purchase, maybe a prompt like "do you still want to purchase ..??"). Note: all this "keep track and launch new purchase later" has to be done by your code/app. Cheers! |
So my question is what kind of API should I use to reuse that |
Yes, as described in the documentation, you must reuse the same SKPayment. |
I'm not so sure about that. https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/PromotingIn-AppPurchases/PromotingIn-AppPurchases.html states:
|
I stand corrected! Thanks (I've updated my previous comment) |
👋 apologies in advance for my newbie questions, but I'm having a hard time figuring out how to accomplish this. Please see this code sample with inline questions: // unable to get this link to work in safari, it just takes me to google?
// itms-services://?action=purchaseIntent&bundleId=com.example.app&productIdentifier=product_name
// where I am so far in my implementation:
import StoreKit
import SwiftyStoreKit
class AppDelegate: SKPaymentTransactionObserver {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
// I think this is correct and working properly, but who knows! :)
SwiftyStoreKit.completeTransactions(atomically: true) { purchases in
for purchase in purchases {
if purchase.transaction.transactionState == .purchased || purchase.transaction.transactionState == .restored {
if purchase.needsFinishTransaction {
SwiftyStoreKit.finishTransaction(purchase.transaction)
}
handlePurchase(purchase.productId)
}
}
}
// if all IAPs would be supported (I only have one, anyway) you can just return true here?
SwiftyStoreKit.shouldAddStorePaymentHandler = { payment, product in
return true
}
}
// ...and here? do I need both this and the above?
func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, for product: SKProduct) -> Bool {
return true
}
// do I need to worry about completing transactions here?
// do I need to verify the receipt here? (I try via a shared function, but not sure if I have the receipt)
// ... or can I see an auto renewing subscription status here, like the expiration date?
// ...or can I just pass to my handlePurchase function if there's a local receipt?
// is transaction.payment.productIdentifier == purchase.productId?
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
if transaction.transactionState == .purchased || transaction.transactionState == .restored {
handlePurchase(transaction.payment.productIdentifier)
}
}
}
}
// for normal IAPs done from within the app, and for completeTransactions, I do something like this:
func handlePurchase(_ productId: String) {
let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: sharedSecret)
SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in
if case .success(let receipt) = result {
let purchaseResult = SwiftyStoreKit.verifySubscription(ofType: .autoRenewable, productId: productId, inReceipt: receipt)
switch purchaseResult {
case .purchased(let expiryDate, _):
PurchaseHandler().setPaidExpiring(expiryDate)
case .expired(let expiryDate, _):
showError("Subscription expired \(expiryDate)", message: "Please renew your membership")
case .notPurchased:
showError("Receipt validation error. Please contact support", message: "Error code: notPurchased")
}
} else if case .error(let error) = result {
showError("Receipt validation error. Please contact support", message: "\(error)")
} else {
showError("Receipt validation error. Please contact support", message: "No details available")
}
}
} Apologies again for the series of questions, but I haven't been able to find any full examples and I'm terribly confused about this. Any help would be appreciated! |
Hi, When using the existing implementation I received this rejection from Apple review... "We noticed that your app did not fully meet the terms and conditions for auto-renewing subscriptions, as specified in Schedule 2, section 3.8(b) of the Paid Applications agreement. When the user initiates an in-app purchase on the App Store, they are taken into your app to continue the transaction. However, information about the subscription must be displayed to the user prior to the purchase: Your app must include links to your privacy policy and Terms of Use, and users should be able to access these links at any time. Next Steps To resolve this issue, please revise your app to include the missing information prior to initiating any auto-renewing subscription purchases within your app." Anyone been through this part of the process? It seems like they are asking me to show the same view controller I do before the user initiates a subscription in-app, which seems a bit daft if jumping in from an App Store request? I think that to implement this I would need SwiftyStoreKit to call a function in my code instead of completing purchase, but I may be wrong? Am I misinterpreting their message? |
Hi
It seems like they are asking me to show the same view controller I do before the user initiates a subscription in-app,l
This is correct. I also got rejected with the same message and this is what I had to do to get approved 😊
…Sent from my iPhone
On 6 Jul 2019, at 22:52, Solublepeter ***@***.***> wrote:
Hi,
When using the existing implementation I received this rejection from Apple review...
"We noticed that your app did not fully meet the terms and conditions for auto-renewing subscriptions, as specified in Schedule 2, section 3.8(b) of the Paid Applications agreement.
When the user initiates an in-app purchase on the App Store, they are taken into your app to continue the transaction.
However, information about the subscription must be displayed to the user prior to the purchase:
• Title of publication or service
• Length of subscription (time period and content or services provided during each subscription period)
Price of subscription, and price per unit if appropriate
Your app must include links to your privacy policy and Terms of Use, and users should be able to access these links at any time.
Next Steps
To resolve this issue, please revise your app to include the missing information prior to initiating any auto-renewing subscription purchases within your app."
Anyone been through this part of the process? It seems like they are asking me to show the same view controller I do before the user initiates a subscription in-app, which seems a bit daft if jumping in from an App Store request?
I think that to implement this I would need SwiftyStoreKit to call a function in my code instead of completing purchase, but I may be wrong? Am I misinterpreting their message?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
Thanks, that’s really helpful, can I ask how you approached it? By which I mean- did you add your own handler for the system call, or modify your copy of SwiftStoreKit? |
When I got this rejection, I didn't bothered doing it in the proper way. This is my code: SwiftyStoreKit.shouldAddStorePaymentHandler = { [weak self] _, _ in
self?.showUpgrade()
return false
} In short, when the app receives a prompt to upgrade started from the App Store, I show the upgrade screen as if it was the user opening it from within the app. Previously this was required as Apple wanted you to show all the subscription legalese stuff. |
--Can u explain brief. same view controller means which view subscription page or payment page? |
@zntfdr Hi my app first screen is already ProScreen so can i write this code in didFinishLaunchingWithOptions or should i still need to show ProScreen again in this method ? |
Platform
Version
ℹ swift-4.0 branch
Report
Issue summary
ℹ As of iOS 11, we have a new optional paymentQueue method that SwiftyStoreKit's
PaymentQueueController
can implement.What did you expect to happen
ℹ As far as I can tell,
PaymentQueueController
is an internal class (not accessible from outside) therefore there's no way to implement this in our app.What happened instead
ℹ I simply added this code inside
SwiftyStoreKit/PaymentQueueController.swift
:Question
Is there a better way to do this? What I did above works as long as I don't update the pods: then I will have to do it again (for each
$ pod update
).The text was updated successfully, but these errors were encountered: