基于 Block 的 iOS7 内购收据验证存储库管理器。请注意,您必须已正确设置 iTunes Connect,已有一些内购项目。示例应用没有用户可视反馈,但您可以通过控制台跟踪其进度。该应用只能在 iDevice 上运行,并且 不能 在模拟器中运行。
SCPStoreKitManager 通过 CocoaPods 提供。要安装它,只需将以下行添加到您的 Podfile
pod 'SCPStoreKitManager'
src
的文件夹中,包含了 SCPStoreKitManager
文件夹中的所需文件。如果不想验证收据,则只需将 Categories
文件夹和 SCPStoreKitManager.h + .m
复制到您的项目中。SCPStoreKitManager
文件夹,然后单击 在 Finder 中显示。这应在新的 Finder 窗口中打开保存框架的位置。搜索路径
部分,您需要添加头文件和库的路径。为此,双击 头文件搜索路径 项。这应弹出一个窗口。回到我们之前在第 3 步打开的 finder 窗口中,按住旁边的文件夹并拖动到 头文件搜索路径 窗口。"$(SRCROOT)/ExampleProject/SCPStoreKitManager"
的内容。现在,必须将最后一列中的下拉值从 non-recursive
更改为 recursive
。点击保存。SCPStoreKitReceiptValidator > openSSL > lib
。同样,当选择 lib
文件夹时,按住旁边的文件夹并拖动到 库搜索路径 窗口。"$(SRCROOT)/ExampleProject/SCPStoreKitManager/SCPStoreKitReceiptValidator/openSSL/lib"
的路径。点击保存。注意:如果你想使用自己的OpenSSL实现,请将这些搜索路径链接到你的实现。
现在你应该能够构建项目并且不出现错误。如果你的项目确实有错误,请确保两个搜索路径都设置为 recursive
。
框架分为两部分。第一部分是从iTunes检索应用内购买项目并处理它们的购买。第二部分是接收验证。你不需要使用接收验证,但是建议保护你的应用内购买(IAPs)。
这是一个基于块封装StoreKit委托方法的包装。
有四个实例方法可以通过共享实例访问。
- (void)requestProductsWithIdentifiers:(NSSet *)productsSet productsReturnedSuccessfully:(ProductsReturnedSuccessfully)productsReturnedSuccessfullyBlock invalidProducts:(InvalidProducts)invalidProductsBlock failure:(Failure)failureBlock;
这个方法接收一组产品标识符(NSSet
)。这些产品标识符应与你在iTunes Connect中设置的应用内购买(IAP)的产品ID相匹配。如有需要,请参考Apple文档以获取帮助。
这将请求你所请求的每个产品的IAP详情,在成功请求后,将调用success
块,返回一个包含SKProduct
的NSArray
。如果你请求了不在iTunes Connect上的产品,这些标识符将作为一个包含失败标识符的NSString
的NSArray
返回。
失败块将捕捉其他错误,如无法连接到iTunes。
//Request the product details from iTunes
[[SCPStoreKitManager sharedInstance] requestProductsWithIdentifiers:productIdentifiers
productsReturnedSuccessfully:^(NSArray *products) {
NSLog(@"Products : %@", products);
}
invalidProducts:^(NSArray *invalidProducts) {
NSLog(@"Invalid Products : %@", invalidProducts);
}
failure:^(NSError *error) {
NSLog(@"Error : %@", [error localizedDescription]);
}];
- (void)requestPaymentForProduct:(SKProduct *)product paymentTransactionStatePurchasing:(PaymentTransactionStatePurchasing)paymentTransactionStatePurchasingBlock paymentTransactionStatePurchased:(PaymentTransactionStatePurchased)paymentTransactionStatePurchasedBlock paymentTransactionStateFailed:(PaymentTransactionStateFailed)paymentTransactionStateFailedBlock paymentTransactionStateRestored:(PaymentTransactionStateRestored)paymentTransactionStateRestoredBlock failure:(Failure)failureBlock;
此方法接收你希望能够请求支付的SKProduct。根据SKProduct事务的状态,将调用四个不同的块。使用这些块是为了在支付过程中更新你的UI。除了失败块之外,每个块都将返回一个包含SKPaymentTransaction
的NSArray
。当调用到paymentTransactionStatePurchased
块时,Apple已经为在事务数组中返回的产品支付了款项。此时,你应该解锁所需的内容或添加所需的内容以完成购买。
//Request payment for product
[[SCPStoreKitManager sharedInstance] requestPaymentForProduct:_products[indexPath.row]
paymentTransactionStatePurchasing:^(NSArray *transactions) {
NSLog(@"Purchasing products : %@", transactions);
}
paymentTransactionStatePurchased:^(NSArray *transactions) {
NSLog(@"Purchased products : %@", transactions);
}
paymentTransactionStateFailed:^(NSArray *transactions) {
NSLog(@"Failed products : %@", transactions);
}
paymentTransactionStateRestored:^(NSArray *transactions) {
NSLog(@"Restored products : %@", transactions);
}
failure:^(NSError *error) {
NSLog(@"Failure : %@", [error localizedDescription]);
}];
- (void)restorePurchasesPaymentTransactionStateRestored:(PaymentTransactionStateRestored)paymentTransactionStateRestoredBlock paymentTransactionStateFailed:(PaymentTransactionStateFailed)paymentTransactionStateFailedBlock failure:(Failure)failureBlock;
如果你提供应用内购买(IAP),你必须提供一个方法来恢复任何已执行的IAP。有关更多信息,请参阅更多资讯。要实现此目的,请在共享实例上调用此方法,所有先前的交易都将返回一个包含SKPaymentTransaction
的NSArray
到PaymentTransactionStateRestored
块中。
//Request to restore previous purchases
[[SCPStoreKitManager sharedInstance] restorePurchasesPaymentTransactionStateRestored:^(NSArray *transactions) {
NSLog(@"Restored transactions : %@", transactions);
}
paymentTransactionStateFailed:^(NSArray *transactions) {
NSLog(@"Failed to restore transactions : %@", transactions);
}
failure:^(NSError *error) {
NSLog(@"Failure : %@", [error localizedDescription]);
}];
- (NSString *)localizedPriceForProduct:(SKProduct *)product;
接收一个SKProduct并返回一个价格字符串的产品该方法,该价格与手机的区域设置相匹配。
[productPriceLabel setText:[[SCPStoreKitManager sharedInstance] localizedPriceForProduct:product]];
- (void)validateReceiptWithBundleIdentifier:(NSString *)bundleIdentifier bundleVersion:(NSString *)bundleVersion tryAgain:(BOOL)tryAgain showReceiptAlert:(BOOL)showReceiptAlert alertViewTitle:(NSString *)alertViewTitle alertViewMessage:(NSString *)alertViewMessage success:(Success)successBlock failure:(Failure)failureBlock;
有一个方法可以验证应用程序发票未被苹果公司篡改。此方法确实需要几个参数,但它们我认为它们是有用的。
参数说明
(NSString *)bundleIdentifier
这应该与您应用的包标识符匹配。这个值是在应用程序中以硬编码方式设置的,而不是从info.plist中检索得到的。这样做的原因是为了防止任何人通过编辑plist文件来匹配任何已购买的所有IAP的收据。
bundleVersion:(NSString *)bundleVersion
您不仅要知道收据是否为正确的应用程序,还要知道是正确的版本。同样地,为了避免编辑plist,这个值也是硬编码的。
(BOOL)tryAgain
当一个App首次安装时,没有收据。这个BOOL
值用来确定验证器是否应该尝试从Apple请求新的/更新的收据。这样做可能会显得有些奇怪,因为为此用户必须输入他们的Apple ID详细信息。如果将其设置为NO
,则不会尝试刷新或从Apple请求收据,而只验证设备上已存在的任何收据。
(BOOL)showReceiptAlert
如果设置为YES
,则会显示UIAlertView,告知用户在显示输入Apple ID细节的警示之前,为什么会被要求输入Apple ID。这可以给用户提供信心,因为它可能会让人感到担忧,如果在没有任何预先通知或背景的情况下,一打开应用就要求输入Apple ID细节。在显示警示时,用户会得到一个是或否选项来请求收据。
(NSString *)alertViewTitle and (NSString *)alertViewMessage
这两个参数允许您为警示视图提供自定义的标题和消息。
(Success)successBlock
一旦收据被验证,您可以确信收据是真实的。当这个block被调用时,您会接收到一个SCPStoreKitReceipt
。关于SCPStoreKitReceipt
的详细信息请参见以下内容。
(Failure)failureBlock
如果将要验证的收据是无效的,您可以在失败的块中处理它。
//Validate the Apps receipt
[[SCPStoreKitReceiptValidator sharedInstance] validateReceiptWithBundleIdentifier:@"me.ste.SCPStoreKitManager"
bundleVersion:@"1.0"
tryAgain:YES
showReceiptAlert:YES
alertViewTitle:nil
alertViewMessage:nil
success:^(SCPStoreKitReceipt *receipt) {
//Here you would do some further checks such as :
//Validate that the number of coins/tokens the user has does not exceed the number they have paid for
//Unlock any non-consumable items
NSLog(@"App receipt : %@", [receipt fullDescription]);
//Enumerate through the IAPs and unlock their features
[[receipt inAppPurchases] enumerateObjectsUsingBlock:^(SCPStoreKitIAPReceipt *iapReceipt, NSUInteger idx, BOOL *stop) {
NSLog(@"Previous purchase of '%@' on %@", [iapReceipt productIdentifier], [iapReceipt purchaseDate]);
}];
} failure:^(NSError *error) {
NSLog(@"%@", [error fullDescription]);
}];
有两大类收据:SCPStoreKitReceipt
和SCPStoreKitIAPReceipt
。每一类都包含非常有用的数据。您不需要对任何这些收据进行init
操作,您将它们从SCPStoreKitReceiptValidator
方法中获得。
所有收据都有相同的辅助方法。
- (NSDictionary *)fullDescription;
这个方法仅仅将收据输出为一个NSDictionary
。
这个收据包含了我们可以用来验证App收据是否为这个App和这个设备的一些数据。
属性包括
@property (nonatomic, strong, readonly) NSString *bundleIdentifier;
@property (nonatomic, strong, readonly) NSData *bundleIdentifierData;
@property (nonatomic, strong, readonly) NSData *hash;
@property (nonatomic, strong, readonly) NSData *opaqueValue;
@property (nonatomic, strong, readonly) NSString *originalVersion;
@property (nonatomic, strong, readonly) NSString *version;
@property (nonatomic, strong, readonly) NSMutableArray *inAppPurchases;
关于这些属性的更多信息,请查阅WWDC13会议。两点值得注意是
NSString *originalVersion 如果您将应用从付费应用更改为增值付费应用,您不希望让现有客户感到不悦,在他们为应用付费之后再让他们为IAP付费。利用这个属性,您可以检查应用的原始购买版本,如果它在您的增值付费更改之前,那么您就知道您需要解锁他们在原始购买时应用的全部功能。
这个收据包含了单个IAP的所有详情。在这个收据中也有一些有用的属性,它们包括
@property (nonatomic, strong, readonly) NSString *productIdentifier;
@property (nonatomic, strong, readonly) NSNumber *quantity;
@property (nonatomic, strong, readonly) NSDate *cancellationDate;
@property (nonatomic, strong, readonly) NSDate *originalPurchaseDate;
@property (nonatomic, strong, readonly) NSDate *purchaseDate;
@property (nonatomic, strong, readonly) NSString *transactionIdentifier;
@property (nonatomic, strong, readonly) NSString *originalTransactionIdentifier;
@property (nonatomic, strong, readonly) NSDate *subscriptionExpiryDate;
@property (nonatomic, strong, readonly) NSNumber *webItemId;
使用这些属性的一些方法包括
NSString *productIdentifier
这个用于确定购买的产品是什么,以及需要解锁哪些功能和产品。
NSNumber *quantity
如果这是一件消耗品,例如硬币或代币,你可以计算购买了多少件此类物品,然后将这个数量与用户的余额进行比较,以检查余额是否被篡改。(.plist编辑。)如果此产品给用户10个代币,并且他们购买了两次,那么他们应该最多有20个代币。如果他们拥有更多,那么你知道有人篡改了,需要处理这个问题。
twitter:@ste_prescott。
本项目按照MIT许可证提供。
版权所有 (C) 2014 Ste Prescott
在此特此无条件地授予任何获得此软件及其相关文档副本(以下简称“软件”)的人员在此软件上毫无限制地处理、包括但不限于使用、复制、修改、合并、发布、分发、再许可以及/或出售软件副本的权利,并允许软件接受人进行此操作,但需遵守以下条件
以上版权声明和本许可声明应包含在软件的副本或实质部分中。
软件按“原样”提供,不提供任何形式的保证,无论是明示的、暗示的,包括但不限于对适销性、特定用途适用性和非侵权的保证。在任何情况下,作者或版权所有者均不对任何索赔、损害或其他责任承担责任,无论该责任是否在合同之诉、侵权或其他责任中产生,几乎所有与软件或其使用或其他操作有关或由此产生的。