@end
在restoreTapped:中:
- (void)restoreTapped:(id)sender
{
NSLog(@"Restore tapped!");
UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:@"Restore Content"
message:@"Would you like to check for and restore anyprevious purchases?"
delegate:self cancelButtonTitle:@"Cancel"otherButtonTitles:@"OK", nil];
alertView.delegate = self;
[alertView show];
}
#pragma mark - UIAlertViewDelegate
- (void)alertView:(UIAlertView *)alertViewdidDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (buttonIndex == alertView.firstOtherButtonIndex)
{
[[HMIAPHelper sharedInstance] restoreCompletedTransactions];
}
}
删除app,重装,并点击‘restore’,你的应用应该会执行以上功能。
5,结束。
在继续之前,你有2个需要完成的点击。
1,在你购买一个non-consumable产品后,你会发现仍然是购买按钮显示,并未显示‘installed’字样。点击将会崩溃。因为你有一个断言Assert,去保证这个产品是否允许购买(此处不可购买,因为你已经完成了购买)。你有多种方式来实现更改界面的功能:监听,委托或者其他。但在此,我们将使用KVO。
KVO:允许你关注在一个对象中的任何属性的实时变化,此处只需关注purchaseInprogress或者purchase的变化即可,接着刷新。
完成该功能,进入HMStoreDetailViewController.m文件,在viewWillAppear中,替换如下:
- (void)viewWillAppear:(BOOL)animated {[super viewWillAppear:animated];self.statusLabel.hidden = YES;[self refresh];
[self.product addObserver:selfforKeyPath:@"purchaseInProgress" options:0 context:nil];
[self.product addObserver:selfforKeyPath:@"purchase" options:0 context:nil];
}
- (void)viewWillDisappear:(BOOL)animated {[super viewWillDisappear:animated];[self.product removeObserver:self
forKeyPath:@"purchaseInProgress" context:nil];[self.product removeObserver:self
forKeyPath:@"purchase" context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPathofObject:(id)object change:(NSDictionary *)changecontext:(void *)context {
[self refresh];}
当willAppear视图时,将关注2个属性,当disappear的时候,取消关注。
当关注的属性有变化时,将调用observeValueForKeyPath,刷新之。
2,在产品列表界面需要知道产品状态的变化。
打开HMStoreListViewController.m文件,添加变量:
@implementation HMStoreListViewController {
NSArray * _products;
NSNumberFormatter * _priceFormatter;
BOOL _observing;
}
添加:
- (void)viewWillAppear:(BOOL)animated {[super viewWillAppear:animated];[self addObservers];[self.tableView reloadData];
}
- (void)viewWillDisappear:(BOOL)animated {[super viewWillDisappear:animated];[self removeObservers];
}
#pragma mark - KVO
- (void)addObservers {
if (_observing || _products == nil) return;_observing = TRUE;
for (IAPProduct * product in _products) {
[product addObserver:self
forKeyPath:@"purchaseInProgress" options:0 context:nil];
[product addObserver:self forKeyPath:@"purchase" options:0context:nil];
}}
- (void)removeObservers {
if (!_observing) return;
_observing = FALSE;
for (IAPProduct * product in _products) {
[product removeObserver:selfforKeyPath:@"purchaseInProgress" context:nil];
[product removeObserver:self forKeyPath:@"purchase"context:nil];
}}
- (void)observeValueForKeyPath:(NSString *)keyPathofObject:(id)object change:(NSDictionary *)changecontext:(void *)context {
IAPProduct * product = (IAPProduct *)object;
int row = [_products indexOfObject:product];
NSIndexPath * indexPath = [NSIndexPath indexPathForRow:row
inSection:0];
[self.tableView reloadRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationNone];
}
- (void)setProducts:(NSArray *)products {[self removeObservers];
_products = products;
[self addObservers];
}
包括添加和删除对product数组元素的监听。
修改reload方法,应用setProducts:方法:
- (void)reload {
[self setProducts:nil];[self.tableView reloadData];[[HMIAPHelper sharedInstance]
requestProductsWithCompletionHandler:^(BOOL success, NSArray*products) {
if (success) {
[self setProducts:products];[self.tableView reloadData];
}
[self.refreshControl endRefreshing];}];
}
运行程序,检测下,完美。
验证购买的可用性
当你进行IAp购买,对于返回的ok信息你不能100%保证是真从apple那里得来的,加入没有使用‘receipt validation’的话。
当你进行IAP购买时,apple将返回给你一块数据,叫做‘receipt’。这是一个带有本次交易的加密签名信息的私有数据块。这么做的主要目当然是为了安全。现在你需要将这个receipt信息发送到apple提供的一个特殊的‘receipt validation’服务器上,用来确保这个是有效的。
(不执行这一步的危险性已经被证实:俄罗斯的一个黑客易安装的IAP应用,使用户可以接受任何免费的IAP产品:你配置的DNS使你的设备路由到黑客提供的服务器上,而不是apple的购买服务器上,并且在设备上你还配置了一个欺骗证书,使黑客服务器是可信的。之后不论何时,你进行一个IAP购买请求,请求将转向何可服务器,并且黑客服务器一直返回‘done and paid for’已经购买成功的信息,接着,app将解锁购买的内容--------该黑客在IOS6将不能发挥作用了。然而还有其他的黑客可以实现该欺骗手法)。
更多IAP黑客讨论:http://www.macworld.com/article/1167677/hacker_exploits_ios_flaw_for_free_in_app_purchases.html
无服务器解决方案:
apple官方建议:链接你自己的服务器去验证信息,然后才是建议链接apple的服务器去验证。前者安全性更高。
在此暂时介绍无自己服务器的情况即链接apple服务器去验证
在解锁内容之前,需要验证。
在本项目资源文件中,你将看到有个文件夹:VerificationController,将其拖拽到libs中。并导入security.framework库。
在IAPHelper.m中:
#import "VerificationController.h"
添加方法:
- (void)validateReceiptForTransaction:(SKPaymentTransaction *)transaction
{
IAPProduct * product =_products[transaction.payment.productIdentifier];
VerificationController * verifier =[VerificationController sharedInstance];
[verifier verifyPurchase:transactioncompletionHandler:^(BOOL success)
{
if (success)
{
NSLog(@"Successfully verified receipt!");
[self provideContentForTransaction:transaction
productIdentifier:transaction.payment.productIdentifier];
}
else {
NSLog(@"Failed to validate receipt.");
product.purchaseInProgress = NO;
[[SKPaymentQueue defaultQueue]
finishTransaction: transaction];
}}];
}
这个方法是简单的调用apple的代码(略改动)去验证交易的有效性,并据此是否解锁内容。
最后是在completetrasaction和restoreTransaction中调用该方法,而不是直接立即提供解锁内容。
- (void)completeTransaction:(SKPaymentTransaction *)transaction
{
NSLog(@"completeTransaction...");
[self validateReceiptForTransaction:transaction];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction
{
NSLog(@"restoreTransaction...");
[self validateReceiptForTransaction:transaction];
}
运行程序,结果如前,但有了更好的安全性。
(引入验证时,注意在verifyPurchase:complete:方法中,用的是sandbox接口,你在发布app前要将其改成buy实际的购买验证接口)。iap 详细,布布扣,bubuko.com
iap 详细
原文:http://www.cnblogs.com/yunis/p/3921233.html