zoukankan      html  css  js  c++  java
  • iOS之内购

    很久之前就想出一篇IOS内付费的教程,但是一查网上的教程实在太多了,有的写得真的蛮不错的,就心想算了,于是就保存在草稿箱了。至于为什么写完它呢!真是说来话长,最近公司有个项目经理跑来问我有关苹果内付费相关的细节,跟他聊了半天,从项目对接苹果官方支付接口聊到了如何查看App收益,最后终于使他有了一些眉目,但是悲催的是还要我继续去跟他们项目的程序员讲解(真是疯了),所以我就决定给他们项目写一个内购的文档,所以我顺便把这篇博客完成吧!

    首先进入苹果的ItunesConnection(https://itunesconnect.apple.com)点击左上角的加号新建一个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文件

    [objc] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. //  
    2. //  PaymentViewController.h  
    3. //  IAPPayTest  
    4. //  
    5. //  Created by silicon on 14-10-28.  
    6. //  Copyright (c) 2014年 silicon. All rights reserved.  
    7. //  
    8.   
    9. #import <UIKit/UIKit.h>  
    10.   
    11. #import <StoreKit/StoreKit.h>  
    12.   
    13. @interface PaymentViewController : UIViewController<SKPaymentTransactionObserver,SKProductsRequestDelegate>  
    14.   
    15. @property (strong, nonatomic) IBOutlet UITextField *productID;  
    16.   
    17. @property (strong, nonatomic) IBOutlet UIButton *purchase;  
    18.   
    19. - (IBAction)purchaseFunc:(id)sender;  
    20.   
    21. @end  

    .m文件

    [objc] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. //  
    2. //  PaymentViewController.m  
    3. //  IAPPayTest  
    4. //  
    5. //  Created by silicon on 14-10-28.  
    6. //  Copyright (c) 2014年 silicon. All rights reserved.  
    7. //  
    8.   
    9. #import "PaymentViewController.h"  
    10.   
    11. @interface PaymentViewController ()  
    12.   
    13. @end  
    14.   
    15. @implementation PaymentViewController  
    16.   
    17. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil  
    18. {  
    19.     self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];  
    20.     if (self) {  
    21.         // Custom initialization  
    22.     }  
    23.     return self;  
    24. }  
    25.   
    26. - (void)viewDidLoad  
    27. {  
    28.     [super viewDidLoad];  
    29.     // Do any additional setup after loading the view from its nib.  
    30.       
    31.     [[SKPaymentQueue defaultQueue] addTransactionObserver:self];  
    32.     self.productID.text = @"com.games.ztyxs.product_point.1";  
    33. }  
    34.   
    35. - (void)didReceiveMemoryWarning  
    36. {  
    37.     [super didReceiveMemoryWarning];  
    38.     // Dispose of any resources that can be recreated.  
    39. }  
    40.   
    41. - (IBAction)purchaseFunc:(id)sender {  
    42.     NSString *product = self.productID.text;  
    43.     if([SKPaymentQueue canMakePayments]){  
    44.         [self requestProductData:product];  
    45.     }else{  
    46.         NSLog(@"不允许程序内付费");  
    47.     }  
    48. }  
    49.   
    50. //请求商品  
    51. - (void)requestProductData:(NSString *)type{  
    52.     NSLog(@"-------------请求对应的产品信息----------------");  
    53.     NSArray *product = [[NSArray alloc] initWithObjects:type, nil nil];  
    54.   
    55.     NSSet *nsset = [NSSet setWithArray:product];  
    56.     SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];  
    57.     request.delegate = self;  
    58.     [request start];  
    59.       
    60. }  
    61.   
    62. //收到产品返回信息  
    63. - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{  
    64.   
    65.     NSLog(@"--------------收到产品反馈消息---------------------");  
    66.     NSArray *product = response.products;  
    67.     if([product count] == 0){  
    68.         NSLog(@"--------------没有商品------------------");  
    69.         return;  
    70.     }  
    71.       
    72.     NSLog(@"productID:%@", response.invalidProductIdentifiers);  
    73.     NSLog(@"产品付费数量:%d",[product count]);  
    74.       
    75.     SKProduct *p = nil;  
    76.     for (SKProduct *pro in product) {  
    77.         NSLog(@"%@", [pro description]);  
    78.         NSLog(@"%@", [pro localizedTitle]);  
    79.         NSLog(@"%@", [pro localizedDescription]);  
    80.         NSLog(@"%@", [pro price]);  
    81.         NSLog(@"%@", [pro productIdentifier]);  
    82.           
    83.         if([pro.productIdentifier isEqualToString:self.productID.text]){  
    84.             p = pro;  
    85.         }  
    86.     }  
    87.       
    88.     SKPayment *payment = [SKPayment paymentWithProduct:p];  
    89.       
    90.     NSLog(@"发送购买请求");  
    91.     [[SKPaymentQueue defaultQueue] addPayment:payment];  
    92. }  
    93.   
    94. //请求失败  
    95. - (void)request:(SKRequest *)request didFailWithError:(NSError *)error{  
    96.     NSLog(@"------------------错误-----------------:%@", error);  
    97. }  
    98.   
    99. - (void)requestDidFinish:(SKRequest *)request{  
    100.     NSLog(@"------------反馈信息结束-----------------");  
    101. }  
    102.   
    103.   
    104. //监听购买结果  
    105. - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{  
    106.     for(SKPaymentTransaction *tran in transaction){  
    107.           
    108.         switch (tran.transactionState) {  
    109.             case SKPaymentTransactionStatePurchased:  
    110.                 NSLog(@"交易完成");  
    111.                   
    112.                 break;  
    113.             case SKPaymentTransactionStatePurchasing:  
    114.                 NSLog(@"商品添加进列表");  
    115.                   
    116.                 break;  
    117.             case SKPaymentTransactionStateRestored:  
    118.                 NSLog(@"已经购买过商品");  
    119.               
    120.                 break;  
    121.             case SKPaymentTransactionStateFailed:  
    122.                 NSLog(@"交易失败");  
    123.                   
    124.                 break;  
    125.             default:  
    126.                 break;  
    127.         }  
    128.     }  
    129. }  
    130.   
    131. //交易结束  
    132. - (void)completeTransaction:(SKPaymentTransaction *)transaction{  
    133.     NSLog(@"交易结束");  
    134.       
    135.     [[SKPaymentQueue defaultQueue] finishTransaction:transaction];  
    136. }  
    137.   
    138.   
    139. - (void)dealloc{  
    140.     [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];  
    141.     [super dealloc];  
    142. }  
    143.   
    144. @end  

    代码就这么多,到这边我们的IOS内购教程就接近尾声了,在测试的时候还有几点因素要注意一下:

    1.沙盒环境测试appStore内购流程的时候,请使用没越狱的设备。

    2.请务必使用真机来测试,一切以真机为准。

    3.项目的Bundle identifier需要与您申请AppID时填写的bundleID一致,不然会无法请求到商品信息。

    讲了这么多,附上几张测试截屏给大家展示一下:

    请求商品时的打印日志:

    交易成功后:

    手机截屏:

    要求输入AppStore帐密,使用测试生成的即可:

    确定购买:

    交易完成:

    当我们的交易完成后还要去appstore 上面去验证票据信息是否正确,这样我们才可以给玩家发放道具,apple官方文档

    [objc] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. //交易结束  
    2. - (void)completeTransaction:(SKPaymentTransaction *)transaction{  
    3.     NSLog(@"交易结束");  
    4.     //交易验证  
    5.     NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL];  
    6.     NSData *receipt = [NSData dataWithContentsOfURL:recepitURL];  
    7.       
    8.     if(!receipt){  
    9.           
    10.     }  
    11.       
    12.     NSError *error;  
    13.     NSDictionary *requestContents = @{  
    14.                                       @"receipt-data": [receipt base64EncodedStringWithOptions:0]  
    15.                                       };  
    16.     NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents  
    17.                                                           options:0  
    18.                                                             error:&error];  
    19.       
    20.     if (!requestData) { /* ... Handle error ... */ }  
    21.       
    22.     //In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt  
    23.     //In the real environment, use https://buy.itunes.apple.com/verifyReceipt  
    24.     // Create a POST request with the receipt data.  
    25.     NSURL *storeURL = [NSURL URLWithString:@"https://buy.itunes.apple.com/verifyReceipt"];  
    26.     NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];  
    27.     [storeRequest setHTTPMethod:@"POST"];  
    28.     [storeRequest setHTTPBody:requestData];  
    29.       
    30.     // Make a connection to the iTunes Store on a background queue.  
    31.     NSOperationQueue *queue = [[NSOperationQueue alloc] init];  
    32.     [NSURLConnection sendAsynchronousRequest:storeRequest queue:queue  
    33.                            completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {  
    34.                                if (connectionError) {  
    35.                                    /* ... Handle error ... */  
    36.                                } else {  
    37.                                    NSError *error;  
    38.                                    NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];  
    39.                                    if (!jsonResponse) { /* ... Handle error ...*/ }  
    40.                                    /* ... Send a response back to the device ... */  
    41.                                    //Parse the Response  
    42.                                }  
    43.                            }];  
    44.   
    45.       
    46.       
    47.     [[SKPaymentQueue defaultQueue] finishTransaction:transaction];  
    48. }  




  • 相关阅读:
    easyui的datagrid分页写法小结
    @ResponseBody
    JSONObject.parseObject(jsonStr);和JSONObject.fromObject(jsonStr);
    测试cnblogs的代码折叠展开功能和zoom.js实现图片放大缩小冲突的问题
    使用git将项目上传到github(最简单方法)
    @Transactional注解事务不回滚不起作用无效
    SpringBoot开发项目,引入JPA找不到findOne方法
    mysql8.0 Authentication plugin 'caching_sha2_password' cannot be loaded
    Hibernate JPA中@Transient、@JsonIgnoreProperties、@JsonIgnore、@JsonFormat、@JsonSerialize等注解解释
    java 获取计算机名称, ip, mac地址
  • 原文地址:https://www.cnblogs.com/zhun/p/5783874.html
Copyright © 2011-2022 走看看