iOS 蘋果內(nèi)購的血與淚(坑爹的蘋果)

iOS蘋果內(nèi)購

這邊文章對(duì)于注冊流程和注意事項(xiàng)說的非常的詳細(xì)了但是里面還是有很多的坑,我自己封了一個(gè)類用于針對(duì)自己項(xiàng)目的使用地址如下

個(gè)人demo里面有個(gè)

屏幕快照 2019-03-25 上午10.28.18.png

里面需要注意的幾個(gè)點(diǎn):

#pragma mark - SKPaymentTransactionObserver // 監(jiān)聽購買結(jié)果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{
    [CXLoadingHud dismissHud];
    for (SKPaymentTransaction *tran in transactions) {
        //NSLog(@"%ld====",(long)tran.transactionState);
        switch (tran.transactionState) {
            case SKPaymentTransactionStatePurchased: //交易完成
                //訂閱特殊處理
                if(tran.originalTransaction){
                    //如果是自動(dòng)續(xù)費(fèi)的訂單originalTransaction會(huì)有內(nèi)容
                    NSLog(@"自動(dòng)續(xù)費(fèi)的訂單,originalTransaction = %@",tran.originalTransaction.transactionIdentifier);
                    //tran.originalTransaction.transactionIdentifier
                    //SKPaymentTransaction
                    [self completeTransaction:tran isAutomatically:YES];
                    //[self verifyPurchaseWithPaymentTransaction:tran isTestServer:NO];
                }else{
                    //普通購買,以及 第一次購買 自動(dòng)訂閱 第一次自動(dòng)訂閱一定會(huì)走這個(gè)方法,如果訂購成功后一定會(huì)走上面的判斷
                   // NSLog(@"%@-------",tran.transactionIdentifier);
                    [self completeTransaction:tran isAutomatically:NO];
                }
                
                break;
            case SKPaymentTransactionStatePurchasing://商品添加進(jìn)列表
#if DEBUG
                
                //NSLog(@"%ld====",tran.error.code);
                //NSLog(@"%@====",[[NSString alloc]initWithData:tran.payment.requestData encoding:NSUTF8StringEncoding]);
                //[TDGAVirtualCurrency onChargeRequst:@"" iapId:@"" currencyAmount:0 currencyType:@"" virtualCurrencyAmount:0 paymentType:@""];
#endif
                break;
            case SKPaymentTransactionStateRestored://購買過
#if DEBUG
                NSLog(@"已經(jīng)購買過商品");
#endif
                // 消耗型不支持恢復(fù)購買
                //[[SKPaymentQueue defaultQueue] finishTransaction:tran];
                break;
            case SKPaymentTransactionStateFailed://交易失敗
  如果tran.error 打印報(bào)錯(cuò)的話,沙盒測試的時(shí)候總是報(bào)無法連接itunes ,再確保你自己的前期準(zhǔn)備工作是ok的話,大概率都是蘋果自己的沙盒測試服務(wù)器自己gg了,經(jīng)歷過一次,坑爹的玩意,咋都找不出來,最后是蘋果自己的問題
                  NSLog(@"%@====",tran.error);
                //SKErrorUnknown
                [self failedTransaction:tran];
                
                break;
            default:
                break;
        }
    }
}

// 交易結(jié)束
- (void)completeTransaction:(SKPaymentTransaction *)transaction isAutomatically:(BOOL)isAutomatically{
    // Your application should implement these two methods.
    //    票據(jù)的校驗(yàn)是保證內(nèi)購安全完成的非常關(guān)鍵的一步,一般有三種方式:
    //    1、服務(wù)器驗(yàn)證,獲取票據(jù)信息后上傳至信任的服務(wù)器,由服務(wù)器完成與App Store的驗(yàn)證(提倡使用此方法,比較安全)我選擇的這種,但是會(huì)丟單,不過我暫時(shí)沒有處理,準(zhǔn)備用一個(gè)plist文件存儲(chǔ)下在app再次啟動(dòng)的時(shí)候請求下后臺(tái),不過這個(gè)只是減少了丟單率而且,蘋果的支付做得很垃圾
    //    2、本地票據(jù)校驗(yàn)
    //    3、本地App Store請求驗(yàn)證
    
    //    NSString * productIdentifier = transaction.payment.productIdentifier;
    //    NSString * receipt = [transaction.transactionReceipt base64EncodedString];
    //    if ([productIdentifier length] > 0) {
    //
    //    }
    NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL];
    NSData *receipt = [NSData dataWithContentsOfURL:recepitURL];
    // 向自己的服務(wù)器驗(yàn)證購買憑證
    //NSError *error;
    //轉(zhuǎn)化為base64字符串
    NSString *receiptString=[receipt base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
//網(wǎng)上轉(zhuǎn)換成base64后后臺(tái)一直解析不了需要如下處理除去receiptdata中的特殊字符
    NSString *receipt1=[receiptString stringByReplacingOccurrencesOfString:@"\n" withString:@""];
    NSString *receipt2=[receipt1 stringByReplacingOccurrencesOfString:@"\r" withString:@""];
    NSString *receipt3=[receipt2 stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"];
    //最終將這個(gè)receipt3的發(fā)送給服務(wù)器去驗(yàn)證就沒問題啦!
    //自動(dòng)訂閱(自動(dòng)續(xù)費(fèi)月卡)需要多加一個(gè)參數(shù)
    
    NSString * product_id = transaction.payment.productIdentifier;
    NSString * transaction_id = transaction.transactionIdentifier;
    
    
    NSMutableDictionary * requestContents = [[NSMutableDictionary alloc]init];
#希望各位處理的時(shí)候能將  transaction_id 和 receipt都傳給后臺(tái)因?yàn)樘O果的receipt解析后里面有很多數(shù)據(jù),transaction_id能夠幫助后臺(tái)迅速定位最新的訂單,因?yàn)闇y試的時(shí)候發(fā)現(xiàn)receipt里面有個(gè)數(shù)組,一般是最后一條數(shù)據(jù)是最新的,但是坑爹的蘋果總是變,有時(shí)候是數(shù)組的第一條,所以transaction_id至關(guān)重要,而且在自動(dòng)訂閱和消耗性兩類中,自動(dòng)訂閱的 transaction_id2和transaction_id在第一次請求的時(shí)候是一致的,后面的話,要區(qū)分只能transaction_id所以這個(gè)傳給后臺(tái)至關(guān)重要
transaction_id2 = transaction.originalTransaction.transactionIdentifier;
        NSString * transaction_id = transaction.transactionIdentifier;
        [requestContents addEntriesFromDictionary:@{@"receipt": receipt3,@"password":secretKey,@"product_id":product_id,@"transaction_id":transaction_id,@"originalTransaction":transaction_id2}];
    }else{
        if (self.parmas.allKeys.count > 0) {
            [requestContents addEntriesFromDictionary:@{@"receipt": receipt3,@"uid":self.parmas[@"uid"],@"amount":self.parmas[@"amount"],@"actorid":self.parmas[@"userRoleId"],@"server":self.parmas[@"serverId"],@"order_no":self.parmas[@"cpOrderNo"],@"password":secretKey,@"product_id":product_id,@"transaction_id":transaction_id}];
        }
    }
    
    NSString * parameters = [self parameters:requestContents];
    NSString * url = isAutomatically ? autoURL : consumptionURL;
    NSURL *storeURL = [NSURL URLWithString:url];
    NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
    [storeRequest setHTTPMethod:@"POST"];
    [storeRequest setHTTPBody:[parameters dataUsingEncoding:NSUTF8StringEncoding]];
    [storeRequest setTimeoutInterval:30];
    
    NSURLSession *session = [NSURLSession sharedSession];
    
    NSURLSessionDataTask * dataTask = [session dataTaskWithRequest:storeRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        //服務(wù)器返回的responseObject是gbk編碼的字符串,通過gbk編碼轉(zhuǎn)碼就行了,轉(zhuǎn)碼方法如下:
        NSString*gbkStr = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
        //轉(zhuǎn)碼之后再轉(zhuǎn)utf8解析
        NSDictionary * jsonDict = [NSJSONSerialization JSONObjectWithData:[gbkStr dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:nil];
      
        if (jsonDict.allKeys.count > 0) {
            if ([[jsonDict objectForKey:@"code"]intValue] == 0) {
                //[CXLoadingHud showHudWithText:@"購買成功" delay:2];
                NSDictionary * dataDict = jsonDict[@"data"];
                [[CXInformationCollect collectInfo]fb_mobile_purchase:dataDict[@"amount"] currency:@""];
                [[CXInformationCollect collectInfo]af_purchase:@{@"amount":dataDict[@"amount"]}];
            }else if ([[jsonDict objectForKey:@"code"]intValue] == 1){
                [CXLoadingHud showHudWithText:@"服務(wù)器驗(yàn)簽失敗" delay:2];
                
            }
        }
        
    }];
    
    [dataTask resume];

    //本地像蘋果app store驗(yàn)證,上面是像自己的服務(wù)器驗(yàn)證
    //[self verifyPurchaseWithPaymentTransaction:transaction isTestServer:NO];
    // 驗(yàn)證成功與否都注銷交易,否則會(huì)出現(xiàn)虛假憑證信息一直驗(yàn)證不通過,每次進(jìn)程序都得輸入蘋果賬號(hào)
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    //[self verifyPurchaseWithPaymentTransaction:transaction isTestServer:NO];
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,488評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,034評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,327評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,554評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,337評(píng)論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,883評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,975評(píng)論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,114評(píng)論 0 286
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,625評(píng)論 1 332
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,555評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,737評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,244評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,973評(píng)論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,615評(píng)論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,343評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,699評(píng)論 2 370

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

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,147評(píng)論 4 61
  • 此生只為詩詞度, 唯有清心吾成全; 不與世事洞明柬, 只愿你有盡賢言。 ~ 送給 中國古典文學(xué)院士, 著...
    秋葉的味道閱讀 417評(píng)論 6 6
  • 丹江一幼 孫亞婷 第一天我們迎來了張時(shí)慈老師的"音樂課程模式"張老師她用形象生動(dòng)的語言和精美的課件為我們帶...
    Syt_0de4閱讀 242評(píng)論 0 0
  • 一.獲取字符串中:a.所有的正整數(shù)b.所有的負(fù)整數(shù)c.所有的浮點(diǎn)數(shù)d.所有的非負(fù)浮點(diǎn)數(shù) 二、不定項(xiàng)選擇題 能夠完全...
    跟我念一遍閱讀 1,172評(píng)論 0 2
  • 【學(xué)號(hào)】2017101446 【姓名】張瑩 【性別】女 【城市】云南楚雄 【簡書號(hào)】zy08081422 【擅長】...
    17數(shù)446張瑩閱讀 418評(píng)論 0 0