Swift實現iOS內購

前言

  • Swift做爲當前在github上成長最快的語言之一,本人在學習iOS不曾學習過OC,所以在作iOS項目過程當中所有采用了Swift,下面詳細介紹下Swift的內購的實現。github地址:https://github.com/RyanLeeLY/...html


起步

  • 首先我就不介紹如何在iTunesConnect上添加內購商品了,總之添加完商品後,咱們須要用到的是productIdentifiers即你填寫的商品ID,這個ID是商品在商店中的惟一標識。

    android

  • 導入StoreKit,建立一個類,SKPaymentTransactionObserver協議:當交易在隊列中有更新或者移出隊列時這個觀察者會被調用;SKProductsRequestDelegate實現該協議的代理會受到商品請求返回的信息。git

public class LYIAPManager: NSObject, SKPaymentTransactionObserver, SKProductsRequestDelegate
  • 自定義協議,LYIAPRequestDelegate處理請求成功後的代理,LYIAPPaymentDelegate交易時的代理,
    LYIAPDelegate繼承上面兩個協議,方便使用。github

@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, 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:Error)
    @objc optional func transactionRestoreFinished(_ isSuccess:Bool)
    
}

public let LYIAP = LYIAPManager.LYIAPInstance

public class LYIAPManager: NSObject, SKPaymentTransactionObserver, 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,實現協議LYIAPDelegatejson

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

}

部分代碼參考來源:SaiWu博客園swift

相關文章
相關標籤/搜索