根据公司的需求,我们的APP内是有内购的,从最开始开始做,到现在,也经历了好些,我觉得有些地方的注意点还得写写.
内购的代码,网上都有这里简单的copy一下
首先,购买苹果的商品,得确定一下,这个内购商品是否存在
-(void) request_products_list_from_apple:(NSArray *) productIDs{
NSSet *productSet = [NSSet setWithArray:productIDs];
SKProductsRequest *apple_request = [[SKProductsRequest alloc] initWithProductIdentifiers:productSet];
apple_request.delegate = self;
NSLog(@"buy---test");
[apple_request start];
}
这段代码是发起获取商品请求,productIDs,是在iTunes里建立商品的对应的ID ,如果有对应的商品会在下面这个代理回调中返回商品信息
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
NSArray *products = [response products];
if (products != nil && [products count]>0) {
for(SKProduct *product in products){
NSLog(@"product info");
NSLog(@"SKProduct 描述信息%@", [product description]);
NSLog(@"产品标题 %@" , product.localizedTitle);
NSLog(@"产品描述信息: %@" , product.localizedDescription);
NSLog(@"价格: %@" , product.price);
NSLog(@"Product id: %@" , product.productIdentifier);
}
}
}
确定商品存在以后就可以发起支付请求了,当然在获取商品之前最好先添加观察者
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
然后发起支付请求
SKPayment *payment = [SKPayment paymentWithProduct:buy_product];//这个buy_product就是你要购买的商品,从上面代理回调返回的商品中筛选出来的
[[SKPaymentQueue defaultQueue] addPayment:payment];
支付过程中发生的变化,都会在下面回调中出现
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
for (SKPaymentTransaction *trans in transactions) {
switch (trans.transactionState) {
case SKPaymentTransactionStateFailed:
{
NSLog(@" trans Failed");
switch(trans.error.code) {
case SKErrorUnknown:
errorMsg = @"无法连接iTunes Store(如有疑问,可以询问苹果客服)";
break;
case SKErrorClientInvalid:
errorMsg = @"客户端验证错误";
break;
case SKErrorPaymentCancelled:
errorMsg = @"订单已取消";
break;
case SKErrorPaymentNotAllowed:
errorMsg = @"当前苹果设备无法购买商品(如有疑问,可以询问苹果客服)";
break;
case SKErrorPaymentInvalid:
errorMsg = @"购买商品标识无效";
break;
case SKErrorStoreProductNotAvailable:
errorMsg = @"在目前的商店内产品不可购买";
break;
case SKErrorCloudServicePermissionDenied:
errorMsg = @"用户已不允许访问云服务信息(如有疑问,可以询问苹果客服)";
break;
case SKErrorCloudServiceNetworkConnectionFailed:
errorMsg = @"设备没有联网";
break;
}
[[SKPaymentQueue defaultQueue] finishTransaction:trans];
}
break;
case SKPaymentTransactionStatePurchased:
{
NSLog(@" trans Purchased");
NSData *receiptData;
receiptData = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
if (receiptData != nil) {
NSDictionary *requestContents = @{
@"receipt-data": [receiptData base64EncodedStringWithOptions:0]
};
}
NSError *error;
NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
options:0
error:&error];
NSString *base64_receipt = [[NSString alloc] initWithData:requestData encoding:NSUTF8StringEncoding];//这个是最后的验证信息,建议传给服务端去验证(本地建议用keychain保存)
[[SKPaymentQueue defaultQueue] finishTransaction: trans];
}
break;
case SKPaymentTransactionStateDeferred:
{
NSLog(@" trans Deferred");
}
break;
case SKPaymentTransactionStateRestored:
{
NSLog(@"恢复购买");
NSData *receiptData;
receiptData = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
NSString *receipt = [[NSString alloc] initWithData:receiptData encoding:NSUTF8StringEncoding];
[[SKPaymentQueue defaultQueue] finishTransaction:trans];
}
break;
case SKPaymentTransactionStatePurchasing:
{
NSLog(@" trans Purchasing");
}
break;
default:
{
NSLog(@"");
}
break;
}
}
}
-(void)request:(SKRequest *)request didFailWithError:(NSError *)error{
NSLog(@"request fail");//请求失败
}
-(void)requestDidFinish:(SKRequest *)request{
NSLog(@"request finish");//请求完成
}
上述过程基本就完成了内购的流程,其实也不难,但是,就这短短的流程之间就可能会发生好些问题,
在内购时,要加是否越狱的判断,代码顺便也贴出来吧
#define USER_APP_PATH @"/User/Applications/"
+(BOOL)isJailBreak{
//判断文件是否存在
NSArray *arr = @[@"/Applications/Cydia.app",
@"/Library/MobileSubstrate/MobileSubstrate.dylib",
@"/bin/bash",
@"/usr/sbin/sshd",
@"/etc/apt" ];
for (NSString *str in arr) {
if ([[NSFileManager defaultManager] fileExistsAtPath:str]) {
NSLog(@"The device is jail broken! path === %@",str);
return YES;
}
}
//return NO;
//判断cydia应用是否存在
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"cydia://"]]) {
NSLog(@"The device is jail broken!");
return YES;
}
// return NO;
//不越狱的机器没有这个权限来判定的
if ([[NSFileManager defaultManager] fileExistsAtPath:USER_APP_PATH]) {
NSLog(@"The device is jail broken!");
NSArray *applist = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:USER_APP_PATH error:nil];
NSLog(@"applist = %@", applist);
return YES;
}
// return NO;
//读取环境变量
// char *env = getenv("DYLD_INSERT_LIBRARIES");
// NSLog(@"%s", env);
// if (env) {
// NSLog(@"The device is jail broken!");
// return YES;
// }
NSLog(@"The device is NOT jail broken!");
return NO;
}
内购完成,苹果返回的验证最好在服务端进行验证,网络不佳的情况下,本地最好保存在钥匙串(keychain)中,保存在沙盒可能删掉app,凭证就没有了,这个凭证是非常重要的,内购的唯一依据,苹果只管这个商品有没有效,不会对购买的商品是否一致验证,有的人可以利用现成有效的凭证刷单的
沙箱测试账号,可以在调试状态下,模拟真实购买流程,基本和正常账号是一样的,不过不能在线上版本使用
给苹果审核商品的时候注意名称,有敏感词汇,会被拒
审核app版本的时候,最好注明哪些不是内购商品,否则容易被误解,导致被拒
就先写这些,以后有在补充...