iOS應用內購/內付費


最近應用審核被拒絕了,原因是因為使用了第三方支付,蘋果說需要使用到應用內購買。但是查了下相關資料,太雜且不詳細,好吧,所以現在自己來寫博客咯,把我所知道的一一列出來,可能內容有點亂,大家就將就着些吧

一、創建應用

首先進入蘋果的ItunesConnection(https://itunesconnect.apple.com)點擊左上角的加號新建一個App應用,點擊后該網站會彈出一個信息編輯框,大家只要將上面的信息填充完畢點擊save即可在蘋果的app平台上擁有一個屬於自己的App。

在套裝ID的上,需要提前為該App申請一個AppID以及BundleID,只要是申請成功了就會在選擇列表中顯示出來。如果有人有疑問如何申請,請看我之前那一篇推送的博客,里面有詳細的步驟。附上鏈接點擊打開鏈接

這里順便多說一句這個ItunesConnect是用來干嘛的,它是蘋果公司給個人或企業提供管理自己App的一個平台。在這個平台上開發者可以新建,刪除和管理自己的App應用,開發者可以根據需求對App應用進行上架與下架,編輯App信息,生成測試app所需的信息。具體操作我就不細說了。

二、填寫協議、稅務和銀行業務

我們打開ItunesConnect,進入協議、稅務和銀行業務,配置相關信息。

填寫相關信息,因為我是已經申請成功了,所以看到了就是下圖

進入協議、稅務和銀行業務頁面后,會有3種合同類型,如果你之前沒有主動申請過去合同,那么一般你現在激活的合同只有iOS Free Application一種。

頁面內容分為兩塊

  • Request Contracts(申請合同)
  • Contracts In Effect(已生效合同)。

合同類型分為3種

  • iOS Free Application(免費應用合同)
  • iOS Paid Application(付費應用合同)
  • iAd App筆者暫時只申請過付費應用合同,所以下面主要講一下付費應用合同的申請流程。


1、填寫聯系方式

 

    • 我們點擊Contact Info下方的Set Up按鈕可以進入聯系方式填寫頁面,如下圖:

如果你沒有添加過聯系人,你需要通過Add New Contact按鈕來添加一個新的聯系人。然后指定聯系人的職務,職務如下:

      • Senior Management:高管
      • Financial:財務
      • Technical:技術支持
      • Legal:法務
      • Marketing:市場推廣

如果你是獨立開發者,可以全部填你自己一個人。

2、填寫銀行信息

  我們點擊Bank Info下方的Set Up按鈕可以進入聯系方式填寫頁面,如下圖:  

  選擇你的銀行賬戶,如果你沒有,點擊旁邊的Add Bank Account添加一個賬戶。下面是添加一個賬戶的流程。

  2-1、選擇銀行所在的國家

  

 

  

  2-2、填寫銀行CNAPS Code
    如果你不知道CNAPS Code是多少,可以點擊Look up Transit Number來查詢,查詢時會根據3個關鍵信息來查詢,如下:

    • Bank Name:銀行的英文名稱(不能是拼音)
    • City:銀行所在的城市英文名稱(中國的城市用拼音)
    • Postal Code:郵編
      然后在下面就會出來備選的銀行,選擇正確的銀行后,點擊next,進入下一步。

    
    

  2-3、確認銀行信息

    
  2-4、填寫銀行賬號信息

    • Bank Account Number:銀行賬號
    • Confirm Bank Account Number:再次輸入銀行賬號
    • Account Holder Name:持卡人姓名,中文名用拼寫,名在前,姓在后
    • Bank Account Currency:貨幣類型,一般國內的開發者選擇CNY

    
  2-5、確認所有信息
    
 3、填寫稅務信息 

  稅務信息這一塊了解不是很多,不過因為是國內開發者,可以不用太費心,稅務信息分3種:

  • U.S Tax Forms: 美國稅務
  • Australia Tax Forms:澳大利亞稅務
  • Canada Tax Forms: 加拿大稅務

  

  我選擇的是U.S Tax Forms,選擇后會問你兩個問題,第一個問題如下:詢問你是否是美國居民,有沒有美國伙伴關系或者美國公司,如果沒有直接選擇No。

  
  接下來第二個問題如下:詢問你有沒有在美國的商業性活動,沒有也直接選No。

  

  然后填寫你的稅務信息,包括以下幾點:

  • Individual or Organization Name:個人或者組織名稱
  • Country of incorporation: 所在國家
  • Type of Beneficial Owner:受益方式,獨立開發者選個人
  • Permanent Residence:居住地址
  • Mailing address:郵寄地址
  • Name of Person Making this Declaration:聲明人
  • Title:頭銜

  填寫完這些信息后就可以提交了

  
  
 4、等待審核

  當你填寫完所有資料后,合同狀態就會變成Processing, 大概24小時內就會有結果。

三、創建購買項目

 進入ItunesConnect,點擊你的App,選擇上面“功能”中的"App內購買項目",就可以看到右邊有一個+號,如下圖(最近因為被IPV6的問題給拒絕了)

  
單擊+號,選擇你需要創建的購買類型

  • 1、消耗型項目
  • 2、非消耗型項目
  • 3、自動續訂型項目
  • 4、免費訂閱
  • 5、非續訂訂閱
      選擇你需要創建的購買項目類型后,輸入產品的參考名稱、產品的ID,選擇產品售價等級,然后單擊添加語言輸入你的產品相關信息,再上傳屏幕預覽,單擊保存即創建購買項目成功。如下圖:   

  在上圖所示的編輯框中輸入,商品名稱,產品ID以及價格等級,在這邊說明一下:

  1.商品名稱根據你的消費道具的實際意義來說明,比如“100顆寶石”,“100金幣”等。

  2.產品ID是比較重要的,由項目自定義,只要唯一即可,像我一般都是用App的bundleID加一個后綴來表示,這樣既跟項目關聯又具有唯一性。

  3.價格等級的話“查看價格表”中有對應的說明,可以對照着表中每個國家的貨幣價格與等級來選擇。

  創建購買項目成功后,回到App內購買項目,就可以看到我們剛剛創建的購買項目了。

四、添加應用購買測試帳號

  我們打開ItunesConnect,選擇“用戶和職能”選項, 我們可以看到有一個“沙箱技術測試員”,我們單擊“+”號,添加我們的測試帳號,如下圖:

  
  然后我們的相關信息,我就不一一列舉了,如下圖:

  
單擊“存儲”,我們的測試帳號就創建成功了。下面開始擼代碼了

五、擼代碼咯

  1.首先在項目工程中加入“storekit.framework”,加入頭文件#import <StoreKit/StoreKit.h>

  2.在進行使用App內購買頁面.h文件中加入“SKPaymentTransactionObserver,SKProductsRequestDelegate”監聽機制  

  我就開始貼代碼吧

  1 - (void)viewDidLoad {
  2     
  3     [super viewDidLoad];
  4    
  5     // 設置購買隊列的監聽器
  6     [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
  7     
  8     if([SKPaymentQueue canMakePayments]){
  9                     //productID就是你在創建購買項目時所填寫的產品ID
 10                     [self requestProductData: productID];
 11                 }else{
 12                     self.ringIndicator.hidden = YES;
 13                     //                    NSLog(@"不允許程序內付費");
 14                     UIAlertView *alertError = [[UIAlertView alloc] initWithTitle:@"溫馨提示"
 15                                                                          message:@"請先開啟應用內付費購買功能。"
 16                                                                         delegate:nil
 17                                                                cancelButtonTitle:@"確定"
 18                                                                otherButtonTitles: nil];
 19                     [alertError show];
 20                 }
 21 
 22 }
 23 #pragma mark - 請求商品
 24 //請求商品
 25 - (void)requestProductData:(NSString *)type{
 26     NSLog(@"-------------請求對應的產品信息----------------");
 27     NSArray *product = [[NSArray alloc] initWithObjects:type, nil];
 28     
 29     NSSet *nsset = [NSSet setWithArray:product];
 30     SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
 31     request.delegate = self;
 32     [request start];
 33     //    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
 34 }
 35 
 36 #pragma mark 收到產品返回信息
 37 - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
 38     self.ringIndicator.hidden = YES;
 39     NSLog(@"--------------收到產品反饋消息---------------------");
 40     NSArray *product = response.products;
 41     if([product count] == 0){
 42         [self showHUDTipWithTitle:@"沒有該商品"];
 43         NSLog(@"--------------沒有商品------------------");
 44         return;
 45     }
 46     
 47     SKProduct *p = nil;
 48     for (SKProduct *pro in product) {
 49         NSLog(@"pro info");
 50         NSLog(@"SKProduct 描述信息:%@", [pro description]);
 51         NSLog(@"localizedTitle 產品標題:%@", [pro localizedTitle]);
 52         NSLog(@"localizedDescription 產品描述信息:%@", [pro localizedDescription]);
 53         NSLog(@"price 價格:%@", [pro price]);
 54         NSLog(@"productIdentifier Product id:%@", [pro productIdentifier]);
 55         
 56         if([pro.productIdentifier isEqualToString: productID]){
 57             p = pro;
 58             money = [NSString stringWithFormat:@"%@",[pro price]];
 59         }else{
 60             NSLog(@"不不不相同");
 61         }
 62     }
 63     SKPayment *payment = [SKPayment paymentWithProduct:p];
 64     //    SKPayment *payment = [SKPayment paymentWithProductIdentifier:PayKey];
 65     NSLog(@"發送購買請求");
 66     [[SKPaymentQueue defaultQueue] addPayment:payment];
 67 }
 68 
 69 //請求失敗
 70 - (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
 71     //     [self showHUDTipWithTitle:@"請求失敗,錯誤"];
 72     self.ringIndicator.hidden = YES;
 73     NSLog(@"------------------錯誤-----------------:%@", error);
 74     UIAlertView *alerView =  [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Alert",NULL) message:[error localizedDescription]
 75                                                        delegate:nil cancelButtonTitle:NSLocalizedString(@"Close",nil) otherButtonTitles:nil];
 76     [alerView show];
 77 }
 78 
 79 - (void)requestDidFinish:(SKRequest *)request{
 80     //    [self showHUDTipWithTitle:@"反饋信息結束"];
 81     NSLog(@"------------反饋信息結束-----------------");
 82 }
 83 
 84 #pragma mark 監聽購買結果
 85 //監聽購買結果
 86 - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
 87     self.ringIndicator.hidden = YES;
 88     NSLog(@" 監聽購買結果 -----paymentQueue--------");
 89     for (SKPaymentTransaction *transaction in transactions)
 90     {
 91         switch (transaction.transactionState)
 92         {
 93             case SKPaymentTransactionStatePurchased:{
 94                 NSLog(@"-----交易完成 --------");
 95                 //交易完成
 96                 [self commitSeversSucceeWithTransaction:transaction];
 97                 
 98                 
 99             }
100                 break;
101             case SKPaymentTransactionStateFailed:{
102                 NSLog(@"-----交易失敗 --------");
103                 //交易失敗
104                 [self failedTransaction:transaction];
105                 
106             }
107                 break;
108             case SKPaymentTransactionStateRestored:{
109                 NSLog(@"-----已經購買過該商品(重復支付) --------");
110                 //已經購買過該商品
111                 [self restoreTransaction:transaction];
112                 //                 [self commitSeversSucceeWithTransaction:transaction];
113                 
114                 
115             }
116             case SKPaymentTransactionStatePurchasing:  {
117                 //商品添加進列表
118                 NSLog(@"-----商品添加進列表 --------");
119             }
120                 break;
121             default:
122                 break;
123         }
124     }
125 }
126 
127 //交易結束
128 - (void)completeTransaction: (SKPaymentTransaction *)transaction
129 {
130     NSLog(@" 交易結束 -----completeTransaction--------");
131     
132     // Remove the transaction from the payment queue.
133     [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
134     
135 }
136 
137 - (void)commitSeversSucceeWithTransaction:(SKPaymentTransaction *)transaction
138 {
139     
140     NSString * productIdentifier = transaction.payment.productIdentifier;
141     NSLog(@"productIdentifier Product id:%@", productIdentifier);
142     NSString *transactionReceiptString= nil;
143     
144     //系統IOS7.0以上獲取支付驗證憑證的方式應該改變,切驗證返回的數據結構也不一樣了。
145     
146     if(SYSTEMVERSION >= 7.0){
147         // 驗證憑據,獲取到蘋果返回的交易憑據
148         // appStoreReceiptURL iOS7.0增加的,購買交易完成后,會將憑據存放在該地址
149         NSURLRequest * appstoreRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle]appStoreReceiptURL]];
150         NSError *error = nil;
151         NSData * receiptData = [NSURLConnection sendSynchronousRequest:appstoreRequest returningResponse:nil error:&error];
152         
153         transactionReceiptString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
154     }else{
155         
156         NSData * receiptData = transaction.transactionReceipt;
157         //        transactionReceiptString = [receiptData base64EncodedString];
158         transactionReceiptString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
159     }
160     //    NSLog(@"transactionReceiptString == %@",transactionReceiptString);
161     [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
162     // 向自己的服務器驗證購買憑證
163    // [self SendRequestWithtransactionReceiptString:transactionReceiptString Transaction:transaction];
164     
165     
166 }
167 //記錄交易
168 -(void)recordTransaction:(NSString *)product{
169     NSLog(@"-----記錄交易--------");
170 }
171 
172 //處理下載內容
173 -(void)provideContent:(NSString *)product{
174     NSLog(@"-----下載--------");
175 }
176 
177 - (void)failedTransaction:(SKPaymentTransaction *)transaction {
178     if(transaction.error.code != SKErrorPaymentCancelled) {
179         NSLog(@"購買失敗");
180         UIAlertView *alerView2 =  [[UIAlertView alloc] initWithTitle:@"溫馨提示"
181                                                              message:@"購買該套餐失敗,請重新嘗試購買"
182                                                             delegate:nil cancelButtonTitle:NSLocalizedString(@"關閉",nil) otherButtonTitles:nil];
183         
184         [alerView2 show];
185     } else {
186         NSLog(@"用戶取消交易");
187         
188         UIAlertView *alerView2 =  [[UIAlertView alloc] initWithTitle:@"溫馨提示"
189                                                              message:@"您已取消該購買"
190                                                             delegate:nil cancelButtonTitle:NSLocalizedString(@"關閉",nil) otherButtonTitles:nil];
191         
192         [alerView2 show];
193     }
194     [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
195 }
196 
197 
198 -(void) paymentQueueRestoreCompletedTransactionsFinished: (SKPaymentTransaction *)transaction{
199     
200 }
201 
202 
203 - (void) restoreTransaction: (SKPaymentTransaction *)transaction
204 {
205     NSLog(@" 交易恢復處理");
206     [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
207     
208 }
209 
210 -(void) paymentQueue:(SKPaymentQueue *) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error{
211     NSLog(@"-------paymentQueue----");
212 }
213 
214 #pragma mark connection delegate
215 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
216 {
217     NSLog(@"connection==%@",  [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
218 }

-(void)dealloc

{

    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];//解除監聽

 }

代碼我就不一一解釋了。接下來我就給大家看運行后的結果。

 


  




 

最后給大家補充點東西,在與服務器做較驗的時候,考慮到網絡異常情況,服務器的驗證應該是一個可恢復的隊列,如果失敗了,應該進行重試。

與蘋果的驗證接口文檔在https://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/VerifyingStoreReceipts/VerifyingStoreReceipts.html#//apple_ref/doc/uid/TP4008267-CH104-SW3 。簡單來說就是將購買憑證用Base64編碼,然后Post給蘋果的驗證服務器,蘋果將驗證結果以JSON形式返回。

蘋果AppStore線上的購買憑證地址是:https://buy.itunes.apple.com/verifyReceipt , 測試地址是:https://sandbox.itunes.apple.com/verifyReceipt

注意事項:

蘋果在審核應用時,只會在沙盒(sandbox)環境購買,其產生的購買憑證,也只能連接蘋果的測試驗證服務器。但是審核的應用又是連接的線上服務器,那應該判斷蘋果正式驗證服務器的返回狀態碼,如果 是21007,則再一次連接測試服務器進行驗證即可。https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Introduction.html  上有對返回的狀態碼的詳細說明。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM