iOS內(nèi)購遇到問題記錄(未完待續(xù))

網(wǎng)上有很多內(nèi)購教程,推薦幾篇比較寫的比較好的
文章一: iOS內(nèi)購(IAP,In App Purchases-在APP內(nèi)部支付),設(shè)置及使用
文章二: iOS開發(fā)內(nèi)購全套圖文教程,這篇文章比較老,但是下面的評論可以看看,很有價值
文章三: iOS 內(nèi)購2017.4

1、內(nèi)購集成成功,但是請求到的商品數(shù)是0
// 獲取商品的查詢結(jié)果
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response

打印商品的數(shù)量response.products.count結(jié)果一直是零,打印response.invalidProductIdentifiers發(fā)現(xiàn)商品是無效狀態(tài)
解決方法:

可能出現(xiàn)的商品列表為0的情況:

  • 1.銀行信息未填寫完整。
  • 2.Bundle ID 不統(tǒng)一, bundleID要與iTunes Connect上你App的相同,不然是請求不到產(chǎn)品信息的
2、購買消耗性產(chǎn)品后, 再次購買會提示您已購買此APP內(nèi)購項目,此項目將免費恢復(fù)
再次購買曾購買過的商品

解決方法:

監(jiān)聽購買結(jié)果,當(dāng)失敗和成功時代碼中要調(diào)用如下代碼:
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
該方法通知蘋果支付隊列該交易已完成,不然就會已發(fā)起相同 ID 的商品購買就會有此項目將免費恢復(fù)的提示。

3、每次重新進(jìn)入選購商品頁面,都會提示之前購買商品后的成功或者失敗提示即再次運行-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions這個方法

解決方法:

在購買成功或者失敗后,沒有調(diào)用[[SKPaymentQueue defaultQueue] finishTransaction:transaction];告訴蘋果支付隊列,改交易已經(jīng)完成,
在購買成功或者失敗后,加上這句代碼

4、注意事項

在沙盒環(huán)境下真機(jī)測試內(nèi)購時,請去app store中注銷你的apple ID,不然發(fā)起支付購買請求后會直接case:SKPaymentTransactionStateFailed。使用沙盒測試員的賬號時不需要真正花錢的。

5、校驗票據(jù)

票據(jù)的校驗是保證內(nèi)購安全完成的非常關(guān)鍵的一步,一般有三種方式:
1、服務(wù)器驗證,獲取票據(jù)信息后上傳至信任的服務(wù)器,由服務(wù)器完成與App Store的驗證(提倡使用此方法,比較安全)
2、本地票據(jù)校驗
3、本地App Store請求驗證

a)、本地票據(jù)校驗

票據(jù)驗證部分部分可參考此文章

本地票據(jù)校驗的一般步驟
要驗證收據(jù),請按順序執(zhí)行以下測試:
1.找到收據(jù)。
如果沒有收據(jù),則驗證失敗。
2.驗證收據(jù)是否由Apple 正確簽署
如果收據(jù)不是由 Apple 簽署,則驗證失敗。
3.驗證收據(jù)中的Bundle Identifier(數(shù)據(jù)包標(biāo)識符)與在Info.plist文件中含有您要的CFBundleIdentifier值的硬編碼常量相匹配。
如果兩者不匹配,則驗證失敗。
4.驗證收據(jù)中的版本標(biāo)識符字符串與在Info.plist文件中含有您要的CFBundleShortVersionString值(macOS)或
CFBundleVersion 值(iOS)的硬編碼常量相匹配。
如果兩者不匹配,則驗證失敗。
5.按照“計算 GUID 的哈希(Hash)(第 8 頁)”所述計算GUID 的哈希(Hash)。
如果結(jié)果與收據(jù)中的哈希(Hash)不匹配,則驗證失敗。
如果通過所有測試,則驗證成功。

注意: Bundle Identifier(數(shù)據(jù)包標(biāo)識符)和版本標(biāo)識符字符串是UTF-8 字符串,而不僅僅是一系列字節(jié)。確保您相應(yīng)地編寫比較邏輯代碼。

如果您的 App 支持“批量購買計劃”,請檢查收據(jù)的有效日期。

b)、App Store驗證:
// 14.交易成功,與服務(wù)器比對
// 交易結(jié)束,當(dāng)交易結(jié)束后還要去appstore上驗證支付信息是否都正確,只有所有都正確后,我們就可以給用戶發(fā)放我們的虛擬物品了。
- (void)completeTransaction:(SKPaymentTransaction *)transaction
{

    NSString * str=[[NSString alloc]initWithData:transaction.transactionReceipt encoding:NSUTF8StringEncoding];
    NSString *environment = [self environmentForReceipt:str]; // 判斷當(dāng)前的環(huán)境(正式環(huán)境還是測試環(huán)境)
    NSLog(@"environment:%@",environment);
    
    // 驗證憑據(jù),獲取到蘋果返回的交易憑據(jù)
    // appStoreReceiptURL iOS7.0增加的,購買交易完成后,會將憑據(jù)存放在該地址,目前蘋果公司提倡的獲取購買憑證的方法
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    // 從沙盒中獲取到購買的票據(jù)信息
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
    /**
        BASE64 常用的編碼方案,通常用于數(shù)據(jù)傳輸,以及加密算法的基礎(chǔ)算法,傳輸過程中能夠保證數(shù)據(jù)傳輸?shù)姆€(wěn)定性
        BASE64是可以編碼和解碼的
        base64位的產(chǎn)品驗證碼單,base64是服務(wù)端和蘋果進(jìn)行校驗所必須的,蘋果的文檔要求憑證經(jīng)過Base64加密
    */
    NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    NSString *sendString = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];

    NSURL *StoreURL=nil;
    // 沙盒狀態(tài)下使用:https://sandbox.itunes.apple.com/verifyReceipt來驗證
    // 生產(chǎn)環(huán)境下使用:https://buy.itunes.apple.com/verifyReceipt
    if ([environment isEqualToString:@"environment = Sandbox"]) {
        
        StoreURL= [[NSURL alloc] initWithString: @"https://sandbox.itunes.apple.com/verifyReceipt"];
    }
    else{
        
        StoreURL= [[NSURL alloc] initWithString: @"https://buy.itunes.apple.com/verifyReceipt"];
    }
    
    //這個二進(jìn)制數(shù)據(jù)由服務(wù)器進(jìn)行驗證;zl
    NSData *postData = [NSData dataWithBytes:[sendString UTF8String] length:[sendString length]];

    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];

    /*
     *  這里可以等待上面請求的數(shù)據(jù)完成后并且state = 0 驗證憑據(jù)成功來判斷后進(jìn)入自己服務(wù)器邏輯的判斷,也可以直接進(jìn)行服務(wù)器邏輯的判斷,驗證憑據(jù)也就是一個安全的問題。樓主這里沒有用state = 0 來判斷。
     *  完整結(jié)束此次在App Store的交易,沒有這句代碼的調(diào)用,下次購買會提示已經(jīng)購買該商品
     *  [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
     */
    NSLog(@"請求成功后的數(shù)據(jù):%@",dic);
    
    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ù)器進(jìn)行下發(fā)操作,或者在這里通過判斷得到用戶將會得到多少虛擬物品,在后面([self getApplePayDataToServerRequsetWith:transaction];的地方)上傳上面自己的服務(wù)器。
        }
    }
    //此方法為將這一次操作上傳給我本地服務(wù)器,記得在上傳成功過后一定要記得銷毀本次操作。調(diào)用[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
//    [self getApplePayDataToServerRequsetWith:transaction];

    
}

-(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)境的變量
    if (arr.count > 2) {
        NSString *environment = arr[2];
        return environment;
    }
    
    return nil;
  
}

注意:

獲取到票據(jù)以后我們通過App Store來驗證票據(jù)是否真實
沙盒狀態(tài)下使用:https://sandbox.itunes.apple.com/verifyReceipt來驗證
生產(chǎn)環(huán)境下使用:https://buy.itunes.apple.com/verifyReceipt
附常見的驗證狀態(tài)代碼:

狀態(tài)碼

如果此時未獲取到票據(jù)的信息,使用SKReceiptRefreshRequest來刷新票據(jù)結(jié)果。

SKReceiptRefreshRequest *refreshReceiptRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:@{}];
refreshReceiptRequest.delegate = self;
[refreshReceiptRequest start];
6、漏單問題
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 最近有個項目客戶總是反應(yīng)掉單,于是乎就看了看內(nèi)購相關(guān)的東西,發(fā)現(xiàn)坑還真是不少,這里做個總結(jié)。 IAP,即in-Ap...
    糖炒0栗子閱讀 18,479評論 0 22
  • 自己開發(fā)的視頻直播項目,牽涉到充值金幣,用到了蘋果公司的內(nèi)購,趴坑了兩天,這里總結(jié)下實現(xiàn)蘋果內(nèi)購。 一. 創(chuàng)建測試...
    Leo丶Dicaprio閱讀 3,502評論 8 7
  • iOS應(yīng)用如果涉及到支付功能,分為兩類:第三方支付和蘋果內(nèi)購。那么什么情況下選擇使用第三方支付,又在什么情況下選擇...
    ZfRee閱讀 39,218評論 36 66
  • 每個優(yōu)秀的人,并不是與生俱來就帶著光環(huán),也不一定是比別人幸運。他們只是在任何一件小事上,都對自己有所要求,不因舒適...
    圓圓的兔兔閱讀 146評論 0 0
  • 慵懶是罪,卻總被無罪釋放。 每天醒來,總覺得后背像是與床縫在了一起,感覺腰肢失去了脊梁骨,撐不起疲軟的身軀。一個翻...
    景漓脩閱讀 317評論 0 0

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