ios ~ 內(nèi)購:向服務(wù)器發(fā)送憑據(jù),服務(wù)器和App Store校驗

內(nèi)購最新流程:僅限使用swift:https://juejin.cn/post/6974733392260644895

【iOS 內(nèi)購:自動訂閱功能】

1、第一步:調(diào)用蘋果支付

#pragma mark -- 蘋果支付
- (void)ApplePay {
    if ([SKPaymentQueue canMakePayments]) { 
        // 6.請求蘋果后臺商品
        [self getRequestAppleProduct];
    }
}

// 請求蘋果商品
- (void)getRequestAppleProduct {
    // 7. 這里的com.czchat.CZChat01就對應(yīng)著蘋果后臺的商品ID思灌,他們是通過這個ID進行聯(lián)系的。
    NSArray *product = [[NSArray alloc] initWithObjects:self.BuyID, nil];
    
    NSSet *nsset = [NSSet setWithArray:product];
    
    // 8.初始化請求
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
    request.delegate = self;
    
    [SVProgressHUD showWithStatus:@"正在加載"];
    // 9.開始請求
    [request start];
}

// 10.接收到產(chǎn)品的返回信息,然后用返回的商品信息進行發(fā)起購買請求
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
    NSArray *products = response.products;
    
    // 如果服務(wù)器沒有產(chǎn)品
    if (products.count == 0) {
        NSLog(@"nothing");
        return;
    }
    
    SKProduct *requestProduct = nil;
    for (SKProduct *pro in products) {
        
        NSLog(@"%@", [pro description]);
        NSLog(@"%@", [pro localizedTitle]);
        NSLog(@"%@", [pro localizedDescription]);
        NSLog(@"%@", [pro price]);
        NSLog(@"%@", [pro productIdentifier]);
        
        // 11. 如果后臺消費條目的ID與我這里需要的請求的一樣(用于確保訂單的正確性)
        if ([pro.productIdentifier isEqualToString:self.BuyID]) {
            requestProduct = pro;
        }
    }
    NSLog(@"%@", requestProduct);
    
    //12. 發(fā)送購買請求
    NSArray *transactions = [SKPaymentQueue defaultQueue].transactions;
    if (transactions.count > 0) {
        // 檢測是否有未完成的交易
        SKPaymentTransaction *transaction = [transactions firstObject];
        if (transaction.transactionState == SKPaymentTransactionStatePurchased) {
            [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
            return;
        } else {
            
            SKPayment *payment = [SKPayment paymentWithProduct:requestProduct];
            [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
            [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
            
            [[SKPaymentQueue defaultQueue] addPayment:payment];
        }
        
    } else {
        SKPayment *payment = [SKPayment paymentWithProduct:requestProduct];
        [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
        [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
        
        [[SKPaymentQueue defaultQueue] addPayment:payment];
    }
    
}

// 請求失敗
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
    NSLog(@"%@", error);
    [SVProgressHUD dismiss];
}

// 反饋請求的產(chǎn)品信息結(jié)束后
- (void)requestDidFinish:(SKRequest *)request {
    NSLog(@"信息反饋結(jié)束");
}

// 13.購買監(jiān)聽結(jié)果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {
    for (SKPaymentTransaction *transaction in transactions) {
        NSLog(@"------");
        
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchased:
                NSLog(@"交易完成");
                NSLog(@"購買完成啃炸,向自己的服務(wù)器驗證------ %@", transaction.payment.applicationUsername);
                NSLog(@"購買完成,向自己的服務(wù)器驗證------ %@", transaction.payment.hyb_toJsonString);

                // 將交易從交易隊列中刪除
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                
                if (transaction.originalTransaction) { // 訂閱特殊處理
                    // 如果是自動續(xù)費的訂單originalTransaction會有內(nèi)容
                    
                } else {
                    //普通購買卓舵,以及 第一次購買 自動訂閱

//                [self completedTransaction:transaction]; // 本地校驗交易憑據(jù)
                    [self getApplePayDataToServerRequsetWith:transaction]; // 后臺服務(wù)器校驗App Store交易憑據(jù)
                }
               
                break;
            case SKPaymentTransactionStatePurchasing:
                NSLog(@"商品添加進列表");
                break;
            case SKPaymentTransactionStateRestored:
                NSLog(@"已經(jīng)購買過商品");
                [SVProgressHUD dismiss];
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed:
                if (transaction.error.code == SKErrorPaymentCancelled) {
                    NSLog(@"用戶取消了購買請求");
                } else if (transaction.error.code == SKErrorCloudServiceNetworkConnectionFailed) {
                    NSLog(@"設(shè)備無法鏈接到網(wǎng)絡(luò)");
                }
                else {
                    NSLog(@"交易失斈嫌谩!L屯濉裹虫!");
                }
                [SVProgressHUD dismiss];
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
            case SKPaymentTransactionStateDeferred:
                NSLog(@"已在隊列中,等待外部操作");
                break;
            default:
                break;
        }
    }
}

2融击、第二步:后臺服務(wù)器端驗證App Store票據(jù)(為了安全筑公,需要自己服務(wù)器驗證App Store票據(jù))

注意:字符串:@"{"receipt-data" : "%@"}",服務(wù)器解碼時尊浪,帶上“receipt-data”字段匣屡,不然base64解碼失敗

蘋果返回的訂單中in_app是一個數(shù)組,要想獲取到對應(yīng)的某一個訂單信息拇涤,就如下這樣:
特別說明:如果你需要當(dāng)前票據(jù)的唯一號捣作,取in_app中最后一個票據(jù)的transaction_id就行,即transaction.transactionIdentifier工育,這個transaction_id和蘋果返回的訂單中的transaction_id比較虾宇,獲取對應(yīng)的訂單,改變數(shù)據(jù)庫用戶的交易記錄如绸。

#pragma mark -- -- 后臺服務(wù)器端驗證App Store票據(jù)  -- --
/** 注意:這里之后可以不用自己去驗證嘱朽,直接調(diào)用自己服務(wù)器接口旭贬,讓后臺去APP Store 驗證*/
/** 將App Store返回的交易憑據(jù)發(fā)送到后臺服務(wù)器,由后臺服務(wù)器驗證搪泳,code==200稀轨,提示成功 */
// (14.): 交易結(jié)束,當(dāng)交易結(jié)束后還要后臺服務(wù)器端去App Store上驗證支付信息是否都正確岸军,只有所有都正確后奋刽,我們就可以給用戶發(fā)放我們的虛擬產(chǎn)品了。
- (void)getApplePayDataToServerRequsetWith:(SKPaymentTransaction *)transaction {
    // 唯一transaction_id
    NSString *transaction_id = transaction.transactionIdentifier;

    NSString *str = [[NSString alloc] initWithData:transaction.transactionReceipt encoding:NSUTF8StringEncoding];
    NSLog(@"------ 完成交易調(diào)用的方法getApplePayDataToServerRequsetWith 1----------");
    
    // 獲取設(shè)備端app的交易收據(jù)數(shù)據(jù)艰赞,使用NSBundle的方法定位app的收據(jù)佣谐,并用Base64編碼。將此 Base64 編碼數(shù)據(jù)發(fā)送到您的服務(wù)器方妖。
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
    
    
    
    if (!receiptData) {
        NSLog(@"沒有交易收據(jù), NO receipt");
    } else {
        // 獲取編碼格式的收據(jù)
        NSString *encodedReceipt = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
        
        // 注意:字符串:@"{\"receipt-data\" : \"%@\"}"狭魂,服務(wù)器解碼時,帶上“receipt-data”字段党觅,不然base64解碼失敗
        // NSString *sendString = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodedReceipt];
        
        // server解析json字符串和端上一樣雌澄,不帶轉(zhuǎn)義字符“\”根本解析不出來
        // 先將憑據(jù)轉(zhuǎn)成字典,在將字典轉(zhuǎn)成json字符串(添加了receipt-data)
        NSDictionary *sendDic = @{@"receipt-data" : encodedReceipt};
        NSData *data = [NSJSONSerialization dataWithJSONObject:sendDic options:NSJSONWritingPrettyPrinted error:nil];
        NSString *sendString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        
        [self getApplePayDataToServerRequestString:sendString withTransaction:transaction];
        
    }
    
    
}

//(15.):發(fā)送到服務(wù)器杯瞻,由服務(wù)器判斷交易收據(jù)(生成預(yù)訂單镐牺,成功后在發(fā)送到服務(wù)器校驗App Store憑據(jù))
- (void)getApplePayDataToServerRequestString:(NSString *)receiptString withTransaction:(SKPaymentTransaction *)transaction {
    
    if(user_uuid == nil)
    {
        LoginViewCTRL *loginViewCTRL = [[LoginViewCTRL alloc] init];
        loginViewCTRL.loginVieCTRLBlock = ^{
        };
        [self.navigationController pushViewController:loginViewCTRL animated:YES];
        return;
    }
    
    NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
    NSString *app_Version = [infoDictionary objectForKey:@"CFBundleShortVersionString"];
    
    NSString *pay_member_id = @"";
    if ([self.BuyID isEqualToString:@"1MonthMember"]) {
        pay_member_id = @"21";
    }else if ([self.BuyID isEqualToString:@"6MontherMember"])
    {
        pay_member_id = @"23";
    }else if ([self.BuyID isEqualToString:@"12MonthMember"])
    {
        pay_member_id = @"24";
    }else if ([self.BuyID isEqualToString:@"buyMember03"])
    {
        pay_member_id = @"45";
    }
    
    NSDictionary *dic = @{
        @"user_uuid":wy_user_uuid?:@"",
        @"app_version":app_Version,
        @"pay_method":@"3",
        @"pay_source":@"6",
        @"pay_for":self.normalMemberBut.selected?@"1":@"11",
        @"client_type":@"2",
        @"pay_member_id":[NSString stringWithFormat:@"%@", pay_member_id]
    };
    NSArray *keyArray = [dic allKeys]; // 將dic中的全部key取出,并放到數(shù)組
    
    // 根據(jù)ASCII碼,將參數(shù)key從小到大排序(升序)
    NSStringCompareOptions comparisonOptions =NSCaseInsensitiveSearch|NSNumericSearch|NSWidthInsensitiveSearch|NSForcedOrderingSearch;
    NSComparator sort = ^(NSString *obj1, NSString *obj2) {
        NSRange range = NSMakeRange(0, obj1.length);
        return [obj1 compare:obj2 options:comparisonOptions range:range];
    };
    NSArray *resultArr = [keyArray sortedArrayUsingComparator:sort];
//    NSLog(@"字符串?dāng)?shù)組排序結(jié)果%@",resultArr);
    
    NSMutableArray *paramValueArr = [NSMutableArray arrayWithCapacity:resultArr.count];
    for (NSString *str in resultArr) {
        // 將key對應(yīng)的value魁莉,存到數(shù)組睬涧,用“7500KM”組成字符串
        NSString *tokenStr = [dic objectForKey:[NSString stringWithFormat:@"%@", str]];
        if (tokenStr.length > 0) {
            [paramValueArr addObject:tokenStr];
        }
    }
    
//    NSLog(@"字符串?dāng)?shù)組Value排序結(jié)果%@",paramValueArr);
    NSString *token = [paramValueArr componentsJoinedByString:@"7500KM"];
    token = [JXUtilTool md5HexDigest:token];
    
    NSDictionary *params = @{
        @"user_uuid":wy_user_uuid?:@"",
        @"app_version":app_Version,
        @"pay_method":@"3", // 支付方式 1:阿里 2:微信 3:蘋果
        @"pay_source":@"6",// 1.二維碼支付 2.H5支付 3.支付寶手機網(wǎng)站支付 4.支付寶電腦網(wǎng)站支付 5.微信JSAPI 6.APP
        @"pay_for":self.normalMemberBut.selected?@"1":@"11",// 1: 會員 2:金幣 3:禮品卡 4:商城 10其它(自定義生成的訂單)11閱讀會員
        @"client_type":@"2", // 1.安卓 2ios 3.pc
        @"pay_member_id":[NSString stringWithFormat:@"%@", pay_member_id], // 會員類型:1月、6月沛厨、12月宙地、一年
        @"token":[NSString stringWithFormat:@"%@", token]
    };
    
    __weak typeof(self)weakSelf = self;
    [[AFShareManager sharedManager] POST:[AFShareManager LFT_UrlString:@"/pay/member"] parameters:params progress:^(NSProgress * _Nonnull uploadProgress) {
            
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            NSLog(@"%@", task.currentRequest.URL);
            NSInteger reCode = [[responseObject objectForKey:@"reCode"] integerValue];
            if (reCode == 200) {
//                responseObject = [Tool Handle7500kmresponseObject:responseObject]; // 解碼
                NSDictionary *dataDic = [NSDictionary dictionary];
                dataDic = [responseObject objectForKey:@"data"];
                
                [self getApplePayDataToServerRequestString:receiptString withBookingOrderDictionary:dataDic withTransaction:transaction];
            } else {
                NSString *message = [responseObject objectForKey:@"reMessage"];
                [KAlertViewFactory showToastWithMessage:message];
                [SVProgressHUD dismiss];
            }
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            NSLog(@"%@", task.currentRequest.URL);
            NSLog(@"error ---->>>> %@", error);
            [SVProgressHUD dismiss];
        }];
    
}

// (16.)發(fā)送到后臺服務(wù)器,校驗App Store憑據(jù)
- (void)getApplePayDataToServerRequestString:(NSString *)receiptString withBookingOrderDictionary:(NSDictionary *)bookingOrderDic withTransaction:(SKPaymentTransaction *)transaction {
    
    NSDictionary *params = @{
        @"base64Receipt":receiptString,
        @"out_trade_no":[NSString stringWithFormat:@"%@", bookingOrderDic[@"out_trade_no"]],
        @"price":[NSString stringWithFormat:@"%@", bookingOrderDic[@"price"]],
        @"user_uuid":[NSString stringWithFormat:@"%@", bookingOrderDic[@"user_uuid"]]
    };
    
    [[AFShareManager sharedManager] POST:[AFShareManager LFT_UrlString:@"/member/verifyReceipt"] parameters:params progress:^(NSProgress * _Nonnull uploadProgress) {
            
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            NSLog(@"%@", task.currentRequest.URL);
            NSInteger reCode = [[responseObject objectForKey:@"reCode"] integerValue];
            if (reCode == 200) { // 驗證完成逆皮,改變展示的信息
                [self GainPersonInfo];
                [[WL_Tool getInstance] GainUserInfo:self];
                UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:@"購買成功" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles: nil];
                [alert show];
                
                
            } else {
                NSString *message = [NSString stringWithFormat:@"%@",[responseObject valueForKey:@"reMessage"]];
                // [KAlertViewFactory showToastWithMessage:message];
                
                UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:message delegate:nil cancelButtonTitle:@"確定" otherButtonTitles: nil];
                [alert show];
            }
            
            [SVProgressHUD dismiss];
            [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            NSLog(@"%@", task.currentRequest.URL);
            NSLog(@"error ---->>>> %@", error);
            [SVProgressHUD dismiss];
            [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
        }];
    
}

2、第二步:(第二種参袱,不需要服務(wù)器驗證电谣,自己在客戶端驗證,不安全抹蚀,容易被破解剿牺,導(dǎo)致賺錢少哦)

#pragma mark -- 方法(completedTransaction:)和方法(SubmitDataTrade_no: memberType: Transaction:)
// 14. 交易結(jié)束,當(dāng)交易結(jié)束后還要去App Store上驗證支付信息是否都正確环壤,只有所有都正確后晒来,我們就可以給用戶發(fā)放我們的虛擬產(chǎn)品了。
- (void)completedTransaction:(SKPaymentTransaction *)transaction {
    
    NSString *str = [[NSString alloc] initWithData:transaction.transactionReceipt encoding:NSUTF8StringEncoding];
    
    NSString *environment = [self environmentForReceipt:str];
    NSLog(@"------ 完成交易調(diào)用的方法completedTransaction 1----------%@", environment);
    
    
    // 驗證憑據(jù)郑现,獲取到蘋果返回的交易憑據(jù)
    // appStoreReceiptURL iOS 7.0 增加的湃崩,購買交易完成后荧降,會將憑據(jù)存放在該地址
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    // 從沙盒中獲取到購買憑據(jù)
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
    /**
     20     BASE64 常用的編碼方案,通常用于數(shù)據(jù)傳輸攒读,以及加密算法的基礎(chǔ)算法朵诫,傳輸過程中能夠保證數(shù)據(jù)傳輸?shù)姆€(wěn)定性
     21     BASE64 是可以解密的
     22     */
    NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    
    NSString *sendString = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
    NSLog(@"______%@", sendString);
    
    /** 注意:這里之后可以不用自己去驗證,直接調(diào)用自己服務(wù)器接口薄扁,讓后臺去APP Store 驗證*/
    
    NSURL *storeUrl = nil;
    if ([environment isEqualToString:@"environment=Sandbox"]) {
        storeUrl = [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"];
        
    } else {
        storeUrl = [NSURL URLWithString:@"https://buy.itunes.apple.com/verifyReceipt"];
    }
    
    // 這個二進制數(shù)據(jù)由服務(wù)器進行驗證:zl
    NSData *postData = [NSData dataWithBytes:[sendString UTF8String] length:[sendString length]];
    NSLog(@"++++++%@", postData);
    NSMutableURLRequest *connectionRequest = [NSMutableURLRequest requestWithURL:storeUrl];
    
    [connectionRequest setHTTPMethod:@"POST"];
    [connectionRequest setTimeoutInterval:50.0]; // 120.0 ----50.0zl
    [connectionRequest setCachePolicy:NSURLRequestUseProtocolCachePolicy];
    [connectionRequest setHTTPBody:postData];
    
    // 開始請求
    NSError *error = nil;
    NSData *responseData = [NSURLConnection sendSynchronousRequest:connectionRequest returningResponse:nil error:&error];
    
    if (error) {
        NSLog(@"驗證購買過程中發(fā)生的錯誤剪返,錯誤信息:%@", error.localizedDescription);
        return;
    }
    NSDictionary *dic=[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:nil];
    NSLog(@"請求成功后的數(shù)據(jù):%@",dic);
    //這里可以等待上面請求的數(shù)據(jù)完成后并且state = 0 驗證憑據(jù)成功來判斷后進入自己服務(wù)器邏輯的判斷,也可以直接進行服務(wù)器邏輯的判斷,驗證憑據(jù)也就是一個安全的問題。樓主這里沒有用state = 0 來判斷邓梅。
    //  [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    
    NSString *product = transaction.payment.productIdentifier;
    
    NSLog(@"transaction.payment.productIdentifier++++%@",product);
    
    if ([product length] > 0)
    {
        NSArray *tt = [product componentsSeparatedByString:@"."];
        
        NSString *bookid = [tt lastObject];
        
        if([bookid length] > 0)
        {
            
            NSLog(@"打印bookid%@",bookid);
            //這里可以做操作把用戶對應(yīng)的虛擬物品通過自己服務(wù)器進行下發(fā)操作,或者在這里通過判斷得到用戶將會得到多少虛擬物品,在后面([self getApplePayDataToServerRequsetWith:transaction];的地方)上傳上面自己的服務(wù)器脱盲。
            NSDictionary *receipt = [dic valueForKey:@"receipt"];
            NSArray *in_app = [receipt valueForKey:@"in_app"];
            
            for (int i = 0; i<in_app.count; i++) {
                
                NSDictionary *tempDic = [in_app objectAtIndex:i];
                NSString *transaction_id = [tempDic valueForKey:@"transaction_id"];
                NSString *product_id = [tempDic valueForKey:@"product_id"];
                
                if ([product_id isEqualToString:@"wl_1MonthMember"]) {
                    [self SubmitDataTrade_no:transaction_id memberType:@"21" Transaction:transaction];
                }else if ([product_id isEqualToString:@"wl_6MontherMember"])
                {
                    [self SubmitDataTrade_no:transaction_id memberType:@"23" Transaction:transaction];
                }else if ([product_id isEqualToString:@"wl_12MonthMember"])
                {
                    [self SubmitDataTrade_no:transaction_id memberType:@"24" Transaction:transaction];
                }else if ([product_id isEqualToString:@"buyMember03"])
                {
                    [self SubmitDataTrade_no:transaction_id memberType:@"45" Transaction:transaction];
                }
                    
            }
            
        }
    }
    [SVProgressHUD dismiss];
    //此方法為將這一次操作上傳給我本地服務(wù)器,記得在上傳成功過后一定要記得銷毀本次操作。調(diào)用[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    //[self getApplePayDataToServerRequsetWith:transaction];
}

-(void)SubmitDataTrade_no:(NSString *)trade_no memberType:(NSString *)memberType Transaction:(SKPaymentTransaction *)transaction
{
    
    NSString *user_uuid = [[NSUserDefaults standardUserDefaults] objectForKey:@"user_uuid"];
    if(user_uuid == nil)
    {
        LoginViewCTRL *loginViewCTRL = [[LoginViewCTRL alloc] init];
        loginViewCTRL.loginVieCTRLBlock = ^{
        };
        [self.navigationController pushViewController:loginViewCTRL animated:YES];
        
    }else
    {
        [SVProgressHUD showWithStatus:@"正在加載"];
        NSString *url=[NSString stringWithFormat:@"%@%@",ServerOtherUrl,@"/pay/applepay.do"];
        NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
        NSString *app_Version = [infoDictionary objectForKey:@"CFBundleShortVersionString"];
        NSString *token = [NSString stringWithFormat:@"%@%@%@%@%@",user_uuid,trade_no,memberType,app_Version,token_check_bit];
        
        token = [JXUtilTool md5HexDigest:token];
        NSLog(@"user_uuid=%@&trade_no=%@&memberType=%@&app_version=%@&token=%@",user_uuid,trade_no,memberType,app_Version,token);
        NSDictionary *parameters=@{@"user_uuid":user_uuid,
                                   @"trade_no":trade_no,
                                   @"memberType":memberType,
                                   @"app_version":app_Version,
                                   @"token":token
                                   };
        //AFN管理者調(diào)用get請求方法
        [[AFShareManager sharedManager] GET:url parameters:parameters progress:^(NSProgress * _Nonnull downloadProgress) {
            
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            //請求成功返回數(shù)據(jù) 根據(jù)responseSerializer 返回不同的數(shù)據(jù)格式
            NSLog(@"%@",task.currentRequest.URL);
            NSInteger reCode = [[responseObject valueForKey:@"reCode"] integerValue];
            if (reCode == 200)
            {
                [self GainPersonInfo];
                [[WL_Tool getInstance] GainUserInfo:self];
                UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:@"購買成功" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles: nil];
                [alert show];
                
                
            }else
            {
                NSString *message = [NSString stringWithFormat:@"%@",[responseObject valueForKey:@"reMessage"]];
                UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:message delegate:nil cancelButtonTitle:@"確定" otherButtonTitles: nil];
                [alert show];
            }
            [SVProgressHUD dismiss];
            [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            //請求失敗
            NSLog(@"%@",task.currentRequest.URL);
            NSLog(@"error-->%@",error);
            [SVProgressHUD dismiss];
            [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
            
        }];
    }
    
}

3日缨、第三步:


// 結(jié)束后钱反,一定要銷毀
- (void)dealloc {
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

//  在第二步中調(diào)用(第二種,不安全的驗證票據(jù)方法中):
- (NSString *)environmentForReceipt:(NSString *)str {
    
    str = [str stringByReplacingOccurrencesOfString:@"\r\n" withString:@""];
    str = [str stringByReplacingOccurrencesOfString:@"\n" withString:@""];
    str = [str stringByReplacingOccurrencesOfString:@"\t" withString:@""];
    str = [str stringByReplacingOccurrencesOfString:@" " withString:@""];
    str = [str stringByReplacingOccurrencesOfString:@"\"" withString:@""];
        
    NSArray *arr = [str componentsSeparatedByString:@";"];
    
    // 存儲收據(jù)環(huán)境的變量
    NSString *environment = arr[2];
    return environment;
}

4殿遂、漏訂單處理:

#pragma mark -- Apple內(nèi)購:漏單情況
- (void)appleRequest {
    
    /** Apple內(nèi)購:漏單情況 */
    //讀取用戶狀態(tài)和配置信息到單例中
    if ([GWUserInfoContext sharedUserInfoContext].userInfo.token &&[GWUserInfoContext sharedUserInfoContext].userInfo.name.length !=0) {
//        GWUserInfoModel *model = [GWUserInfoContext sharedUserInfoContext].userInfo;
        NSString *user_id = [NSString stringWithFormat:@"%ld", [GWUserInfoContext sharedUserInfoContext].userInfo.userId];
        
        if (user_id != nil) {
            NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
            NSArray *receiptArray = [userDefaults objectForKey:[NSString stringWithFormat:@"%@Apple_ReceiptData_MemberArray", user_id]];
            if (receiptArray != nil) { // 本地有值
                if (receiptArray.count > 0) {
                    NSMutableArray *receipt_MutableArray = [NSMutableArray arrayWithCapacity:0];
                    receipt_MutableArray = [receiptArray mutableCopy];
                    
                    dispatch_async(dispatch_get_global_queue(0, 0), ^{
                        // 處理耗時操作的代碼塊...
                        for (NSDictionary *dic in receipt_MutableArray) {
                            [self getApplePayWithOrderDictionary:dic]; // 可以多次請求數(shù)據(jù)庫數(shù)據(jù)
                        }
                        //通知主線程刷新
                        dispatch_async(dispatch_get_main_queue(), ^{
                            //回調(diào)或者說是通知主線程刷新诈铛,(這里可以保存、刪除一下數(shù)據(jù))

                        });
                    });
                }
            }
            
            [userDefaults synchronize];
        }
    }
}

// (11.)發(fā)送到后臺服務(wù)器墨礁,校驗App Store憑據(jù)
- (void)getApplePayWithOrderDictionary:(NSDictionary *)orderDic {
    
    
//    [GWUserInfoContext sharedUserInfoContext].userInfo = [GWUtilities GetNSUserDefaults];
    NSString *user_id = [NSString stringWithFormat:@"%ld", [GWUserInfoContext sharedUserInfoContext].userInfo.userId];
    
    NSString *payloadStr = [NSString stringWithFormat:@"%@", orderDic[@"receipt-data"]];
    
    NSDictionary *params = @{
        @"payload":[NSString stringWithFormat:@"%@", payloadStr],
        @"transactionId":[NSString stringWithFormat:@"%@", orderDic[@"transaction_id"]],
        @"oid":[NSString stringWithFormat:@"%@", orderDic[@"order_id"]]
    };
    [BRNetworkHepler postWithUrl:@"/golf/vip/iosPay" params:params headers:nil success:^(GWHttpBaseResponseModel *responseObject, NSString *message) {
        
        NSInteger code = responseObject.code;
        if (code == 200) {
            
            NSDictionary *dataDic = responseObject.data;
            NSString *order_id = [NSString stringWithFormat:@"%@", dataDic[@"oid"]];
            
            // 票據(jù)驗證成功:
            NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
            NSArray *receiptArray = [userDefaults objectForKey:[NSString stringWithFormat:@"%@Apple_ReceiptData_MemberArray", user_id]];
            if (receiptArray != nil) { // 本地含有儲存 票據(jù)
                
                NSMutableArray *receipt_MutableArray = [NSMutableArray arrayWithCapacity:0];
                receipt_MutableArray = [receiptArray mutableCopy];
                for (NSDictionary *dic in receipt_MutableArray) {
                    if ([[NSString stringWithFormat:@"%@", dic[@"order_id"]] isEqualToString:order_id]) {
                        [receipt_MutableArray removeObject:dic];
                    }
                }
                
                if (receipt_MutableArray.count > 0) {
                    NSArray *Apple_ReceiptData_MemberArray = [receipt_MutableArray copy];
                    [userDefaults setObject:Apple_ReceiptData_MemberArray forKey:[NSString stringWithFormat:@"%@Apple_ReceiptData_MemberArray", user_id]];
                } else {
                    [userDefaults removeObjectForKey:[NSString stringWithFormat:@"%@Apple_ReceiptData_MemberArray", user_id]];
                }
        
            } else {
                
            }
            [userDefaults synchronize];
            
        } else {
            NSLog(@"message ===>>> %@", responseObject.msg);
        }
        
    } failure:^(NSError *error, NSString *message) {
        NSLog(@"error ====>>>> %@", error);
        NSLog(@"message ===>>> %@", message);
    }];
}
一些相關(guān)網(wǎng)址:
1幢竹、https://developer.apple.com/documentation/storekit/original_api_for_in-app_purchase/persisting_a_purchase?language=objc
2、https://developer.apple.com/documentation/storekit/original_api_for_in-app_purchase/setting_up_the_transaction_observer_for_the_payment_queue?language=objc
3恩静、iOS 內(nèi)購(In-App Purchase)總結(jié)

https://developer.apple.com/documentation/storekit/original_api_for_in-app_purchase/validating_receipts_with_the_app_store?language=objc

4焕毫、https://www.gowhich.com/blog/1010
5、https://developer.apple.com/documentation/storekit?language=objc&changes=latest_minor
6驶乾、http://www.reibang.com/p/f12b4b3b96f8
7邑飒、內(nèi)購漏單處理:http://www.reibang.com/p/bf7e42a3c73b
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市级乐,隨后出現(xiàn)的幾起案子疙咸,更是在濱河造成了極大的恐慌,老刑警劉巖风科,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撒轮,死亡現(xiàn)場離奇詭異,居然都是意外死亡贼穆,警方通過查閱死者的電腦和手機题山,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來故痊,“玉大人顶瞳,你說我怎么就攤上這事。” “怎么了慨菱?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵焰络,是天一觀的道長。 經(jīng)常有香客問我抡柿,道長舔琅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任洲劣,我火速辦了婚禮备蚓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘囱稽。我一直安慰自己郊尝,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布战惊。 她就那樣靜靜地躺著流昏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吞获。 梳的紋絲不亂的頭發(fā)上况凉,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天,我揣著相機與錄音各拷,去河邊找鬼刁绒。 笑死,一個胖子當(dāng)著我的面吹牛烤黍,可吹牛的內(nèi)容都是我干的知市。 我是一名探鬼主播,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼速蕊,長吁一口氣:“原來是場噩夢啊……” “哼嫂丙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起规哲,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤跟啤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后唉锌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腥光,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年糊秆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片议双。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡痘番,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情汞舱,我是刑警寧澤伍纫,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站昂芜,受9級特大地震影響莹规,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜泌神,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一良漱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧欢际,春花似錦母市、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至浑槽,卻和暖如春蒋失,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背桐玻。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工篙挽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人畸冲。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓嫉髓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親邑闲。 傳聞我的和親對象是個殘疾皇子算行,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,947評論 2 355

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