最近应用审核被拒绝了,原因是因为使用了第三方支付,苹果说需要使用到应用内购买。但是查了下相关资料,太杂且不详细,好吧,所以现在自己来写博客咯,把我所知道的一一列出来,可能内容有点乱,大家就将就着些吧
一、创建应用
首先进入苹果的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按钮可以进入联系方式填写页面,如下图:
- 我们点击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 上有对返回的状态码的详细说明。