SwiftyStoreKit is a lightweight In App Purchases framework for iOS, tvOS, watchOS, macOS, and Mac Catalyst ⛺

Overview

License Platform Language Build Issues Slack

SwiftyStoreKit is a lightweight In App Purchases framework for iOS, tvOS, watchOS, macOS, and Mac Catalyst.

Features

  • Super easy-to-use block-based API
  • Support for consumable and non-consumable in-app purchases
  • Support for free, auto-renewable and non-renewing subscriptions
  • Support for in-app purchases started in the App Store (iOS 11)
  • Support for subscription discounts and offers
  • Remote receipt verification
  • Verify purchases, subscriptions, subscription groups
  • Downloading content hosted with Apple
  • iOS, tvOS, watchOS, macOS, and Catalyst compatible

Contributions Wanted

SwiftyStoreKit makes it easy for an incredible number of developers to seemlessly integrate in-App Purchases. This project, however, is now community-led. We need help building out features and writing tests (see issue #550).

Maintainers Wanted

The author is no longer maintaining this project actively. If you'd like to become a maintainer, join the Slack workspace and enter the #maintainers channel. Going forward, SwiftyStoreKit should be made for the community, by the community.

More info here: The Future of SwiftyStoreKit: Maintainers Wanted.

Requirements

If you've shipped an app in the last five years, you're probably good to go. Some features (like discounts) are only available on new OS versions, but most features are available as far back as:

iOS watchOS tvOS macOS Mac Catalyst
8.0 6.2 9.0 10.10 13.0

Installation

There are a number of ways to install SwiftyStoreKit for your project. Swift Package Manager, CocoaPods, and Carthage integrations are the preferred and recommended approaches.

Regardless, make sure to import the project wherever you may use it:

import SwiftyStoreKit

Swift Package Manager

The Swift Package Manager is a tool for automating the distribution of Swift code and is integrated into Xcode and the Swift compiler. This is the recommended installation method. Updates to SwiftyStoreKit will always be available immediately to projects with SPM. SPM is also integrated directly with Xcode.

If you are using Xcode 11 or later:

  1. Click File
  2. Swift Packages
  3. Add Package Dependency...
  4. Specify the git URL for SwiftyStoreKit.
https://github.com/bizz84/SwiftyStoreKit.git

Carthage

To integrate SwiftyStoreKit into your Xcode project using Carthage, specify it in your Cartfile:

github "bizz84/SwiftyStoreKit"

NOTE: Please ensure that you have the latest Carthage installed.

CocoaPods

SwiftyStoreKit can be installed as a CocoaPod and builds as a Swift framework. To install, include this in your Podfile.

use_frameworks!

pod 'SwiftyStoreKit'

Contributing

Got issues / pull requests / want to contribute? Read here.

Documentation

Full documentation is available on the SwiftyStoreKit Wiki. As SwiftyStoreKit (and Apple's StoreKit) gains features, platforms, and implementation approaches, new information will be added to the Wiki. Essential documentation is available here in the README and should be enough to get you up and running.

App startup

Complete Transactions

Apple recommends to register a transaction observer as soon as the app starts:

Adding your app's observer at launch ensures that it will persist during all launches of your app, thus allowing your app to receive all the payment queue notifications.

SwiftyStoreKit supports this by calling completeTransactions() when the app starts:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
	// see notes below for the meaning of Atomic / Non-Atomic
	SwiftyStoreKit.completeTransactions(atomically: true) { purchases in
	    for purchase in purchases {
	        switch purchase.transaction.transactionState {
	        case .purchased, .restored:
	            if purchase.needsFinishTransaction {
	                // Deliver content from server, then:
	                SwiftyStoreKit.finishTransaction(purchase.transaction)
	            }
	            // Unlock content
	        case .failed, .purchasing, .deferred:
	            break // do nothing
	        }
	    }
	}
    return true
}

If there are any pending transactions at this point, these will be reported by the completion block so that the app state and UI can be updated.

If there are no pending transactions, the completion block will not be called.

Note that completeTransactions() should only be called once in your code, in application(:didFinishLaunchingWithOptions:).

Purchases

Retrieve products info

SwiftyStoreKit.retrieveProductsInfo(["com.musevisions.SwiftyStoreKit.Purchase1"]) { result in
    if let product = result.retrievedProducts.first {
        let priceString = product.localizedPrice!
        print("Product: \(product.localizedDescription), price: \(priceString)")
    }
    else if let invalidProductId = result.invalidProductIDs.first {
        print("Invalid product identifier: \(invalidProductId)")
    }
    else {
        print("Error: \(result.error)")
    }
}

Purchase a product (given a product id)

  • Atomic: to be used when the content is delivered immediately.
SwiftyStoreKit.purchaseProduct("com.musevisions.SwiftyStoreKit.Purchase1", quantity: 1, atomically: true) { result in
    switch result {
    case .success(let purchase):
        print("Purchase Success: \(purchase.productId)")
    case .error(let error):
        switch error.code {
        case .unknown: print("Unknown error. Please contact support")
        case .clientInvalid: print("Not allowed to make the payment")
        case .paymentCancelled: break
        case .paymentInvalid: print("The purchase identifier was invalid")
        case .paymentNotAllowed: print("The device is not allowed to make the payment")
        case .storeProductNotAvailable: print("The product is not available in the current storefront")
        case .cloudServicePermissionDenied: print("Access to cloud service information is not allowed")
        case .cloudServiceNetworkConnectionFailed: print("Could not connect to the network")
        case .cloudServiceRevoked: print("User has revoked permission to use this cloud service")
        default: print((error as NSError).localizedDescription)
        }
    }
}
  • Non-Atomic: to be used when the content is delivered by the server.
SwiftyStoreKit.purchaseProduct("com.musevisions.SwiftyStoreKit.Purchase1", quantity: 1, atomically: false) { result in
    switch result {
    case .success(let product):
        // fetch content from your server, then:
        if product.needsFinishTransaction {
            SwiftyStoreKit.finishTransaction(product.transaction)
        }
        print("Purchase Success: \(product.productId)")
    case .error(let error):
        switch error.code {
        case .unknown: print("Unknown error. Please contact support")
        case .clientInvalid: print("Not allowed to make the payment")
        case .paymentCancelled: break
        case .paymentInvalid: print("The purchase identifier was invalid")
        case .paymentNotAllowed: print("The device is not allowed to make the payment")
        case .storeProductNotAvailable: print("The product is not available in the current storefront")
        case .cloudServicePermissionDenied: print("Access to cloud service information is not allowed")
        case .cloudServiceNetworkConnectionFailed: print("Could not connect to the network")
        case .cloudServiceRevoked: print("User has revoked permission to use this cloud service")
        default: print((error as NSError).localizedDescription)
        }
    }
}

Additional Purchase Documentation

These additional topics are available on the Wiki:

Restore Previous Purchases

According to Apple - Restoring Purchased Products:

In most cases, all your app needs to do is refresh its receipt and deliver the products in its receipt. The refreshed receipt contains a record of the user’s purchases in this app, on this device or any other device.

Restoring completed transactions creates a new transaction for every completed transaction the user made, essentially replaying history for your transaction queue observer.

See the Receipt Verification section below for how to restore previous purchases using the receipt.

This section shows how to restore completed transactions with the restorePurchases method instead. When successful, the method returns all non-consumable purchases, as well as all auto-renewable subscription purchases, regardless of whether they are expired or not.

  • Atomic: to be used when the content is delivered immediately.
0 { print("Restore Success: \(results.restoredPurchases)") } else { print("Nothing to Restore") } } ">
SwiftyStoreKit.restorePurchases(atomically: true) { results in
    if results.restoreFailedPurchases.count > 0 {
        print("Restore Failed: \(results.restoreFailedPurchases)")
    }
    else if results.restoredPurchases.count > 0 {
        print("Restore Success: \(results.restoredPurchases)")
    }
    else {
        print("Nothing to Restore")
    }
}
  • Non-Atomic: to be used when the content is delivered by the server.
0 { for purchase in results.restoredPurchases { // fetch content from your server, then: if purchase.needsFinishTransaction { SwiftyStoreKit.finishTransaction(purchase.transaction) } } print("Restore Success: \(results.restoredPurchases)") } else { print("Nothing to Restore") } } ">
SwiftyStoreKit.restorePurchases(atomically: false) { results in
    if results.restoreFailedPurchases.count > 0 {
        print("Restore Failed: \(results.restoreFailedPurchases)")
    }
    else if results.restoredPurchases.count > 0 {
        for purchase in results.restoredPurchases {
            // fetch content from your server, then:
            if purchase.needsFinishTransaction {
                SwiftyStoreKit.finishTransaction(purchase.transaction)
            }
        }
        print("Restore Success: \(results.restoredPurchases)")
    }
    else {
        print("Nothing to Restore")
    }
}

What does atomic / non-atomic mean?

For more information about atomic vs. non-atomic restorations, view the Wiki page here.

Downloading content hosted with Apple

More information about downloading hosted content is available on the Wiki.

To start downloads (this can be done in purchaseProduct(), completeTransactions() or restorePurchases()):

SwiftyStoreKit.purchaseProduct("com.musevisions.SwiftyStoreKit.Purchase1", quantity: 1, atomically: false) { result in
    switch result {
    case .success(let product):
        let downloads = purchase.transaction.downloads
        if !downloads.isEmpty {
            SwiftyStoreKit.start(downloads)
        }
    case .error(let error):
        print("\(error)")
    }
}

To check the updated downloads, setup a updatedDownloadsHandler block in your AppDelegate:

SwiftyStoreKit.updatedDownloadsHandler = { downloads in
    // contentURL is not nil if downloadState == .finished
    let contentURLs = downloads.flatMap { $0.contentURL }
    if contentURLs.count == downloads.count {
        // process all downloaded files, then finish the transaction
        SwiftyStoreKit.finishTransaction(downloads[0].transaction)
    }
}

To control the state of the downloads, SwiftyStoreKit offers start(), pause(), resume(), cancel() methods.

Receipt verification

This helper can be used to retrieve the (encrypted) local receipt data:

let receiptData = SwiftyStoreKit.localReceiptData
let receiptString = receiptData.base64EncodedString(options: [])
// do your receipt validation here

However, the receipt file may be missing or outdated. Use this method to get the updated receipt:

SwiftyStoreKit.fetchReceipt(forceRefresh: true) { result in
    switch result {
    case .success(let receiptData):
        let encryptedReceipt = receiptData.base64EncodedString(options: [])
        print("Fetch receipt success:\n\(encryptedReceipt)")
    case .error(let error):
        print("Fetch receipt failed: \(error)")
    }
}

Use this method to (optionally) refresh the receipt and perform validation in one step.

let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: "your-shared-secret")
SwiftyStoreKit.verifyReceipt(using: appleValidator, forceRefresh: false) { result in
    switch result {
    case .success(let receipt):
        print("Verify receipt success: \(receipt)")
    case .error(let error):
        print("Verify receipt failed: \(error)")
    }
}

Additional details about receipt verification are available on the wiki.

Verifying purchases and subscriptions

Once you have retrieved the receipt using the verifyReceipt method, you can verify your purchases and subscriptions by product identifier.

Verify Purchase

let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: "your-shared-secret")
SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in
    switch result {
    case .success(let receipt):
        let productId = "com.musevisions.SwiftyStoreKit.Purchase1"
        // Verify the purchase of Consumable or NonConsumable
        let purchaseResult = SwiftyStoreKit.verifyPurchase(
            productId: productId,
            inReceipt: receipt)
            
        switch purchaseResult {
        case .purchased(let receiptItem):
            print("\(productId) is purchased: \(receiptItem)")
        case .notPurchased:
            print("The user has never purchased \(productId)")
        }
    case .error(let error):
        print("Receipt verification failed: \(error)")
    }
}

Verify Subscription

This can be used to check if a subscription was previously purchased, and whether it is still active or if it's expired.

From Apple - Working with Subscriptions:

Keep a record of the date that each piece of content is published. Read the Original Purchase Date and Subscription Expiration Date field from each receipt entry to determine the start and end dates of the subscription.

When one or more subscriptions are found for a given product id, they are returned as a ReceiptItem array ordered by expiryDate, with the first one being the newest.

let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: "your-shared-secret")
SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in
    switch result {
    case .success(let receipt):
        let productId = "com.musevisions.SwiftyStoreKit.Subscription"
        // Verify the purchase of a Subscription
        let purchaseResult = SwiftyStoreKit.verifySubscription(
            ofType: .autoRenewable, // or .nonRenewing (see below)
            productId: productId,
            inReceipt: receipt)
            
        switch purchaseResult {
        case .purchased(let expiryDate, let items):
            print("\(productId) is valid until \(expiryDate)\n\(items)\n")
        case .expired(let expiryDate, let items):
            print("\(productId) is expired since \(expiryDate)\n\(items)\n")
        case .notPurchased:
            print("The user has never purchased \(productId)")
        }

    case .error(let error):
        print("Receipt verification failed: \(error)")
    }
}

Further documentation on verifying subscriptions is available on the wiki.

Subscription Groups

From Apple Docs - Offering Subscriptions:

A subscription group is a set of in-app purchases that you can create to provide users with a range of content offerings, service levels, or durations to best meet their needs. Users can only buy one subscription within a subscription group at a time. If users would want to buy more that one type of subscription — for example, to subscribe to more than one channel in a streaming app — you can put these in-app purchases in different subscription groups.

You can verify all subscriptions within the same group with the verifySubscriptions method. Learn more on the wiki.

Notes

The framework provides a simple block based API with robust error handling on top of the existing StoreKit framework. It does NOT persist in app purchases data locally. It is up to clients to do this with a storage solution of choice (i.e. NSUserDefaults, CoreData, Keychain).

Change Log

See the Releases Page.

Sample Code

The project includes demo apps for iOS and macOS showing how to use SwiftyStoreKit. Note that the pre-registered in app purchases in the demo apps are for illustration purposes only and may not work as iTunes Connect may invalidate them.

Essential Reading

I have also written about building SwiftyStoreKit on Medium:

Troubleshooting

Video Tutorials

Jared Davidson: In App Purchases! (Swift 3 in Xcode : Swifty Store Kit)

@rebeloper: Ultimate In-app Purchases Guide

Credits

Many thanks to phimage for adding macOS support and receipt verification.

Apps using SwiftyStoreKit

It would be great to showcase apps using SwiftyStoreKit here. Pull requests welcome :)

A full list of apps is published on AppSight.

Comments
  • App Store login popup appearing all the time!

    App Store login popup appearing all the time!

    Platform

    • [x] iOS
    • [ ] macOS
    • [ ] tvOS

    In app purchase type

    • [ ] Consumable
    • [ ] Non-consumable
    • [x] Auto-Renewable Subscription
    • [ ] Non-Renewing Subscription

    Environment

    • [x] Sandbox
    • [x] Production

    Version

    0.11.0

    Related issues

    Report

    Issue summary

    I call verifyReceipt and verifySubscription in applicationDidFinishLaunchingWithOptions to verify that the user is still a valid subscriber of my premium service.

    Right now in Test Flight beta testing I see the App Store login alert popping up every time I open the app. This is very annoying.

    Is there a better best practise way to verify subscription status? I know Apple recommends doing this via my own server, but I do not want to go this way.

    What did you expect to happen

    No popup to sign in. Verification runs in the background.

    What happened instead

    Right now in Test Flight beta testing I see the App Store login alert popping up every time I open the app.

    status: needs analysis 
    opened by funkenstrahlen 63
  • PaymentQueueController's shouldAddStorePayment method Implementation

    PaymentQueueController's shouldAddStorePayment method Implementation

    Platform

    • iOS

    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:

      @available(iOS 11.0, *)
      func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, for product: SKProduct) -> Bool {
        return true
      }
    

    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).

    type: enhancement iOS & iPadOS help wanted area: purchase flows 
    opened by zntfdr 45
  • `retrieveProductsInfo` returns valid or invalid product randomly in sandbox

    `retrieveProductsInfo` returns valid or invalid product randomly in sandbox

    Platform

    • [x] iOS
    • [ ] macOS
    • [ ] tvOS

    In app purchase type

    • [x] Consumable
    • [ ] Non-consumable
    • [ ] Auto-Renewable Subscription
    • [ ] Non-Renewing Subscription

    Environment

    • [x] Sandbox
    • [ ] Production

    Version

    0.8.2

    Related issues

    N/A

    Report

    Issue summary

    Not sure if that's a real bug or just due to the sandbox environment: when fetching products from StoreKit using SwiftyStoreKit.retrieveProductsInfo, I'm randomly getting a valid or invalid product. Is that expected?

    SwiftyStoreKit.retrieveProductsInfo(productsIdentifier) { result in
      print(result)
    }
    

    which is called directly in didFinishLaunchingWithOptions

    What did you expect to happen

    Always getting a valid product.

    What happened instead

    Valid or invalid.. depending of a mysterious mind!

    type: question 
    opened by tbaranes 27
  • Add support for interrupted transactions

    Add support for interrupted transactions

    Summary

    Seems related to #593 and #606.

    Per documentation provided by https://developer.apple.com/documentation/storekit/in-app_purchase/testing_in-app_purchases_with_sandbox, interrupted purchases will send two transaction notifications for a given purchase:

    1. a .failed state as the interruption begins
    2. a .success state afterwards with matching product identifier and quantity

    This diff attempts to account for this flow, which would end tracking for a particular purchase attempt on its first failure

    Notes

    I haven't been able to test this flow to completion using the Sandbox environment, since "Accept"-ing terms doesn't seem to trigger a successful transaction in the payment queue. I'd appreciate any assistance in understanding how to test this flow, or if a Feedback to Apple may be in order. (Update: See note below)

    Update: Use a relatively new Sandbox tester account for testing the "Interrupted Purchases" flow, as it seems older accounts run into issues where the purchase is never made successful after accepting terms.

    opened by maciesielka 23
  • Xcode 8 & Swift 3.0 compatibility

    Xcode 8 & Swift 3.0 compatibility

    hey guys,

    I would like to know if theres a status or any workaround to use this pod with xcode 8.. I'm getting errors as soon it converts to swift 3.0..

    type: enhancement 
    opened by SaifAlDilaimi 23
  • Constant Popup

    Constant Popup

    Platform

    • [ ] iOS

    In app purchase type

    • [ ] Auto-Renewable Subscription

    Environment

    • [ ] Sandbox
    • [ ] Production

    Version

    ℹ Latest

    Report

    Constant pops asking for apple id password even when app is closed. At least 2 an hour.

    type: bug area: purchase flows type: apple bug 
    opened by WillMays1 19
  • Please consider move back *refreshReceipt*

    Please consider move back *refreshReceipt*

    Platform

    • [x] iOS
    • [ ] macOS
    • [ ] tvOS

    In app purchase type

    • [ ] Consumable
    • [ ] Non-consumable
    • [x] Auto-Renewable Subscription
    • [ ] Non-Renewing Subscription

    Environment

    • [x] Sandbox
    • [ ] Production

    Version

    ℹ 4.0.0

    Related issues

    ℹ N/A.

    Report

    Issue summary

    ℹ I'm migrating from version 3 to 4 and found the API removed the method refreshReceipt. My app handles the verification flow on server. So in the client, I need to retrieve the receiptData and post to my server. So I called refreshReceipt before upload it to server to make sure it is the latest but now it's not available anymore. I know I can workaround by just upload the localReceipt, but I'm not sure it is latest or not. Please advise.

    What did you expect to happen

    ℹ Migrate from version 3 to 4

    What happened instead

    ℹ Need refreshReceipt method.

    type: enhancement type: question 
    opened by pddkhanh 19
  • Use of undeclared type 'SKError' on XCode 9 beta

    Use of undeclared type 'SKError' on XCode 9 beta

    Platform

    • [x] iOS
    • [ ] macOS
    • [ ] tvOS

    In app purchase type

    • [ ] Consumable
    • [x] Non-consumable
    • [ ] Auto-Renewable Subscription
    • [ ] Non-Renewing Subscription

    Environment

    • [x] Sandbox
    • [ ] Production

    Version

    0.10.3

    Issue summary

    Using XCode 9 all SKError references are marked as error : Use of undeclared type 'SKError'

    opened by wasappi 19
  • Purchase Flow succeeds and then restarts on FIRST purchase with new sandbox account.

    Purchase Flow succeeds and then restarts on FIRST purchase with new sandbox account.

    Bug Report

    While testing some new features, I came across this bug and am able to reproduce it. It appears that when you make a first purchase in sandbox, you get the all clear from the apple side of the transaction, then the purchase confirmation window starts again and you have to go through the motions to select purchase a seconds time. This only happens on the very first purchase you make with a new sandbox account. Subsequent purchases and renewals do not cause such action.

    To Reproduce Steps to reproduce the behavior:

    1. Create a new sandbox account
    2. uninstall and reinstall your app from your computer so as to delete all old data
    3. attempt to make any In-App purchase

    Expected behavior You should go through the billing flow once, the purchase should complete and the product be made available. Yet this is not happening. As stated, on this first purchase, you are made to basically "start over" with the whole billing flow procedure like it hadn't;t happened before.

    Platform Information

    • OS: iOS 14.2 Xcode 12.2
    • Purchase Type: Testes with non-consumables and Auto-Renewing Subscriptions
    • Environment: Sandbox

    Thank you.

    type: bug 
    opened by louiskabo 17
  • Add Purchased Protocol

    Add Purchased Protocol

    Add a shared protocol allowing Purchase, PurchaseDetails and ReceiptItem to be get a common treatment in simple cases

    I noticed that I was getting different response types for purchases in

    SwiftyStoreKit.completeTransactions (in .purchased and .restored cases) SwiftyStoreKit.restorePurchases (for results.restoredPurchases) SwiftyStoreKit.purchaseProducd (in .success case)

    however - all I needed from each was the original purchase date and the productID.

    this protocol allows me to route the three outputs through to a common purchase function.

    type: enhancement area: purchase flows 
    opened by ConfusedVorlon 15
  • Non-renewing subscription cannot be restored

    Non-renewing subscription cannot be restored

    I was trying to use the swiftystorekit in my project replacing the old existing In App purchase framework, the purchase is working fine but the restore purchases is not working. I tried with different scenerio's but later i came to know that i am not getting any restoreProductIds or restorefailedProducts, when i call the restorePurchases method. It's always says nothing to restore in the last else statement. How are using the restore purchases? how are getting the existing purchases? I searched in demo app but there is not much to grab and there is no information in the framework classes also. Help me to fix this.

    type: question answered 
    opened by satishreddy17 14
  • Upcoming changes to the App Store receipt signing certificate

    Upcoming changes to the App Store receipt signing certificate

    I have recently got this news to the apple developer news channel, https://developer.apple.com/news/?id=ytb7qj0x

    So I want to know that will this affect my implementation to check the status of user purchase status. I am currently using this function to verify receipt, so that I can check the user's purchase status, SwiftyStoreKit.verifyReceipt(using: AppleReceiptValidator)

    Response on this will be very helpful to me.

    opened by nikunjgabani 1
  • User logout or switch account verification

    User logout or switch account verification

    Bug Report

    A clear and concise description of what the bug is.

    I want to get the user's latest purchase receipt through fetchReceipt to refresh the receipt. But all errors for user cancel login etc are networkError, how should I handle this.

    type: bug 
    opened by huanglins 0
  • iOS 16 (am not sure) restoring purchases hangs?

    iOS 16 (am not sure) restoring purchases hangs?

    Restoring purchases hangs

    I am not sure if this is AppStore issue, but today I cannot successfully call restoring purchases method. Completion handler is never called - hangs. Anyone else with this issue?

    My third app still built with Xcode 13 is ok. Both my other apps built with Xcode 14 have the same issues - hangs. I guess the problem could not be with Bitcode setting No (it is not recommended anymore having Bitcode Yes on Pods)?

    EDIT: More specifically, this one hangs restoreCompletedTransactions

    type: question 
    opened by sabiland 44
  • receipt[

    receipt["cancellation_date"] != nil, always return .notPurchased

    Bug Report

    when user buy a autoRenewable product ,the product has not expire, when user cancel the autoRenewable , the restore buy call verifySubscriptions() function always return .notPurchased

    To Reproduce Steps to reproduce the behavior:

    1. Step one...

    Expected behavior A clear and concise description of what you expected to happen.

    Platform Information

    • OS: [e.g. iOS 13.4, watchOS 6.2.1, tvOS 9.2.3, macOS 10.14.3, Catalyst 13.0]
    • Purchase Type: [e.g. consumable, non-consumable, auto-renewable subscription, non-renewing subscription, discount]
    • Environment: [e.g. sandbox, app review, production]
    • SwiftyStoreKit version: [e.g. 0.16]

    Additional context

    Add any other context about the problem here.

    Potentially Related Issues

    • Issue #___

    Screenshots If applicable, add screenshots to help explain your problem.

    class func verifySubscriptions(
        ofType type: SubscriptionType,
        productIds: Set<String>,
        inReceipt receipt: ReceiptInfo,
        validUntil date: Date = Date()
    ) -> VerifySubscriptionResult {
    
        // The values of the latest_receipt and latest_receipt_info keys are useful when checking whether an auto-renewable subscription is currently active. By providing any transaction receipt for the subscription and checking these values, you can get information about the currently-active subscription period. If the receipt being validated is for the latest renewal, the value for latest_receipt is the same as receipt-data (in the request) and the value for latest_receipt_info is the same as receipt.
        let (receipts, duration) = getReceiptsAndDuration(for: type, inReceipt: receipt)
        let receiptsInfo = filterReceiptsInfo(receipts: receipts, withProductIds: productIds)
        let nonCancelledReceiptsInfo = receiptsInfo.filter { receipt in receipt["cancellation_date"] == nil }
        if nonCancelledReceiptsInfo.count == 0 {
            return .notPurchased
        }
    
    type: bug 
    opened by kongyuluEleven 0
  • Verify Receipt Doesn't Work

    Verify Receipt Doesn't Work

    I have implemented SwiftyStoreKit.verifyReceipt and SwiftyStoreKit.verifySubscription methods. While debugging, debugger never goes inside switch statement although result contain "success" when debugged in InAppReceiptVerificator class verifyReceipt() method.

    Here is the function :

        func SubscriptionVerify(productID: String){
            SwiftyStoreKit.verifyReceipt(using: appleValidator!) { result in
                switch result {
                case .success(let receipt):
                    let productId = productID
                    // Verify the purchase of a Subscription
                    let purchaseResult = SwiftyStoreKit.verifySubscription(
                        ofType: .autoRenewable, // or .nonRenewing (see below)
                        productId: productId,
                        inReceipt: receipt)
                    
                        
                    switch purchaseResult {
                    case .purchased(let expiryDate, let items):
                        print("\(productId) is valid until \(expiryDate)\n\(items)\n")
                        
                    case .expired(let expiryDate, let items):
                        print("\(productId) is expired since \(expiryDate)\n\(items)\n")
                    case .notPurchased:
                        print("The user has never purchased \(productId)")
                    }
    
                case .error(let error):
                    print("Receipt verification failed: \(error)")
                }
            }
        }
    
    type: question 
    opened by 166341Aqeedat 1
Releases(0.16.4)
  • 0.16.4(Dec 6, 2021)

    What's Changed

    • Fix watch build on xcode 12.5 by @fnuky in https://github.com/bizz84/SwiftyStoreKit/pull/645
    • Add "SwiftyStoreKit to RevenueCat Migration Guide" to README by @bizz84 in https://github.com/bizz84/SwiftyStoreKit/pull/649
    • Add link to Glassfy to README by @bizz84 in https://github.com/bizz84/SwiftyStoreKit/pull/654
    • Add "is_upgraded" properties for ReceiptItem by @ManyLattice in https://github.com/bizz84/SwiftyStoreKit/pull/656
    • Add app that uses SwiftyStoreKit by @jamestapping in https://github.com/bizz84/SwiftyStoreKit/pull/650
    • Communicate deferred transactions to the app by @azouts in https://github.com/bizz84/SwiftyStoreKit/pull/652
    • Added Written Tutorial section / Added Tutorial by @jamestapping in https://github.com/bizz84/SwiftyStoreKit/pull/651

    New Contributors

    • @fnuky made their first contribution in https://github.com/bizz84/SwiftyStoreKit/pull/645
    • @ManyLattice made their first contribution in https://github.com/bizz84/SwiftyStoreKit/pull/656
    • @jamestapping made their first contribution in https://github.com/bizz84/SwiftyStoreKit/pull/650
    • @azouts made their first contribution in https://github.com/bizz84/SwiftyStoreKit/pull/652

    Full Changelog: https://github.com/bizz84/SwiftyStoreKit/compare/0.16.3...0.16.4

    Source code(tar.gz)
    Source code(zip)
  • 0.16.1(Jul 19, 2020)

  • 0.16.0(Jun 5, 2020)

    This update includes some changes and improvements that will (hopefully) fix some common issues as well as expand support for long-awaited features.

    New Features

    • watchOS support. Apple has introduced in-app purchases on the Apple Watch starting with watchOS 6.2. Now you can use SwiftyStoreKit to take advantage of this additional platform.
    • Discount support. Now your app can offer in-app purchase discounts to users through SwiftyStoreKit (#458).
    • Cancellable requests. Attempt to cancel requests sent with SwiftyStoreKit.

    Improvements & Bug Fixes

    • Sandbox purchasing process should be improved. New checks exist to help you identify when a request is being sent in the sandbox and to isolate sandbox-specific errors.
    • Added Purchased protocol to create common conformance for Purchase, PurchaseDetails, and ReceiptItem.
    • Fixed callback issues when restoring items.
    • Added a new API to retrieve all product identifiers, getDistinctPurchaseIds.
    • Extended support for your own receipt verification process.
    • ReceiptItem now conforms to Codable.
    • Additional extensions on existing StoreKit classes for localizedSubscriptionPeriod, localizedPrice, and localizedDiscountPrice.
    Source code(tar.gz)
    Source code(zip)
  • 0.15.1(Feb 18, 2020)

  • 0.15.0(Apr 30, 2019)

  • 0.14.1(Nov 19, 2018)

  • 0.14.0(Nov 15, 2018)

  • 0.13.3(Jul 16, 2018)

  • 0.13.2(Jul 16, 2018)

  • 0.13.1(Jul 16, 2018)

  • 0.13.0(Jan 31, 2018)

  • 0.12.0(Dec 31, 2017)

    • Add verifySubscriptions method to check all subscriptions in a group at once (#333, related issue: #194)
    • Rename verifySubscription(type:productId:inReceipt:validUntil:) to verifySubscription(ofType:productId:inReceipt:validUntil:) (#333)
    • Add video tutorials section in README (#328, #330, see #326)
    • Update iOS Demo App (#327, see #147)
    Source code(tar.gz)
    Source code(zip)
  • 0.11.3(Dec 23, 2017)

  • 0.11.2(Dec 20, 2017)

  • 0.11.1(Dec 17, 2017)

  • 0.11.0(Oct 16, 2017)

    • Add fetchReceipt method. Update verifyReceipt to use it (#278, related issues: #272, #223).
    • Update fetchReceipt and ReceiptValidator to use receipt as Data rather than String. This is consistent with localReceiptData (#284, see #272).
    • Remove password from ReceiptValidator protocol as this is specific to AppleReceiptValidator (#281, see #263). Note: This is an API breaking change.
    • Unwrap receipt["receipt"]?["in_app"] in two steps (addresses casting problems) (#283, related issue #256).
    Source code(tar.gz)
    Source code(zip)
  • 0.10.8(Sep 25, 2017)

  • 0.10.7(Aug 21, 2017)

  • 0.10.6(Aug 21, 2017)

  • 0.10.5(Aug 3, 2017)

  • 0.10.4(Jul 18, 2017)

  • 0.10.3(Jun 3, 2017)

  • 0.10.2(May 29, 2017)

  • 0.10.1(May 18, 2017)

  • 0.10.0(May 18, 2017)

    API removed: refreshReceipt

    This release simplifies the receipt verification flows by removing the refreshReceipt method from the public API.

    Now clients only need to call verifyReceipt and the receipt is refreshed internally if needed.

    Addressed in #213, related issue: #42.

    The documentation in the README and various methods has also been considerably improved.

    Source code(tar.gz)
    Source code(zip)
  • 0.9.3(May 18, 2017)

  • 0.9.2(May 15, 2017)

  • 0.9.1(May 12, 2017)

    This is a minor release which includes a fix for #185 (addressed in #206). Summary:

    When a purchase succeeds, it is desirable to get access to the purchased SKProduct in the completion block, so that it's possible to query the price and other properties.

    With this change, this is now possible:

    SwiftyStoreKit.purchaseProduct("productId", atomically: true) { result in
        if case .success(let purchase) = result {
            // Deliver content from server, then:
            if purchase.needsFinishTransaction {
                SwiftyStoreKit.finishTransaction(purchase.transaction)
            }
            print("Purchased product with price: \(purchase.product.price)")
        }
    }
    
    Source code(tar.gz)
    Source code(zip)
Owner
Andrea Bizzotto
Flutter GDE ❖ Creator of codewithandrea.com ❖ YouTube: nnbd.me/yt ❖ Complete Dart Course: nnbd.me/dart
Andrea Bizzotto
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
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
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
InAppPurchase - A Simple and Lightweight framework for In App Purchase

InAppPurchase A Simple, Lightweight and Safe framework for In App Purchase Feature Simple and Light ?? Support Promoting In-App Purchases ?? No need t

Jin Sasaki 269 Dec 15, 2022
🍎 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
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
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
🚀 Create XCFrameworks with ease! A Command Line Tool to create XCFramework for multiple platforms at one shot! The better way to deal with XCFrameworks for iOS, Mac Catalyst, tvOS, macOS, and watchOS.

Surmagic ?? Create XCFramework with ease! A Command Line Tool to create XCFramework for multiple platforms at one shot! The better way to deal with XC

Muhammed Gurhan Yerlikaya 260 Dec 28, 2022
Lightweight In App Purchases Swift framework for iOS 8.0+, tvOS 9.0+ and macOS 10.10+ ⛺

SwiftyStoreKit is a lightweight In App Purchases framework for iOS, tvOS, watchOS, macOS, and Mac Catalyst. Features Super easy-to-use block-based API

Andrea Bizzotto 6.1k Jan 7, 2023
In-app purchases and subscriptions made easy. Support for iOS, iPadOS, watchOS, and Mac.

In-app purchases and subscriptions made easy. Support for iOS, iPadOS, watchOS, and Mac.

RevenueCat 1.6k Jan 6, 2023
Lightweight Networking and Parsing framework made for iOS, Mac, WatchOS and tvOS.

NetworkKit A lightweight iOS, Mac and Watch OS framework that makes networking and parsing super simple. Uses the open-sourced JSONHelper with functio

Alex Telek 30 Nov 19, 2022
EventBroadcaster is a lightweight event handler framework, written in swift for iOS, macOS, tvOS & watchOS applications.

EventBroadcaster is a lightweight event handler framework, written in swift for iOS, macOS, tvOS & watchOS applications.

Ali Samaiee 4 Oct 5, 2022
Easily show HUDs with SwiftUI! Lightweight SwiftUI wrapper for JGProgressHUD for iOS, tvOS, Catalyst.

JGProgressHUD-SwiftUI This is a lightweight and easy-to-use SwiftUI wrapper for JGProgressHUD, giving you access to the large and proven feature set o

Jonas Gessner 78 Dec 21, 2022
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
Grid-based app for macOS Catalyst (Mac Idiom) with navigation stack

Catalyst Grid App Simple Catalyst example (Mac idiom) of a grid-based app with a push/pop navigation stack. Includes back button in toolbar that auto-

Steven Troughton-Smith 120 Nov 14, 2022