iOS 内购 IAP 流程

iOS内购也算是老生常谈的一个技术点了, 但是对于我这种刚刚接触的人来讲, 还是踩了不少的坑, 这几天刚搞完, 来简单的总结一下内购的流程.
暂且不说申请税务, 添加银行卡 这些步骤, 直接进入代码.

首先了解一下iOS IAP内购的总的流程, 如下图:

内购大致的流程图

那么根据上图所述6点进行 代码阐述.

  1. Appdelegate中应用程序启动时 设置transectionObserver:
    SKPaymentQueue.default().add(self)
  2. 根据后端返回的产品id 或者 产品id 的集合, 进行产品的查询.
@discardableResult
    public func requestProductsInfo(productIds: [String]) -> SKProductsRequest {
        let request = SKProductsRequest(productIdentifiers: Set(productIds))
        request.delegate = self
        request.start()
        return request
    }
  1. SKProductsRequestDelegate代理方法中 可以找到接收到苹果服务器返回的查询产品方法
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        let products = response.products
        print("products : \(products)")
/// 拿到数据集合 进行UI界面的更新
#warning UpdateUI
    }

4, 5 . 如果后端有订单系统的话, 那么当点击某一款产品时, 要向后端请求生成订单号. 没有订单系统, 不要订单号也是可以的. 然后根据product创建payment实例, 并将其放入paymentQueue中, 发起付款请求:

 public func purchaseProduct(product: SKProduct, orderId: String?) {
        let payment = SKMutablePayment(product: product)
        //设置订单号
        if let orderId = orderId {
            payment.applicationUsername = orderId
        }
        SKPaymentQueue.default().add(payment)
    }
  1. . 收到付款的回调, 回调中会将transection 带出来, 可以根据transection.transactionState 来判断交易的状态, 根据交易状态来进行不同的处理, 要记住, 处理结束之后 一定要调用结束交易的代码:
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {
            //            print("有交易更新")
          switch transaction.transactionState {
                 case .purchasing:
                   printLog("商品添加进列表")
                case .purchased:
                   printLog("交易完成")
               case .failed:
                     if let swiftError = transaction.error {
                           let error = swiftError as NSError
                           if error.code == SKError.Code.paymentCancelled.rawValue {
                                         //                    printLog("交易取消")
                             } else {
                                       //                    printLog("交易失败")
                             }
                    }

            SKPaymentQueue.default().finishTransaction(transaction)
        case .restored:
            //            printLog("已经购买过商品")
            SKPaymentQueue.default().finishTransaction(transaction)
        case .deferred:
            break
        @unknown default:
            break
             }
      }
}

要注意:

  • 发起productRequest之前要判断当前设备或者AppId是否支持IAP:
 func canMakePurchases() -> Bool {
        return SKPaymentQueue.canMakePayments()
 }
  • 如果当前产品为非消耗类型的, 那么只能购买一次, 再次购买的话就会出现以下图片:


    非消耗类型产品, 多次购买

    如果你的产品中有非消耗类型的产品, 那么需要判断当前产品是否已经购买过, 如果购买过, 苹果要求要有一个恢复购买按钮, 用来恢复当前appid的权益, 否则会被guildline 3.1.1 拒绝掉. 可通过下面的步骤查询到当前已经购买过的产品:

1. 在进入商品列表或者购买页面的时候 向PaymentQueue中添加存储已经完成交易的操作: SKPaymentQueue.default().restoreCompletedTransactions()
2. 接下来会出发SKPaymentTransactionObserver中的完成交易的代理方法, 在代理方法中 可以将已经完成交易的产品 存储起来:
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
        // 处理已经购买过的商品.
        for transaction in queue.transactions {
            let productId = transaction.payment.productIdentifier
            printLog("已经购买过的产品id = \(productId)")
            /// 可以将产品或者产品id放入集合中 存储起来.
        }
    }

如果产品类型属于非续期订阅, 那么以上方法是查不出来的. 因为非续期订阅 是可以无限次购买的产品, 产品有有效期, 买的越多, 有效期越长, 例如为期一年的已归档文章目录订阅.

写在最后

关于iOS内购的代码流程 差不多也就这些, 但是如果要写出体系比较健壮的内购,例如: 发起购买时不会丢单, 如果丢单, 如何处理, 如何应对各种异常情况, 还是需要很大的经理和时间投入, 不单单要客户端处理好每一步, 而且需要服务端的紧密配合. 我还有很多的小细节没有涉及到, 如果有什么不对, 欢迎指正交流.
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容