AppStore 内购

借助第三方库:SwiftyStoreKit
AppStore内购流程:


 1. 获取商品列表(可以从自己服务器获取,但是商品需要绑定AppStore创建商品的id)
 2. 用户选择并购买商品的时候,发起Storekit支付并等待返回结果
 3. 如果支付成功,则获取Receipt
 4. 获取Receipt之后,则进行AppStore支付结果验证。验证成功之后才可以发放商品。
 
 关于验证分两种:一种是本地验证,一种是服务端验证。
 如果本地验证在在获取到了Receipt之后可以直接verifyPurchase,根据结果进行产品发放。
 如果服务端验证在需要将Receipt发送到服务端,并由服务端发放商品,并且结果告知客户端。

以上是正常流程。但是很有可能在支付过程中发生崩溃,所以需要做容错处理。如果用户购买了商品并且没有对AppStore结果进行验证的话,那么当App再次启动的时候我们会收到transaction事件,此时我们可以再次处理后面的验证工作。

下面是部分代码:
在App启动的时候监听transaction事件,并进行验证处理。

 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        SwiftyStoreKit.completeTransactions(atomically: true) { purchases in
            for purchase in purchases {
                switch purchase.transaction.transactionState {
                case .purchased:
                	//这里只保存了用户最后一笔商品的支付情况
                    let payData = CfgMgr.share.getPayData()
                    if payData != nil{
                        //info[""] = userId
                        //info["productId"] = productId
                        //info["applePid"] = applePid
                        //info["money"] = money
                        
                        //校验用户的最后一笔订单
                        let userId = payData!["userId"] as! String
                        let applePid = payData!["applePid"] as! String
                        if userId == CfgMgr.share.getUserId() && purchase.productId == applePid{
                            let productId = payData!["productId"] as! Int
                            let money = payData!["money"] as! String
                            //注意在这里获取Receipt的时候不能刷新Recepit否则会失败
                            self.doFetchReceipt(userId: userId, productId: productId, applePid: applePid, money: money)
                            //这里获取Receipt并且上传检验产品结果
                            break
                        }
                    }
                    
                    if purchase.needsFinishTransaction {
                        // Deliver content from server, then:没有确认的订单
                        SwiftyStoreKit.finishTransaction(purchase.transaction)
                    }
                    // Unlock content
                case .restored:
                    if purchase.needsFinishTransaction {
                        // Deliver content from server, then:没有确认的订单
                        SwiftyStoreKit.finishTransaction(purchase.transaction)
                    }
                case .failed, .purchasing, .deferred:
                    break // do nothing
                }
            }
        }
        return true
    }

 func doFetchReceipt(userId: String, productId: Int, applePid: String, money: String){
        //不能刷新,刷新会导致失败
        SwiftyStoreKit.fetchReceipt(forceRefresh: false) { [unowned self]result in
            switch result {
            case .success(let receiptData):
                let encryptedReceipt = receiptData.base64EncodedString(options: [])
                NetMgr.share.postIosOrder(userId: userId, productId: productId, applePid: applePid, money: money, payload: encryptedReceipt, completion: onRespPostIosOrder)
                print("Fetch receipt success:\n\(encryptedReceipt)")
            case .error(let error):
                print("Fetch receipt failed: \(error)")
            }
        }
    }

购买:

func onCharge(){
        _productItem = ProductMgr.share.findItem(productId: ProductMgr.share.selProductId)
        
        if _productItem == nil{
            showTipView(title: nil, msg: "产品id不存在,请刷新后重试", okText: "确定", okClick: {}, cancelText: nil, cancelClick: {})
            return
        }
        
        showLoadingView(title: "AppStore支付中...")
        //"fmspeed_ceshi_2"
        SwiftyStoreKit.purchaseProduct(_productItem!.applePid, quantity: 1, atomically: false) { [unowned self] result in
            //self.hideLoadingView()
            switch result {
            case .success(let purchase):
                var price = purchase.product.localizedPrice
                price = price?.replacingOccurrences(of: "¥", with: "")
                price = price?.replacingOccurrences(of: " ", with: "")
                let downloads = purchase.transaction.downloads
                if !downloads.isEmpty {
                    SwiftyStoreKit.start(downloads)
                }
                
                if purchase.needsFinishTransaction {
                	//注意这里千万不要finishTransaction,否则下面验证AppStore购买结果将会查询不到该笔商品。
                    //SwiftyStoreKit.finishTransaction(purchase.transaction)
                    print("needsFinishTransaction")
                }
                CfgMgr.share.setPayData(userId: _userId, productId: _productItem!.id, applePid: _productItem!.applePid, money: price == nil ? "0.00":price!)
                doFetchReceipt(applePid:purchase.productId,money:price == nil ? "0.00":price!)
                
            case .error(let error):
                var errMsg = ""
                switch error.code {
                case .unknown: errMsg = "Unknown error. Please contact support"
                case .clientInvalid: errMsg = "Not allowed to make the payment"
                case .paymentCancelled: errMsg = "用户取消支付"
                case .paymentInvalid: errMsg = "该产品不存在,请联系客服确认"
                case .paymentNotAllowed: errMsg = "The device is not allowed to make the payment"
                case .storeProductNotAvailable: errMsg = "The product is not available in the current storefront"
                case .cloudServicePermissionDenied: errMsg = "Access to cloud service information is not allowed"
                case .cloudServiceNetworkConnectionFailed: errMsg = "Could not connect to the network"
                case .cloudServiceRevoked: errMsg = "User has revoked permission to use this cloud service"
                default: errMsg = (error as NSError).localizedDescription
                }
                print(errMsg)
                hideLoadingView()
                showTipView(title: "支付失败", msg: errMsg, okText: "确定", okClick: {}, cancelText: nil, cancelClick: {})
            }
        }
        
    }

func doFetchReceipt(applePid:String,money:String){
        showLoadingView(title: "正在获取支付结果...")
        SwiftyStoreKit.fetchReceipt(forceRefresh: true) { [unowned self]result in
            switch result {
            case .success(let receiptData):
                let encryptedReceipt = receiptData.base64EncodedString(options: [])
                /*let appleValidator = AppleReceiptValidator(service: .sandbox, sharedSecret: "a92f7a4a41e24082a1094da6f330efb9")
                SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in
                    switch result {
                    case .success(let receipt):
                        print(receipt)
                        // Verify the purchase of Consumable or NonConsumable
                        let purchaseResult = SwiftyStoreKit.verifyPurchase(
                            productId: applePid,
                            inReceipt: receipt)
                            
                        switch purchaseResult {
                        case .purchased(let receiptItem):
                            print("\(applePid) is purchased: \(receiptItem)")
                        case .notPurchased:
                            print("The user has never purchased \(applePid)")
                        }
                    case .error(let error):
                        print("Receipt verification failed: \(error)")
                    }
                }*/
                showLoadingView(title: "验证支付结果...")
                //这里是发送到服务端验证的,如果不想服务端验证,则可以直接在本地SwiftyStoreKit.verifyPurchase验证
                NetMgr.share.postIosOrder(userId: _userId, productId: _productItem!.id, applePid: applePid, money: money, payload: encryptedReceipt, completion: onRespPostIosOrder)
                print("Fetch receipt success:\n\(encryptedReceipt)")
            case .error(let error):
                print("Fetch receipt failed: \(error)")
                hideLoadingView()
                showTipView(title: nil, msg: "获取支付结果异常,请重试或者联系人工客服?", okText: "重试", okClick: {
                    doFetchReceipt(applePid: applePid,money: money)
                }, cancelText:"取消", cancelClick: {})
            }
        }
    }

Note:验证结果之前千万不要finishTransaction,否则后面验证将会查询不到该笔商品。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值