热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

详解iOS应用程序内购/内付费(一)

这篇文章主要介绍了详解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

2.在.h文件中加入“SKPaymentTransactionObserver,SKProductsRequestDelegate”监听机制

下面贴上内购的核心代码,就几个函数,我在这边就不在做更多详细的解释了,各位看官可以运行跑一下就一目了然了。
.h文件

// 
// PaymentViewController.h 
// IAPPayTest 
// 
// Created by silicon on 14-10-28. 
// Copyright (c) 2014年 silicon. All rights reserved. 
// 
 
#import  
 
#import  
 
@interface PaymentViewController : UIViewController 
 
@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]; 
} 

好了,所有的内购流程基本上讲完了,原谅我在图片上的涂抹,因为关系到产品的敏感词汇所以希望大家能够不介意。以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • Spring Cloud Config 使用 Vault 作为配置存储
    本文探讨了如何在Spring Cloud Config中集成HashiCorp Vault作为配置存储解决方案,基于Spring Cloud Hoxton.RELEASE及Spring Boot 2.2.1.RELEASE版本。文章还提供了详细的配置示例和实践建议。 ... [详细]
  • JavaScript:简洁与复杂之间的平衡
    本文探讨了在编写JavaScript教程时,如何在保持内容简洁的同时,确保初学者能够理解并应用实际开发中的复杂问题。文章通过具体示例分析了不同层次的JavaScript代码实现。 ... [详细]
  • 在程序运行过程中,各种编程语言都会动态创建对象,并为其分配内存。当这些对象不再使用时,释放其所占内存变得至关重要,以确保资源的有效利用。本文深入探讨了垃圾回收(GC)的工作原理,包括如何识别、何时及如何回收不再使用的对象。 ... [详细]
  • 微信小程序中实现位置获取的全面指南
    本文详细介绍了如何在微信小程序中实现地理位置的获取,包括通过微信官方API和腾讯地图API两种方式。文中不仅涵盖了必要的准备工作,如申请开发者密钥、下载并配置SDK等,还提供了处理用户授权及位置信息获取的具体代码示例。 ... [详细]
  • 本文探讨如何利用Java反射技术来模拟Webwork框架中的URL解析过程。通过这一实践,读者可以更好地理解Webwork及其后续版本Struts2的工作原理,尤其是它们在MVC架构下的角色。 ... [详细]
  • 本文详细介绍如何在IntelliJ IDEA 14中打包Android应用APK文件,并提供查询SHA1值的具体步骤。 ... [详细]
  • 本文探讨了如何在Node.js环境中,通过Tor网络使用的SOCKS5代理执行HTTP请求。文中不仅提供了基础的实现方法,还介绍了几种常用的库和工具,帮助开发者解决遇到的问题。 ... [详细]
  • 迎接云数据库新时代:程序员如何应对变革?
    在数据无处不在的时代,数据库成为了管理和处理数据的核心工具。从早期的信息记录方式到现代的云数据库,数据库技术经历了巨大的变革。本文将探讨云数据库的特点及其对程序员的影响。 ... [详细]
  • 本文探讨了浏览器的同源策略限制及其对 AJAX 请求的影响,并详细介绍了如何在 Spring Boot 应用中优雅地处理跨域请求,特别是当请求包含自定义 Headers 时的解决方案。 ... [详细]
  • 本文将详细介绍小蚁智能行车记录仪的各项性能,包括其1296P高清分辨率的实际表现及ADAS辅助驾驶系统的功能测试。通过白天、夜晚及低光环境下的视频测试,全面评估这款设备的实用性。 ... [详细]
  • AngularJS在IE7中的兼容性解决方案
    本文探讨了在较旧版本的Internet Explorer(如IE7)中使用AngularJS时遇到的问题及解决方法,包括HTML标签的正确书写方式以及如何添加对JSON的支持。 ... [详细]
  • 本文档详细介绍了在 Kubernetes 集群中部署 ETCD 数据库的过程,包括实验环境的准备、ETCD 证书的生成及配置、以及集群的启动与健康检查等关键步骤。 ... [详细]
  • 详解 | 日志系统ViseLog的基本使用与功能
    本文详细介绍了日志系统ViseLog的使用方法及其核心功能,旨在帮助开发者更好地理解和利用这一工具,提高开发效率。 ... [详细]
  • 探讨在构建类似Viber或WhatsApp的聊天应用时,如何有效实现客户端(Web、Android、iOS)与服务器之间的连接。本文将分析使用WebSockets标准及其替代方案的优劣。 ... [详细]
  • 本文探讨了C++中如何正确使用+运算符来处理字符串和数字的拼接问题,分析了为何某些操作有效而另一些则会引发编译错误。 ... [详细]
author-avatar
或许就是一场梦_913
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有