zoukankan      html  css  js  c++  java
  • iOS应用内购/内付费

    最近应用审核被拒绝了,原因是因为使用了第三方支付,苹果说需要使用到应用内购买。但是查了下相关资料,太杂且不详细,好吧,所以现在自己来写博客咯,把我所知道的一一列出来,可能内容有点乱,大家就将就着些吧

    一、创建应用

    首先进入苹果的ItunesConnection(https://itunesconnect.apple.com)点击左上角的加号新建一个App应用,点击后该网站会弹出一个信息编辑框,大家只要将上面的信息填充完毕点击save即可在苹果的app平台上拥有一个属于自己的App。

    在套装ID的上,需要提前为该App申请一个AppID以及BundleID,只要是申请成功了就会在选择列表中显示出来。如果有人有疑问如何申请,请看我之前那一篇推送的博客,里面有详细的步骤。附上链接点击打开链接

    这里顺便多说一句这个ItunesConnect是用来干嘛的,它是苹果公司给个人或企业提供管理自己App的一个平台。在这个平台上开发者可以新建,删除和管理自己的App应用,开发者可以根据需求对App应用进行上架与下架,编辑App信息,生成测试app所需的信息。具体操作我就不细说了。

    二、填写协议、税务和银行业务

    我们打开ItunesConnect,进入协议、税务和银行业务,配置相关信息。

    填写相关信息,因为我是已经申请成功了,所以看到了就是下图

    进入协议、税务和银行业务页面后,会有3种合同类型,如果你之前没有主动申请过去合同,那么一般你现在激活的合同只有iOS Free Application一种。

    页面内容分为两块

    • Request Contracts(申请合同)
    • Contracts In Effect(已生效合同)。

    合同类型分为3种

    • iOS Free Application(免费应用合同)
    • iOS Paid Application(付费应用合同)
    • iAd App笔者暂时只申请过付费应用合同,所以下面主要讲一下付费应用合同的申请流程。


    1、填写联系方式

      • 我们点击Contact Info下方的Set Up按钮可以进入联系方式填写页面,如下图:

    如果你没有添加过联系人,你需要通过Add New Contact按钮来添加一个新的联系人。然后指定联系人的职务,职务如下:

        • Senior Management:高管
        • Financial:财务
        • Technical:技术支持
        • Legal:法务
        • Marketing:市场推广

    如果你是独立开发者,可以全部填你自己一个人。

    2、填写银行信息

      我们点击Bank Info下方的Set Up按钮可以进入联系方式填写页面,如下图:  

      选择你的银行账户,如果你没有,点击旁边的Add Bank Account添加一个账户。下面是添加一个账户的流程。

      2-1、选择银行所在的国家

      

      

      2-2、填写银行CNAPS Code
        如果你不知道CNAPS Code是多少,可以点击Look up Transit Number来查询,查询时会根据3个关键信息来查询,如下:

      • Bank Name:银行的英文名称(不能是拼音)
      • City:银行所在的城市英文名称(中国的城市用拼音)
      • Postal Code:邮编
        然后在下面就会出来备选的银行,选择正确的银行后,点击next,进入下一步。

        
        

      2-3、确认银行信息

        
      2-4、填写银行账号信息

      • Bank Account Number:银行账号
      • Confirm Bank Account Number:再次输入银行账号
      • Account Holder Name:持卡人姓名,中文名用拼写,名在前,姓在后
      • Bank Account Currency:货币类型,一般国内的开发者选择CNY

        
      2-5、确认所有信息
        
     3、填写税务信息 

      税务信息这一块了解不是很多,不过因为是国内开发者,可以不用太费心,税务信息分3种:

    • U.S Tax Forms: 美国税务
    • Australia Tax Forms:澳大利亚税务
    • Canada Tax Forms: 加拿大税务

      

      我选择的是U.S Tax Forms,选择后会问你两个问题,第一个问题如下:询问你是否是美国居民,有没有美国伙伴关系或者美国公司,如果没有直接选择No。

      
      接下来第二个问题如下:询问你有没有在美国的商业性活动,没有也直接选No。

      

      然后填写你的税务信息,包括以下几点:

    • Individual or Organization Name:个人或者组织名称
    • Country of incorporation: 所在国家
    • Type of Beneficial Owner:受益方式,独立开发者选个人
    • Permanent Residence:居住地址
    • Mailing address:邮寄地址
    • Name of Person Making this Declaration:声明人
    • Title:头衔

      填写完这些信息后就可以提交了

      
      
     4、等待审核

      当你填写完所有资料后,合同状态就会变成Processing, 大概24小时内就会有结果。

    三、创建购买项目

     进入ItunesConnect,点击你的App,选择上面“功能”中的"App内购买项目",就可以看到右边有一个+号,如下图(最近因为被IPV6的问题给拒绝了)

      
    单击+号,选择你需要创建的购买类型

    • 1、消耗型项目
    • 2、非消耗型项目
    • 3、自动续订型项目
    • 4、免费订阅
    • 5、非续订订阅
        选择你需要创建的购买项目类型后,输入产品的参考名称、产品的ID,选择产品售价等级,然后单击添加语言输入你的产品相关信息,再上传屏幕预览,单击保存即创建购买项目成功。如下图:   

      在上图所示的编辑框中输入,商品名称,产品ID以及价格等级,在这边说明一下:

      1.商品名称根据你的消费道具的实际意义来说明,比如“100颗宝石”,“100金币”等。

      2.产品ID是比较重要的,由项目自定义,只要唯一即可,像我一般都是用App的bundleID加一个后缀来表示,这样既跟项目关联又具有唯一性。

      3.价格等级的话“查看价格表”中有对应的说明,可以对照着表中每个国家的货币价格与等级来选择。

      创建购买项目成功后,回到App内购买项目,就可以看到我们刚刚创建的购买项目了。

    四、添加应用购买测试帐号

      我们打开ItunesConnect,选择“用户和职能”选项, 我们可以看到有一个“沙箱技术测试员”,我们单击“+”号,添加我们的测试帐号,如下图:

      
      然后我们的相关信息,我就不一一列举了,如下图:

      
    单击“存储”,我们的测试帐号就创建成功了。下面开始撸代码了

    五、撸代码咯

      1.首先在项目工程中加入“storekit.framework”,加入头文件#import <StoreKit/StoreKit.h>

      2.在进行使用App内购买页面.h文件中加入“SKPaymentTransactionObserver,SKProductsRequestDelegate”监听机制  

      我就开始贴代码吧

      1 - (void)viewDidLoad {
      2     
      3     [super viewDidLoad];
      4    
      5     // 设置购买队列的监听器
      6     [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
      7     
      8     if([SKPaymentQueue canMakePayments]){
      9                     //productID就是你在创建购买项目时所填写的产品ID
     10                     [self requestProductData: productID];
     11                 }else{
     12                     self.ringIndicator.hidden = YES;
     13                     //                    NSLog(@"不允许程序内付费");
     14                     UIAlertView *alertError = [[UIAlertView alloc] initWithTitle:@"温馨提示"
     15                                                                          message:@"请先开启应用内付费购买功能。"
     16                                                                         delegate:nil
     17                                                                cancelButtonTitle:@"确定"
     18                                                                otherButtonTitles: nil];
     19                     [alertError show];
     20                 }
     21 
     22 }
     23 #pragma mark - 请求商品
     24 //请求商品
     25 - (void)requestProductData:(NSString *)type{
     26     NSLog(@"-------------请求对应的产品信息----------------");
     27     NSArray *product = [[NSArray alloc] initWithObjects:type, nil];
     28     
     29     NSSet *nsset = [NSSet setWithArray:product];
     30     SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
     31     request.delegate = self;
     32     [request start];
     33     //    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
     34 }
     35 
     36 #pragma mark 收到产品返回信息
     37 - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
     38     self.ringIndicator.hidden = YES;
     39     NSLog(@"--------------收到产品反馈消息---------------------");
     40     NSArray *product = response.products;
     41     if([product count] == 0){
     42         [self showHUDTipWithTitle:@"没有该商品"];
     43         NSLog(@"--------------没有商品------------------");
     44         return;
     45     }
     46     
     47     SKProduct *p = nil;
     48     for (SKProduct *pro in product) {
     49         NSLog(@"pro info");
     50         NSLog(@"SKProduct 描述信息:%@", [pro description]);
     51         NSLog(@"localizedTitle 产品标题:%@", [pro localizedTitle]);
     52         NSLog(@"localizedDescription 产品描述信息:%@", [pro localizedDescription]);
     53         NSLog(@"price 价格:%@", [pro price]);
     54         NSLog(@"productIdentifier Product id:%@", [pro productIdentifier]);
     55         
     56         if([pro.productIdentifier isEqualToString: productID]){
     57             p = pro;
     58             money = [NSString stringWithFormat:@"%@",[pro price]];
     59         }else{
     60             NSLog(@"不不不相同");
     61         }
     62     }
     63     SKPayment *payment = [SKPayment paymentWithProduct:p];
     64     //    SKPayment *payment = [SKPayment paymentWithProductIdentifier:PayKey];
     65     NSLog(@"发送购买请求");
     66     [[SKPaymentQueue defaultQueue] addPayment:payment];
     67 }
     68 
     69 //请求失败
     70 - (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
     71     //     [self showHUDTipWithTitle:@"请求失败,错误"];
     72     self.ringIndicator.hidden = YES;
     73     NSLog(@"------------------错误-----------------:%@", error);
     74     UIAlertView *alerView =  [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Alert",NULL) message:[error localizedDescription]
     75                                                        delegate:nil cancelButtonTitle:NSLocalizedString(@"Close",nil) otherButtonTitles:nil];
     76     [alerView show];
     77 }
     78 
     79 - (void)requestDidFinish:(SKRequest *)request{
     80     //    [self showHUDTipWithTitle:@"反馈信息结束"];
     81     NSLog(@"------------反馈信息结束-----------------");
     82 }
     83 
     84 #pragma mark 监听购买结果
     85 //监听购买结果
     86 - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
     87     self.ringIndicator.hidden = YES;
     88     NSLog(@" 监听购买结果 -----paymentQueue--------");
     89     for (SKPaymentTransaction *transaction in transactions)
     90     {
     91         switch (transaction.transactionState)
     92         {
     93             case SKPaymentTransactionStatePurchased:{
     94                 NSLog(@"-----交易完成 --------");
     95                 //交易完成
     96                 [self commitSeversSucceeWithTransaction:transaction];
     97                 
     98                 
     99             }
    100                 break;
    101             case SKPaymentTransactionStateFailed:{
    102                 NSLog(@"-----交易失败 --------");
    103                 //交易失败
    104                 [self failedTransaction:transaction];
    105                 
    106             }
    107                 break;
    108             case SKPaymentTransactionStateRestored:{
    109                 NSLog(@"-----已经购买过该商品(重复支付) --------");
    110                 //已经购买过该商品
    111                 [self restoreTransaction:transaction];
    112                 //                 [self commitSeversSucceeWithTransaction:transaction];
    113                 
    114                 
    115             }
    116             case SKPaymentTransactionStatePurchasing:  {
    117                 //商品添加进列表
    118                 NSLog(@"-----商品添加进列表 --------");
    119             }
    120                 break;
    121             default:
    122                 break;
    123         }
    124     }
    125 }
    126 
    127 //交易结束
    128 - (void)completeTransaction: (SKPaymentTransaction *)transaction
    129 {
    130     NSLog(@" 交易结束 -----completeTransaction--------");
    131     
    132     // Remove the transaction from the payment queue.
    133     [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    134     
    135 }
    136 
    137 - (void)commitSeversSucceeWithTransaction:(SKPaymentTransaction *)transaction
    138 {
    139     
    140     NSString * productIdentifier = transaction.payment.productIdentifier;
    141     NSLog(@"productIdentifier Product id:%@", productIdentifier);
    142     NSString *transactionReceiptString= nil;
    143     
    144     //系统IOS7.0以上获取支付验证凭证的方式应该改变,切验证返回的数据结构也不一样了。
    145     
    146     if(SYSTEMVERSION >= 7.0){
    147         // 验证凭据,获取到苹果返回的交易凭据
    148         // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
    149         NSURLRequest * appstoreRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle]appStoreReceiptURL]];
    150         NSError *error = nil;
    151         NSData * receiptData = [NSURLConnection sendSynchronousRequest:appstoreRequest returningResponse:nil error:&error];
    152         
    153         transactionReceiptString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    154     }else{
    155         
    156         NSData * receiptData = transaction.transactionReceipt;
    157         //        transactionReceiptString = [receiptData base64EncodedString];
    158         transactionReceiptString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    159     }
    160     //    NSLog(@"transactionReceiptString == %@",transactionReceiptString);
    161     [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    162     // 向自己的服务器验证购买凭证
    163    // [self SendRequestWithtransactionReceiptString:transactionReceiptString Transaction:transaction];
    164     
    165     
    166 }
    167 //记录交易
    168 -(void)recordTransaction:(NSString *)product{
    169     NSLog(@"-----记录交易--------");
    170 }
    171 
    172 //处理下载内容
    173 -(void)provideContent:(NSString *)product{
    174     NSLog(@"-----下载--------");
    175 }
    176 
    177 - (void)failedTransaction:(SKPaymentTransaction *)transaction {
    178     if(transaction.error.code != SKErrorPaymentCancelled) {
    179         NSLog(@"购买失败");
    180         UIAlertView *alerView2 =  [[UIAlertView alloc] initWithTitle:@"温馨提示"
    181                                                              message:@"购买该套餐失败,请重新尝试购买"
    182                                                             delegate:nil cancelButtonTitle:NSLocalizedString(@"关闭",nil) otherButtonTitles:nil];
    183         
    184         [alerView2 show];
    185     } else {
    186         NSLog(@"用户取消交易");
    187         
    188         UIAlertView *alerView2 =  [[UIAlertView alloc] initWithTitle:@"温馨提示"
    189                                                              message:@"您已取消该购买"
    190                                                             delegate:nil cancelButtonTitle:NSLocalizedString(@"关闭",nil) otherButtonTitles:nil];
    191         
    192         [alerView2 show];
    193     }
    194     [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    195 }
    196 
    197 
    198 -(void) paymentQueueRestoreCompletedTransactionsFinished: (SKPaymentTransaction *)transaction{
    199     
    200 }
    201 
    202 
    203 - (void) restoreTransaction: (SKPaymentTransaction *)transaction
    204 {
    205     NSLog(@" 交易恢复处理");
    206     [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    207     
    208 }
    209 
    210 -(void) paymentQueue:(SKPaymentQueue *) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error{
    211     NSLog(@"-------paymentQueue----");
    212 }
    213 
    214 #pragma mark connection delegate
    215 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    216 {
    217     NSLog(@"connection==%@",  [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    218 }

    -(void)dealloc

    {

        [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];//解除监听

     }

    代码我就不一一解释了。接下来我就给大家看运行后的结果。

     


      




    最后给大家补充点东西,在与服务器做较验的时候,考虑到网络异常情况,服务器的验证应该是一个可恢复的队列,如果失败了,应该进行重试。

    与苹果的验证接口文档在https://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/VerifyingStoreReceipts/VerifyingStoreReceipts.html#//apple_ref/doc/uid/TP4008267-CH104-SW3 。简单来说就是将购买凭证用Base64编码,然后Post给苹果的验证服务器,苹果将验证结果以JSON形式返回。

    苹果AppStore线上的购买凭证地址是:https://buy.itunes.apple.com/verifyReceipt , 测试地址是:https://sandbox.itunes.apple.com/verifyReceipt

    注意事项:

    苹果在审核应用时,只会在沙盒(sandbox)环境购买,其产生的购买凭证,也只能连接苹果的测试验证服务器。但是审核的应用又是连接的线上服务器,那应该判断苹果正式验证服务器的返回状态码,如果 是21007,则再一次连接测试服务器进行验证即可。https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Introduction.html  上有对返回的状态码的详细说明。

  • 相关阅读:
    Swoole 协程使用示例及协程优先级
    Swoole 协程简介
    Laravel Redis分布式锁的使用
    Laravel Redis分布式锁实现源码分析
    Swoole 中使用异步任务
    runtime相关面试
    oc笔试题
    属性关键字面试题
    KVC面试题
    KVO面试题
  • 原文地址:https://www.cnblogs.com/sanshengyanhuo/p/5581080.html
Copyright © 2011-2022 走看看