详解iOS应用程序内购/内付费(一)
很久之前就想出一篇ios内付费的教程,但是一查网上的教程实在太多了,有的写得真的蛮不错的,就心想算了,于是就保存在草稿箱了。至于为什么写完它呢!真是说来话长,最近公司有个项目经理跑来问我有关苹果内付费相关的细节,跟他聊了半天,从项目对接苹果官方支付接口聊到了如何查看app收益,最后终于使他有了一些眉目,但是悲催的是还要我继续去跟他们项目的程序员讲解(真是疯了),所以我就决定给他们项目写一个内购的文档,所以我顺便把这篇博客完成吧!
首先进入苹果的itunesconnection()点击左上角的加号新建一个app应用,点击后该网站会弹出一个信息编辑框,大家只要将上面的信息填充完毕点击save即可在苹果的app平台上拥有一个属于自己的app。
在套装id的上,需要提前为该app申请一个appid以及bundleid,只要是申请成功了就会在选择列表中显示出来。
这里顺便多说一句这个itunesconnect是用来干嘛的,它是苹果公司给个人或企业提供管理自己app的一个平台。在这个平台上开发者可以新建,删除和管理自己的app应用,开发者可以根据需求对app应用进行上架与下架,编辑app信息,生成测试app所需的信息,例如账号,邀请码等,还有就是我们今天要讲的内付费功能。当然啦,他的功能可不止我讲的这些,我大致说一下这个平台的作用,如果你经常跟它打交道的话就会慢慢熟悉了。
接下来,我就来为大家演示一下如何添加付费道具,首先打开itunesconnect,显示如下页面:
选择红圈所圈起来的选项,然后将里面的相关信息补充完毕,如果缺少这一步,内购功能是不会成功的。
假如你已经完成了上述相关银行账户的设置,就点击你的app,选择上面标题栏中的"app 内购买项目"
随后点击左上角的 "create new"选项,如下图所示,进入到下一个界面:
这个界面是让你选择消费道具的种类,现在改版的网站是有简体中文翻译的,所以不像以前打开一看都不知道选哪一个,甚至都不知道每个代表的什么意思(比如我第一次遇到的时候,在领导面前真是囧)。它的种类分为如下几种:
一般对项目来说大多数都是选择“消耗型项目”这个种类,比如游戏中购买金币,宝石balabala~之类的,选中之后就会到这个界面中来:
在上图所示的编辑框中输入,商品名称,产品id以及价格等级,在这边说明一下:
1.商品名称根据你的消费道具的实际意义来说明,比如“100颗宝石”,“100金币”等。
2.产品id是比较重要的,由项目自定义,只要唯一即可,像我一般都是用app的bundleid加一个后缀来表示,这样既跟项目关联又具有唯一性。
3.价格等级的话“查看价格表”中有对应的说明,可以对照着表中每个国家的货币价格与等级来选择。
我们继续,在这个网页的接下来部分如图所示:
选择添加语言选项,弹出一个编辑页面:
点击save保存,则会在界面上显示成如下:
最后一步就是点击“选取文件”提交一张苹果它指定像素(640*920)的商品图片,当他上传完毕后点击“save”按钮,我们这第二部分就大工告成了。提交的商品最后会在内购的页面上显示为如图:
这个图是我在已经发布的app上面截取的,添加了3个商品,已经是通过的的状态了(显示绿色),当您刚提交的时候,因为通过苹果的审查需要一段时间所以会显示黄色的等待状态,所以不必担心是不是商品编辑错了。如图:
这部分,我主要给大家演示一下,如何申请测试账号,利用苹果的沙盒测试环境来模拟appstore的购买流程。
在itunesconnect中选择“用户和职能”选项~
随后在左上角的选项中选择沙盒测试者,点击左上角的加号图标增加一位测试者,如图:
编辑好相应的内容,点击保存,就创建了一个测试账号,是不是很简单啊!当然这个账号如果你忘记了密码可以重新生成一个,无关紧要。
顺带多句嘴,不要在正式的appstore上面用沙盒测试的账号来登录,千万要牢记在心,此账号只用于测试环境下~
接下来就是代码部分啦~
1.首先在项目工程中加入“storekit.framework”,加入头文件#import <storekit/storekit.h>
2.在.h文件中加入“skpaymenttransactionobserver,skproductsrequestdelegate”监听机制
下面贴上内购的核心代码,就几个函数,我在这边就不在做更多详细的解释了,各位看官可以运行跑一下就一目了然了。
.h文件
// // paymentviewcontroller.h // iappaytest // // created by silicon on 14-10-28. // copyright (c) 2014年 silicon. all rights reserved. // #import <uikit/uikit.h> #import <storekit/storekit.h> @interface paymentviewcontroller : uiviewcontroller<skpaymenttransactionobserver,skproductsrequestdelegate> @property (strong, nonatomic) iboutlet uitextfield *productid; @property (strong, nonatomic) iboutlet uibutton *purchase; - (ibaction)purchasefunc:(id)sender; @end
.m文件
// // paymentviewcontroller.m // iappaytest // // created by silicon on 14-10-28. // copyright (c) 2014年 silicon. all rights reserved. // #import "paymentviewcontroller.h" @interface paymentviewcontroller () @end @implementation paymentviewcontroller - (id)initwithnibname:(nsstring *)nibnameornil bundle:(nsbundle *)nibbundleornil { self = [super initwithnibname:nibnameornil bundle:nibbundleornil]; if (self) { // custom initialization } return self; } - (void)viewdidload { [super viewdidload]; // do any additional setup after loading the view from its nib. [[skpaymentqueue defaultqueue] addtransactionobserver:self]; self.productid.text = @"com.games.ztyxs.product_point.1"; } - (void)didreceivememorywarning { [super didreceivememorywarning]; // dispose of any resources that can be recreated. } - (ibaction)purchasefunc:(id)sender { nsstring *product = self.productid.text; if([skpaymentqueue canmakepayments]){ [self requestproductdata:product]; }else{ nslog(@"不允许程序内付费"); } } //请求商品 - (void)requestproductdata:(nsstring *)type{ nslog(@"-------------请求对应的产品信息----------------"); nsarray *product = [[nsarray alloc] initwithobjects:type, nil nil]; nsset *nsset = [nsset setwitharray:product]; skproductsrequest *request = [[skproductsrequest alloc] initwithproductidentifiers:nsset]; request.delegate = self; [request start]; } //收到产品返回信息 - (void)productsrequest:(skproductsrequest *)request didreceiveresponse:(skproductsresponse *)response{ nslog(@"--------------收到产品反馈消息---------------------"); nsarray *product = response.products; if([product count] == 0){ nslog(@"--------------没有商品------------------"); return; } nslog(@"productid:%@", response.invalidproductidentifiers); nslog(@"产品付费数量:%d",[product count]); skproduct *p = nil; for (skproduct *pro in product) { nslog(@"%@", [pro description]); nslog(@"%@", [pro localizedtitle]); nslog(@"%@", [pro localizeddescription]); nslog(@"%@", [pro price]); nslog(@"%@", [pro productidentifier]); if([pro.productidentifier isequaltostring:self.productid.text]){ p = pro; } } skpayment *payment = [skpayment paymentwithproduct:p]; nslog(@"发送购买请求"); [[skpaymentqueue defaultqueue] addpayment:payment]; } //请求失败 - (void)request:(skrequest *)request didfailwitherror:(nserror *)error{ nslog(@"------------------错误-----------------:%@", error); } - (void)requestdidfinish:(skrequest *)request{ nslog(@"------------反馈信息结束-----------------"); } //监听购买结果 - (void)paymentqueue:(skpaymentqueue *)queue updatedtransactions:(nsarray *)transaction{ for(skpaymenttransaction *tran in transaction){ switch (tran.transactionstate) { case skpaymenttransactionstatepurchased: nslog(@"交易完成"); break; case skpaymenttransactionstatepurchasing: nslog(@"商品添加进列表"); break; case skpaymenttransactionstaterestored: nslog(@"已经购买过商品"); break; case skpaymenttransactionstatefailed: nslog(@"交易失败"); break; default: break; } } } //交易结束 - (void)completetransaction:(skpaymenttransaction *)transaction{ nslog(@"交易结束"); [[skpaymentqueue defaultqueue] finishtransaction:transaction]; } - (void)dealloc{ [[skpaymentqueue defaultqueue] removetransactionobserver:self]; [super dealloc]; } @end
代码就这么多,到这边我们的ios内购教程就接近尾声了,在测试的时候还有几点因素要注意一下:
1.沙盒环境测试appstore内购流程的时候,请使用没越狱的设备。
2.请务必使用真机来测试,一切以真机为准。
3.项目的bundle identifier需要与您申请appid时填写的bundleid一致,不然会无法请求到商品信息。
讲了这么多,附上几张测试截屏给大家展示一下:
请求商品时的打印日志:
交易成功后:
手机截屏:
要求输入appstore帐密,使用测试生成的即可:
确定购买:
交易完成:
当我们的交易完成后还要去appstore 上面去验证票据信息是否正确,这样我们才可以给玩家发放道具,apple官方文档:
//交易结束 - (void)completetransaction:(skpaymenttransaction *)transaction{ nslog(@"交易结束"); //交易验证 nsurl *recepiturl = [[nsbundle mainbundle] appstorereceipturl]; nsdata *receipt = [nsdata datawithcontentsofurl:recepiturl]; if(!receipt){ } nserror *error; nsdictionary *requestcontents = @{ @"receipt-data": [receipt base64encodedstringwithoptions:0] }; nsdata *requestdata = [nsjsonserialization datawithjsonobject:requestcontents options:0 error:&error]; if (!requestdata) { /* ... handle error ... */ } //in the test environment, use https://sandbox.itunes.apple.com/verifyreceipt //in the real environment, use https://buy.itunes.apple.com/verifyreceipt // create a post request with the receipt data. nsurl *storeurl = [nsurl urlwithstring:@"https://buy.itunes.apple.com/verifyreceipt"]; nsmutableurlrequest *storerequest = [nsmutableurlrequest requestwithurl:storeurl]; [storerequest sethttpmethod:@"post"]; [storerequest sethttpbody:requestdata]; // make a connection to the itunes store on a background queue. nsoperationqueue *queue = [[nsoperationqueue alloc] init]; [nsurlconnection sendasynchronousrequest:storerequest queue:queue completionhandler:^(nsurlresponse *response, nsdata *data, nserror *connectionerror) { if (connectionerror) { /* ... handle error ... */ } else { nserror *error; nsdictionary *jsonresponse = [nsjsonserialization jsonobjectwithdata:data options:0 error:&error]; if (!jsonresponse) { /* ... handle error ...*/ } /* ... send a response back to the device ... */ //parse the response } }]; [[skpaymentqueue defaultqueue] finishtransaction:transaction]; }
好了,所有的内购流程基本上讲完了,原谅我在图片上的涂抹,因为关系到产品的敏感词汇所以希望大家能够不介意。以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读