1、ios开发之 内购
大家都知道做iOS開發(fā)本身的收入有三種來源:出售應用、內購和廣告。國內用戶通常很少直接購買應用,因此對于開發(fā)者而言(特別是個人開發(fā)者),內購和廣告收入就成了主要的收入來源。內購營銷模式,通常軟件本身是不收費的,但是要獲得某些特權就必須購買一些道具,而內購的過程是由蘋果官方統一來管理的,所以和Game Center一樣,在開發(fā)內購程序之前要做一些準備工作(下面的準備工作主要是針對真機的,模擬器省略Provisioning Profile配置過程):
開發(fā)內購應用時需要使用StoreKit.framework,下面是這個框架中常用的幾個類:
SKProduct:可購買的產品(例如上面設置的能量瓶、強力手套等),其productIdentifier屬性對應iTunes Connect中配置的“產品ID“,但是此類不建議直接初始化使用,而是要通過SKProductRequest來加載可用產品(避免出現購買到無效的產品)。
SKProductRequest:產品請求類,主要用于加載產品列表(包括可用產品和不可用產品),通常加載完之后會通過其-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response代理方法獲得響應,拿到響應中的可用產品。
SKPayment:產品購買支付類,保存了產品ID、購買數量等信息(注意與其對應的有一個SKMutablePayment對象,此對象可以修改產品數量等信息)。
SKPaymentQueue:產品購買支付隊列,一旦將一個SKPayment添加到此隊列就會向蘋果服務器發(fā)送請求完成此次交易。注意交易的狀態(tài)反饋不是通過代理完成的,而是通過一個交易監(jiān)聽者(類似于代理,可以通過隊列的addTransactionObserver來設置)。
SKPaymentTransaction:一次產品購買交易,通常交易完成后支付隊列會調用交易監(jiān)聽者的-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction方法反饋交易情況,并在此方法中將交易對象返回。
SKStoreProductViewController:應用程序商店產品展示視圖控制器,用于在應用程序內部展示此應用在應用商店的情況。(例如可以使用它讓用戶在應用內完成評價,注意由于本次演示的示例程序沒有正式提交到應用商店,所以在此暫不演示此控制器視圖的使用)。
了解了以上幾個常用的開發(fā)API之后,下面看一下應用內購買的流程:
下面通過一個示例程序演示內購和恢復的整個過程,程序界面大致如下:
主界面中展示了所有可購買產品和售價,以及購買情況。?
選擇一個產品點”購買“可以購買此商品,購買完成后刷新購買狀態(tài)(如果是非消耗品則顯示已購買,如果是消耗品則顯示購買個數)。
程序卸載后重新安裝可以點擊”恢復購買“來恢復已購買的非消耗品。
程序代碼:
// // KCMainTableViewController.m // kctest // // Created by Kenshin Cui on 14/4/5. // Copyright (c) 2015年 cmjstudio. All rights reserved. //#import "KCMainTableViewController.h" #import <StoreKit/StoreKit.h> #define kAppStoreVerifyURL @"https://buy.itunes.apple.com/verifyReceipt" //實際購買驗證URL #define kSandboxVerifyURL @"https://sandbox.itunes.apple.com/verifyReceipt" //開發(fā)階段沙盒驗證URL//定義可以購買的產品ID,必須和iTunes Connect中設置的一致 #define kProductID1 @"ProtectiveGloves" //強力手套,非消耗品 #define kProductID2 @"GoldenGlobe" //金球,非消耗品 #define kProductID3 @"EnergyBottle" //能量瓶,消耗品@interface KCMainTableViewController ()<SKProductsRequestDelegate,SKPaymentTransactionObserver>@property (strong,nonatomic) NSMutableDictionary *products;//有效的產品 @property (assign,nonatomic) int selectedRow;//選中行 @end@implementation KCMainTableViewController #pragma mark - 控制器視圖方法 - (void)viewDidLoad {[super viewDidLoad];[self loadProducts];[self addTransactionObjserver]; }#pragma mark - UI事件 //購買產品 - (IBAction)purchaseClick:(UIBarButtonItem *)sender {NSString *productIdentifier=self.products.allKeys[self.selectedRow];SKProduct *product=self.products[productIdentifier];if (product) {[self purchaseProduct:product];}else{NSLog(@"沒有可用商品.");}} //恢復購買 - (IBAction)restorePurchaseClick:(UIBarButtonItem *)sender {[self restoreProduct]; }#pragma mark - UITableView數據源方法- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {return 1; }- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {return self.products.count; }- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {static NSString *identtityKey=@"myTableViewCellIdentityKey1";UITableViewCell *cell=[self.tableView dequeueReusableCellWithIdentifier:identtityKey];if(cell==nil){cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identtityKey];}cell.accessoryType=UITableViewCellAccessoryNone;NSString *key=self.products.allKeys[indexPath.row];SKProduct *product=self.products[key];NSString *purchaseString;NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];if ([product.productIdentifier isEqualToString:kProductID3]) {purchaseString=[NSString stringWithFormat:@"已購買%i個",[defaults integerForKey:product.productIdentifier]];}else{if([defaults boolForKey:product.productIdentifier]){purchaseString=@"已購買";}else{purchaseString=@"尚未購買";}}cell.textLabel.text=[NSString stringWithFormat:@"%@(%@)",product.localizedTitle,purchaseString] ;cell.detailTextLabel.text=[NSString stringWithFormat:@"%@",product.price];return cell; } #pragma mark - UITableView代理方法 -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{UITableViewCell *currentSelected=[tableView cellForRowAtIndexPath:indexPath];currentSelected.accessoryType=UITableViewCellAccessoryCheckmark;self.selectedRow=indexPath.row; } -(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath{UITableViewCell *currentSelected=[tableView cellForRowAtIndexPath:indexPath];currentSelected.accessoryType=UITableViewCellAccessoryNone; }#pragma mark - SKProductsRequestd代理方法 /*** 產品請求完成后的響應方法** @param request 請求對象* @param response 響應對象,其中包含產品信息*/ -(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{//保存有效的產品_products=[NSMutableDictionary dictionary];[response.products enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {SKProduct *product=obj;[_products setObject:product forKey:product.productIdentifier];}];//由于這個過程是異步的,加載成功后重新刷新表格[self.tableView reloadData]; } -(void)requestDidFinish:(SKRequest *)request{NSLog(@"請求完成."); } -(void)request:(SKRequest *)request didFailWithError:(NSError *)error{if (error) {NSLog(@"請求過程中發(fā)生錯誤,錯誤信息:%@",error.localizedDescription);} }#pragma mark - SKPaymentQueue監(jiān)聽方法 /*** 交易狀態(tài)更新后執(zhí)行** @param queue 支付隊列* @param transactions 交易數組,里面存儲了本次請求的所有交易對象*/ -(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{[transactions enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {SKPaymentTransaction *paymentTransaction=obj;if (paymentTransaction.transactionState==SKPaymentTransactionStatePurchased){//已購買成功NSLog(@"交易\"%@\"成功.",paymentTransaction.payment.productIdentifier);//購買成功后進行驗證[self verifyPurchaseWithPaymentTransaction];//結束支付交易[queue finishTransaction:paymentTransaction];}else if(paymentTransaction.transactionState==SKPaymentTransactionStateRestored){//恢復成功,對于非消耗品才能恢復,如果恢復成功則transaction中記錄的恢復的產品交易NSLog(@"恢復交易\"%@\"成功.",paymentTransaction.payment.productIdentifier);[queue finishTransaction:paymentTransaction];//結束支付交易//恢復后重新寫入偏好配置,重新加載UITableView[[NSUserDefaults standardUserDefaults]setBool:YES forKey:paymentTransaction.payment.productIdentifier];[self.tableView reloadData];}else if(paymentTransaction.transactionState==SKPaymentTransactionStateFailed){if (paymentTransaction.error.code==SKErrorPaymentCancelled) {//如果用戶點擊取消NSLog(@"取消購買.");}NSLog(@"ErrorCode:%i",paymentTransaction.error.code);}}]; } //恢復購買完成 -(void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue{NSLog(@"恢復完成."); }#pragma mark - 私有方法 /*** 添加支付觀察者監(jiān)控,一旦支付后則會回調觀察者的狀態(tài)更新方法:-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions*/ -(void)addTransactionObjserver{//設置支付觀察者(類似于代理),通過觀察者來監(jiān)控購買情況[[SKPaymentQueue defaultQueue] addTransactionObserver:self]; } /*** 加載所有產品,注意產品一定是從服務器端請求獲得,因為有些產品可能開發(fā)人員知道其存在性,但是不經過審核是無效的;*/ -(void)loadProducts{//定義要獲取的產品標識集合NSSet *sets=[NSSet setWithObjects:kProductID1,kProductID2,kProductID3, nil];//定義請求用于獲取產品SKProductsRequest *productRequest=[[SKProductsRequest alloc]initWithProductIdentifiers:sets];//設置代理,用于獲取產品加載狀態(tài)productRequest.delegate=self;//開始請求[productRequest start]; } /*** 購買產品** @param product 產品對象*/ -(void)purchaseProduct:(SKProduct *)product{//如果是非消耗品,購買過則提示用戶NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];if ([product.productIdentifier isEqualToString:kProductID3]) {NSLog(@"當前已經購買\"%@\" %i 個.",kProductID3,[defaults integerForKey:product.productIdentifier]);}else if([defaults boolForKey:product.productIdentifier]){NSLog(@"\"%@\"已經購買過,無需購買!",product.productIdentifier);return;}//創(chuàng)建產品支付對象SKPayment *payment=[SKPayment paymentWithProduct:product];//支付隊列,將支付對象加入支付隊列就形成一次購買請求if (![SKPaymentQueue canMakePayments]) {NSLog(@"設備不支持購買.");return;}SKPaymentQueue *paymentQueue=[SKPaymentQueue defaultQueue];//添加都支付隊列,開始請求支付 // [self addTransactionObjserver];[paymentQueue addPayment:payment]; }/*** 恢復購買,對于非消耗品如果應用重新安裝或者機器重置后可以恢復購買* 注意恢復時只能一次性恢復所有非消耗品*/ -(void)restoreProduct{SKPaymentQueue *paymentQueue=[SKPaymentQueue defaultQueue];//設置支付觀察者(類似于代理),通過觀察者來監(jiān)控購買情況 // [paymentQueue addTransactionObserver:self];//恢復所有非消耗品[paymentQueue restoreCompletedTransactions]; }/*** 驗證購買,避免越獄軟件模擬蘋果請求達到非法購買問題**/ -(void)verifyPurchaseWithPaymentTransaction{//從沙盒中獲取交易憑證并且拼接成請求體數據NSURL *receiptUrl=[[NSBundle mainBundle] appStoreReceiptURL];NSData *receiptData=[NSData dataWithContentsOfURL:receiptUrl];NSString *receiptString=[receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];//轉化為base64字符串NSString *bodyString = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", receiptString];//拼接請求數據NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];//創(chuàng)建請求到蘋果官方進行購買驗證NSURL *url=[NSURL URLWithString:kSandboxVerifyURL];NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url];requestM.HTTPBody=bodyData;requestM.HTTPMethod=@"POST";//創(chuàng)建連接并發(fā)送同步請求NSError *error=nil;NSData *responseData=[NSURLConnection sendSynchronousRequest:requestM returningResponse:nil error:&error];if (error) {NSLog(@"驗證購買過程中發(fā)生錯誤,錯誤信息:%@",error.localizedDescription);return;}NSDictionary *dic=[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:nil];NSLog(@"%@",dic);if([dic[@"status"] intValue]==0){NSLog(@"購買成功!");NSDictionary *dicReceipt= dic[@"receipt"];NSDictionary *dicInApp=[dicReceipt[@"in_app"] firstObject];NSString *productIdentifier= dicInApp[@"product_id"];//讀取產品標識//如果是消耗品則記錄購買數量,非消耗品則記錄是否購買過NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];if ([productIdentifier isEqualToString:kProductID3]) {int purchasedCount=[defaults integerForKey:productIdentifier];//已購買數量[[NSUserDefaults standardUserDefaults] setInteger:(purchasedCount+1) forKey:productIdentifier];}else{[defaults setBool:YES forKey:productIdentifier];}[self.tableView reloadData];//在此處對購買記錄進行存儲,可以存儲到開發(fā)商的服務器端}else{NSLog(@"購買失敗,未通過驗證!");} } @end運行效果(這是程序在卸載后重新安裝的運行效果,卸載前已經購買”強力手套“,因此程序運行后點擊了”恢復購買“):
轉載于:https://www.cnblogs.com/panyuluoye/p/4971421.html
總結
以上是生活随笔為你收集整理的1、ios开发之 内购的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Tomcat中配置MySQL数据库连接池
- 下一篇: seaweedfs 源码笔记(一)