前言
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.
}
}
部分代码参考来源:SaiWu博客园