Skip to content

wtsnz/PurchaseKit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PurchaseKit

Version Author Build Passing Swift
Platforms MIT
Cocoapods Carthage SPM


Introduction

What's this?

PurchaseKit is an In-App Purchase Framework written in Swift.

Requirements

  • iOS 8.0+
  • macOS 10.10+
  • tvOS 9.0+
  • Xcode 9 with Swift 4

Installation

CocoaPods

pod 'PurchaseKit'

Contribution

You are welcome to fork and submit pull requests.

License

PurchaseKit is open-sourced software, licensed under the MIT license.

Usage

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.

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

	PurchaseKit.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:
                   PurchaseKit.finishTransaction(purchase.transaction)
               }
               print("purchased: \(purchase)")
	        }
	    }
	}
 	return true
}

Purchases

Retrieve products info

PurchaseKit.retrieveProductsInfo(["cn.meniny.PurchaseKit.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 {
        return alertWithTitle("Could not retrieve product info", message: "Invalid product identifier: \(invalidProductId)")
    }
    else {
	     print("Error: \(result.error)")
    }
}

Purchase a product

PurchaseKit.purchaseProduct("cn.meniny.PurchaseKit.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")
        }
    }
}
PurchaseKit.purchaseProduct("cn.meniny.PurchaseKit.Purchase1", quantity: 1, atomically: false) { result in
    switch result {
    case .success(let product):
        // fetch content from your server, then:
        if product.needsFinishTransaction {
            PurchaseKit.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")
        }
    }
}
PurchaseKit.retrieveProductsInfo(["cn.meniny.PurchaseKit.Purchase1"]) { result in
    if let product = result.retrievedProducts.first {
        PurchaseKit.purchaseProduct(product, quantity: 1, atomically: true) { result in
            // handle result (same as above)
        }
    }
}

Restore previous purchases

PurchaseKit.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")
    }
}
PurchaseKit.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 {
                PurchaseKit.finishTransaction(purchase.transaction)
            }
        }
        print("Restore Success: \(results.restoredPurchases)")
    }
    else {
        print("Nothing to Restore")
    }
}

Receipt verification

Retrieve local receipt

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

Verify Receipt

let appleValidator = PKAppleReceiptValidator(service: .production)
PurchaseKit.verifyReceipt(using: appleValidator, password: "your-shared-secret", forceRefresh: false) { result in
    switch result {
    case .success(let receipt):
        print("Verify receipt Success: \(receipt)")
    case .error(let error):
        print("Verify receipt Failed: \(error)")
	}
}

Verifying purchases and subscriptions

Verify Purchase

let appleValidator = PKAppleReceiptValidator(service: .production)
PurchaseKit.verifyReceipt(using: appleValidator, password: "your-shared-secret") { result in
    switch result {
    case .success(let receipt):
        // Verify the purchase of Consumable or NonConsumable
        let purchaseResult = PurchaseKit.verifyPurchase(
            productId: "cn.meniny.PurchaseKit.Purchase1",
            inReceipt: receipt)

        switch purchaseResult {
        case .purchased(let receiptItem):
            print("Product is purchased: \(receiptItem)")
        case .notPurchased:
            print("The user has never purchased this product")
        }
    case .error(let error):
        print("Receipt verification failed: \(error)")
    }
}

Verify Subscription

let appleValidator = PKAppleReceiptValidator(service: .production)
PurchaseKit.verifyReceipt(using: appleValidator, password: "your-shared-secret") { result in
    switch result {
    case .success(let receipt):
        // Verify the purchase of a Subscription
        let purchaseResult = PurchaseKit.verifySubscription(
            type: .autoRenewable, // or .nonRenewing (see below)
            productId: "cn.meniny.PurchaseKit.Subscription",
            inReceipt: receipt)

        switch purchaseResult {
        case .purchased(let expiryDate, let receiptItems):
            print("Product is valid until \(expiryDate)")
        case .expired(let expiryDate, let receiptItems):
            print("Product is expired since \(expiryDate)")
        case .notPurchased:
            print("The user has never purchased this product")
        }

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

Auto-Renewable

let purchaseResult = PurchaseKit.verifySubscription(
            type: .autoRenewable,
            productId: "cn.meniny.PurchaseKit.Subscription",
            inReceipt: receipt)

Non-Renewing

// validDuration: time interval in seconds
let purchaseResult = PurchaseKit.verifySubscription(
            type: .nonRenewing(validDuration: 3600 * 24 * 30),
            productId: "cn.meniny.PurchaseKit.Subscription",
            inReceipt: receipt)

Purchasing and verifying a subscription

let productId = "your-product-id"
PurchaseKit.purchaseProduct(productId, atomically: true) { result in

    if case .success(let purchase) = result {
        // Deliver content from server, then:
        if purchase.needsFinishTransaction {
            PurchaseKit.finishTransaction(purchase.transaction)
        }

        let appleValidator = PKAppleReceiptValidator(service: .production)
        PurchaseKit.verifyReceipt(using: appleValidator, password: "your-shared-secret") { result in

            if case .success(let receipt) = result {
                let purchaseResult = PurchaseKit.verifySubscription(
                    type: .autoRenewable,
                    productId: productId,
                    inReceipt: receipt)

                switch purchaseResult {
                case .purchased(let expiryDate, let receiptItems):
                    print("Product is valid until \(expiryDate)")
                case .expired(let expiryDate, let receiptItems):
                    print("Product is expired since \(expiryDate)")
                case .notPurchased:
                    print("This product has never been purchased")
                }

            } else {
                // receipt verification error
            }
        }
    } else {
        // purchase error
    }
}

About

💰iOS In-App Purchase Framework.

Resources

License

Stars

Watchers

Forks

Packages

No packages published