2018iOS內(nèi)購詳細(xì)教程&iOS內(nèi)購我踩過的坑(出現(xiàn)的問題以及優(yōu)化方案&一些細(xì)節(jié)處理)

首先,在我上一次發(fā)的這篇文章里http://www.reibang.com/p/d2b3f297ba9e 存在幾個(gè)容易出現(xiàn)問題的點(diǎn)吃度,在實(shí)際的運(yùn)營中確實(shí)也出現(xiàn)過匹涮,所以針對出現(xiàn)的問題也做了一下優(yōu)化,希望對大家有幫助。

問題一

在前一篇內(nèi)購的代碼中坞生,在實(shí)際運(yùn)營中仔役,出現(xiàn)過獲取不到平臺訂單號,也就是applicationUsername為nil的情況 恨胚,這種情況是因?yàn)樘O果返回參數(shù)為nil導(dǎo)致的漏單骂因,所以說這個(gè)參數(shù)并不是完全的可靠。所以赃泡,有必要在這個(gè)參數(shù)返回為nil 的時(shí)候做一下處理寒波。首先有兩種方案:

一、可以在本地進(jìn)行存儲升熊,先去判斷蘋果返回的這個(gè)applicationUsername是否為nil 如果為nil,去判斷傳入的這個(gè)平臺訂單號是否有值俄烁,如果有,直接使用這個(gè)值(一般走購買流程级野,這個(gè)值都是有的即可页屠,如果是走的漏單流程,applicationUsername 為nil 且此時(shí)是不傳入平臺訂單號的蓖柔,這時(shí)候就應(yīng)該在發(fā)起購買的之后存儲這個(gè)訂單號辰企,具體存儲可以將拉起購買傳進(jìn)來的平臺order作為值,然后transaction.transactionIdentifier作為key(注:實(shí)測當(dāng)憑證為key 存儲的話况鸣,在有訂閱類型商品的收會(huì)出現(xiàn)同一個(gè)商品不同時(shí)段憑證不一樣的情況牢贸,導(dǎo)致取不到存儲的order,因此通過transaction.transactionIdentifier(唯一事務(wù)id)來進(jìn)行存儲,這個(gè)實(shí)測確保是唯一的)存儲到某個(gè)目錄下,文件名可以用:平臺訂單號.plist的格式保存。所以在下次走漏單的流程時(shí)候蘋果返回applicatonUsername為nil 的 镐捧,可以去遍歷存儲的plist,然后將鍵為該憑證的值取出來潜索,這就是與購買憑證匹配的平臺訂單號了。

二懂酱、第二種方法需要跟服務(wù)端交互 竹习,也就是在拉起內(nèi)購的時(shí)候,在獲取到憑證的時(shí)候列牺,將憑證和傳進(jìn)來的平臺訂單號一起post到你們家后臺整陌,當(dāng)在獲取蘋果返回的applicationUsername為nil 的時(shí)候 ,通過憑證去后臺將這個(gè)平臺訂單號取回來就可以了瞎领。第二種方法需要進(jìn)行兩部交互有點(diǎn)繁瑣蔓榄。

問題二

在此前的文章中的這一段代碼,在實(shí)際運(yùn)營中出現(xiàn)過因?yàn)橛唵翁枮閚il 而無法刪除已經(jīng)成功的憑證默刚,不停向服務(wù)器發(fā)起訪問的問題,部分代碼如下:

  -(void)checkIAPFiles:(SKPaymentTransaction *)transaction{

NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;
NSArray *cacheFileNameArray = [fileManager contentsOfDirectoryAtPath:[SandBoxHelper iapReceiptPath] error:&error];

if (error == nil) {
    
    for (NSString *name in cacheFileNameArray) {
        
        if ([name hasSuffix:@".plist"]){
            //如果有plist后綴的文件逃魄,說明就是存儲的購買憑證
            NSString *filePath = [NSString stringWithFormat:@"%@/%@", [SandBoxHelper iapReceiptPath], name];
            
            [self sendAppStoreRequestBuyPlist:filePath trans:transaction];
        }
    }
    
} else {
    
    [RRHUD hide];
    
 }
}



  #pragma mark -- 根據(jù)訂單號來移除本地憑證的方法
-(void)successConsumptionOfGoodsWithOrder:(NSString * )cpOrder{

NSFileManager *fileManager = [NSFileManager defaultManager];
NSError * error;
if ([fileManager fileExistsAtPath:[SandBoxHelper iapReceiptPath]]) {
    
    NSArray * cacheFileNameArray = [fileManager contentsOfDirectoryAtPath:[SandBoxHelper iapReceiptPath] error:&error];
    
    if (error == nil) {
        
        for (NSString * name in cacheFileNameArray) {
            
            NSString * filePath = [NSString stringWithFormat:@"%@/%@", [SandBoxHelper iapReceiptPath], name];
            
            [self removeReceiptWithPlistPath:filePath ByCpOrder:cpOrder];
            
        }
    }
}

}

#pragma mark -- 根據(jù)訂單號來刪除 存儲的憑證
-(void)removeReceiptWithPlistPath:(NSString *)plistPath ByCpOrder:(NSString *)cpOrder{

NSFileManager *fileManager = [NSFileManager defaultManager];
NSError * error;
NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath];
NSString * order = [dic objectForKey:@"order"];

if ([cpOrder isEqualToString:order]) {
    
    //移除與游戲cp訂單號一樣的plist 文件
    BOOL ifRemove =  [fileManager removeItemAtPath:plistPath error:&error];
    
    if (ifRemove) {
        
        NSLog(@"成功訂單移除成功");
        
    }else{
        
        NSLog(@"成功訂單移除失敗");
    }
    
}else{
    
    NSLog(@"本地?zé)o與之匹配的訂單");
 }
}

以上代碼的話會(huì)在獲取不到訂單號的時(shí)候刪除不了本地的已經(jīng)成功的訂單荤西。會(huì)導(dǎo)致 -(void)checkIAPFiles:(SKPaymentTransaction *)transaction這個(gè)方法始終判斷這個(gè)目錄下有文件。在購買的時(shí)候不停地向服務(wù)端發(fā)起訪問,加劇服務(wù)器壓力邪锌。

針對以上的問題勉躺,我簡化了一些流程,考慮到蘋果內(nèi)購有獨(dú)有的漏單操作觅丰。其實(shí)不必要存儲憑證也可以實(shí)現(xiàn)補(bǔ)單流程 饵溅。優(yōu)化代碼我貼出來,具體關(guān)于applicationUsername為nil 的處理妇萄,大家可以根據(jù)需求從中選擇一個(gè)來進(jìn)行處理 蜕企。至于文件我也會(huì)更新到github ,鏈接請看評論,有需要大家可以下載使用冠句。至于該文件中的一些block 或者你們看不懂的回調(diào)其實(shí)跟我的業(yè)務(wù)有關(guān)轻掩,因?yàn)槲沂菍慡DK給別人用的所以要回調(diào)購買的結(jié)果給研發(fā)做判斷進(jìn)行發(fā)貨操作。不關(guān)乎你們的內(nèi)購邏輯懦底。一下是優(yōu)化之后的內(nèi)購代碼 :
IPAPurchase.h

IPAPurchase.h
#import <Foundation/Foundation.h>


/**
 block

 @param isSuccess 是否支付成功
 @param certificate 支付成功得到的憑證(用于在   自己服務(wù)器驗(yàn)證)
 @param errorMsg 錯(cuò)誤信息
 */
typedef void(^PayResult)(BOOL isSuccess,NSString *certificate,NSString *errorMsg);

@interface IPAPurchase : NSObject
@property (nonatomic, copy)PayResult  payResultBlock;

 內(nèi)購支付
 @param productID 內(nèi)購商品ID
 @param payResult 結(jié)果
 */
-(void)buyProductWithProductID:(NSString *)productID payResult:(PayResult)payResult;

@end

IPAPurchase.m

//  IPAPurchase.m
//  iOS_Purchase
//  Created by zhanfeng on 2017/6/6.
//  Copyright ? 2017年 unlock_liujia. All rights reserved.

#import "IPAPurchase.h"
#import "ULSDKConfig.h"
#import <StoreKit/StoreKit.h>
#import <StoreKit/SKPaymentTransaction.h>

static NSString * const receiptKey = @"receipt_key";

dispatch_queue_t iap_queue(){
static dispatch_queue_t as_iap_queue;
static dispatch_once_t onceToken_iap_queue;
dispatch_once(&onceToken_iap_queue, ^{
    as_iap_queue = dispatch_queue_create("com.iap.queue", DISPATCH_QUEUE_CONCURRENT);
});

return as_iap_queue;

}

@interface IPAPurchase()<SKPaymentTransactionObserver,
SKProductsRequestDelegate>
{
SKProductsRequest *request;
}
//購買憑證
@property (nonatomic,copy)NSString *receipt;//存儲base64編碼的交易憑證

//產(chǎn)品ID
@property (nonnull,copy)NSString * profductId;

@end

static IPAPurchase * manager = nil;

@implementation IPAPurchase
#pragma mark -- 單例方法
+ (instancetype)manager{

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    
    if (!manager) {
        manager = [[IPAPurchase alloc] init];
    }
});

   return manager;
}

#pragma mark -- 添加內(nèi)購監(jiān)聽者
-(void)startManager{

dispatch_sync(iap_queue(), ^{

[[SKPaymentQueue defaultQueue] addTransactionObserver:manager];

 });

}

#pragma mark -- 移除內(nèi)購監(jiān)聽者
-(void)stopManager{

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
    
    });

}

#pragma mark -- 發(fā)起購買的方法
-(void)buyProductWithProductID:(NSString *)productID payResult:(PayResult)payResult{

[self removeAllUncompleteTransactionsBeforeNewPurchase];

self.payResultBlock = payResult;

[RRHUD showWithContainerView:RR_keyWindow status:NSLocalizedString(@"Buying...", @"")];

self.profductId = productID;

if (!self.profductId.length) {
    
    UIAlertView * alertView = [[UIAlertView alloc]initWithTitle:@"Warm prompt" message:@"There is no corresponding product." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
    
    [alertView show];
}

if ([SKPaymentQueue canMakePayments]) {
    
    [self requestProductInfo:self.profductId];
    
}else{
    
UIAlertView * alertView = [[UIAlertView alloc]initWithTitle:@"Warm prompt" message:@"Please turn on the in-app paid purchase function first." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
    
[alertView show];
    
  }

}

 #pragma mark -- 結(jié)束上次未完成的交易
-(void)removeAllUncompleteTransactionsBeforeNewPurchase{

NSArray* transactions = [SKPaymentQueue defaultQueue].transactions;

if (transactions.count >= 1) {

    for (NSInteger count = transactions.count; count > 0; count--) {
        
        SKPaymentTransaction* transaction = [transactions objectAtIndex:count-1];
        
        if (transaction.transactionState == SKPaymentTransactionStatePurchased||transaction.transactionState == SKPaymentTransactionStateRestored) {
            
            [[SKPaymentQueue defaultQueue]finishTransaction:transaction];
        }
    } 
}else{ 
     NSLog(@"沒有歷史未消耗訂單");
  }
}


#pragma mark -- 發(fā)起購買請求
-(void)requestProductInfo:(NSString *)productID{

NSArray * productArray = [[NSArray alloc]initWithObjects:productID,nil];

NSSet * IDSet = [NSSet setWithArray:productArray];

request = [[SKProductsRequest alloc]initWithProductIdentifiers:IDSet];

request.delegate = self;

[request start];

}

#pragma mark -- SKProductsRequestDelegate 查詢成功后的回調(diào)
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSArray *myProduct = response.products;

if (myProduct.count == 0) {
    
    [RRHUD hide];
    [RRHUD showErrorWithContainerView:UL_rootVC.view status:NSLocalizedString(@"No Product Info", @"")];
    
    if (self.payResultBlock) {
        self.payResultBlock(NO, nil, @"無法獲取產(chǎn)品信息唇牧,購買失敗");
    }
    
    return;
}

SKProduct * product = nil;

for(SKProduct * pro in myProduct){
    
    NSLog(@"SKProduct 描述信息%@", [pro description]);
    NSLog(@"產(chǎn)品標(biāo)題 %@" , pro.localizedTitle);
    NSLog(@"產(chǎn)品描述信息: %@" , pro.localizedDescription);
    NSLog(@"價(jià)格: %@" , pro.price);
    NSLog(@"Product id: %@" , pro.productIdentifier);
    
    if ([pro.productIdentifier isEqualToString:self.profductId]) {
        
        product = pro;
        
        break;
    }
}

if (product) {
    
    SKMutablePayment * payment = [SKMutablePayment paymentWithProduct:product];
    //使用蘋果提供的屬性,將平臺訂單號復(fù)制給這個(gè)屬性作為透傳參數(shù)
    payment.applicationUsername = self.order;
    
    [[SKPaymentQueue defaultQueue] addPayment:payment];
    
}else{
    
    NSLog(@"沒有此商品信息");
  }
}

//查詢失敗后的回調(diào)
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {

if (self.payResultBlock) {
    self.payResultBlock(NO, nil, [error localizedDescription]);
  }
}

//如果沒有設(shè)置監(jiān)聽購買結(jié)果將直接跳至反饋結(jié)束;
-(void)requestDidFinish:(SKRequest *)request{

}

#pragma mark -- 監(jiān)聽結(jié)果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{

//當(dāng)用戶購買的操作有結(jié)果時(shí)聚唐,就會(huì)觸發(fā)下面的回調(diào)函數(shù)丐重,
for (SKPaymentTransaction * transaction in transactions) {
    
    switch (transaction.transactionState) {
            
        case SKPaymentTransactionStatePurchased:{
            
            [self completeTransaction:transaction];
            
        }break;
            
        case SKPaymentTransactionStateFailed:{
            
            [self failedTransaction:transaction];
            
        }break;
            
        case SKPaymentTransactionStateRestored:{//已經(jīng)購買過該商品
            
            [self restoreTransaction:transaction];
            
        }break;
            
        case SKPaymentTransactionStatePurchasing:{
            
            NSLog(@"正在購買中...");
            
        }break;
            
        case SKPaymentTransactionStateDeferred:{
            
            NSLog(@"最終狀態(tài)未確定");
            
        }break;
            
        default:
            break;
    }
  }
}

//完成交易
#pragma mark -- 交易完成的回調(diào)
- (void)completeTransaction:(SKPaymentTransaction *)transaction
{    
[self getAndSaveReceipt:transaction]; //獲取交易成功后的購買憑證
}

#pragma mark -- 處理交易失敗回調(diào)
- (void)failedTransaction:(SKPaymentTransaction *)transaction{

[RRHUD hide];

NSString * error = nil;

if(transaction.error.code != SKErrorPaymentCancelled) {
    
    [RRHUD showInfoWithContainerView:UL_rootVC.view status:NSLocalizedString(@"Buy Failed", @"")];
    error = [NSString stringWithFormat:@"%ld",transaction.error.code];
    
} else {
    
    [RRHUD showInfoWithContainerView:UL_rootVC.view status:NSLocalizedString(@"Buy Canceled", @"")];
    error = [NSString stringWithFormat:@"%ld",transaction.error.code];
    
}

if (self.payResultBlock) {
    self.payResultBlock(NO, nil, error);
}

[[SKPaymentQueue defaultQueue]finishTransaction:transaction];

}

- (void)restoreTransaction:(SKPaymentTransaction *)transaction{

[RRHUD hide];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}


#pragma mark -- 獲取購買憑證
-(void)getAndSaveReceipt:(SKPaymentTransaction *)transaction{

//獲取交易憑證
NSURL * receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];
NSData * receiptData = [NSData dataWithContentsOfURL:receiptUrl];
NSString * base64String = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
//初始化字典
NSMutableDictionary * dic = [[NSMutableDictionary alloc]init];

NSString * order = transaction.payment.applicationUsername;

//如果這個(gè)返回為nil

NSLog(@"后臺訂單號為訂單號為%@",order);

[dic setValue: base64String forKey:receiptKey];
[dic setValue: order forKey:@"order"];
[dic setValue:[self getCurrentZoneTime] forKey:@"time"];
NSString * userId;

if (self.userid) {
    
    userId = self.userid;
    [[NSUserDefaults standardUserDefaults]setObject:userId forKey:@"unlock_iap_userId"];
    
}else{
    
    userId = [[NSUserDefaults standardUserDefaults]
              objectForKey:@"unlock_iap_userId"];
}

if (userId == nil||[userId length] == 0) {
    
    userId = @"走漏單流程未傳入userId";
}

if (order == nil||[order length] == 0) {
    
    order = @"蘋果返回透傳參數(shù)為nil";
}

[[ULSDKAPI shareAPI]sendLineWithPayOrder:order UserId:userId Receipt:base64String LineNumber:@"IPAPurchase.m 337"];

NSString *fileName = [NSString UUID];

NSString *savedPath = [NSString stringWithFormat:@"%@/%@.plist", [SandBoxHelper iapReceiptPath], fileName];
[dic setValue: userId forKey:@"user_id"];

//這個(gè)存儲成功與否其實(shí)無關(guān)緊要
BOOL ifWriteSuccess = [dic writeToFile:savedPath atomically:YES];

if (ifWriteSuccess){

    NSLog(@"購買憑據(jù)存儲成功!");

}else{
    
    NSLog(@"購買憑據(jù)存儲失敗");
}

[self sendAppStoreRequestBuyWithReceipt:base64String userId:userId paltFormOrder:order trans:transaction];

}

-(void)getPlatformAmountInfoWithOrder:(NSString *)transOrcer{

[[ULSDKAPI shareAPI]getPlatformAmountWithOrder:transOrcer success:^(id responseObject) {
    
    if (RequestSuccess) {
        
    _platformAmount = [[responseObject objectForKey:@"data"]objectForKey:@"amount"];
    _amount_type = [[responseObject objectForKey:@"data"]objectForKey:@"amount_type"];
    _third_goods_id = [[responseObject objectForKey:@"data"]objectForKey:@"third_goods_id"];
    
    [FBSDKAppEvents logEvent:@"pay_in_sdk" valueToSum:[_platformAmount doubleValue] parameters:@{@"fb_currency":@"USD",@"amount":_platformAmount,@"amount_type":_amount_type,@"third_goods_id":_third_goods_id}];
    
    }else{
        
        NSLog(@"%@",[responseObject objectForKey:@"message"]);
    }
    
} failure:^(NSString *failure) {

}];

}

#pragma mark -- 存儲成功訂單
-(void)SaveIapSuccessReceiptDataWithReceipt:(NSString *)receipt Order:(NSString *)order UserId:(NSString *)userId{

NSMutableDictionary * mdic = [[NSMutableDictionary alloc]init];
[mdic setValue:[self getCurrentZoneTime] forKey:@"time"];
[mdic setValue: order forKey:@"order"];
[mdic setValue: userId forKey:@"userid"];
[mdic setValue: receipt forKey:receiptKey];
NSString *fileName = [NSString UUID];
NSString * successReceiptPath = [NSString stringWithFormat:@"%@/%@.plist", [SandBoxHelper SuccessIapPath], fileName];
//存儲購買成功的憑證
[self insertReceiptWithReceiptByReceipt:receipt withDic:mdic  inReceiptPath:successReceiptPath];
}



-(void)insertReceiptWithReceiptByReceipt:(NSString *)receipt withDic:(NSDictionary *)dic inReceiptPath:(NSString *)receiptfilePath{

BOOL isContain = NO;

NSFileManager *fileManager = [NSFileManager defaultManager];
NSError * error;
NSArray * cacheFileNameArray = [fileManager contentsOfDirectoryAtPath:[SandBoxHelper SuccessIapPath] error:&error];

if (cacheFileNameArray.count == 0) {
    
    [dic writeToFile:receiptfilePath atomically:YES];
    
    if ([dic writeToFile:receiptfilePath atomically:YES]) {
        
        NSLog(@"寫入購買憑據(jù)成功");
        
    }
    
}else{
   
    if (error == nil) {
     
        for (NSString * name in cacheFileNameArray) {

            NSString * filePath = [NSString stringWithFormat:@"%@/%@", [SandBoxHelper SuccessIapPath], name];
            NSMutableDictionary *localdic = [NSMutableDictionary dictionaryWithContentsOfFile:filePath];
            
            if ([localdic.allValues containsObject:receipt]) {
                
                isContain = YES;
                
            }else{
                
                continue;
            }
        }
        
    }else{
        
        NSLog(@"讀取本文存儲憑據(jù)失敗");
    }
    
}

if (isContain == NO) {
    
BOOL  results = [dic writeToFile:receiptfilePath atomically:YES];
    
if (results) {
    
    NSLog(@"寫入憑證成功");
}else{
    
    NSLog(@"寫入憑證失敗");
}
    
}else{
    
    NSLog(@"已經(jīng)存在憑證請勿重復(fù)寫入");
  }
}

#pragma mark -- 獲取系統(tǒng)時(shí)間的方法
-(NSString *)getCurrentZoneTime{

NSDate * date = [NSDate date];
NSDateFormatter*formatter = [[NSDateFormatter alloc]init];
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSString*dateTime = [formatter stringFromDate:date];
return dateTime;

}

#pragma mark -- 去服務(wù)器驗(yàn)證購買
-(void)sendAppStoreRequestBuyWithReceipt:(NSString *)receipt userId:(NSString *)userId paltFormOrder:(NSString * )order trans:(SKPaymentTransaction *)transaction{

[[ULSDKAPI shareAPI]sendLineWithPayOrder:order UserId:userId Receipt:receipt LineNumber:@"IPAPurchase.m 474"];
#pragma mark -- 發(fā)送信息去驗(yàn)證是否成功
[[ULSDKAPI shareAPI] sendVertifyWithReceipt:receipt order:order userId:userId success:^(ULSDKAPI *api, id responseObject) {
    
    if (RequestSuccess) {
        
        [RRHUD hide];
        [RRHUD showSuccessWithContainerView:UL_rootVC.view status:NSLocalizedString(@"Buy Success", @"")];
        //結(jié)束交易方法
        [[SKPaymentQueue defaultQueue]finishTransaction:transaction];
        
        [self getPlatformAmountInfoWithOrder:order];
        
        NSData * data = [NSData dataWithContentsOfFile:[[[NSBundle mainBundle] appStoreReceiptURL] path]];
        NSString *result = [data base64EncodedStringWithOptions:0];
        
        if (self.payResultBlock) {
            self.payResultBlock(YES, result, nil);
        }
        
        //這里將成功但存儲起來
        [self SaveIapSuccessReceiptDataWithReceipt:receipt Order:order UserId:userId];
        [self successConsumptionOfGoodsWithReceipt:receipt];
       
    }else{
#pragma mark -- callBack 回調(diào)
        [api sendVertifyWithReceipt:receipt order:order userId:userId success:^(ULSDKAPI *api, id responseObject) {
            
            if (RequestSuccess) {
                
                [RRHUD hide];
                
                [RRHUD showSuccessWithContainerView:UL_rootVC.view status:NSLocalizedString(@"Buy Success", @"")];
                
                [[SKPaymentQueue defaultQueue]finishTransaction:transaction];
                
                [self getPlatformAmountInfoWithOrder:order];
                
                NSData *data = [NSData dataWithContentsOfFile:[[[NSBundle mainBundle] appStoreReceiptURL] path]];
                
                NSString *result = [data base64EncodedStringWithOptions:0];
                
                if (self.payResultBlock) {
                    self.payResultBlock(YES, result, nil);
                }
                
                //存儲成功訂單
                [self SaveIapSuccessReceiptDataWithReceipt:receipt Order:order UserId:userId];
                //刪除已成功訂單
                [self successConsumptionOfGoodsWithReceipt:receipt];
                    }
#pragma 校驗(yàn)發(fā)貨失敗 1
                } failure:^(ULSDKAPI *api, NSString *failure) {
                    
                    [RRHUD hide];
                    [RRHUD showErrorWithContainerView:UL_rootVC.view status:NSLocalizedString(@"Request Error", @"")];
                    
                }];
                
            }else{
                
                [RRHUD hide];
#pragma mark --發(fā)送錯(cuò)誤報(bào)告
                [api sendFailureReoprtWithReceipt:receipt order:order success:^(ULSDKAPI *api, id responseObject) {
                    
                } failure:^(ULSDKAPI *api, NSString *failure) {
                    
                    [RRHUD hide];
                }];
                
            }
            
        } failure:^(ULSDKAPI *api, NSString *failure) {
            
            [RRHUD hide];
        }];
        
    }
    
} failure:^(ULSDKAPI *api, NSString *failure) {
    
    [RRHUD hide];
    
    [api VertfyFailedRePostWithUserId:userId Order:order jsonStr:failure];
    
}];
}

#pragma mark -- 根據(jù)訂單號來移除本地憑證的方法
-(void)successConsumptionOfGoodsWithReceipt:(NSString * )receipt{

NSFileManager *fileManager = [NSFileManager defaultManager];
NSError * error;
if ([fileManager fileExistsAtPath:[SandBoxHelper iapReceiptPath]]) {
    
    NSArray * cacheFileNameArray = [fileManager contentsOfDirectoryAtPath:[SandBoxHelper iapReceiptPath] error:&error];
    
    if (error == nil) {
        
        for (NSString * name in cacheFileNameArray) {
            
            NSString * filePath = [NSString stringWithFormat:@"%@/%@", [SandBoxHelper iapReceiptPath], name];
            
            [self removeReceiptWithPlistPath:filePath ByReceipt:receipt];
            
        }
    }
}
}

#pragma mark -- 根據(jù)訂單號來刪除 存儲的憑證
  -(void)removeReceiptWithPlistPath:(NSString *)plistPath ByReceipt:(NSString *)receipt{

NSFileManager *fileManager = [NSFileManager defaultManager];
NSError * error;
NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath];
NSString * localReceipt = [dic objectForKey:@"receipt_key"];
//通過憑證進(jìn)行對比
if ([receipt isEqualToString:localReceipt]) {
  
    BOOL ifRemove = [fileManager removeItemAtPath:plistPath error:&error];
    
    if (ifRemove) {
        
        NSLog(@"成功訂單移除成功");
        
    }else{
        
        NSLog(@"成功訂單移除失敗");
    }
    
}else{
    
    NSLog(@"本地?zé)o與之匹配的訂單");
}
}

@end

代碼可能粘貼不齊,細(xì)節(jié)調(diào)整之后和最新的單例類內(nèi)購文件已經(jīng)同步到GitHub 了杆查,如有需要可以去gitHud 直接下載源文件扮惦,建議自己揣摩清楚之后再參考或者引入自己的項(xiàng)目,之所謂知其所以也要知其所以然根灯。另外在-(void)getAndSaveReceipt:(SKPaymentTransaction *)transaction径缅;方法里面沒有對applicationUsername為nil 做處理,大家可以跟我說的哪兩種思路進(jìn)行解決烙肺,我在這里就不代碼演示了纳猪。大家多動(dòng)腦~ github鏈接如下:https://github.com/jiajiaailaras/ULIPAPurchase

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市桃笙,隨后出現(xiàn)的幾起案子氏堤,更是在濱河造成了極大的恐慌,老刑警劉巖搏明,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鼠锈,死亡現(xiàn)場離奇詭異,居然都是意外死亡星著,警方通過查閱死者的電腦和手機(jī)购笆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來虚循,“玉大人同欠,你說我怎么就攤上這事样傍。” “怎么了铺遂?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵衫哥,是天一觀的道長。 經(jīng)常有香客問我襟锐,道長撤逢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任粮坞,我火速辦了婚禮蚊荣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘捞蚂。我一直安慰自己妇押,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布姓迅。 她就那樣靜靜地躺著敲霍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪丁存。 梳的紋絲不亂的頭發(fā)上肩杈,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音解寝,去河邊找鬼扩然。 笑死,一個(gè)胖子當(dāng)著我的面吹牛聋伦,可吹牛的內(nèi)容都是我干的夫偶。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼觉增,長吁一口氣:“原來是場噩夢啊……” “哼兵拢!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起逾礁,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤说铃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后嘹履,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腻扇,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年砾嫉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了幼苛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡焕刮,死狀恐怖蚓峦,靈堂內(nèi)的尸體忽然破棺而出舌剂,到底是詐尸還是另有隱情,我是刑警寧澤暑椰,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站荐绝,受9級特大地震影響一汽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜低滩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一召夹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧恕沫,春花似錦监憎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至迄委,卻和暖如春褐筛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叙身。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工渔扎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人信轿。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓晃痴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親财忽。 傳聞我的和親對象是個(gè)殘疾皇子倘核,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

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