美文网首页内购
iOS开发之内购(Swift)

iOS开发之内购(Swift)

作者: TRACER_ | 来源:发表于2017-05-27 13:55 被阅读752次

    关于协议税务和银行业务的资料填写以及申请我就不写了,自己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。后来还是自己默默的翻遍了网上的内购文。恩,总算可以支付了,后期如果有什么问题,我会继续补充的。
    鉴于网上的图文博客比较多,我基本都是以文字的形式大概说明了一下操作流程。

    相关文章

      网友评论

        本文标题:iOS开发之内购(Swift)

        本文链接:https://www.haomeiwen.com/subject/ztdwittx.html