最近應用審核被拒絕了,原因是因為使用了第三方支付,蘋果說需要使用到應用內購買。但是查了下相關資料,太雜且不詳細,好吧,所以現在自己來寫博客咯,把我所知道的一一列出來,可能內容有點亂,大家就將就着些吧
一、創建應用
首先進入蘋果的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 上有對返回的狀態碼的詳細說明。
