iOS应用程序内购/内付费

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS应用程序内购/内付费相关的知识,希望对你有一定的参考价值。

最近接触了一个叫StoreKit的framework,一脸懵逼···

然后搜索了一下:Apple Developer Document

Embed a store in your app. Process financial transactions associated with the purchase of content and services.引入一个Store在您的应用程序。处理金融交易与购买相关的内容和服务。

说白了,就是苹果内购/内付费啊~

赶紧学习去了···

但是由于公司的开发者账号我不想随便动··· 所以不能截图给大家···

原文地址:点击打开链接


然后 当当当··· 开始分(zhuan)享(zai)啦(有少量修改)~~~


1.新建App应用

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

技术分享

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


这里顺便多说一句这个ItunesConnect是用来干嘛的,它是苹果公司给个人或企业提供管理自己App的一个平台。在这个平台上开发者可以新建,删除和管理自己的App应用,开发者可以根据需求对App应用进行上架与下架,编辑App信息,生成测试app所需的信息,例如账号,邀请码等,还有就是我们今天要讲的内付费功能。当然啦,他的功能可不止我讲的这些,我大致说一下这个平台的作用,如果你经常跟它打交道的话就会慢慢熟悉了。


2.添加付费道具


接下来,我就来为大家演示一下如何添加付费道具,首先打开iTunesConnect,显示如下页面:

技术分享


选择红圈所圈起来的选项,然后将里面的相关信息补充完毕,如果缺少这一步,内购功能是不会成功的。

假如你已经完成了上述相关银行账户的设置,就点击你的App,选择上面标题栏中的"App 内购买项目"

技术分享


随后点击左上角的 "create new"选项,如下图所示,进入到下一个界面:

技术分享



这个界面是让你选择消费道具的种类,现在改版的网站是有简体中文翻译的,所以不像以前打开一看都不知道选哪一个,甚至都不知道每个代表的什么意思(比如我第一次遇到的时候,在领导面前真是囧)。它的种类分为如下几种:


技术分享



技术分享技术分享

一般对项目来说大多数都是选择“消耗型项目”这个种类,比如游戏中购买金币,宝石balabala~之类的,选中之后就会到这个界面中来:

技术分享


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

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

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

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


我们继续,在这个网页的接下来部分如图所示:

技术分享


选择添加语言选项,弹出一个编辑页面:

技术分享


点击save保存,则会在界面上显示成如下:

技术分享


最后一步就是点击“选取文件”提交一张苹果它指定像素(640*920)的商品图片,当他上传完毕后点击“save”按钮,我们这第二部分就大工告成了。提交的商品最后会在内购的页面上显示为如图:

技术分享


这个图是我在已经发布的app上面截取的,添加了3个商品,已经是通过的的状态了(显示绿色),当您刚提交的时候,因为通过苹果的审查需要一段时间所以会显示黄色的等待状态,所以不必担心是不是商品编辑错了。如图:

技术分享


3.申请测试账号


这部分,我主要给大家演示一下,如何申请测试账号,利用苹果的沙盒测试环境来模拟AppStore的购买流程。

在ItunesConnect中选择“用户和职能”选项~

技术分享



随后在左上角的选项中选择沙盒测试者,点击左上角的加号图标增加一位测试者,如图:

技术分享

编辑好相应的内容,点击保存,就创建了一个测试账号,是不是很简单啊!

技术分享

当然这个账号如果你忘记了密码可以重新生成一个,无关紧要。

顺带多句嘴,不要在正式的appstore上面用沙盒测试的账号来登录,千万要牢记在心,此账号只用于测试环境下~


4.上代码

接下来就是代码部分啦~

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

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

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

.h文件

#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文件
#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];

    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

6.测试

代码就这么多,到这边我们的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];
}

好了,所有的内购流程基本上讲完了,原谅我在图片上的涂抹,因为关系到产品的敏感词汇所以希望大家能够不介意。谢谢~

























































以上是关于iOS应用程序内购/内付费的主要内容,如果未能解决你的问题,请参考以下文章

iOS 审核因为“内购”被拒的解决方案!

iOS苹果内购(详细步骤)

iOS苹果内购(详细步骤)

IOS内购(IAP)的那些事

怎么跨区内购苹果

iOS: 实现苹果的内购