iOS:为 App 添加内购买项目

思维导图

前言

在 AppStore 里,付费应用的下载量往往比免费应用的下载量少的多。上架过应用的开发者,可能会有这样的体会:当一个应用免费的时候,每天能有几十上百次的下载,但是一旦设置为收费的时候,下载量立即暴跌,即使只设成了一元,可能好几星期都没人下载。

收费的应用不好做,与其长时间零下载,还不如设置为免费,每天看看下载量来的舒服。

但是作为一个开发者,开发免费应用的动力,显然比不上开发收费应用。应用免费了,开发的动力也少了。因此,我们可以考虑在应用里面加入内购买项目,通过免费下载吸引用户,再用内购买项目让用户按需购买,自己赚点零花钱。

本文将用一个真实的例子(已上架 AppStore:Catch - 捕捉稍纵即逝的灵感),介绍为 App 添加内购买项目的流程。

必要的准备

添加内购买项目之前,需要有一个付费的开发者账号,并在开发者账号的“协议、税务和银行业务”里完善银行卡等信息,否则 Apple 无法将应用的收入转给你。

你还需要准备一个已上架或者未上架的应用,应用在首次添加内购买项目时需要与新的应用版本一起提交审核。

内购买项目的用户界面

在添加内购买项目前,你需要在你的应用里,自己设计好商品展示的界面,Apple 只负责购买的流程。这个界面在接下去新建内购买项目时也会用到。

下图是一个实际应用的内购商品展示界面:

在 iTunesConnect 上新建内购买项目

接下来,在 iTunesConnect 上的应用页面,选择“功能”-“App 内购买项目“,点击加号新建项目,如下图:

之后会要求选择内购买项目的类型,这里以“消费型项目”为例。在接下来的表里填写相应的信息。产品 ID 一般以 App 的 Bundle ID 为前缀再加上自定义的产品后缀,如:com.self.purchase1。在审核信息里上传刚刚的内购商品展示界面,供审查员审核。其他信息自行填写即可。

新建内购买项目后,在内购买项目列表里显示为“准备提交”状态。现在在准备提交的 App 版本中添加刚刚新建的内购买项目。

完成内购买相关代码

关于内购买的代码,本文将使用一个现成的框架:SwiftyStoreKit,这样有利于理清思路。对于直接使用 StoreKit 完成整个内购买的流程,将在下一篇文章里讨论。

首先在项目里添加 SwiftyStoreKit 框架。具体添加的方法上面的链接里已有说明。
总共的代码有三部分,如下:

一、获取商品信息,用于在界面上显示:

SwiftyStoreKit.retrieveProductsInfo([productID]) {[weak self ] result in
    if let product = result.retrievedProducts.first {
        let priceString = product.localizedPrice!
        print("Product: \(product.localizedDescription), price: \(priceString)")
    }else if let invalidProductId = result.invalidProductIDs.first {
        print("Could not retrieve product info .Invalid product identifier: \(invalidProductId)")
    }else {
        print("Error: \(result.error)")
    }
}

商品信息包括商品的价格和名称,保存在 SKProduct 对象里。汇率的问题 Apple 已经帮我们处理好,商品的名称会根据我们在 iTunesConect 里填写的本地化信息自动选择,因此我们只需要直接使用 localizedPricelocalizedTitlelocalizedDescription

二、购买商品(当用户点击购买后):

func purchase(_ productID: String) {
    SwiftyStoreKit.purchaseProduct(productID, atomically: true) { [weak self] result in
        if case .success(let product) = result {
        // Deliver content from server, then:
        if product.needsFinishTransaction {
            SwiftyStoreKit.finishTransaction(product.transaction)
            }
        }
        if let alert = self?.alertForPurchaseResult(result) {
            self?.showAlert(alert)
        }
    }
}

productID 就是内购买项目的 ID,如:com.self.appname.purchase1。productID 可以事先在程序中定义,如果想从网络上获取 productID 的话,只能从自己的服务器中获取,iTunesConnect 并不提供获取内购买商品列表的 API。

三、完成购买事务:

func completeIAPTransactions() {
    SwiftyStoreKit.completeTransactions(atomically: true) { products in
        for product in products {
            if product.transaction.transactionState == .purchased || product.transaction.transactionState == .restored {
                if product.needsFinishTransaction {
                // Deliver content from server, then:
                    SwiftyStoreKit.finishTransaction(product.transaction)
                }
                print("purchased: \(product.productId)")
            }
        }
    }
}

在 application:didFinishLaunchingWithOptions 里调用上述的函数。因为 Apple 建议在 app 载入的时候为购买事务添加一个观察者:

Adding your app's observer at launch ensures that it will persist during all launches of your app, thus allowing your app to receive all the payment queue notifications.

SwiftyStoreKit 用 completeTransactions 函数实现了这个功能。这样,如果在载入的时候有任何进行中的事务,就可以对它们进行处理,更新 app 的状态和 UI。

内购买测试

完成内购买的代码后,在提交审核之前,还应该对它进行测试。Apple 为应用内购买项目提供了沙箱(sandbox)测试,让你使用虚拟的货币模拟内购过程。

a) 在 iTunesConnect 的“用户与职能”里添加沙箱技术测试员。

b) 在 iPhone 设置里的 “iTunes Store 与 App Store” 将原来的账号注销。但是不要登陆测试员的账号。

c) 打开待测试内购买的 app,点击内购买的项目进行购买。这时候,会提示登录 App Store,使用沙箱测试员的账号登录。

d) 之后会弹出购买信息确认窗口,注意窗口内的提示信息 “Environment: Sandbox“,表示是在沙箱中测试,购买的货币是虚拟的。如果没有这一行的话,则是在真实的环境中进行交易,使用的就是真实的货币了。

结语

测试完成后就可以提交审核了。如果是首次添加内购买项目,记得在 app 审核页添加内购项目一起提交审核。当再次添加内购买项目,就可以独立审核内购买项目,而无需提交新的 app 版本。当然,这样做的前提是 app 能够从自己的服务器上获取内购买项目的 productID,否则 app 无法获取新内购买项目的信息。

本文是以消费性内购买项目为例,对于其他类型的内购买项目,方法大同小异,更多的信息请参考官方文档

欢迎访问我的Github:LinShiwei (Lin Shiwei) · GitHub

有任何疑问的话,欢迎在下方评论区讨论。

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,544评论 25 707
  • 原文地址:http://blog.csdn.net/xiaominghimi/article/details/69...
    LuxDark阅读 5,880评论 2 4
  • 在我们应用开发中我们经常在自己的项目中使用到支付,下面我们来谈谈iOS这块的支付;iOS支付主要分为两类,第三方支...
    Hither阅读 8,102评论 9 42
  • 现实真的很残酷,身边没有一个真心朋友,经历多了,就很难再把自己的心交出去。 “你以为别人都和你想的一样吗,真心...
    DYdream阅读 249评论 0 0
  • 如果次元允许,我想活在动漫里! 我向党和人民保证,我有了特异功能一定不会干违法乱纪的事。 (胸前的红领巾更鲜艳了呢...
    小酒官儿阅读 325评论 0 1