소스 검색

Add PaymentTransaction details to completion block of purchase and restore purchases methods.
Updated view controllers to show usage accordingly

Andrea Bizzotto 8 년 전
부모
커밋
d0cf35eaed

+ 16 - 3
SwiftyStoreKit-iOS-Demo/ViewController.swift

@@ -80,15 +80,28 @@ class ViewController: UIViewController {
         SwiftyStoreKit.purchaseProduct(AppBundleId + "." + purchase.rawValue, atomically: true) { result in
         SwiftyStoreKit.purchaseProduct(AppBundleId + "." + purchase.rawValue, atomically: true) { result in
             NetworkActivityIndicatorManager.networkOperationFinished()
             NetworkActivityIndicatorManager.networkOperationFinished()
             
             
+            if case .success(_, let transaction) = result {
+                // Deliver content from server, then:
+                if let transaction = transaction {
+                    SwiftyStoreKit.finishTransaction(transaction)
+                }
+            }
             self.showAlert(self.alertForPurchaseResult(result))
             self.showAlert(self.alertForPurchaseResult(result))
         }
         }
     }
     }
+    
     @IBAction func restorePurchases() {
     @IBAction func restorePurchases() {
         
         
         NetworkActivityIndicatorManager.networkOperationStarted()
         NetworkActivityIndicatorManager.networkOperationStarted()
         SwiftyStoreKit.restorePurchases(atomically: true) { results in
         SwiftyStoreKit.restorePurchases(atomically: true) { results in
             NetworkActivityIndicatorManager.networkOperationFinished()
             NetworkActivityIndicatorManager.networkOperationFinished()
             
             
+            for product in results.restoredProducts {
+                // Deliver content from server, then:
+                if let transaction = product.transaction {
+                    SwiftyStoreKit.finishTransaction(transaction)
+                }
+            }
             self.showAlert(self.alertForRestorePurchases(results))
             self.showAlert(self.alertForRestorePurchases(results))
         }
         }
     }
     }
@@ -193,7 +206,7 @@ extension ViewController {
 
 
     func alertForPurchaseResult(_ result: SwiftyStoreKit.PurchaseResult) -> UIAlertController {
     func alertForPurchaseResult(_ result: SwiftyStoreKit.PurchaseResult) -> UIAlertController {
         switch result {
         switch result {
-        case .success(let productId):
+        case .success(let productId, _):
             print("Purchase Success: \(productId)")
             print("Purchase Success: \(productId)")
             return alertWithTitle("Thank You", message: "Purchase completed")
             return alertWithTitle("Thank You", message: "Purchase completed")
         case .error(let error):
         case .error(let error):
@@ -220,8 +233,8 @@ extension ViewController {
             print("Restore Failed: \(results.restoreFailedProducts)")
             print("Restore Failed: \(results.restoreFailedProducts)")
             return alertWithTitle("Restore failed", message: "Unknown error. Please contact support")
             return alertWithTitle("Restore failed", message: "Unknown error. Please contact support")
         }
         }
-        else if results.restoredProductIds.count > 0 {
-            print("Restore Success: \(results.restoredProductIds)")
+        else if results.restoredProducts.count > 0 {
+            print("Restore Success: \(results.restoredProducts)")
             return alertWithTitle("Purchases Restored", message: "All purchases have been restored")
             return alertWithTitle("Purchases Restored", message: "All purchases have been restored")
         }
         }
         else {
         else {

+ 18 - 4
SwiftyStoreKit-macOS-Demo/ViewController.swift

@@ -75,7 +75,14 @@ class ViewController: NSViewController {
 
 
     func purchase(_ purchase: RegisteredPurchase) {
     func purchase(_ purchase: RegisteredPurchase) {
 
 
-        SwiftyStoreKit.purchaseProduct(AppBundleId + "." + purchase.rawValue) { result in
+        SwiftyStoreKit.purchaseProduct(AppBundleId + "." + purchase.rawValue, atomically: true) { result in
+
+            if case .success(_, let transaction) = result {
+                // Deliver content from server, then:
+                if let transaction = transaction {
+                    SwiftyStoreKit.finishTransaction(transaction)
+                }
+            }
 
 
             self.showAlert(self.alertForPurchaseResult(result))
             self.showAlert(self.alertForPurchaseResult(result))
         }
         }
@@ -83,8 +90,15 @@ class ViewController: NSViewController {
 
 
     @IBAction func restorePurchases(_ sender: AnyObject?) {
     @IBAction func restorePurchases(_ sender: AnyObject?) {
 
 
-        SwiftyStoreKit.restorePurchases() { results in
+        SwiftyStoreKit.restorePurchases(atomically: true) { results in
             
             
+            for product in results.restoredProducts {
+                // Deliver content from server, then:
+                if let transaction = product.transaction {
+                    SwiftyStoreKit.finishTransaction(transaction)
+                }
+            }
+
             self.showAlert(self.alertForRestorePurchases(results))
             self.showAlert(self.alertForRestorePurchases(results))
         }
         }
     }
     }
@@ -207,8 +221,8 @@ extension ViewController {
             print("Restore Failed: \(results.restoreFailedProducts)")
             print("Restore Failed: \(results.restoreFailedProducts)")
             return alertWithTitle("Restore failed", message: "Unknown error. Please contact support")
             return alertWithTitle("Restore failed", message: "Unknown error. Please contact support")
         }
         }
-        else if results.restoredProductIds.count > 0 {
-            print("Restore Success: \(results.restoredProductIds)")
+        else if results.restoredProducts.count > 0 {
+            print("Restore Success: \(results.restoredProducts)")
             return alertWithTitle("Purchases Restored", message: "All purchases have been restored")
             return alertWithTitle("Purchases Restored", message: "All purchases have been restored")
         }
         }
         else {
         else {

+ 25 - 7
SwiftyStoreKit/InAppProductPurchaseRequest.swift

@@ -25,18 +25,22 @@
 import StoreKit
 import StoreKit
 import Foundation
 import Foundation
 
 
+public protocol PaymentTransaction { }
+
+extension SKPaymentTransaction : PaymentTransaction { }
+
 class InAppProductPurchaseRequest: NSObject, SKPaymentTransactionObserver {
 class InAppProductPurchaseRequest: NSObject, SKPaymentTransactionObserver {
 
 
     enum TransactionResult {
     enum TransactionResult {
-        case purchased(productId: String)
-        case restored(productId: String)
+        case purchased(productId: String, transaction: PaymentTransaction?)
+        case restored(productId: String, transaction: PaymentTransaction?)
         case failed(error: Error)
         case failed(error: Error)
     }
     }
     
     
     typealias RequestCallback = ([TransactionResult]) -> ()
     typealias RequestCallback = ([TransactionResult]) -> ()
     private let callback: RequestCallback
     private let callback: RequestCallback
     private var purchases : [SKPaymentTransactionState: [String]] = [:]
     private var purchases : [SKPaymentTransactionState: [String]] = [:]
-
+    
     var paymentQueue: SKPaymentQueue {
     var paymentQueue: SKPaymentQueue {
         return SKPaymentQueue.default()
         return SKPaymentQueue.default()
     }
     }
@@ -68,6 +72,14 @@ class InAppProductPurchaseRequest: NSObject, SKPaymentTransactionObserver {
         return request
         return request
     }
     }
     
     
+    class func finishTransaction(_ transaction: PaymentTransaction) {
+        guard let skTransaction = transaction as? SKPaymentTransaction else {
+            print("Object is not a SKPaymentTransaction: \(transaction)")
+            return
+        }
+        SKPaymentQueue.default().finishTransaction(skTransaction)
+    }
+    
     // MARK: Private methods
     // MARK: Private methods
     private func startPayment(_ product: SKProduct, applicationUsername: String = "") {
     private func startPayment(_ product: SKProduct, applicationUsername: String = "") {
         guard let _ = product._productIdentifier else {
         guard let _ = product._productIdentifier else {
@@ -111,8 +123,11 @@ class InAppProductPurchaseRequest: NSObject, SKPaymentTransactionObserver {
             switch transactionState {
             switch transactionState {
             case .purchased:
             case .purchased:
                 if isPurchaseRequest {
                 if isPurchaseRequest {
-                    transactionResults.append(.purchased(productId: transactionProductIdentifier))
-                    paymentQueue.finishTransaction(transaction)
+                    let pendingTransaction = atomically ? nil : transaction
+                    transactionResults.append(.purchased(productId: transactionProductIdentifier, transaction: pendingTransaction))
+                    if atomically {
+                        paymentQueue.finishTransaction(transaction)
+                    }
                 }
                 }
             case .failed:
             case .failed:
                 // TODO: How to discriminate between purchase and restore?
                 // TODO: How to discriminate between purchase and restore?
@@ -124,8 +139,11 @@ class InAppProductPurchaseRequest: NSObject, SKPaymentTransactionObserver {
                 paymentQueue.finishTransaction(transaction)
                 paymentQueue.finishTransaction(transaction)
             case .restored:
             case .restored:
                 if !isPurchaseRequest {
                 if !isPurchaseRequest {
-                    transactionResults.append(.restored(productId: transactionProductIdentifier))
-                    paymentQueue.finishTransaction(transaction)
+                    let pendingTransaction = atomically ? nil : transaction
+                    transactionResults.append(.restored(productId: transactionProductIdentifier, transaction: pendingTransaction))
+                    if atomically {
+                        paymentQueue.finishTransaction(transaction)
+                    }
                 }
                 }
             case .purchasing:
             case .purchasing:
                 // In progress: do nothing
                 // In progress: do nothing

+ 22 - 10
SwiftyStoreKit/SwiftyStoreKit.swift

@@ -55,6 +55,9 @@ public class SwiftyStoreKit {
     #if os(iOS) || os(tvOS)
     #if os(iOS) || os(tvOS)
     private var receiptRefreshRequest: InAppReceiptRefreshRequest?
     private var receiptRefreshRequest: InAppReceiptRefreshRequest?
     #endif
     #endif
+    
+    
+    
     // MARK: Enums
     // MARK: Enums
     public struct RetrieveResults {
     public struct RetrieveResults {
         public let retrievedProducts: Set<SKProduct>
         public let retrievedProducts: Set<SKProduct>
@@ -69,11 +72,15 @@ public class SwiftyStoreKit {
         case paymentNotAllowed
         case paymentNotAllowed
     }
     }
     public enum PurchaseResult {
     public enum PurchaseResult {
-        case success(productId: String)
+        case success(productId: String, transaction: PaymentTransaction?)
         case error(error: PurchaseError)
         case error(error: PurchaseError)
     }
     }
+    public struct RestoredProduct {
+        public let productId: String
+        public let transaction: PaymentTransaction?
+    }
     public struct RestoreResults {
     public struct RestoreResults {
-        public let restoredProductIds: [String]
+        public let restoredProducts: [RestoredProduct]
         public let restoreFailedProducts: [(Swift.Error, String?)]
         public let restoreFailedProducts: [(Swift.Error, String?)]
     }
     }
     public enum RefreshReceiptResult {
     public enum RefreshReceiptResult {
@@ -152,6 +159,11 @@ public class SwiftyStoreKit {
             completion(results)
             completion(results)
         }
         }
     }
     }
+    
+    public class func finishTransaction(_ transaction: PaymentTransaction) {
+     
+        InAppProductPurchaseRequest.finishTransaction(transaction)
+    }
 
 
     /**
     /**
      * Return receipt data from the application bundle. This is read from Bundle.main.appStoreReceiptURL
      * Return receipt data from the application bundle. This is read from Bundle.main.appStoreReceiptURL
@@ -259,29 +271,29 @@ public class SwiftyStoreKit {
 
 
     private func processPurchaseResult(_ result: InAppProductPurchaseRequest.TransactionResult) -> PurchaseResult {
     private func processPurchaseResult(_ result: InAppProductPurchaseRequest.TransactionResult) -> PurchaseResult {
         switch result {
         switch result {
-        case .purchased(let productId):
-            return .success(productId: productId)
+        case .purchased(let productId, let transaction):
+            return .success(productId: productId, transaction: transaction)
         case .failed(let error):
         case .failed(let error):
             return .error(error: .failed(error: error))
             return .error(error: .failed(error: error))
-        case .restored(let productId):
+        case .restored(let productId, _):
             return .error(error: .failed(error: storeInternalError(code: InternalErrorCode.restoredPurchaseWhenPurchasing.rawValue, description: "Cannot restore product \(productId) from purchase path")))
             return .error(error: .failed(error: storeInternalError(code: InternalErrorCode.restoredPurchaseWhenPurchasing.rawValue, description: "Cannot restore product \(productId) from purchase path")))
         }
         }
     }
     }
     
     
     private func processRestoreResults(_ results: [InAppProductPurchaseRequest.TransactionResult]) -> RestoreResults {
     private func processRestoreResults(_ results: [InAppProductPurchaseRequest.TransactionResult]) -> RestoreResults {
-        var restoredProductIds: [String] = []
+        var restoredProducts: [RestoredProduct] = []
         var restoreFailedProducts: [(Swift.Error, String?)] = []
         var restoreFailedProducts: [(Swift.Error, String?)] = []
         for result in results {
         for result in results {
             switch result {
             switch result {
-            case .purchased(let productId):
+            case .purchased(let productId, _):
                 restoreFailedProducts.append((storeInternalError(code: InternalErrorCode.purchasedWhenRestoringPurchase.rawValue, description: "Cannot purchase product \(productId) from restore purchases path"), productId))
                 restoreFailedProducts.append((storeInternalError(code: InternalErrorCode.purchasedWhenRestoringPurchase.rawValue, description: "Cannot purchase product \(productId) from restore purchases path"), productId))
             case .failed(let error):
             case .failed(let error):
                 restoreFailedProducts.append((error, nil))
                 restoreFailedProducts.append((error, nil))
-            case .restored(let productId):
-                restoredProductIds.append(productId)
+            case .restored(let productId, let transaction):
+                restoredProducts.append(RestoredProduct(productId: productId, transaction: transaction))
             }
             }
         }
         }
-        return RestoreResults(restoredProductIds: restoredProductIds, restoreFailedProducts: restoreFailedProducts)
+        return RestoreResults(restoredProducts: restoredProducts, restoreFailedProducts: restoreFailedProducts)
     }
     }
     
     
     private func requestProducts(_ productIds: Set<String>, completion: @escaping (RetrieveResults) -> ()) {
     private func requestProducts(_ productIds: Set<String>, completion: @escaping (RetrieveResults) -> ()) {