InAppPurchase - A Simple and Lightweight framework for In App Purchase

Overview

InAppPurchase

Build Status Carthage compatible Version Platform codecov

A Simple, Lightweight and Safe framework for In App Purchase

Feature

  • Simple and Light πŸ‘
  • Support Promoting In-App Purchases πŸ’°
  • No need to consider StoreKit! 😎
  • High coverage and safe βœ…

Installation

Carthage

github "jinSasaki/InAppPurchase"

CocoaPods

pod "InAppPurchase"

Usage

Setup Observer

NOTE: This method should be called at launch.

let iap = InAppPurchase.default
iap.addTransactionObserver(fallbackHandler: {
    // Handle the result of payment added by Store
    // See also `InAppPurchase#purchase`
})

If you want to detect the unexpected transactions, pass addTransactionObserver() with fallbackHandler.
For example, your app requested a payment, but it crashed in that process. That transaction is not finished, and then will receive at next launch.
This fallbackHandler is called when any handlers are not set to InAppPurchase via purchase(productIdentifier: handler:) method and so on.

Promoting In App Purchases is available from iOS 11. InAppPurchase supports it!

Add observer with shouldAddStorePaymentHandler.
See also SKPaymentTransactionObserver#paymentQueue(_:shouldAddStorePayment:for:)and Promoting In-App Purchases Guides

promoting

let iap = InAppPurchase.default
iap.set(shouldAddStorePaymentHandler: { (product) -> Bool in
    // Return whether starting payment
}, handler: { (result) in
    // Handle the result of payment added by Store
    // See also `InAppPurchase#purchase`
})

⚠️ Do not use Product#priceLocale

Only if purchase via AppStore Promoting, SKProduct#priceLocale has been not initialized. It occurs a BAD_ACCESS crash. This is a StoreKit bug. InAppPurchace resolved the crash that is occurred when received the payment, but it occurs when accessed Product#priceLocale yet. So, I recommend not to use Product#priceLocale in AppStore Promoting Payment process.

Stop payment observing if needed.

let iap = InAppPurchase.default
iap.removeTransactionObserver()

Fetch Product Information

let iap = InAppPurchase.default
iap.fetchProduct(productIdentifiers: ["PRODUCT_ID"], handler: { (result) in
    switch result {
    case .success(let products):
        // Use products
    case .failure(let error):
        // Handle `InAppPurchase.Error`
    }
})

Restore Completed Transaction

let iap = InAppPurchase.default
iap.restore(handler: { (result) in
    switch result {
    case .success(let productIds):
        // Restored with product ids
    case .failure(let error):
        // Handle `InAppPurchase.Error`
    }
})

Purchase

let iap = InAppPurchase.default
iap.purchase(productIdentifier: "PRODUCT_ID", handler: { (result) in
    // This handler is called if the payment purchased, restored, deferred or failed.

    switch result {
    case .success(let response):
        // Handle `PaymentResponse`
    case .failure(let error):
        // Handle `InAppPurchase.Error`
    }
})

For Dependency Injection

The purchase logic in the App should be safe and testable.

For example, you implemented a class to execute In-App-Purchase as follows.

// PurchaseService.swift

import Foundation
import InAppPurchase

final class PurchaseService {
    static let shared = PurchaseService()

    func purchase() {
        // Purchase with `InAppPurchase`
        InAppPurchase.default.purchase(productIdentifier: ...) {
            // Do something
        }
    }
}

It is hard to test this class because using the InAppPurchase.default in the purchase process.

This PurchaseService can be refactored to inject the dependency.
Use InAppPurchaseProvidable protocol.

// PurchaseService.swift

import Foundation
import InAppPurchase

final class PurchaseService {
    static let shared = PurchaseService()

    let iap: InAppPurchaseProvidable

    init(iap: InAppPurchaseProvidable = InAppPurchase.default) {
        self.iap = iap
    }

    func purchase() {
        // Purchase with `InAppPurchase`
        iap.purchase(productIdentifier: ...) {
            // Do something
        }
    }
}

And then you can test PurchaseService easily with InAppPurchaseStubs.framework.

// PurchaseServiceTests.swift

import XCTest
@testable import YourApp
import InAppPurchaseStubs

// Test
final class PurchaseServiceTests: XCTestCase {
    func testPurchase() {
        let expectation = self.expectation(description: "purchase handler was called.")
        let iap = StubInAppPurchase(purchaseHandler: { productIdentifier, handler in
            // Assert productIdentifier, handler, and so on.
        })
        let purchaseService = PurchaseService(iap: iap)
        purchaseService.purchase(productIdentifier: ...) {
            // Assert result
            expectation.fulfill()
        }

        wait(for: [expectation], timeout: 1)
    }
}

If you want more information for test, see also InAppPurchaseStubs and Tests.

Requirements

  • iOS 9.0+
  • Xcode 9+
  • Swift 4+

License

MIT

Comments
  • Purchase method's completion handler is not called

    Purchase method's completion handler is not called

    Describe the bug Neither the success nor the failure are called. I've completed a successful purchase many times, but I can't do anything after the purchase has completed.

    To Reproduce My sample code.

        static func removeAds() {
            let iap = InAppPurchase.default
            iap.addTransactionObserver()
            iap.purchase(productIdentifier: "1") { result in
                switch result {
                case .success(let state):
                    print("SUCCESS: \(state)")
                    UserDefaults.standard.removeAds = true
                case .failure(let error):
                    print(error)
                }
            }
            iap.removeTransactionObserver()
        }
    

    Is something wrong my code?

    Screenshots IMG_3627 UNADJUSTEDNONRAW_thumb_55df

    Desktop (please complete the following information):

    • iOS 12

    Smartphone (please complete the following information):

    • iOS 7 Device
    question waiting-for-reply 
    opened by ArtSabintsev 4
  • fetchProduct Error

    fetchProduct Error

    Here is my purchase button, first I attempt to see if my IAP is reachable. it's not!

    @IBAction func purchase(_ sender: Any) {
            let iap = InAppPurchase.default
            iap.fetchProduct(productIdentifiers: ["AppUnlock"], handler: { (result) in
                switch result {
                case .success(let products):
                    print("ok")
                // Use products
                case .failure(let error):
                    print(error)
                    // Handle `InAppPurchase.Error`
    
                }
            })
        }
    
    

    Here is my IAP in iTunes Connect.

    image

    I receive the error:

    invalid(["AppUnlock"])

    ....

    What's going on here?

    waiting-for-reply 
    opened by bloodmajik 4
  • Make PaymentState.state and transaction public.

    Make PaymentState.state and transaction public.

    Hi, with InAppPurchase v2.1.4. I did validate the transaction which returned by the callback of addTransactionObserver. After v2.3.0, I found I can't do that anymore.

    image

    It will be work after making state and transaction public. Thanks.

    opened by yanyin1986 3
  • Cocoapod can't install latest version 2.2.0

    Cocoapod can't install latest version 2.2.0

    Describe the bug Cocoapod can't install latest version 2.2.0

    CocoaPods could not find compatible versions for pod "InAppPurchase":
      In Podfile:
        InAppPurchase (= 2.1.5)
    

    or 2.2.0

    To Reproduce Steps to reproduce the behavior:

    1. pod 'InAppPurchase', '2.1.5'
    2. pod install

    Expected behavior Version 2.1.5 or 2.2.0 to be installed

    Smartphone (please complete the following information):

    • Device: Any
    • OS: iOS 13
    • XCode: 11.1
    waiting-for-reply 
    opened by netgfx 2
  • 'PeriodUnit' is only available on iOS 11.2 or newer

    'PeriodUnit' is only available on iOS 11.2 or newer

    When i try to build a project with deployment target 9, i get a compile error:

    Product.swift:66:28: 'PeriodUnit' is only available on iOS 11.2 or newer
    

    Your readme says the requirement is iOS 9.0+.

    I'm on macOS Mojave, Xcode 10.

    opened by erf 2
  • Cocoapod iOS version change

    Cocoapod iOS version change

    I used InAppPurchase in my podspec and this error shows

    ERROR | [iOS] xcodebuild: InAppPurchase/Sources/Product.swift:66:28: error: 'PeriodUnit' is only available on iOS 11.2 or newer

    so can you change the ' s.platform = :ios, '9.0'' to " s.platform = :ios, '11.2' " in InAppPurchase.podspec

    thank you

    opened by cillyfly 2
  • cocoapods not working

    cocoapods not working

    I added this line to my podfile:

    pod 'InAppPurchase'

    then call "pod install" and I get this error:

    [!] Unable to find a specification forInAppPurchase``

    My other pod dependencies work correctly (ie. PromiseKit, SwiftyJSON, etc)

    waiting-for-reply 
    opened by dboydor 2
  • Include the transaction with PaymentState.restored

    Include the transaction with PaymentState.restored

    Helps with #26 but not a complete solution, as the product IDs are not returned in the restore handler. Rather, you would switch on the payment state in the fallback handler.

    InAppPurchase.default.addTransactionObserver(fallbackHandler: { result in
        switch result {
        case .success(let state):
            switch state {
            case .purchased(let transaction), .restored(let transaction):
                // update the local cache (UserDefaults, keychain, etc.)
            // ...
            }
        // ...
        }
    })
    
    InAppPurchase.default.restore()
    
    opened by swoolcock 1
  • How to disable sandbox env on TestFlight?

    How to disable sandbox env on TestFlight?

    I uploaded a version to the TestFlight version, ready to submit to the Apple Store. But in-app purchases are in the Sandbox environment, how do I turn off the Sandbox mode?

    Snipaste_2022-08-14_09-40-41

    opened by cdoky 0
  • XCode 13 'Product' is ambiguous for type lookup in this context

    XCode 13 'Product' is ambiguous for type lookup in this context

    Describe the bug I get an error about 'Product' is ambiguous for type lookup in this context

    To Reproduce Steps to reproduce the behavior: Any mention of Product

    Expected behavior Not throw an error, maybe it needs to be renamed?

    Screenshots 9076F1E9-F4FD-43B7-B540-691B3AD0BC4A_4_5005_c

    Desktop (please complete the following information):

    • OS: iOS
    • Browser Mobile
    • Version XCode 13.0
    opened by netgfx 3
  • Xcode:

    Xcode: "found 1 file(s) which are unhandled"

    Describe the bug I am using InAppPurchase in my apps, via SPM. Somehow, on a successful build, a warning pops up saying that 1 file, which is this library's Info.plist. Functionality unaffected though.

    To Reproduce Steps to reproduce the behavior:

    1. Add library to project, via SPM select InAppPurchase
    2. Build

    Screenshots Screen Shot 2021-06-02 at 10 49 02 PM

    Desktop (please complete the following information):

    • OS: macOS 11.2.3
    • Xcode: 12.5

    Smartphone (please complete the following information):

    • Device: iPhone 11 Pro Max
    • OS: iOS 14.5.1
    opened by vincentneo 0
  • Request: Swift Package Manager support

    Request: Swift Package Manager support

    Is your feature request related to a problem? Please describe. I currently can't use PM to download your package. It would be great to get PM support to download this library via Swift PM. When I try

    What I see when I try to download from your git repo directly from Xcode: Screen Shot 2020-02-23 at 10 50 54 AM

    What I would like to see eventually: Screen Shot 2020-02-23 at 10 44 27 AM

    Describe the solution you'd like While I have not made one myself, I founded this article as a possible start point: https://www.swiftbysundell.com/articles/managing-dependencies-using-the-swift-package-manager/

    My general understanding is that a manifest needs to be made along with some other tweaks.

    BTW Thank you so much for making such a useful library. I've used it a lot in the past.

    opened by kotarofujita 1
Releases(2.8.0)
Owner
Jin Sasaki
iOS Application Developer
Jin Sasaki
Apphud SDK is a lightweight open-source Swift library to manage auto-renewable subscriptions and other in-app purchases in your iOS app.

Apphud SDK Apphud SDK is a lightweight open-source Swift library to manage auto-renewable subscriptions and other in-app purchases in your iOS app. No

Apphud 143 Dec 16, 2022
Mercato is a lightweight In-App Purchases (StoreKit 2) library for iOS, tvOS, watchOS, macOS, and Mac Catalyst.

Mercato Mercato is a lightweight In-App Purchases (StoreKit 2) library for iOS, tvOS, watchOS, macOS, and Mac Catalyst. Installation Swift Package Man

Pavel T 49 Jan 4, 2023
Apple's Framework to support in-app purchases and interaction with the App Store

Apple's Framework to support in-app purchases and interaction with the App Store.

paigeshin 0 Dec 5, 2021
🍎 An App to check whether a non-App Store app is in App Store.

AppStorify ?? An App to check whether a non-App Store app is in App Store. Benfits Use App Store's upgrade mechanism instead of app's. App Store apps

seedgou 58 Dec 7, 2022
MerchantKit - A modern In-App Purchases management framework for iOS developers

MerchantKit dramatically simplifies the work indie developers have to do in order to add premium monetizable components to their applications. Track purchased products, offer auto-renewing subscriptions, restore transactions, and much more.

Benjamin Mayo 1.1k Dec 17, 2022
An easy way to access reviews for your app instead of writing repetitive and redundant codes for every app.

AppStoreReviewManager An easy way to access reviews for your app instead of writing repetitive and redundant codes for every app. Requirements iOS 9.0

Jinya 4 Dec 23, 2022
Appirater - A utility that reminds your iPhone app's users to review the app.

Introduction Appirater is a class that you can drop into any iPhone app (iOS 4.0 or later) that will help remind your users to review your app on the

Arash Payan 4.7k Jan 7, 2023
Harpy - Notify users when a new version of your app is available and prompt them to upgrade.

After 6 years of Harpy and 4 years of Siren, I have decided to deprecate Harpy in favor of Siren. Why? Siren is written in Swift and has a feature set

Arthur Ariel Sabintsev 2.6k Dec 29, 2022
Siren - Notify users when a new version of your app is available and prompt them to upgrade.

Siren ?? Notify users when a new version of your app is available and prompt them to upgrade. Table of Contents Meta About Features Screenshots Ports

Arthur Ariel Sabintsev 4.1k Dec 27, 2022
An App where you can find product and see its detail.

MeliProductos Project ???? > An App where you can find product and see its detail. ???? > Una App donde puedes encontrar tus productos y ver su detall

Joaquin Segovia 3 May 6, 2022
AppVersion - Keep users on the up-to date version of your App.

?? App Version Don't let you users to get stuck on outdated version of your app. Automatic update tracking using Semantic Versioning Buil-in UI alerts

Ameba Labs 31 Sep 30, 2022
TPInAppReceipt is a lightweight, pure-Swift library for reading and validating Apple In App Purchase Receipt locally.

TPInAppReceipt is a lightweight, pure-Swift library for reading and validating Apple In App Purchase Receipt locally. Features Read all

Pavel T 520 Jan 4, 2023
In App Purchase Manager framework for iOS

InAppFramework In App Purchase Manager framework for iOS Disclaimer I know it's been too long since the last update, quite a few things happened in my

SΓ‘ndor Gyulai 40 May 23, 2020
IOS15-SwiftUI-InAppPurchaseDemo - In-App Purchase Demo app written with SwiftUI

iOS15-SwiftUI-InAppPurchaseDemo In-App Purchase Demo app written with SwiftUI If

null 5 Jul 20, 2022
iOS SDK for cross-platform in-app purchase and subscription infrastructure, revenue analytics, engagement automation, and integrations

Qonversion is the data platform to power in-app subscription revenue growth. fast in-app subscriptions implementation back-end infrastructure to valid

Qonversion 253 Dec 18, 2022
Ruby Gem for Rails - Easy iTunes In-App Purchase Receipt validation, including auto-renewable subscriptions

Monza is a ruby gem that makes In-App Purchase receipt and Auto-Renewable subscription validation easy. You should always validate receipts on the ser

Gabriel 159 Jan 7, 2023
A jailed in-app purchase cracker for iOS 12.2-15.6

Satella Jailed For, um, educational purposes only or something. Definitely don't use this to pirate in-app purchases in apps to which you don't have l

Lilly 314 Dec 31, 2022
Lightweight & Simple Framework For Creating Message App

Lightweight & Simple Framework For Creating Message App

Frameworks 3 Nov 14, 2021
Simple, lightweight and flexible debug logging framework written in Swift

AELog Simple, lightweight and flexible debug logging minion written in Swift If you find yourself in upcoming statements, then you probably want to us

Marko Tadić 28 Jul 6, 2022
A simple and lightweight matching library for XCTest framework.

Match A simple and lightweight matching library for XCTest framework. Getting started Swift Package Manager You can add Match to your project by addin

MichaΕ‚ Tynior 6 Oct 23, 2022