IAPHelper集成文檔

當前版本功能支持的功能

  • 內(nèi)購商品列表請求-基于Block回調風格返回商品請求結果
  • 內(nèi)購商品預加載- 應用啟動后自動向蘋果內(nèi)購服務器請求內(nèi)購商品列表
  • 支持自動訂閱
  • 支持非消耗型內(nèi)購商品購買 如移除廣告,功能解鎖等
  • 支持本地內(nèi)購訂單持久化
  • 支持本地會員有效期查詢
  • 支持已購商品恢復購買
  • 支持不同權益檔次會員,不同檔次權益有效期互不干擾
    (如需實現(xiàn)高級會員過期后才生效普通會員,可根據(jù)各檔次會員有效期自行重新疊加計算有效期)

當前版本暫不支持的功能

  • 新用戶折扣優(yōu)惠購買
  • 老用戶折扣優(yōu)惠購買

集成步驟及模塊初始化

1.將IAHelper工程拖入到想要集成內(nèi)購模塊的 Workspace 中


image.png

2.將 IAPHelper.framework 添加到項目工程中

image.png
  1. 在需要用到 IAPHeper 模塊的地方,導入 IAPHelper 模塊
#import <IAPHelper/IAPHelper.h>

4.在工程的AppDelegate的初始化方法中 設置程序支持的內(nèi)購項目。
注意:無論內(nèi)購項是否在售賣狀態(tài),只要曾經(jīng)有用戶購買過,均需添加,因為其將會決定用戶的相關權益有效期計算

+(void)initialize{
    //NOTE: 商品信息錄入必須在內(nèi)購模塊啟動前
    //自動訂閱組
    IARenewalProductInfo * autoWeekProductInfo = [[IARenewalProductInfo alloc] initWithProductId:Group_First_Auto_Renew_First_Level periodType:ProductPeriodPerWeek probabtionPeriodType:ProductProbationPeriodTypeNone groupId:kVIPGroupId isAutoRenewType:YES];
    
    IARenewalProductInfo * autoMonthProductInfo = [[IARenewalProductInfo alloc] initWithProductId:Group_First_Auto_Renew_Second_Level periodType:ProductPeriodPerMonth probabtionPeriodType:ProductProbationPeriodTypeNone groupId:kVIPGroupId isAutoRenewType:YES];
    
    IARenewalProductInfo * autoYearProductInfo = [[IARenewalProductInfo alloc] initWithProductId:Group_First_Auto_Renew_Third_Level periodType:ProductPeriodPerYear probabtionPeriodType:ProductProbationPeriodTypeNone groupId:kVIPGroupId isAutoRenewType:YES];
    
    //訂閱組A
    IARenewalProductInfo * monthProductInfoA = [[IARenewalProductInfo alloc] initWithProductId:Program_A_First_Level_Original periodType:ProductPeriodPerMonth probabtionPeriodType:ProductProbationPeriodTypeNone groupId:kVIPGroupId isAutoRenewType:NO];
    
    IARenewalProductInfo * yearProductInfoA = [[IARenewalProductInfo alloc] initWithProductId:Program_A_Second_Level_Original periodType:ProductPeriodPerYear probabtionPeriodType:ProductProbationPeriodTypeNone groupId:kVIPGroupId isAutoRenewType:NO];
    
    IARenewalProductInfo * foreverProductInfoA = [[IARenewalProductInfo alloc] initWithProductId:Program_A_Third_Level_Original periodType:ProductPeriodForever probabtionPeriodType:ProductProbationPeriodTypeNone groupId:kVIPGroupId isAutoRenewType:NO];
    
    //訂閱組B
    IARenewalProductInfo * monthProductInfoB = [[IARenewalProductInfo alloc] initWithProductId:Program_B_First_Level_Original periodType:ProductPeriodPerMonth probabtionPeriodType:ProductProbationPeriodTypeNone groupId:kSVIPGroupId isAutoRenewType:NO];
    
    IARenewalProductInfo * yearProductInfoB = [[IARenewalProductInfo alloc] initWithProductId:Program_B_Second_Level_Original periodType:ProductPeriodPerYear probabtionPeriodType:ProductProbationPeriodTypeNone groupId:kSVIPGroupId isAutoRenewType:NO];
    
    IARenewalProductInfo * foreverProductInfoB= [[IARenewalProductInfo alloc] initWithProductId:Program_B_Third_Level_Original periodType:ProductPeriodForever probabtionPeriodType:ProductProbationPeriodTypeNone groupId:kSVIPGroupId isAutoRenewType:NO];
    
    //配置會員權益等計時型商品內(nèi)購項。錄入黃金月度會員,黃金年度會員,周會員,永久會員等
    [[IAPaymentCommon shareInstance] configRenewalProductInfosFromDeveloper:@[
        autoWeekProductInfo,autoMonthProductInfo,autoYearProductInfo,
        monthProductInfoA,yearProductInfoA,foreverProductInfoA,
        monthProductInfoB,yearProductInfoB,foreverProductInfoB
    ]];
    
    //配置普通非消耗型內(nèi)購項。例如:移除廣告內(nèi)購,解鎖關卡內(nèi)購
//    IABaseProductInfo * removeAdsProduct = [[IABaseProductInfo alloc] initWithProductId:@"removeAds"];
//    IABaseProductInfo * unlockMoreFuncProduct = [[IABaseProductInfo alloc] initWithProductId:@"unlockMoreFuncs"];
//    [[IAPaymentCommon shareInstance] configNormalProductInfosFromDeveloper:@[removeAdsProduct,unlockMoreFuncProduct]];
}
  1. 在應用啟動時,調用 IAPaymentCommon 的 load 方法來完成內(nèi)購模塊加載
    注意:需要傳入的參數(shù)為該Apple開發(fā)者賬號下的共享密鑰,該密鑰將影響本地購買憑據(jù)驗證,進而影響相關權益有效期計算
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    //內(nèi)購通用模塊加載
    [[IAPaymentCommon shareInstance] load:IAPSecretKey];
    
//    未正常完成的交易
//    NSArray * unfinishedTransactions = [[IAPaymentCommon shareInstance] getUnFinishedPaymentTransactions];
//    if (unfinishedTransactions.count) {
//
//    }
    
    return YES;
}

商品請求、購買、恢復購買

商品請求

商品請求是后臺靜默執(zhí)行的,不包含UI顯示(系統(tǒng)彈出的除外),故為了交互友好,請在具體業(yè)務中添加相關等待提示框,狀態(tài)提示框UI顯示

[[IAPaymentCommon shareInstance] requestAllPreloadProductsWithCompletion:^(NSArray<IABaseProductInfo *> * _Nullable productArray, IAProductRequestStatus status, NSError * _Nullable error) {
        if (!error && productArray) {
            self.allProductInfos = [NSMutableArray arrayWithArray:productArray];
        }
        
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.refreshControl endRefreshing];
            [self.indicatorView stopAnimating];
            self.indicatorView.hidden = YES;
            [self.tableView reloadData];
        });
    }];

另一種商品請求方式,可精確請求指定商品列表

[[IAPaymentCommon shareInstance] requestMultipleProductIds:[IAPaymentCommon shareInstance].allProductIds complateHandler:^(NSArray<IABaseProductInfo *> * _Nullable productArray, IAProductRequestStatus status, NSError * _Nullable error) {
        //code here to refresh UI
    }];

商品購買

商品購買是后臺靜默執(zhí)行的,不包含UI顯示(系統(tǒng)彈出的除外),故為了交互友好,請在具體業(yè)務中添加相關等待提示框,狀態(tài)提示框UI顯示

MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    hud.label.text = NSLocalizedString(@"購買中...", @"HUD loading title");
    
    IABaseProductInfo * productInfo = self.allProductInfos[indexPath.row];
    [[IAPaymentCommon shareInstance] purchaseProductByProductId:productInfo.productId purchasing:^(NSString * _Nonnull productId, IAPurchasingState purchasingState, SKPaymentTransaction * _Nonnull transaction) {
        
        dispatch_async(dispatch_get_main_queue(), ^{
            if (purchasingState == IAPurchasingStateOrderGenerating) {
                hud.label.text = @"正在生成支付訂單,請稍后...";
            }
            else{
                hud.label.text = @"訂單已生成,正在獲取訂單信息...";
            }
        });
        
    } purchaseComplete:^(NSString * _Nonnull productId, SKPaymentTransaction * _Nullable paymentTransaction, BOOL isSuccess, NSError * _Nullable error) {
        
        dispatch_async(dispatch_get_main_queue(), ^{
            
            [hud hideAnimated:YES];
            
            NSString * message = isSuccess ? @"購買成功!" : @"購買失??!";
            
            MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];

            // Set the custom view mode to show any view.
            hud.mode = MBProgressHUDModeCustomView;
            // Set an image view with a checkmark.
            UIImage *image = [[UIImage imageNamed:@"Checkmark"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
            hud.customView = [[UIImageView alloc] initWithImage:image];
            // Looks a bit nicer if we make it square.
            hud.square = YES;
            // Optional label text.
            hud.label.text = message;

            [hud hideAnimated:YES afterDelay:2.f];
            
            if (isSuccess) {
                [self updateExpireDateInfo];
            }
        });
    } purchaseCancelled:^(NSString * _Nonnull productId, NSError * _Nullable error) {
        
        [hud hideAnimated:YES];
        
        NSString * message = @"購買已取消";
        
        MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];

        // Set the custom view mode to show any view.
        hud.mode = MBProgressHUDModeCustomView;
        // Set an image view with a checkmark.
        UIImage *image = [[UIImage imageNamed:@"Checkmark"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
        hud.customView = [[UIImageView alloc] initWithImage:image];
        // Looks a bit nicer if we make it square.
        hud.square = YES;
        // Optional label text.
        hud.label.text = message;

        [hud hideAnimated:YES afterDelay:2.f];
    }];
恢復購買

商品恢復購買是后臺靜默執(zhí)行的,不包含UI顯示(系統(tǒng)彈出的除外),故為了交互友好,請在具體業(yè)務中添加相關等待提示框,狀態(tài)提示框UI顯示

恢復購買提供有兩個 api 來實現(xiàn),分別是

調用蘋果提供的恢復內(nèi)購Api實現(xiàn)

MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    hud.label.text = NSLocalizedString(@"恢復購買中,請稍后...", @"HUD loading title");
    
    [[IAPaymentCommon shareInstance] restorePurchaseWithComplete:^(NSArray * _Nonnull productIds, BOOL isSuccess, NSError * _Nullable error) {
        
        dispatch_async(dispatch_get_main_queue(), ^{
            [hud hideAnimated:YES];
        });
        [self onRestoreComplete:productIds restoreState:isSuccess error:error];
    }];

調用蘋果提供的刷新購買憑據(jù)Api實現(xiàn)

MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    hud.label.text = NSLocalizedString(@"恢復購買中,請稍后...", @"HUD loading title");
    
    [[IAPaymentCommon shareInstance] refreshReceiptWithComplete:^(NSArray * _Nullable productIds, BOOL isSuccess, NSError * _Nullable error) {
        
        dispatch_async(dispatch_get_main_queue(), ^{
            [hud hideAnimated:YES];
        });
        [self onRestoreComplete:productIds restoreState:isSuccess error:error];
    }];
//恢復購買完成
- (void)onRestoreComplete:(NSArray *)productIds restoreState:(BOOL)isSuccess error:(NSError *)error{
    
    dispatch_async(dispatch_get_main_queue(), ^{
        
        NSString * message = isSuccess ? @"恢復購買成功!" : @"恢復購買失敗!";
        
        MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];

        // Set the custom view mode to show any view.
        hud.mode = MBProgressHUDModeCustomView;
        // Set an image view with a checkmark.
        UIImage *image = [[UIImage imageNamed:@"Checkmark"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
        hud.customView = [[UIImageView alloc] initWithImage:image];
        // Looks a bit nicer if we make it square.
        hud.square = YES;
        // Optional label text.
        hud.label.text = message;

        [hud hideAnimated:YES afterDelay:2.f];
        
        if (isSuccess) {
            [self updateExpireDateInfo];
        }
    });
}

目前并未發(fā)現(xiàn)兩種方式有質的區(qū)別

刷新會員有效期

刷新普通會員有效期

//更新VIP有效期
- (void)updateVIPExpireInfo{
    
    //普通vip
    ProductGroupInfo * vipProductGroupInfo =  [[IAPaymentCommon shareInstance] getProductGroupWithGroupId:kVIPGroupId];
    
    NSTimeInterval timeInterval = vipProductGroupInfo.expireDateMs;
    NSDate * expireDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
    NSDateFormatter * formatter = [[NSDateFormatter alloc] init];
    formatter.dateFormat = @"yyyy-MM-dd";
    NSString * expireDateString = [formatter stringFromDate:expireDate];
    
    NSDate * relativetime = [[IATimeManager defaultManager] getRelativetime];//獲取當前時間
    NSTimeInterval relativeTimeinterval = [relativetime timeIntervalSince1970];
    
    self.vipExpireDateLabel.textColor = [UIColor systemBlueColor];
    //當前是否有已購買且在有效期內(nèi)的自動訂閱內(nèi)購項
    if (vipProductGroupInfo.hasActiveAutoRenewOrder) {
        self.vipExpireDateLabel.text = @"VIP到期時間:自動續(xù)訂中";
    }else{
        if (relativeTimeinterval > timeInterval) {
            self.vipExpireDateLabel.textColor = [UIColor systemRedColor];
            self.vipExpireDateLabel.text = [NSString stringWithFormat:@"VIP會員已于%@過期",expireDateString];
            if (timeInterval == 0) {
                self.vipExpireDateLabel.text = @"未開通";
            }
        }
        else{
            if (timeInterval >= LONG_MAX) {
                self.vipExpireDateLabel.textColor = [UIColor systemPinkColor];
                self.vipExpireDateLabel.text = @"VIP到期時間:永不過期";
            }else{
                self.vipExpireDateLabel.text = [NSString stringWithFormat:@"VIP會員到期時間:%@",expireDateString];
            }
        }
    }
}

刷新超級會員有效期(沒有可不處理)

//更新SVIP有效期
- (void)updatesSVIPExpireInfo{
    
    //超級vip
    ProductGroupInfo * svipProductGroupInfo =  [[IAPaymentCommon shareInstance] getProductGroupWithGroupId:kSVIPGroupId];
    
    NSTimeInterval timeInterval = svipProductGroupInfo.expireDateMs;
    NSDate * expireDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
    NSDateFormatter * formatter = [[NSDateFormatter alloc] init];
    formatter.dateFormat = @"yyyy-MM-dd";
    NSString * expireDateString = [formatter stringFromDate:expireDate];
    
    NSDate * relativetime = [[IATimeManager defaultManager] getRelativetime];//獲取當前時間
    NSTimeInterval relativeTimeinterval = [relativetime timeIntervalSince1970];
    
    self.svipExpireDateLabel.textColor = [UIColor systemBlueColor];
    //當前是否有已購買且在有效期內(nèi)的自動訂閱內(nèi)購項
    if (svipProductGroupInfo.hasActiveAutoRenewOrder) {
        self.svipExpireDateLabel.text = @"SVIP到期時間:自動續(xù)訂中";
    }else{
        if (relativeTimeinterval > timeInterval) {
            self.svipExpireDateLabel.textColor = [UIColor systemRedColor];
            self.svipExpireDateLabel.text = [NSString stringWithFormat:@"SVIP會員已于%@過期",expireDateString];
            if (timeInterval == 0) {
                self.svipExpireDateLabel.text = @"未開通";
            }
        }
        else{
            if (timeInterval >= LONG_MAX) {
                self.svipExpireDateLabel.textColor = [UIColor systemPinkColor];
                self.svipExpireDateLabel.text = @"SVIP到期時間:永不過期";
            }else{
                self.svipExpireDateLabel.text = [NSString stringWithFormat:@"SVIP會員到期時間:%@",expireDateString];
            }
        }
    }
}
憑據(jù)驗證成功通知

因為本內(nèi)購模塊中權益有效期計算是基于本地購買憑據(jù)驗證得來的,所以最真實的有效期信息應該來源于憑據(jù)驗證,故建議在需要展示vip或記錄vip有效期信息的地方注冊本地憑據(jù)驗證成功的通知, 并于業(yè)務中刷新權益有效期信息

//已購訂單憑據(jù)驗證完成通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onReceiptValidCompleteNotification:) name:kReceiptValidCompleteNotification object:nil];
/* 內(nèi)購商品憑據(jù)本地校驗完成通知 */
- (void)onReceiptValidCompleteNotification:(NSNotification *)notification{
    [self updateExpireDateInfo];;
}
商品請求成功通知
//商品請求完成通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onProductsRequestCompleteNotification:) name:kProductInfosRequestSucceedNotification object:nil];
來自AppStore商品購買通知
//來自AppStore商品購買通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppStorePaymentNotification:) name:kPaymentFromAppStoreShouldHandleNotification object:nil];
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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

  • 通過撰寫產(chǎn)品需求文檔(PRD),能夠鍛煉到基本的產(chǎn)品能力,同時也是提高axure操作能力的重要途徑。筆者將以網(wǎng)易嚴...
    銀海系閱讀 4,070評論 4 31
  • 表情是什么,我認為表情就是表現(xiàn)出來的情緒。表情可以傳達很多信息。高興了當然就笑了,難過就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 129,764評論 2 7
  • 16宿命:用概率思維提高你的勝算 以前的我是風險厭惡者,不喜歡去冒險,但是人生放棄了冒險,也就放棄了無數(shù)的可能。 ...
    yichen大刀閱讀 7,968評論 0 4

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