iOS 內(nèi)購IAP(訂閱)和恢復購買的理解及實現(xiàn)[成功可用]

首先介紹實現(xiàn)蘋果內(nèi)購的流程

購買

1.向蘋果服務(wù)器 請求商品列表
2.蘋果服務(wù)器 在回調(diào)中返回商品列表
3.向蘋果服務(wù)器 請求購買商品列表的第一個
4.蘋果服務(wù)器 在回調(diào)中返回購買結(jié)果
5.根據(jù)回調(diào)的結(jié)果 進行處理

恢復購買

1.向蘋果服務(wù)器 請求恢復購買
2.蘋果服務(wù)器 在回調(diào)中返回恢復購買結(jié)果
5.根據(jù)回調(diào)的結(jié)果 進行處理

下面是代碼實現(xiàn)

下面是.h文件的實現(xiàn)

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface PQIAPManager : NSObject

+ (PQIAPManager *)sharedManager;
/**請求購買商品*/
- (void)requestProductWithId:(NSString *)productId;
/**請求恢復購買*/
- (void)restore;

@end

NS_ASSUME_NONNULL_END

下面是.m文件的實現(xiàn)

#import "PQIAPManager.h"
#import <StoreKit/StoreKit.h>
#import <AppTrackingTransparency/AppTrackingTransparency.h>
#import <AdSupport/AdSupport.h>

/**共享密鑰*/
#define KSecretKey @""
/**憑證校驗地址*/
#define KReceiptCheckUrl @""
/**訂閱狀態(tài)檢驗地址*/
#define KStateCheckUrl @""

@interface PQIAPManager()<SKPaymentTransactionObserver,SKProductsRequestDelegate,SKRequestDelegate>

// 交易是否結(jié)束
@property (nonatomic, assign) BOOL isRequestFinished;
// 判斷是購買還是恢復
@property (nonatomic, assign) BOOL isRestore;

@end

@implementation PQIAPManager

// 單例
+ (instancetype)sharedManager{
    static dispatch_once_t pred;
    static PQIAPManager *manager = nil;
    dispatch_once(&pred, ^{
        manager = [[self alloc] init];
    });
    return manager;
}

// 創(chuàng)建時就開始監(jiān)聽購買事件
- (instancetype)init{
    
    if (self = [super init]) {
        [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
        self.isRequestFinished = YES;
    }
    return self;
}

// 銷毀時就移除監(jiān)聽事件
- (void)dealloc{
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}

繼續(xù)在.m文件中添加方法

/**請求商品列表*/
- (void)requestProductWithId:(NSString *)productId {
    self.isRestore = NO;
    if (self.isRequestFinished) {
        
        if ([SKPaymentQueue canMakePayments]) { //用戶允許app內(nèi)購
            if (productId.length) {
                
                self.isRequestFinished = NO; //正在請求
                NSLog(@"%@商品正在請求中...",productId);
                
                NSArray *product = [[NSArray alloc] initWithObjects:productId, nil];
                NSSet *set = [NSSet setWithArray:product];
                SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
                productsRequest.delegate = self;
                [productsRequest start];
            }
        } else { //沒有權(quán)限
            self.isRequestFinished = YES; //完成請求
            NSLog(@"沒有購買權(quán)限");
        }
    } else {
        NSLog(@"上次請求購買未完成营勤,請稍后重試唯咬!");
    }
}
// 請求購買商品列表的第一個
#pragma mark SKProductsRequestDelegate 請求商品列表后的回調(diào)
- (void)productsRequest:(nonnull SKProductsRequest *)request didReceiveResponse:(nonnull SKProductsResponse *)response {
    NSArray *product = response.products;
    if (product.count == 0){
        return;
    } else {
        if (response.invalidProductIdentifiers.count) {
            NSLog(@"商品請求錯誤,error:%@", response.invalidProductIdentifiers);
        }
        NSLog(@"商品詳情:%@",[product[0] description]);
        NSLog(@"商品價格:%@",[product[0] price]);
        NSLog(@"商品名字:%@",[product[0] productIdentifier]);
        
        // 請求購買
        SKPayment *payment = [SKPayment paymentWithProduct:product[0]];
        [[SKPaymentQueue defaultQueue] addPayment:payment];
    }
}
// 處理請求購買后的回調(diào)
#pragma mark - SKProductsRequestDelegate 請求購買后的回調(diào)
- (void)paymentQueue:(nonnull SKPaymentQueue *)queue updatedTransactions:(nonnull NSArray<SKPaymentTransaction *> *)transactions {
    
    if (!self.isRestore) {
        for (SKPaymentTransaction *tran in transactions) {

            switch (tran.transactionState) {
                
                case SKPaymentTransactionStatePurchasing: // 正在交易
                    break;
                
                case SKPaymentTransactionStatePurchased: // 交易完成
                    [self completeTransaction:tran];

                    if (tran.originalTransaction) {
                      // 如果是自動續(xù)費的訂單,originalTransaction會有內(nèi)容
                      if (tran.originalTransaction.transactionIdentifier) {
                        NSLog(@"自動續(xù)費的訂單ID = %@",tran.originalTransaction.transactionIdentifier);
                        [self saveOrderId:tran.originalTransaction.transactionIdentifier];
                      }
                    } else {
                      // 普通購買籽腕,以及第一次購買自動訂閱
                      if (tran.transactionIdentifier) {
                        NSLog(@"普通購買昵观,以及第一次購買自動訂閱ID = %@",tran.transactionIdentifier);
                        [self saveOrderId:tran.transactionIdentifier];
                       }
                     }

                    NSLog(@"購買完成,正在校驗購買憑證...");
                    [self vertifyReceipt]; // 校驗購買憑證
                    break;
                
                case SKPaymentTransactionStateFailed: // 交易失敗
                    [self completeTransaction:tran];
                    NSLog(@"購買失敗!");
                    break;
                
                case SKPaymentTransactionStateRestored: // 已經(jīng)購買過該商品
                    [self completeTransaction:tran];
                    NSLog(@"已經(jīng)購買過該商品血柳!");
                    break;
                
                default:
                    break;
            }
        }
    }
}

- (void)completeTransaction:(SKPaymentTransaction *)transaction {   
    self.isRequestFinished = YES; // 請求完成
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
// 根據(jù)公司需求校驗憑證
- (void)vertifyReceipt{
    
    NSURL * receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    if ([[NSFileManager defaultManager] fileExistsAtPath:[receiptURL path]]){
        
        [self vertifyReceiptSuccess:^{
            NSLog(@"校驗成功!");
        } failure:^{
            NSLog(@"校驗失斏住难捌!");
        }];
        
    } else {
        // 如果憑證為空,則再發(fā)一次憑證請求
        SKReceiptRefreshRequest *refreshReceiptRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:@{}];
        refreshReceiptRequest.delegate = self;
        [refreshReceiptRequest start];
    }
}
// 如果憑證為空鸦难,需要再發(fā)一次憑證請求根吁,處理請求成功后的回調(diào)
#pragma mark -  成功請求憑證后的回調(diào)
- (void)requestDidFinish:(SKRequest *)request{
    if ([request isKindOfClass:[SKReceiptRefreshRequest class]]) {
        NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];
        if ([[NSFileManager defaultManager] fileExistsAtPath:[receiptUrl path]]) {
            [self vertifyReceiptSuccess:^{
              NSLog(@"再次請求憑證后,校驗成功合蔽!");
            } failure:^{
              NSLog(@"再次請求憑證后击敌,校驗失敗拴事!");
            }];
        }
    }
}
// 校驗憑證
- (void)vertifyReceiptSuccess:(void(^)(void))success failure:(void(^)(void))failure {
    NSString *receipt = [self getReceipt];
    // 校驗憑證步驟需要按照你公司的需求來寫
}

// 檢驗訂單是否還在訂閱期內(nèi)
- (void)checkOrderStateSuccess:(void(^)(void))success failure:(void(^)(void))failure {
    NSString *receipt = [self getOrderId];
    // 檢驗訂單步驟需要按照你公司的需求來寫
}

// 獲取憑證
- (NSString *)getReceipt {
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
    NSString *encodeReceiptStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    return encodeReceiptStr;
}

// 保存訂單ID
- (void)saveOrderId:(NSString *)orderId{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setValue:orderId forKey:@"orderId"];
    [defaults synchronize];
}

// 獲取訂單ID
- (NSString *)getOrderId{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    return [defaults valueForKey:@"orderId"];
}

恢復購買

// 恢復購買
- (void)restore {
    self.isRestore = YES;
    [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
 // 恢復成功后的回調(diào)
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue{

    if (queue.transactions.count) {
        for (SKPaymentTransaction *tran in queue.transactions) {
            if (tran.originalTransaction.transactionIdentifier) {
                [self saveOrderId:tran.originalTransaction.transactionIdentifier];
                break;
            }
        }
        // 檢驗訂單是否還在訂閱期內(nèi)
        [self checkOrderStateSuccess:^{
            NSLog(@"訂單還在訂閱期沃斤,校驗成功圣蝎!");
        } failure:^{
            NSLog(@"訂單已過期,校驗失敽馄俊捅彻!");
        }];
    } else {
        // 沒有可恢復的購買項
    }
}
 // 恢復失敗后的回調(diào)
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
     // 沒有可恢復的購買項
}

最后

本篇主要講述了一個詳細的內(nèi)購流程及代碼實現(xiàn),感興趣的給個贊或者關(guān)注~~~
你們的支持就是我更新的動力0霸伞2窖汀!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诚撵,一起剝皮案震驚了整個濱河市缭裆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌寿烟,老刑警劉巖澈驼,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異筛武,居然都是意外死亡缝其,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門徘六,熙熙樓的掌柜王于貴愁眉苦臉地迎上來内边,“玉大人,你說我怎么就攤上這事待锈∧洌” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵竿音,是天一觀的道長和屎。 經(jīng)常有香客問我,道長春瞬,這世上最難降的妖魔是什么柴信? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮宽气,結(jié)果婚禮上随常,老公的妹妹穿的比我還像新娘。我一直安慰自己抹竹,他們只是感情好线罕,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著窃判,像睡著了一般钞楼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上袄琳,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天询件,我揣著相機與錄音燃乍,去河邊找鬼。 笑死宛琅,一個胖子當著我的面吹牛刻蟹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嘿辟,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼舆瘪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了红伦?” 一聲冷哼從身側(cè)響起英古,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎昙读,沒想到半個月后召调,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡蛮浑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年唠叛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沮稚。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡艺沼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出壮虫,到底是詐尸還是另有隱情澳厢,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布囚似,位于F島的核電站,受9級特大地震影響线得,放射性物質(zhì)發(fā)生泄漏饶唤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一贯钩、第九天 我趴在偏房一處隱蔽的房頂上張望募狂。 院中可真熱鬧,春花似錦角雷、人聲如沸祸穷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽雷滚。三九已至,卻和暖如春吗坚,著一層夾襖步出監(jiān)牢的瞬間祈远,已是汗流浹背呆万。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留车份,地道東北人谋减。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像扫沼,于是被迫代替她去往敵國和親出爹。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容