iOS in_App Purchases(內(nèi)購)

首先我們知道什么是內(nèi)購?
- 內(nèi)購就是在我們的App中能夠購買東西
- 當(dāng)然購買東西包括手機(jī),毛絨玩具等日常用品,也包括購買虛擬的例如雙倍經(jīng)驗卡,大血瓶等.
- 那么什么時候使用內(nèi)購呢?

我們還需要知道蘋果公司的盈利模式:

通過蘋果應(yīng)用程序商店有三種主要賺錢的方式:

1. 直接收費(與國內(nèi)大部分用戶 的消費習(xí)慣相悖)

 1.1 廣告
 1.2 O2O -> Online推廣 & Offline交易,閉環(huán)

2. 內(nèi)購:應(yīng)用程序本身的增值產(chǎn)品,游戲裝備,應(yīng)用程序中增值功能同樣可以內(nèi)購

  內(nèi)購:三(蘋果)七(開發(fā)商)開

3.第三方支付:跟應(yīng)用程序無關(guān)的

蘋果不能從中獲利
所以.當(dāng)我們在應(yīng)用中有銷售行為的話,能不走內(nèi)購就不走內(nèi)購,但是增值業(yè)務(wù)的話,必須是走內(nèi)購流程,否則在上架App Store的時候是會被拒絕的.
但是,我們?nèi)绻亲鲭娚填怉pp,那么久不要走內(nèi)購了,因為你的產(chǎn)品銷量越大,你就虧的越多,因為App開發(fā)商還要給蘋果30%的分紅,哦?不,是蘋果先拿到錢,然后再給你70%的分紅.

so,內(nèi)購主要是針對APP內(nèi)置的增值服務(wù)的,就是虛擬商品,一把屠龍寶刀,不就是一張圖片嗎?所以蘋果要分紅.那么App做內(nèi)購的步驟有哪些呢?

1.首先你要有一個開發(fā)者賬號 需要99$.

2.你要有一部 iOS 真機(jī),未越獄版本,越獄版不能測試,只能購買

3.你需要在 蘋果開發(fā)者網(wǎng)站 配置相關(guān)證書 .cer 和 .mobileprovision證書 配置App相關(guān)內(nèi)購信息

3.1 --  配置App(必須要有一些消耗品或者非消耗品), AppID 不能使用統(tǒng)配符. (越獄手機(jī)不能測試內(nèi)購,但是能購買內(nèi)購商品).
  3.1.1  --設(shè)置方式: 在 iTunes Connect 網(wǎng)站里,  ->"App Store" 添加內(nèi)購APP,  然后再進(jìn)入”功能"(設(shè)置 內(nèi)購產(chǎn)品,:非消耗型項目,    一次性消費型項目)
3.2 --  測試賬號:  不用真的花錢.
  3.2.1  -- 設(shè)置方式: “功能”—>” “在用戶和職能”—> “沙箱測試員” —>添加測試賬號" .這個郵箱瞎寫,但是密碼要記住
3.3 --  配置賬戶信息  借記卡/信用卡都行  
  3.3.1--設(shè)置方式: “功能”—>”協(xié)議\稅務(wù)和銀行業(yè)務(wù)”  (都是老板的信息,錢由蘋果公司打給老板)
3.4 -- 在回到 App Store 里將剛剛那些 設(shè)置的Appde內(nèi)購項目添加到 你的App里

很重要卻很容易忽視的細(xì)節(jié)

>>> 在使用未越獄的真機(jī)測試內(nèi)購的時候,因為使用的是測試賬號,是真機(jī).
>>>所以需要在真機(jī)的 “設(shè)置”—> “iTunes Store 和 App Store” —>注銷真實的賬號(否則就真扣錢了) —>換成 剛剛在develop網(wǎng)站里填寫的 內(nèi)購測試賬號

>代碼是敲不完的,我們學(xué)的是思路.

>代碼源于生活,又高于生活.

那么就讓我們結(jié)合實際商場購物來理一下我們寫代碼思路:
1> App 想蘋果服務(wù)器請求可銷售商品
2> 代理方法獲得可銷售商品
3> 展示商品(tableView等)
4>用戶選擇商品
5> 根據(jù)用戶選擇的商品,給用戶小票(商品轉(zhuǎn)payment類型),那小票去排隊
6> 監(jiān)聽 交易隊列變化
7> 用戶去交錢,我們監(jiān)聽用戶付款狀態(tài)
8> 付款成功, 我們給用戶相應(yīng)的增值服務(wù)
9> 結(jié)束交易 (否則用戶下一次進(jìn)來還是在交易)
App內(nèi)購圖示
官方內(nèi)購配圖

下面我們來上代碼:

 //內(nèi)購需要導(dǎo)入框架
#import <StoreKit/StoreKit.h>  
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    //需要在 APP中 設(shè)置 bundel ID 和 在 網(wǎng)站注冊時候一樣.
    // 有內(nèi)購需求的App 需要 安裝在App里按鈕 蘋果develop 網(wǎng)站里面配置好 ios_development.cer 和 xxx.mobileprovision(配置文件),只要雙擊一下,一閃就算安裝好了
}

0>首先:我們需要讀取JSON文件

  //0 需要獲得可銷售商品(就是我們的APP內(nèi)購商品的 productsID)
  NSString * productPath = [[NSBundle mainBundle] pathForResource:@"products.json" ofType:nil];
  
  //把JSON 轉(zhuǎn)為 data
  NSData *jsonData = [NSData dataWithContentsOfFile:productPath];
  
  //反序列化
  NSArray *productArr = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
  NSLog(@"%@",productArr);
  
  //我們需要取出 [{productId:"xxxx"}]  數(shù)組里的字典里面的 key 對應(yīng)的Value值
 
  //KVC 取值
  NSArray *tempArr2 = [productArr valueForKey:@"productId"];
  NSLog(@"%@",tempArr2);
  

1>我們想蘋果發(fā)送銷售App內(nèi)購商品的請求

    //1>App向蘋果請求可銷售商品
    SKProductsRequest * proRequest = [[SKProductsRequest alloc]initWithProductIdentifiers:[NSSet setWithArray:tempArr2]];
 

2>通過代理獲得 蘋果返回的課銷售商品

    //2 >獲取可銷售的 商品 (通過代理)
    //2.1
    proRequest.delegate = self;
    
    //2.2 需要開始請求
    [proRequest start];
    
/** 2.3
 *  當(dāng)獲取到蘋果允許銷售的商品 時候調(diào)用
 *
 *  @param request  銷售內(nèi)購商品的請求
 *  @param response 蘋果對我的請求的返回的響應(yīng) (里面有產(chǎn)品數(shù)組,以及不允許銷售商品的 ID)
 */
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
    
    //遍歷,獲得每一個可銷售商品的信息
    for (SKProduct *product in response.products) {
        
        NSLog(@"產(chǎn)品價格%@---產(chǎn)品ID %@---產(chǎn)品標(biāo)題 %@----產(chǎn)品描述 %@",product.price,product.productIdentifier,product.localizedTitle,product.localizedDescription);
    }
    
    //給全局?jǐn)?shù)組賦值,里面裝可銷售 商品
    self.productsArray = response.products;
    
    //賦值后,需要刷新數(shù)據(jù)源
    [self.tableView reloadData];
}
//2.4 還要在上面遵守代理
@interface ViewController ()<SKProductsRequestDelegate>

//2.5 并且需要賦值一個數(shù)組,所以我們上面定義一個數(shù)組
/**
 *  裝所有 可銷售內(nèi)購商品的數(shù)組
 */
@property (nonatomic, strong) NSArray * productsArray;

3>展示數(shù)據(jù)(可銷售的內(nèi)購商品)

//3 通過tableview展示數(shù)據(jù)  可銷售商品的數(shù)據(jù)
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return 1;
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return self.productsArray.count;
}
//3.1
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    
    static NSString *ID = @"storeCell";
    UITableViewCell *cell  = [tableView dequeueReusableCellWithIdentifier:ID];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
    }
    
    SKProduct *product = self.productsArray[indexPath.row];
    
    cell.textLabel.text = product.localizedTitle;
    //    cell.detailTextLabel.text = product.price.stringValue;
    cell.detailTextLabel.text = [NSString stringWithFormat:@"%@",[product.price description]];  
    return cell;
}

3 補(bǔ)充 :上面給cell賦值時候,不注意引起Crash.分享如下:

  /*引起B(yǎng)UG源代碼:
     cell.detailTextLabel.text = product.price;--->引起Crash.報錯如下 是因為price類型是 NSDecimalNumber.
     報錯如下:
     reason: '-[NSDecimalNumber isEqualToString:]: unrecognized selector sent to instance 0x7fa11ad28f10'
     更改方式1:    cell.detailTextLabel.text = product.price.stringValue;
     更改方式2:    cell.detailTextLabel.text = [NSString stringWithFormat:@"%@",[product.price description]];
     (更改方式1/2 留下一個就行.)
     */
```

> 4> 用戶選擇購買那些增值服務(wù)

//4 用戶選擇商品(屠龍刀 ,大藥瓶等), 代理方法
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{

// 4.1 取出用戶選擇的產(chǎn)品
SKProduct *product = self.productsArray[indexPath.row];

}


> 5> 用戶拿小票排隊等待付款

````
    //5.1 拿商品轉(zhuǎn)換類型 (根據(jù)商品是什么樣的,商家給用戶小票,讓用戶拿著小票去排隊付款)
    SKPayment * payment = [SKPayment paymentWithProduct:product];
    
    //5 .  排隊,等付款
    [[SKPaymentQueue defaultQueue] addPayment:payment];
````
>  6> 監(jiān)聽付款隊列的變化

````
    //6 監(jiān)聽 付款隊列的變化,增加一個監(jiān)聽者,遵守SKPaymentTransactionObserver 協(xié)議就行
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    
  //6.1在上面遵守協(xié)議
   @interface ViewController ()<SKProductsRequestDelegate,SKPaymentTransactionObserver>
````

>7  監(jiān)聽付款隊列的變化 輪到自己付款.

````
/**
 *  7. 監(jiān)聽付款隊列發(fā)生了變化時候調(diào)用   直白:更新付款隊列的情報
 *
 *  @param queue        付款隊列 交易隊列
 *  @param transactions 所有的交易(別人也在交易,存在許多交易在隊列里,放進(jìn)一個數(shù)組里)
 */


-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{}

````

>8 > 根據(jù)付款的狀態(tài),提供對應(yīng)的增值服務(wù)
>9 > 如果交易結(jié)束,關(guān)閉交易

````
/*
 SKPaymentTransactionStatePurchasing,    // 交易正在被加入隊列
 SKPaymentTransactionStatePurchased,     // 交易在隊列中, 用戶已經(jīng)付款.客戶端需要完成交易
 SKPaymentTransactionStateFailed,        // 交易失敗
 SKPaymentTransactionStateRestored,      // 交易從交易歷史中被恢復(fù).  客戶端需要完成交易.
 SKPaymentTransactionStateDeferred       // 交易暫時不確定. 加入交易隊列了,在猶豫要不要付款. iOS8新增
 */
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{
    
    //遍歷所有交易,得出沒有交易的狀態(tài)
    for (SKPaymentTransaction * payTran in transactions) {
        
                if(payTran.transactionState == SKPaymentTransactionStatePurchased){
        
        //8. 用戶付款成功,我們提供相應(yīng)增值服務(wù).
        NSLog(@"用戶已經(jīng)付款,我方App給他提供增值服務(wù)");
        
        //9 結(jié)束交易.  操作隊列結(jié)束本次 交易.
        [[SKPaymentQueue defaultQueue] finishTransaction:payTran];
                }
    
                if(payTran.transactionState == SKPaymentTransactionStateRestored){
        
        //8. 用戶付款成功,我們提供相應(yīng)增值服務(wù).
                    NSLog(@"用戶已經(jīng)付款,我方App給他提供增值服務(wù)");
        
        //9 結(jié)束交易.  操作隊列結(jié)束本次 交易.
                    [[SKPaymentQueue defaultQueue] finishTransaction:payTran];
                }
        
               //增加一個判斷.如果失敗,失敗的原因是什么(不是用戶主動取消的情況)
                 if (payTran.transactionState == SKPaymentTransactionStateFailed) {
                    if (payTran.error.code != SKErrorPaymentCancelled) {
                        NSLog(@"交易失?。?%@", payTran.error.localizedDescription);
                    }
                }
    }   
}
````

> 10 用戶如果換手機(jī)了,怎么辦?之前購買過的可以設(shè)置一鍵恢復(fù)

````
// 10 :一鍵 恢復(fù)購買裝備 (例如換手機(jī)了)
//在viewdidLoad里增加一個復(fù)位按鈕
- (void)viewDidLoad {
    [super viewDidLoad];
   self.navigationItem.title = @"測試";
    UIBarButtonItem *restoreButton = [[UIBarButtonItem alloc]initWithTitle:@"恢復(fù)購買" style:UIBarButtonItemStylePlain target:self action:@selector(restoreButtonClick:)];
        self.navigationItem.leftBarButtonItem = restoreButton;
}

-(void)restoreButtonClick:(UIButton *)sender{
    // 付款隊列 恢復(fù) 已經(jīng)完成的 所有交易
    [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
    
    // 付款隊列 恢復(fù) 已經(jīng)完成的 所有交易 需要應(yīng)用程序的 用戶名. (用戶可能有3個號)(用戶填寫用戶名去后臺驗證密碼)
    //    [[SKPaymentQueue defaultQueue] restoreCompletedTransactionsWithApplicationUsername:<#(nullable NSString *)#>];
}

````

###由于時間倉促,以及個人能力有限,不足之處.或者哪里有不同意見.好的想法還希望不吝告知.共同進(jìn)步.碼字不容易啊!
##最后:源碼在[我的iOS  in_App Purchases(內(nèi)購)](https://github.com/HHQBOOK/in_App_Purchases)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容