前言
Swift作为当前在github上成长最快的语言之一,本人在学习iOS未曾学习过OC,因此在做iOS项目过程中全部采用了Swift,下面详细介绍下Swift的内购的实现。github地址:https://github.com/RyanLeeLY/...
起步
首先我就不介绍如何在iTunesConnect上添加内购商品了,总之添加完商品后,我们需要用到的是productIdentifiers即你填写的商品ID,这个ID是商品在商店中的唯一标识。
导入StoreKit,创建一个类,
SKPaymentTransactionObserver
协议:当交易在队列中有更新或者移出队列时这个观察者会被调用;SKProductsRequestDelegate
实现该协议的代理会受到商品请求返回的信息。
public class LYIAPManager: NSObject,SKPaymentTransactionObserver,SKProductsRequestDelegate
自定义协议,
LYIAPRequestDelegate
处理请求成功后的代理,LYIAPPaymentDelegate
交易时的代理,
LYIAPDelegate继承上面两个协议,方便使用。
@objc public protocol LYIAPDelegate: LYIAPRequestDelegate,LYIAPPaymentDelegate{ } @objc public protocol LYIAPRequestDelegate: NSObjectProtocol{ @objc optional func requestFinished() } @objc public protocol LYIAPPaymentDelegate: NSObjectProtocol{ func transactionPurchased(_ queue: SKPaymentQueue,transaction: SKPaymentTransaction) @objc optional func transactionFailed(_ queue: SKPaymentQueue,transaction: SKPaymentTransaction) @objc optional func transactionRestore(_ queue: SKPaymentQueue,transaction: SKPaymentTransaction) @objc optional func transactionDeferred(_ queue: SKPaymentQueue,transaction: SKPaymentTransaction) @objc optional func transactionPurchasing(_ queue: SKPaymentQueue,transaction: SKPaymentTransaction) @objc optional func transactionRestoreFailedWithError(_ error:NSError) @objc optional func transactionRestoreFinished(_ isSuccess:Bool) }
全部实现
// // LYIAPManager.swift // crater // // Created by 李尧 on 2016/10/11. // Copyright © 2016年 secstudio. All rights reserved. // import UIKit import StoreKit private func printLog<T>(_ message:T,file:String = #file,method:String = #function,line:Int = #line){ #if DEBUG print("\((file as NSString).lastPathComponent)[\(line)],\(method): \(message)") #endif } @objc public protocol LYIAPDelegate: LYIAPRequestDelegate,transaction: SKPaymentTransaction) @objc optional func transactionRestoreFailedWithError(_ error:Error) @objc optional func transactionRestoreFinished(_ isSuccess:Bool) } public let LYIAP = LYIAPManager.LYIAPInstance public class LYIAPManager: NSObject,SKProductsRequestDelegate { fileprivate let VERIFY_RECEIPT_URL = "https://buy.itunes.apple.com/verifyReceipt" fileprivate let ITMS_SANDBox_VERIFY_RECEIPT_URL = "https://sandBox.itunes.apple.com/verifyReceipt" fileprivate var restoreSuccess = false fileprivate var productDict:NSMutableDictionary? static let LYIAPInstance = LYIAPManager() var request:SKProductsRequest? var observer:SKPaymentTransactionObserver? weak var delegate:LYIAPDelegate? weak var requestDelegate:LYIAPRequestDelegate? weak var paymentDelegate:LYIAPPaymentDelegate? fileprivate override init() { } /** Example: let productsIds = NSSet(array: ["com.xxx.xxx.abc"]) */ func setRequestWithProducts(_ productsIds: NSSet,delegate: LYIAPDelegate?) { request = SKProductsRequest(productIdentifiers: productsIds as! Set<String>) request?.delegate = self SKPaymentQueue.default().add(self) if(delegate != nil){ self.delegate = delegate! paymentDelegate = delegate! requestDelegate = delegate! } } func setPaymentTransactionsDelegate(_ delegate: LYIAPPaymentDelegate){ paymentDelegate = delegate } func setProductsRequestDelegate(_ delegate: LYIAPRequestDelegate){ requestDelegate = delegate } func removeRequestDelegate(){ requestDelegate = nil } func removeProductsDelegate(){ paymentDelegate = nil } func startRequest(){ testIsNil() request?.start() } func cancelRequest(){ testIsNil() request?.cancel() } func startPaymentWithProductId(_ productId: String){ //if loaded if(SKPaymentQueue.canMakePayments()){ guard productDict != nil else{ printLog("products haven't been loaded") return } requestPaymentWithProduct(productDict![productId] as! SKProduct) }else{ printLog("IAP is not supported!") } } func restorePayment(){ restoreSuccess = false SKPaymentQueue.default().restoreCompletedTransactions() } public func productsRequest(_ request: SKProductsRequest,didReceive response: SKProductsResponse) { if (productDict == nil) { printLog("first load products") productDict = NSMutableDictionary(capacity: response.products.count) } for product in response.products { printLog("product \(product.productIdentifier) loaded") productDict!.setObject(product,forKey: product.productIdentifier as NSCopying) } requestDelegate?.requestFinished?() } public func paymentQueue(_ queue: SKPaymentQueue,updatedTransactions transactions: [SKPaymentTransaction]) { print("updatedTransactions") for transaction in transactions { switch transaction.transactionState{ case .purchased: printLog("purchased") paymentDelegate?.transactionPurchased(queue,transaction: transaction) SKPaymentQueue.default().finishTransaction(transaction) break case .Failed: printLog("Failed") paymentDelegate?.transactionFailed?(queue,transaction: transaction) SKPaymentQueue.default().finishTransaction(transaction) break case .restored: printLog("restore") restoreSuccess = true paymentDelegate?.transactionRestore?(queue,transaction: transaction) SKPaymentQueue.default().finishTransaction(transaction) break case .purchasing: paymentDelegate?.transactionPurchasing?(queue,transaction: transaction) printLog("purchasing") break case .deferred: paymentDelegate?.transactionDeferred?(queue,transaction: transaction) printLog("deferred") break } } } public func paymentQueue(_ queue: SKPaymentQueue,restoreCompletedTransactionsFailedWithError error: Error) { printLog("retore Failed with error:\(error)") paymentDelegate?.transactionRestoreFailedWithError?(error) } public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) { printLog("finished restore") paymentDelegate?.transactionRestoreFinished?(restoreSuccess) } private func requestPaymentWithProduct(_ product: SKProduct){ let payment = SKPayment(product: product) SKPaymentQueue.default().add(payment) } private func testIsNil(){ if(request == nil){ printLog("request hasn't been init") }else if(request?.delegate == nil){ printLog("request delegate hasn't been set") } } func verifyPruchase(completion:@escaping (NSDictionary?,NSError?) -> Void) { // 验证凭据,获取到苹果返回的交易凭据 let receiptURL = Bundle.main.appStoreReceiptURL // 从沙盒中获取到购买凭据 let receiptData = NSData(contentsOf: receiptURL!) #if DEBUG let url = NSURL(string: ITMS_SANDBox_VERIFY_RECEIPT_URL) #else let url = NSURL(string: VERIFY_RECEIPT_URL) #endif let request = NSMutableURLRequest(url: url! as URL,cachePolicy: NSURLRequest.CachePolicy.useProtocolCachePolicy,timeoutInterval: 10.0) request.httpMethod = "POST" let encodeStr = receiptData?.base64EncodedString(options: NSData.Base64EncodingOptions.endLineWithLineFeed) let payload = NSString(string: "{\"receipt-data\" : \"" + encodeStr! + "\"}") let payloadData = payload.data(using: String.Encoding.utf8.rawValue) request.httpBody = payloadData; let session = URLSession.shared let semaphore = DispatchSemaphore(value: -1) let dataTask = session.dataTask(with: request as URLRequest,completionHandler: {(data,response,error) -> Void in if error != nil{ print("error1") completion(nil,error as NSError?) }else{ if (data==nil) { print("error2") completion(nil,error as NSError?) } do{ let jsonResult: NSDictionary = try JSONSerialization.jsonObject(with: data!,options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary if (jsonResult.count != 0) { // 比对字典中以下信息基本上可以保证数据安全 // bundle_id&application_version&product_id&transaction_id // 验证成功 let receipt = jsonResult["receipt"] as! NSDictionary completion(receipt,nil) } print(jsonResult) }catch{ print("error3") completion(nil,nil) } } semaphore.signal() }) as URLSessionTask dataTask.resume() semaphore.wait() } }
使用方法
新建一个
ViewController
,实现协议LYIAPDelegate
import UIKit import StoreKit class ViewController: UIViewController,LYIAPDelegate { override func viewDidLoad() { super.viewDidLoad() let productsIds = NSSet(array: ["com.xxx.xxx.abc"]) LYIAP.setRequestWithProducts(productsIds,delegate: self) LYIAP.startRequest() // Do any additional setup after loading the view,typically from a nib. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } func requestFinished() { // Do something when products have been loaded. } func transactionPurchased(_ queue: SKPaymentQueue,transaction: SKPaymentTransaction) { // Identifier of the product that has been purchased debugPrint(transaction.payment.productIdentifier) LYIAP.verifyPruchase(completion: {(receipt,error) in // You can verify the transaction. In this callback,you will get the receipt if the transaction is verified by the APPLE. You can compare some tranction infomation with the receipt. debugPrint(receipt) }) } func transactionRestore(_ queue: SKPaymentQueue,transaction: SKPaymentTransaction) { // Identifier of the product that has been restored // You must add restore function to your app accroding to APPLE's provisions debugPrint(transaction.payment.productIdentifier) } func transactionRestoreFinished(_ isSuccess: Bool) { // It is called when restore is finished. isSuccess will be true when some products have been restored successfully. } }