1. 程式人生 > >[iOS]應用內支付(內購)的個人開發過程及坑!

[iOS]應用內支付(內購)的個人開發過程及坑!

本文會給大家詳細介紹iOS內購,這是本人16年5月底的開發過程,希望對看完此篇文章的人有所幫助。
本文基於XcodeVersion 7.3 (7D175)版本,手機是iPhone 6,9.3系統。
部分地方直接摘自網路,基本上是我的邏輯,省時省心省力。

一. 建立測試App

首先你需要登入 App的ItunesConnection,你會看到如下介面
這裡寫圖片描述

簡單的介紹一下這幾個選項
1.我的App主要用於管理自己的App應用,例如編輯資料,上架,下架等。
2.銷售和趨勢主要是來檢視App在各個平臺的下載量,收入等方面資料,裡面有曲線圖等圖文結合的方式給我們參考。
3.付款和財務報告顯示的是你的收入以及付款等相關資訊。
4.iAd主要是跟廣告有關,開發者可以登入到Workbench,通過iAd對應用的廣告進行控制。
5.使用者和職能用於生成相應賬號,例如蘋果沙河測試賬號。
6.協議,稅務和銀行業務則是你銀行相關賬戶的資訊設定。
在這裡我們選擇第一個選項,我的App, 然後點選左上角的加號,新建一個用來測試用的App。
這裡寫圖片描述

點新建 App,會出現新建視窗;
這裡寫圖片描述

在這裡有幾個需要填寫的地方,名稱自己取,平臺IOS,語言選擇了簡體中文,套裝ID也就是你的Bundle Identifier,需要你在Certificates頁面 申請BundleID,SKU可以理解為使用者看一看到的唯一標示,會體現在你的app的App Store的連結中。

二.新增內購

App建立好之後,我們開啟建立的App,在左上角選擇功能,會看到左側的App 內購買專案。我們點選右下角的加號,為App新增內購專案。
這裡寫圖片描述

之後我們會看到型別的選項,如下圖

這裡寫圖片描述

官方的註釋寫的很清楚了,只在這裡簡單的說下前兩種:
- 消耗型專案 就像你玩遊戲需要買金幣,買鑽石等,只要花錢就可以無限次的購買
- 非消耗型專案 就像你在App Store購買App,買了一次之後就不用再買第二次,你擁有永久使用權。
在我們的app中,是充值會員,所以選擇的是第一種,可以無限次購買。

這裡寫圖片描述

這裡有幾個選項,需要填寫商品名稱,產品ID以及價格等級,簡單說明一下
1. 商品名稱根據你的消費道具的實際意義來說明,比如“100顆寶石”,“100金幣”等。
2. 產品ID是比較重要的,由專案自定義,只要唯一即可,因為測試,我在這裡隨便填寫的123,在實際應用中,一定要認真填寫。
3. 價格等級的話“檢視價格表”中有對應的說明,可以對照著表中每個國家的貨幣價格與等級來選擇
接下來是語言選擇,和上傳快照如下圖

這裡寫圖片描述

點選新增語言,填寫名稱和描述,這裡我們依然選擇簡體中文,如下

這裡寫圖片描述

稽核備註,根據實際情況填寫,可以不填。而下面的螢幕快照,則是商品圖片,以畫素為單位,最低尺寸為321,390,尺寸需求如下圖,上傳即可。

這裡寫圖片描述

到這裡為止, 我們的內購專案則新增完成。接下來則是測試階段了。

三.申請沙盒測試賬號(用來測試購買專案)

這個賬號,是利用蘋果的沙盒測試環境來模擬AppStore的購買流程,你肯定不會想要用真實RMB去購買測試吧?
首先我們回到iTunes Connect中,在這裡我們選擇使用者和職能。

這裡寫圖片描述

然後在上面的第三個選項沙箱技術測試員中點選加號,新增測試員。

這裡寫圖片描述

在資訊填寫頁面只簡單說兩句。
所有資訊都可以隨意填寫,不用管是否真實。
App Store地區選擇,一定要選對,它對應的是你建立的App的地區, 你App是中國的話, 在這裡我們依然選擇中國。
此賬號只能用來測試,不要在正式的appstore上使用
填寫完畢,點選儲存後,我們則生成一個測試賬號,當然這個賬號是可以隨時刪除和新增的。

這裡寫圖片描述

之後終於到了寫程式碼的時候了,點開你的Xcode建立你的專案!
大部分程式碼都可以在.m檔案中實現。

#import "ViewController.h"
#import <StoreKit/StoreKit.h>
#import "SVProgressHUD.h"

@interface ViewController ()<SKPaymentTransactionObserver,SKProductsRequestDelegate>
@property (nonatomic,copy) NSString *currentProId;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    button.frame = CGRectMake(100, 100, 100, 100);
    button.backgroundColor = [UIColor greenColor];
    [button setTitle:@"6元" forState:UIControlStateNormal];
    [button addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchDown];
    [self.view addSubview:button];
}


- (void)btnClick:(UIButton *)button
{
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    _currentProId = @"123";
    if([SKPaymentQueue canMakePayments]){
        [self requestProductData:product];
    }else{
        NSLog(@"不允許程式內付費");
    }
}

//去蘋果伺服器請求商品
- (void)requestProductData:(NSString *)type{
    NSLog(@"-------------請求對應的產品資訊----------------");

    [SVProgressHUD showWithStatus:nil maskType:SVProgressHUDMaskTypeBlack];

    NSArray *product = [[NSArray alloc] initWithObjects:type,nil];

    NSSet *nsset = [NSSet setWithArray:product];
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
    request.delegate = self;
    [request start];

}

//收到產品返回資訊
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{

    NSLog(@"--------------收到產品反饋訊息---------------------");
    NSArray *product = response.products;
    if([product count] == 0){
        [SVProgressHUD dismiss];
        NSLog(@"--------------沒有商品------------------");
        return;
    }

    NSLog(@"productID:%@", response.invalidProductIdentifiers);
    NSLog(@"產品付費數量:%lu",(unsigned long)[product count]);

    SKProduct *p = nil;
    for (SKProduct *pro in product) {
        NSLog(@"%@", [pro description]);
        NSLog(@"%@", [pro localizedTitle]);
        NSLog(@"%@", [pro localizedDescription]);
        NSLog(@"%@", [pro price]);
        NSLog(@"%@", [pro productIdentifier]);

        if([pro.productIdentifier isEqualToString:_currentProId]){
            p = pro;
        }
    }

    SKPayment *payment = [SKPayment paymentWithProduct:p];

    NSLog(@"傳送購買請求");
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}

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

- (void)requestDidFinish:(SKRequest *)request{
    [SVProgressHUD dismiss];
    NSLog(@"------------反饋資訊結束-----------------");
}
//沙盒測試環境驗證
#define SANDBOX @"https://sandbox.itunes.apple.com/verifyReceipt"
//正式環境驗證
#define AppStore @"https://buy.itunes.apple.com/verifyReceipt"
/**
 *  驗證購買,避免越獄軟體模擬蘋果請求達到非法購買問題
 *
 */
-(void)verifyPurchaseWithPaymentTransaction{
    //從沙盒中獲取交易憑證並且拼接成請求體資料
    NSURL *receiptUrl=[[NSBundle mainBundle] appStoreReceiptURL];
    NSData *receiptData=[NSData dataWithContentsOfURL:receiptUrl];

    NSString *receiptString=[receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];//轉化為base64字串

    NSString *bodyString = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", receiptString];//拼接請求資料
    NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];


    //建立請求到蘋果官方進行購買驗證
    NSURL *url=[NSURL URLWithString:SANDBOX];
    NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url];
    requestM.HTTPBody=bodyData;
    requestM.HTTPMethod[email protected]"POST";
    //建立連線併發送同步請求
    NSError *error=nil;
    NSData *responseData=[NSURLConnection sendSynchronousRequest:requestM returningResponse:nil error:&error];
    if (error) {
        NSLog(@"驗證購買過程中發生錯誤,錯誤資訊:%@",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"];//讀取產品標識
        //如果是消耗品則記錄購買數量,非消耗品則記錄是否購買過
        NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
        if ([productIdentifier isEqualToString:@"123"]) {
            int purchasedCount=[defaults integerForKey:productIdentifier];//已購買數量
            [[NSUserDefaults standardUserDefaults] setInteger:(purchasedCount+1) forKey:productIdentifier];
        }else{
            [defaults setBool:YES forKey:productIdentifier];
        }
        //在此處對購買記錄進行儲存,可以儲存到開發商的伺服器端
    }else{
        NSLog(@"購買失敗,未通過驗證!");
    }
}
//監聽購買結果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{
    for(SKPaymentTransaction *tran in transaction){
        switch (tran.transactionState) {
            case SKPaymentTransactionStatePurchased:{
                NSLog(@"交易完成");
                // 傳送到蘋果伺服器驗證憑證
                [self verifyPurchaseWithPaymentTransaction];
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];

            }
                break;
            case SKPaymentTransactionStatePurchasing:
                NSLog(@"商品新增進列表");

                break;
            case SKPaymentTransactionStateRestored:{
                NSLog(@"已經購買過商品");

                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
            }
                break;
            case SKPaymentTransactionStateFailed:{
                NSLog(@"交易失敗");
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                [SVProgressHUD showErrorWithStatus:@"購買失敗"];
            }
                break;
            default:
                break;
        }
    }
}

//交易結束
- (void)completeTransaction:(SKPaymentTransaction *)transaction{
    NSLog(@"交易結束");

    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}


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

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

在這裡需要注意幾點,
1. 程式碼中的_currentProId所填寫的是你的購買專案的的ID,這個和第二步建立的內購的productID要一致;本例中是 123。
2. 在監聽購買結果後,一定要呼叫[[SKPaymentQueue defaultQueue] finishTransaction:tran];來允許你從支付佇列中移除交易。
3. 沙盒環境測試appStore內購流程的時候,請使用沒越獄的裝置。
4. 請務必使用真機來測試,一切以真機為準。
5. 專案的Bundle identifier需要與您申請AppID時填寫的bundleID一致,不然會無法請求到商品資訊。
6. 真機測試的時候,一定要退出原來的賬號,才能用沙盒測試賬號
7. 二次驗證,請注意區分巨集, 測試用沙盒驗證,App Store稽核的時候也使用的是沙盒購買,所以驗證購買憑證的時候需要判斷返回Status Code決定是否去沙盒進行二次驗證,為了線上使用者的使用,驗證的順序肯定是先驗證正式環境,此時若返回值為21007,就需要去沙盒二次驗證,因為此購買的是在沙盒進行的。

附:蘋果支付錯誤目錄

Status Code Description
21000 The App Store could not read the JSON object you provided.
21002 The data in the receipt-data property was malformed or missing.
21003 The receipt could not be authenticated.
21004 The shared secret you provided does not match the shared secret on file for your account.Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions.
21005 The receipt server is not currently available.
21006 This receipt is valid but the subscription has expired. When this status code is returned to your server, the receipt data is also decoded and returned as part of the response.Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions.
21007 This receipt is from the test environment, but it was sent to the production environment for verification. Send it to the test environment instead.
21008 This receipt is from the production environment, but it was sent to the test environment for verification. Send it to the production environment instead.

相關推薦

[iOS]應用支付()的個人開發過程

本文會給大家詳細介紹iOS內購,這是本人16年5月底的開發過程,希望對看完此篇文章的人有所幫助。 本文基於XcodeVersion 7.3 (7D175)版本,手機是iPhone 6,9.3系統。 部分地方直接摘自網路,基本上是我的邏輯,省時省心省力。 一

將 75000 行原生 iOS 應用程式移植到 Flutter 後,結果太驚訝

【CSDN編者按】很少有文章,介紹如何將大型應用,移植到Flutter。而本文的作者——一位來自

海康威視Web3.0控制元件個人開發經驗問題總結(轉)

最近在給公司平臺寫視訊監控的頁面需求,於是接觸到了海康威視的視訊控制元件,網上查閱一番資料後,發現有很多大佬們給出了簡易的海康視訊

移動開發實踐’總結

設計 頁面 復制代碼 html posit use 比較 國外 itl 1.input placeholder問題   在chrome 模擬移動端調試時[左邊圖],顯示的非常正常,但是在真機上[右邊圖],placeholder裏面的內容明顯靠上,非常的不美觀

記錄混合APP開發遇到的

col blog 長按 點擊 綁定 click 添加 lec sele 1、在IOS中給body綁定click事件會失效 2、在IOS中<div contenteditable="true"></div>中點擊時可以彈出鍵盤但是無法輸入。加一個樣式-

微信公眾號開發過程--踏指南

微信公眾號,真TM的坑呀。。。 1、先說一個最坑的--訊息回覆 當一次使用者請求時,微信伺服器傳送過來資料是一個XML格式的,這對於我們的開發十分的不友好,必須想一個辦法把XML格式轉化為JSON格式的資料,這樣對於我們下一步的資料操作更加方便,這裡我推薦的是使用xml2js模組 1 var x

微信支付問題---使用wx.requestPayment,支付頁面調不出的巨

1:遇到的問題:openid is invalid     在測試支付的時候發現沒有效果,然後查到返回的資訊為Openid is invalid (40029)昨天百度了一晚上,還是沒有找到這個問題的解決辦法。然後,今天又重新想 了下,發現由於我兩個小程式的appid 弄

程式開發過程體會

    這次開發的程式名為人事管理系統,剛開始經過幾天的學習,我已基本懂得基礎的功能的寫法。但當我真正嘗試著開發這個程式時,很快或是一開始就遇到了問題:總是出現諸如404,500這樣的錯誤,關鍵的是我不懂如何去修改,去改正錯誤,當然我也試著去嘗試,但結果總是不盡如意,往往還會

暑假開發過程開發收穫

開發過程:        7月20日、7月21日 討論大作業的專案型別及確定專案型別(unity3d-遊戲)和專案以及組長分配各組員的任務。        7月22日  下載相關軟體(unity3d、vis

Android 微信支付_掃碼支付,我來幫你填

0.概述: 本文講的是微信支付中的掃碼支付的模式二:該模式下,二維碼連結由微信支付返回給商戶,商戶將得到的二維碼連結轉成二維碼圖片,使用者通過掃碼支付,此方式下生成的二維碼2小時內有效。 微信支付官網地址:https://pay.weixin.qq.com/

Android自動電話錄音開發過程問題

已經做好了Android下的一個電話錄音軟體,利用的是service和broadcast廣播,功能包括開機自啟,來/去電自動錄音.雖然還存在一些問題,但功能無影響.程式碼放在了github上:檢視原始碼

iOS應用裡面包含虛擬商品,支付必須使用蘋果方式

蘋果拒絕App原因: We noticed that your app enables the purchase of content, services, or functionality in the app by means other than the In-App

iOS應用支付(IAP)開發步驟

前兩天和服務端同事一起,完成了應用內付費(以下簡稱IAP, In app purchase)的開發工作。步驟繁多,在此把開發步驟列表整理如下。因為只是步驟列表,所以並不含詳細的說明教程,需要看教程的新手,可以看我附在最後的一些參考連結。 配置Developer.apple.com 為應用建立建立一個

iOS應用支付(IAP)開發中後期的那些

一,Product ID無效? 好了,經過前面的準備後,就到了真正和IAP聯通的步驟了。在輸入一個Product ID向伺服器發起request的時候,很有可能出現失敗的情況,在request屬性InvalidateIdentifier中,你會發現這個Product I

iOS應用支付

本文會給大家詳細介紹iOS內購,這是本人16年5月底的開發過程,希望對看完此篇文章的人有所幫助。  本文基於XcodeVersion 7.3 (7D175)版本,手機是iPhone 6,9.3系統。  部分地方直接摘自網路,基本上是我的邏輯,省時省心省力。 一. 建立測

iOS應用支付(IAP)服務端端校驗詳解

imageview sof 客戶端 標識 知識庫 ndb json replace undle IAP流程 IAP流程分為兩種: 一種是直接使用Apple的服務器進行購買和驗證, 另一種就是自己假設服務器進行驗證。由於國內網絡連接Apple服務器驗證非常慢,而且也為了防止黑

IOS應用支付IAP從零開始詳解,讓你少踩

前言 什麼是IAP,即in-app-purchase 這幾天一直在搞ios的應用內購,查了很多部落格,發現幾乎沒有一篇部落格可以完整的概括出所有的點,為了防止大夥多次查閱資料,所以寫了這一篇部落格,希望大家能夠跟著我,從零開始,寫一個包含內購的應用出來 流程 一般有以下幾種

iOS應用支付(IAP)詳解

在iOS開發中如果涉及到虛擬物品的購買,就需要使用IAP服務,我們今天來看看如何實現。 在實現程式碼之前我們先做一些準備工作,一步步來看。 1、IAP流程 IAP流程分為兩種,一種是直接使用Apple的伺服器進行購買和驗證,另一種就是自己假設伺服

IOS應用(一)的種類

Glossary IAP - In App Purchase, 應用內購。 內購種類 consumable - 可消費的,比如遊戲中的金幣,金幣可以購買遊戲道具或者裝備,這個金幣是可以消費的,用完之後可以再購買。 non-consumable - 不可消費的,一次購買,永久生效,比如去廣告,解鎖遊

iOS開發必收藏】詳解iOS應用程式使用IAP/StoreKit付費、沙盒(SandBox)測試、建立測試賬號流程【2012-12-11日更新獲取"產品付費數量等於0的問題"】

//——2012-12-11日更新   獲取"產品付費數量等於0這個問題"的原因 看到很多童鞋問到,為什麼每次都返回數量等於0?? 其實有童鞋已經找到原因了,原因是你在 ItunesConnect 裡的 “Contracts,