iOS开发之内购(Swift)

关于协议税务和银行业务的资料填写以及申请我就不写了,自己Google+百度吧。毕竟各位大神写的也是蛮详细的。直接上代码了,需要的说明我会以注释的形式大概说一下,当然有什么不对的地方欢迎拍砖😝😝(swift2.2,不要问我为什么不升级)

  • 1、导入StoreKit.framework库(target -> General -> Linked Frameworks and Libraries -> 加号...)
  • 2、导入头文件import StoreKit
  • 3、去开发者账号申请产品ID(iTunes Connect -> 我的APP -> 功能 -> APP内购买项目 -> 点击加号填写相关的信息 -> 填写完会生成一个产品 ID)
  • 4、代码:
  class InAppPurchaseViewController: UIViewController, SKPaymentTransactionObserver, SKProductsRequestDelegate{
      
     // 判断是否在测试环境下(用于客户端自己去苹果服务器验证付款信息,当然不推荐这么做,最好是由服务器去验证,然后返回给客户端)
       private let appStoreUrl = "https://sandbox.itunes.apple.com/verifyReceipt"// 测试环境(沙盒账号付款)
       private let appStoreUrl = "https://buy.itunes.apple.com/verifyReceipt"
    

        override func viewDidLoad() {
            super.viewDidLoad()
            // 开启内购检测
            SKPaymentQueue.defaultQueue().addTransactionObserver(self)   
    }

         func purchaseBtnClick(sender: AnyObject) {
    
              if SKPaymentQueue.canMakePayments() {
                    reuqestProductData("在第三步申请的产品ID")
            }else{
                  print("您已设置不允许程序内购买")
          }
    }

         private func reuqestProductData(type: String) {
            let product = [type]
            let set = NSSet(array: product as [AnyObject])
            let request = SKProductsRequest(productIdentifiers: set as! Set<String>)
            request.delegate = self
            request.start()
  }

          // 监听购买结果
          func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    
              for tran in transactions {
                 // print(tran.error?.description)

                  switch tran.transactionState {
                  case .Purchased:
                      print("购买成功")
                      commitSeversSucceeWithTransaction(tran)
                      SKPaymentQueue.defaultQueue().finishTransaction(tran)
            
                   case .Purchasing:
                       print("用户正在购买")
            
                    case .Restored:
                        print("已经购买过商品")
            
                   case .Failed:
                      SKPaymentQueue.defaultQueue().finishTransaction(tran)
                      print("失败")
            
                  default:
                      break
        }
    }
}

           // 收到产品返回的信息
           func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {
                if response.products.count == 0 {
                print("没有商品")
                }
                var prod: SKProduct?
                for pro in response.products {
                print("------------------")
                print(pro.localizedDescription)
                print(pro.localizedTitle)
                print(pro.price)
                print(pro.productIdentifier)
                print("------------------")
                if pro.productIdentifier == "在第三步申请的产品ID" {
                    prod = pro
                }
            }
            // 发送购买请求
            if let produ = prod {
                let payment = SKPayment(product: produ)
                SKPaymentQueue.defaultQueue().addPayment(payment)
            }   
}

            // 验证(1、客户端自己验证。2、由后台去验证,推荐这个)
            private func commitSeversSucceeWithTransaction(transation: SKPaymentTransaction) {
                let receiptUrl = NSBundle.mainBundle().appStoreReceiptURL
                let receiveData = NSData(contentsOfURL: receiptUrl!)
                // 最好转成Base64,万一返回结果有特殊字符
                let receiptString = receiveData!.base64EncodedStringWithOptions(.EncodingEndLineWithLineFeed)
                // 保存本地(dict随你自己的需求写就好)
                let dict = ["account_id": account, "receipt": "\(receiptString)"]
                userDefault.setObject(dict, forKey: UserDefaultKey.appInPurchase)
                userDefault.synchronize()

                // 客户端自己去苹果服务器验证付款信息
                let bodyString = "{\"receipt-data\" : \"\(receiptString)\"}"
                let bodyData = bodyString.dataUsingEncoding(NSUTF8StringEncoding)
    
                let url = NSURL(string: appStoreUrl)
                let reuqest: NSMutableURLRequest = NSMutableURLRequest(URL: url!)
                reuqest.HTTPBody = bodyData
                reuqest.HTTPMethod = "POST"
                // 请原谅我懒,毕竟我只是写给自己测试的(上线的时候就是由后台去验证的了),就直接强制try了。如果你们真的是要客户端自己验证的话,就乖乖的do - catch 抛异常吧。
                let responseData = try? NSURLConnection.sendSynchronousRequest(reuqest, returningResponse: nil)
                let dic: NSDictionary = try! NSJSONSerialization.JSONObjectWithData(responseData!, options: .AllowFragments) as! NSDictionary
                // let statusCode: Int = dic["status"] as! Int
                // 我打包装到手机(即连到苹果正式验证环境,因为苹果审核的时候就是用你提供的沙盒账号测试的)测试支付的时候发现dic就直接等于21007了,但是debug的时候dic返回很多相关的信息(可能这里命名不应该叫dic,表示有点不太懂为什么会这样)
                // status请参考下面的状态码表
           
          // 由自己的服务器去验证
          把交易成功后获取的凭证receiptString传给后台,让后台去苹果的服务器验证,然后客户端根据后台返回的数据来处理业务(比如刷新UI,设置为会员等等操作)
          validateForServer(receiptString) // 自定义方法
      }
    //  其他代理方法(请求失败,请求成功等等的返回,根据具体的需求自己处理吧)
}
  • 5、添加沙盒测试账号(iTunes Connect -> 用户和智能 -> 点击加号添加即可),然后就自己测试支付吧。

  • 6、需要注意的几点:
    1)在监听购买结果后,一定要调用SKPaymentQueue.defaultQueue().finishTransaction(tran);来允许你从支付队列中移除交易
    2)沙盒环境测试appStore内购流程的时候,请使用未越狱的设备
    3)请务必使用真机来测试,一切以真机为准
    4)真机测试的时候,一定要退出原来的账号,才能用沙盒测试账号,否则会报错连接不到iTunes
    5)开发环境下也需要有证书才能测试,请确保项目的BundleID需要与您申请AppID时填写的bundleID一致
    6)价格不能在后台获取,只能在申请产品ID的时候选择,有多个价格?那就多申请几个产品ID咯。
    7)为保证审核的通过,需要在客户端或server进行双重验证,即,先以线上交易验证地址进行验证,如果苹果正式验证服务器的返回验证码code为21007,则再一次连接沙盒测试服务器进行验证即可。在应用提审时,苹果IAP提审验证时是在沙盒环境的进行的,即:苹果在审核App时,只会在sandbox环境购买,其产生的购买凭证,也只能连接苹果的测试验证服务器,如果没有做双验证,需要特别注意此问题,否则会被拒。
    8)代码仅供参考😁😁😁

  • 7、关于漏单的处理
    // 我还需要理清逻辑+调试代码,端午节后补充吧,已经提前给自己放假,无心写代码。(端午已完,三天假过得飞快有木有)
    关于漏单:用户已经支付了money,但是我们并没有将用户设置为会员或者发放道具等等相关的操作。原因?可能是验证的时候出了问题,也可能是没等到验证完成用户就直接退出app了,可能...(脑补吧,毕竟bug总是千奇百怪,防不胜防)
    我的做法是保存到本地即 NSUserDefaults(代码见上面,我最后还是放弃了这种方法,用了下面内购文里的方法)
    然后一进程序就看看有没有漏单

    推荐一篇关于内购文,写的比我这好多了,各位猿媛请移步吧。

  • 8、越狱手机无解
    网上资料比较少,目前我可能就先这样了,如果点进来的你已经处理了的话,欢迎补充。

状态码表.png

我们的APP一直在用微信支付和支付宝支付。产品说以防万一(我觉得被拒的概率很小),先提前做好。刚知道这个需求的时候特想说句mmp。后来还是自己默默的翻遍了网上的内购文。恩,总算可以支付了,后期如果有什么问题,我会继续补充的。
鉴于网上的图文博客比较多,我基本都是以文字的形式大概说明了一下操作流程。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容

  • 自己开发的视频直播项目,牵涉到充值金币,用到了苹果公司的内购,趴坑了两天,这里总结下实现苹果内购。 一. 创建测试...
    Leo丶Dicaprio阅读 3,384评论 8 7
  • 本文给大家介绍下内购,部分地方是摘自网络。 创建测试App 点击新建App,会出现新建窗口; 二、添加内购 App...
    Jacky_Yang阅读 2,318评论 0 8
  • 参考链接iOS开发之内购-AppStore iOS App提交指南(二)-协议、税务和银行业务iOS应用内付费(I...
    sjzuo阅读 3,007评论 4 4
  • - 消耗型项目 就像你玩游戏需要买金币,买钻石等,只要花钱就可以无限次的购买 - 非消耗型项目 就像你在App S...
    tiger2016阅读 654评论 0 1
  • 公司项目中有虚拟产品,所以要使用苹果内购.自此记录一下苹果内购的流程.前提是已有开发者账号 协议,税务和银行业务 ...
    better栋阅读 2,021评论 0 7