GithubHelp home page GithubHelp logo

Comments (8)

russell-archer avatar russell-archer commented on June 15, 2024 1

Ah, I see what you're trying to do now. I didn't realize you were going to define your own additional paymentQueue(_:shouldAddStorePayment:for:) in your own code.

So, what is needed is for StoreHelper to define paymentQueue(_:shouldAddStorePayment:for:) for the majority of cases where custom handling is NOT required, plus have the ability for a closure to be called in cases like yours where custom handling IS required. Like this?:

public typealias ShouldAddStorePaymentHandler = (_ payment: SKPayment, _ product: SKProduct) -> Bool

public class StoreHelper: ObservableObject {
    :
    /// Optional support for overriding handling of direct App Store purchases of in-app purchase promotions.
    /// See `AppStoreHelper.paymentQueue(_:shouldAddStorePayment:for:)`.
    public var shouldAddStorePaymentHandler: ShouldAddStorePaymentHandler?
    :
}

public class AppStoreHelper: NSObject, SKPaymentTransactionObserver {
    private weak var storeHelper: StoreHelper?
    :
    public func paymentQueue(
        _ queue: SKPaymentQueue, 
        shouldAddStorePayment payment: SKPayment, 
        for product: SKProduct) -> Bool {

        if let handler = storeHelper?.shouldAddStorePaymentHandler { return handler(payment, product) }
        return true
    }
}

// Usage example:

struct StoreHelperDemoApp: App {
    @StateObject var storeHelper = StoreHelper()
    
    var body: some Scene {
        WindowGroup {
            MainView()
                .environmentObject(storeHelper)
                .task {
                    storeHelper.start()  // Start listening for transactions
                    
                    // Custom handling of direct App Store purchases of IAP promotions
                    storeHelper.shouldAddStorePaymentHandler = { payment, product in
                        // Custom handling goes here
                        return false
                    }
                }
        }
    }
}

I think this looks like the right solution! Let me know what you think :-)

from storehelper.

russell-archer avatar russell-archer commented on June 15, 2024 1

Done! Thanks for your patience while I figured out the best solution!

from storehelper.

russell-archer avatar russell-archer commented on June 15, 2024

The paymentQueue(_:shouldAddStorePayment:for:) method is a StoreKit1 thing. It's ONLY required if you have in-app purchase promotions on the App Store and the user attempts to purchase something outside of your app, directly on the App Store. Any other type of in-app purchase that happens uses StoreKit2 and does not involve the paymentQueue(_:shouldAddStorePayment:for:) method at all.

If the user tries to purchase an in-purchase promotion, your app's paymentQueue(_:shouldAddStorePayment:for:) method is called. At that point all we have to do is return true to indicate to the App Store that you wish to proceed with the purchase. The purchase is then handled via StoreHelper's StoreKit1 paymentQueue(_:updatedTransactions) method. As you can see below, all that happens then is that we finish the transaction and then let StoreHelper's StoreKit2-related code that a purchase has happened:

    /// Delegate method for the StoreKit1 payment queue. Note that because our main StoreKit processing is done
    /// via StoreKit2 in StoreHelper, all we have to do here is signal to StoreKit1 to finish purchased, restored
    /// or failed transactions. StoreKit1 purchases are immediately available to StoreKit2 (and vice versa), so
    /// any purchase will be picked up by StoreHelper as required.
    /// - Parameters:
    ///   - queue: StoreKit1 payment queue
    ///   - transactions: Collection of updated transactions (e.g. `purchased`)
    public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {
            switch (transaction.transactionState) {
                case .purchased:
                    SKPaymentQueue.default().finishTransaction(transaction)
                    // Tell StoreKit2-based StoreHelper about purchase
                    Task.init { await storeHelper?.productPurchased(transaction.payment.productIdentifier) }  
                case .restored: fallthrough
                case .failed: SKPaymentQueue.default().finishTransaction(transaction)
                default: break
            }
        }
    }

If you return false from paymentQueue(_:shouldAddStorePayment:for:) you are signalling to the App Store that you want to defer or cancel the purchase. To quote Apple's documentation (https://developer.apple.com/documentation/storekit/skpaymenttransactionobserver/2877502-paymentqueue):

**If you return false, you can continue the transaction later by manually adding the SKPayment payment to the SKPaymentQueue queue. **

So, you'd have cache the payment object and then later re-add it like this: SKPaymentQueue.default().add(payment).

Is this what you want? If so, I can't see why you'd want to do this, because the standard purchase screens would still be displayed.

Perhaps I've misunderstood your requirements. If you're looking to create your own custom purchase page, rather than using StoreHelper's UI, that's very straightforward. Take a look at the SimplePurchaseView example in the StoreHelperDemo project: https://github.com/russell-archer/StoreHelperDemo/blob/main/Shared/SimplePurchaseView.swift

Let me know if I've not understood what you're looking for!

from storehelper.

jasudev avatar jasudev commented on June 15, 2024
shouldAddStorePayment

First of all, thank you for your detailed and kind explanation. What I intended is the following.
When a user selects an in-app purchase promotion from the app store, installs the app from the app store, and enters the app, it is automatically called through the shouldAddStorePayment method.

At this time, if the value returned by the shouldAddStorePayment method is true, the IAP payment bottom sheet provided by the system is exposed, and if false is returned, the bottom sheet is not exposed.

My intention is to expose a separate custom purchase page without entering the bottom sheet in-app payment process by returning false through the shouldAddStorePayment method.

Normally, the shouldAddStorePayment method is a method that is automatically executed by the system, so you can process the return as false and handle the function of exposing a separate custom page within the method.

The reason for this is that even if a user enters through an in-app purchase in the app store, the probability of making a purchase right away is low because the information provided by the system's in-app purchase process (bottom sheet) is limited. Therefore, it is intended to provide details about in-app billing on a separate page, and to provide users with a way to press the checkout button.

In StoreHelper, since the shouldAddStorePayment method returns true, even if a custom page is exposed by implementing the shouldAddStorePayment method externally, there is an issue that the system directly enters the in-app payment process.

I'm not sure if my explanation made sense.
I hope my intentions are conveyed. :)

from storehelper.

russell-archer avatar russell-archer commented on June 15, 2024

Ah! I understand now! Thanks for the explanation :-)

I'll implement something configurable so that you get the choice to have StoreHelper behave as it does now, or to allow for custom processing.

from storehelper.

russell-archer avatar russell-archer commented on June 15, 2024

I've implemented a configurable setting customDirectAppStorePurchasing (see Configuration) that allows you to turn on/off custom processing for direct App Store purchases.

However, I'm not clear on exactly how you'd implement your custom purchase functionality. So I'm not sure how you intend to manually re-add the SKPayment payment object to the SKPaymentQueue queue using SKPaymentQueue.default().add(payment).

Is this something I need to do in StoreHelper, or does your code handle it?

from storehelper.

jasudev avatar jasudev commented on June 15, 2024

I've implemented a configurable setting customDirectAppStorePurchasing (see Configuration) that allows you to turn on/off custom processing for direct App Store purchases.

However, I'm not clear on exactly how you'd implement your custom purchase functionality. So I'm not sure how you intend to manually re-add the SKPayment payment object to the SKPaymentQueue queue using SKPaymentQueue.default().add(payment).

Is this something I need to do in StoreHelper, or does your code handle it?

After testing, I confirmed that the shouldAddStorePayment method additionally defined externally may not be executed because shouldAddStorePayment has already been defined in StoreHelper.

So, rather than defining it as a customDirectAppStorePurchasing setting value, I think it would be better to pass a closure like this:

public typealias ShouldAddStorePaymentHandler = (_ payment: SKPayment, _ product: SKProduct) -> Bool
···
// How to use :
StoreHelper.shouldAddStorePaymentHandler = { payment, product in
        // Custom code
        return false
}

This is just my opinion. Please note.
Thank you.

from storehelper.

jasudev avatar jasudev commented on June 15, 2024

Great. It seems like a good solution to implement the functionality you need. Thank you. 👍

from storehelper.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.