[ios]應(yīng)用內(nèi)支付(內(nèi)購)的開發(fā)

本文給大家介紹下內(nèi)購,部分地方是摘自網(wǎng)絡(luò)。

創(chuàng)建測試App

首先你需要登錄App的ItunesConnection,你會看到如下界面

點(diǎn)擊新建App,會出現(xiàn)新建窗口;



  在之而立有幾個(gè)需要填寫的地方,名稱、平臺、語言簡體中文,套裝ID也就是Bundle Identifier,需要你在Certificates頁面 申請BundleID,SKU可以理解為用戶看到的唯一標(biāo)示,會體現(xiàn)在你的app的App Store的連接中。

二、添加內(nèi)購

App創(chuàng)建好之后,我們打開創(chuàng)建的App,在左上角選擇功能,會看到左側(cè)的App 內(nèi)購買項(xiàng)目。我們點(diǎn)擊右下角的加號,為App添加內(nèi)購項(xiàng)目。



之后我們會看到類型的選項(xiàng),如下圖


  • 消耗型項(xiàng)目 就像你玩游戲需要買金幣,買鉆石等,只要花錢就可以無限次的購買
  • 非消耗型項(xiàng)目 就像你在App Store購買App,買了一次之后就不用再買第二次,你擁有永久使用權(quán)。
    在我們的app中,是充值會員,所以選擇的是第一種,可以無限次購買



    這里有幾個(gè)選項(xiàng),需要填寫商品名稱,產(chǎn)品ID以及價(jià)格等級,簡單說明一下

  1. 商品名稱根據(jù)你的消費(fèi)道具的實(shí)際意義來說明,比如“100顆寶石”,“100金幣”等。
  2. 產(chǎn)品ID是比較重要的,由項(xiàng)目自定義,只要唯一即可,因?yàn)闇y試,我在這里隨便填寫的123,在實(shí)際應(yīng)用中,一定要認(rèn)真填寫。
  3. 價(jià)格等級的話“查看價(jià)格表”中有對應(yīng)的說明,可以對照著表中每個(gè)國家的貨幣價(jià)格與等級來選擇
    接下來是語言選擇,和上傳快照如下圖



審核備注,根據(jù)實(shí)際情況填寫,可以不填。而下面的屏幕快照,則是商品圖片,以像素為單位最低尺寸為321,390,尺寸需要如下圖,上傳即可。



到這里為止,我們的內(nèi)購項(xiàng)目則添加完成。接下來則是測試階段了

三、申請沙盒測試賬號(用來測試購買項(xiàng)目)

這個(gè)賬號,是利用蘋果的沙盒測試環(huán)境來模擬AppStore的購買流程
首先我們回到iTunes Connect中,在這里我們選擇用戶和職能



然后再上面的第三個(gè)選項(xiàng)沙盒技術(shù)測試員中點(diǎn)擊?,添加測試員。


  1. 在信息填寫頁面只簡單說兩句。
  2. 所有信息都可以隨意填寫,不用管是否真實(shí)。
  3. App Store地區(qū)選擇,一點(diǎn)要選對,它對應(yīng)的是你創(chuàng)建的App的地區(qū),你App是中國的話,在這里我們依然選擇中國。
  4. 此賬號只能用來測試,不要在正式的appstore上使用
  5. 填寫完畢,點(diǎn)擊保存后,我們則生成一個(gè)測試賬號,當(dāng)然這個(gè)賬號是可以隨時(shí)刪除和添加的。



    之后就是寫代碼了。。。

- (IBAction)buy50:(id)sender {
    [self buy:IAP0p20];
}

- (void)buy:(int)type {
    buyType = type;
    
    if ([SKPaymentQueue canMakePayments]) {
        [self requestProductData];
        NSLog(@"****************允許程序內(nèi)購********************");
    } else {
        NSLog(@"****************不允許程序內(nèi)購********************");

        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"您的手機(jī)沒有打開程序內(nèi)付費(fèi)購買" delegate:nil cancelButtonTitle:@"關(guān)閉" otherButtonTitles:nil, nil];
        [alertView show];
    }
}
- (void)requestProductData {
    NSLog(@"****************請求對應(yīng)的產(chǎn)品信息********************");
    
    NSArray *products = nil;
    
    switch (buyType) {
        case IAP0p20:
        {
            currentProductId = ProductID_IAP0p20;
            products = [[NSArray alloc] initWithObjects:ProductID_IAP0p20, nil];
        }
            break;
            
        default:
            break;
    }
    
    NSSet *productSet = [NSSet setWithArray:products];
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:productSet];
    request.delegate = self;
    [request start];
}

#pragma mark - SKProductsRequestDelegate

// 請求協(xié)議//收到的產(chǎn)品信息
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
    NSLog(@"****************收到產(chǎn)品反饋信息********************");

    NSArray *myProduct = response.products;
    
    if (myProduct.count == 0) {
        NSLog(@"****************沒有商品********************");
        return;
    }
    
    NSLog(@"productID:%@", response.invalidProductIdentifiers);
    NSLog(@"產(chǎn)品付費(fèi)數(shù)量:%lu", (unsigned long)[myProduct count]);
    
    SKProduct *p = nil;
    
    for (SKProduct *product in myProduct) {
        if ([product.productIdentifier isEqualToString:currentProductId]) {
            p = product;
        }
    }
    
    SKPayment *payment = [SKPayment paymentWithProduct:p];
    
    NSLog(@"****************發(fā)送購買請求********************");
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}


- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
    NSLog(@"****************請求失敗********************");

}

- (void)requestDidFinish:(SKRequest *)request {
    NSLog(@"****************反饋信息結(jié)束********************");
}


#pragma mark - 監(jiān)聽購買結(jié)果

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {
    for(SKPaymentTransaction *tran in transactions){
        switch (tran.transactionState) {
            case SKPaymentTransactionStatePurchased:{
                NSLog(@"交易完成");
                [self verifyPurchaseWithPaymentTransaction];
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                
            }
                break;
            case SKPaymentTransactionStatePurchasing:
                NSLog(@"商品添加進(jìn)列表");
                
                break;
            case SKPaymentTransactionStateRestored:{
                NSLog(@"已經(jīng)購買過商品");
                
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
            }
                break;
            case SKPaymentTransactionStateFailed:{
                NSLog(@"交易失敗");
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
            }
                break;
            default:
                break;
        }
    }
}

- (void)completeTransaction:(SKPaymentTransaction *)transaction {
    NSLog(@"交易結(jié)束");
    
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}

// 沙盒測試環(huán)境驗(yàn)證
#define SANDBOX @"https://sandbox.itunes.apple.com/verifyReceipt"

// 正式環(huán)境驗(yàn)證
#define AppStore @"https://buy.itunes.apple.com/verifyReceipt"

- (void)verifyPurchaseWithPaymentTransaction {
    // 從沙盒中獲取交易憑證并且拼接成請求體數(shù)據(jù)
    
    NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptUrl];
    
    NSString *receiptString=[receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];//轉(zhuǎn)化為base64字符串
    
    NSString *bodyString = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", receiptString];//拼接請求數(shù)據(jù)
    NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
    
    //創(chuàng)建請求到蘋果官方進(jìn)行購買驗(yàn)證
    NSURL *url=[NSURL URLWithString:SANDBOX];
    NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url];
    requestM.HTTPBody=bodyData;
    requestM.HTTPMethod=@"POST";
    //創(chuàng)建連接并發(fā)送同步請求
    NSError *error=nil;
    NSData *responseData=[NSURLConnection sendSynchronousRequest:requestM returningResponse:nil error:&error];
    if (error) {
        NSLog(@"驗(yàn)證購買過程中發(fā)生錯(cuò)誤,錯(cuò)誤信息:%@",error.localizedDescription);
        return;
    }
    NSDictionary *dic=[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:nil];
    NSLog(@"%@",dic);
    if([dic[@"status"] intValue]==0){
        NSLog(@"購買成功!");
        NSDictionary *dicReceipt= dic[@"receipt"];
        NSDictionary *dicInApp=[dicReceipt[@"in_app"] firstObject];
        NSString *productIdentifier= dicInApp[@"product_id"];//讀取產(chǎn)品標(biāo)識
        //如果是消耗品則記錄購買數(shù)量,非消耗品則記錄是否購買過
        NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
        if ([productIdentifier isEqualToString:currentProductId]) {
            int purchasedCount=[defaults integerForKey:productIdentifier];//已購買數(shù)量
            [[NSUserDefaults standardUserDefaults] setInteger:(purchasedCount+1) forKey:productIdentifier];
        }else{
            [defaults setBool:YES forKey:productIdentifier];
        }
        //在此處對購買記錄進(jìn)行存儲,可以存儲到開發(fā)商的服務(wù)器端
    }else{
        NSLog(@"購買失敗,未通過驗(yàn)證!");
    }
}

#pragma mark - 監(jiān)聽釋放

- (void)dealloc {
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}

在這里需要注意幾點(diǎn),

  1. 代碼中的_currentProId所填寫的是你的購買項(xiàng)目的的ID,這個(gè)和第二步創(chuàng)建的內(nèi)購的productID要一致;本例中是 123。
  2. 在監(jiān)聽購買結(jié)果后,一定要調(diào)用[[SKPaymentQueue defaultQueue] finishTransaction:tran];來允許你從支付隊(duì)列中移除交易。
  3. 沙盒環(huán)境測試appStore內(nèi)購流程的時(shí)候,請使用沒越獄的設(shè)備。
  4. 請務(wù)必使用真機(jī)來測試,一切以真機(jī)為準(zhǔn)。
  5. 項(xiàng)目的Bundle identifier需要與您申請AppID時(shí)填寫的bundleID一致,不然會無法請求到商品信息。
  6. 真機(jī)測試的時(shí)候,一定要退出原來的賬號,才能用沙盒測試賬號
  7. 二次驗(yàn)證,請注意區(qū)分宏, 測試用沙盒驗(yàn)證,App Store審核的時(shí)候也使用的是沙盒購買,所以驗(yàn)證購買憑證的時(shí)候需要判斷返回Status Code決定是否去沙盒進(jìn)行二次驗(yàn)證,為了線上用戶的使用,驗(yàn)證的順序肯定是先驗(yàn)證正式環(huán)境,此時(shí)若返回值為21007,就需要去沙盒二次驗(yàn)證,因?yàn)榇速徺I的是在沙盒進(jìn)行的。

文章參考:
[iOS]應(yīng)用內(nèi)支付(內(nèi)購)的個(gè)人開發(fā)過程及坑!

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

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

  • 一.總說內(nèi)購的內(nèi)容 協(xié)議、稅務(wù)和銀行業(yè)務(wù) 信息填寫 內(nèi)購商品的添加 添加沙盒測試賬號 內(nèi)購代碼的具體實(shí)現(xiàn) 內(nèi)購的注...
    默默_David閱讀 3,904評論 0 6
  • 一.總說內(nèi)購的內(nèi)容 協(xié)議、稅務(wù)和銀行業(yè)務(wù) 信息填寫 內(nèi)購商品的添加 添加沙盒測試賬號 內(nèi)購代碼的具體實(shí)現(xiàn) 內(nèi)購的注...
    九洲仙人閱讀 3,037評論 2 3
  • 參考鏈接iOS開發(fā)之內(nèi)購-AppStore iOS App提交指南(二)-協(xié)議、稅務(wù)和銀行業(yè)務(wù)iOS應(yīng)用內(nèi)付費(fèi)(I...
    sjzuo閱讀 3,196評論 4 5
  • 自己開發(fā)的視頻直播項(xiàng)目,牽涉到充值金幣,用到了蘋果公司的內(nèi)購,趴坑了兩天,這里總結(jié)下實(shí)現(xiàn)蘋果內(nèi)購。 一. 創(chuàng)建測試...
    Leo丶Dicaprio閱讀 3,504評論 8 7
  • 《浮世畫家》,原名《An artist of the floating world》,是英籍日裔小說家石黑一雄早年...
    夏槿11閱讀 1,652評論 3 1

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