IAP已经成为, IOS程序开发商获得的重要途径, 这里对相关实现过程做一总结, 自己也备忘一下.
这里写两个类, MKStoreManager 及 MKStoreObserver, 前者主要处理业务逻辑,及对后者的管理. 后者是购买时和服务器交互的实现.
首先说一下MKStoreManager, 把它做成一个单例类, 方便使用, 它主要做三个功能
1. 对购买物品的id做一个校验, 看这些物品能不能购买(非必须且独立的一个过程) 在实例化时只做一次
- (void) requestProductData
{
NSLog(@"第一个物品ID: %@", featureGold1);
myrequest= [[SKProductsRequestalloc] initWithProductIdentifiers: [NSSetsetWithObjects:
featureGold1,featureGold2,featureGold3,featureGold4, featureGold5,featureVip1, featureVip2, featureVip3,nil]]; // add any other product here
myrequest.delegate = self;
[myrequeststart];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSLog(@"成功产品个数:%d",response.products.count);
NSLog(@"失败产品个数:%d",response.invalidProductIdentifiers.count);
[purchasableObjectsaddObjectsFromArray:response.products];
[request release];
}
2. 请求购买物品
- (void) buyFeatureGold :(int)featrueId
{
switch (featrueId) {
//钻石
case 0:
[selfbuyFeature:featureGold1];
break;
case 1:
[selfbuyFeature:featureGold2];
break;
case 2:
[selfbuyFeature:featureGold3];
break;
case 3:
[selfbuyFeature:featureGold4];
break;
case 4:
[selfbuyFeature:featureGold5];
break;
//VIP
case 5:
[selfbuyFeature:featureVip1];
break;
case 6:
[selfbuyFeature:featureVip2];
break;
case 7:
[selfbuyFeature:featureVip3];
break;
default:
break;
}
}
- (void) buyFeature:(NSString*) featureId
{
if ([SKPaymentQueuecanMakePayments])
{
SKPayment *payment = [SKPayment paymentWithProductIdentifier:featureId];
[[SKPaymentQueuedefaultQueue] addPayment:payment];
//_sharedStoreManager.storeObserver.m_bRealBuy = YES;
}
else
{
//您的设备不支持IAP
NSString* l_stringIapDisabled = [Tools getLocalizableText:@"iap_disabled"];
NSString* l_stringConfirm = [Tools getLocalizableText:@"iap_disabled"];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:l_stringIapDisabled
delegate:selfcancelButtonTitle:l_stringConfirm otherButtonTitles: nil];
[alert show];
[alert release];
}
}
3. 处理购买成功或失败时的逻辑
//验证失败
-(void) failedWhenCheck{
CCLOG(@"failedWhenCheck..................");
if([delegaterespondsToSelector:@selector(productPurchasedFailed)]){
[delegateproductPurchasedFailed];
}
}
//购买失败
- (void) failedTransaction: (SKPaymentTransaction *)transaction
{
CCLOG(@"failedTransaction..................");
if([delegaterespondsToSelector:@selector(productPurchasedFailed)]){
[delegateproductPurchasedFailed];
}
#ifdef COCOS2D_DEBUG
NSString *messageToBeShown = [NSStringstringWithFormat:@"Reason: %@, You can try: %@", [transaction.errorlocalizedFailureReason], [transaction.errorlocalizedRecoverySuggestion]];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Unable to complete your purchase"message:messageToBeShown
delegate:selfcancelButtonTitle:@"OK"otherButtonTitles: nil];
[alert show];
[alert release];
#endif
}
-(void) provideContent: (NSString*) productIdentifier
{
CCLOG(@"provideContent..................");
if([delegaterespondsToSelector:@selector(productPurchasedSuccess)]){
[delegateproductPurchasedSuccess];
}
}
第二个类 MKStoreObserver, 它实现了 SKPaymentTransactionObserver 这个接口
它的核心功能是 重写了
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
self.m_getData = [NSMutableDatadata];
for (SKPaymentTransaction *transaction in transactions)
{
CCLOG(@"transaction.transactionState : %d", transaction.transactionState);
switch (transaction.transactionState)
{
//正在加入到交易队列
caseSKPaymentTransactionStatePurchasing:
CCLOG(@"transaction.transactionState-> SKPaymentTransactionStatePurchasing");
break;
//购买成功
caseSKPaymentTransactionStatePurchased:
CCLOG(@"transaction.transactionState-> SKPaymentTransactionStatePurchased");
[self completeTransaction:transaction];
break;
//购买失败,或取消
caseSKPaymentTransactionStateFailed:
CCLOG(@"transaction.transactionState-> SKPaymentTransactionStateFailed");
[self failedTransaction:transaction];
break;
//从用户以前的购买请求中恢复
caseSKPaymentTransactionStateRestored:
CCLOG(@"transaction.transactionState-> SKPaymentTransactionStateRestored");
[self restoreTransaction:transaction];
break;
default:
break;
}
}
}
- (void) failedTransaction: (SKPaymentTransaction *)transaction
{
if (transaction.error.code != SKErrorPaymentCancelled)
{
// Optionally, display an error here.
}
[[MKStoreManagersharedManager] failedTransaction:transaction];
[[SKPaymentQueuedefaultQueue] finishTransaction: transaction];
//[m_getData release];
}
//真正给东西.(一般用不到.)
- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{
//[[MKStoreManager sharedManager] provideContent: transaction.originalTransaction.payment.productIdentifier];
//[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
[selfcompleteTransaction:transaction];
//[m_getData release];
}
- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
#ifndef IAP_CHECK
//直接给东西
[[MKStoreManager sharedManager] provideContent: transaction.payment.productIdentifier];
[[SKPaymentQueuedefaultQueue] finishTransaction: transaction];
#else
[[SKPaymentQueuedefaultQueue] finishTransaction: transaction];
//先验证一下
NSString* tempString =[MKStoreObserver base64forData:[transaction transactionReceipt]];
//NSLog(@"%@", tempString);
NSDictionary *tempDict = [NSDictionary dictionaryWithObject:tempString forKey:@"receipt-data"];
CJSONSerializer *cJSON = [CJSONSerializerserializer];
self.m_verifyData = [cJSON serializeDictionary:tempDict error:nil];
//CCLOG(@"josnValue: %@", m_verifyData);
self.m_getData=[NSMutableDatadata];
//#ifdef IAP_ADHOC_URL
//NSURL *l_verifyURL = [[NSURL alloc] initWithString: @"https://sandbox.itunes.apple.com/verifyReceipt"];
//#else
NSURL *l_verifyURL = [[NSURLalloc] initWithString: @"https://buy.itunes.apple.com/verifyReceipt"];
//#endif
CCLOG(@"l_verifyURL: %@", l_verifyURL);
/*
//TODO:
__strong const char * l_str = [josnValue UTF8String];
int l_iLength = [josnValue length];
NSData *postData = [NSData dataWithBytes:[josnValue UTF8String] length:[josnValue length]];
*/
NSMutableURLRequest *connectionRequest = [NSMutableURLRequest requestWithURL:l_verifyURL];
[connectionRequest setHTTPMethod:@"POST"];
[connectionRequest setTimeoutInterval:120.0];
[connectionRequest setCachePolicy:NSURLRequestUseProtocolCachePolicy];
//[connectionRequest setHTTPBody:postData];
[connectionRequest setHTTPBody:m_verifyData];
NSURLConnection *connection = [[NSURLConnectionalloc]
initWithRequest:connectionRequest
delegate:self];
[connection release];
[l_verifyURL release];
#endif
}
用于给第一类的对象发送, 服务器给出的购买成功及失败消息.
另外还有一个二次校验的功能.
//接收数据,
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
//NSLog(@"%@", [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]);
[m_getData appendData:data];
}
//接收数据完成
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
CJSONDeserializer *cjsondec = [CJSONDeserializerdeserializer];
NSDictionary *dic = [cjsondec deserializeAsDictionary:m_getDataerror:nil];
int l_iIapStatus=-1;
id l_status_object=[dic objectForKey:@"status"];
if (l_status_object) {
l_iIapStatus=[l_status_object intValue];
}
CCLOG(@"MKStoreObserver connectionDidFinishLoading result:%d", l_iIapStatus);
//验证成功
if (l_iIapStatus == 0) {
[[MKStoreManagersharedManager] provideContent: @"succes"];
//[m_getData release];
}
//验证失败
else {
[[MKStoreManagersharedManager] failedWhenCheck];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
switch([(NSHTTPURLResponse *)response statusCode]) {
case 200:
case 206:
break;
case 304:
break;
case 400:
break;
case 404:
break;
case 416:
break;
case 403:
break;
case 401:
case 500:
break;
default:
break;
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(@"MKStoreObserver connestion error and code:%d", error.code);
//TODO:
//超时, 或其它原因失败, 没有测试
if (YES) {
NSString* l_stringTimeOut = [Tools getLocalizableText:@"iap_verify_timeOut"];
NSString* l_stringTryAgain = [Tools getLocalizableText:@"ui_tryAgain"];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:l_stringTimeOut
delegate:selfcancelButtonTitle:l_stringTryAgain otherButtonTitles: nil];
[alert show];
[alert release];
}
//其它
else{
}
}
//重新发送验证
-(void) reVerfifyData{
self.m_getData=[NSMutableDatadata];
CCLOG(@"重新验证IAP....");
//#ifdef IAP_ADHOC_URL
//NSURL *l_verifyURL = [[NSURL alloc] initWithString: @"https://sandbox.itunes.apple.com/verifyReceipt"];
//#else
NSURL *l_verifyURL = [[NSURLalloc] initWithString: @"https://buy.itunes.apple.com/verifyReceipt"];
//#endif
CCLOG(@"l_verifyURL: %@", l_verifyURL);
NSMutableURLRequest *connectionRequest = [NSMutableURLRequest requestWithURL:l_verifyURL];
[connectionRequest setHTTPMethod:@"POST"];
[connectionRequest setTimeoutInterval:120.0];
[connectionRequest setCachePolicy:NSURLRequestUseProtocolCachePolicy];
//[connectionRequest setHTTPBody:postData];
[connectionRequest setHTTPBody:m_verifyData];
NSURLConnection *connection = [[NSURLConnectionalloc]
initWithRequest:connectionRequest
delegate:self];
[connection release];
[l_verifyURL release];
}
#pragma mark UIAlertView处理
//弹出的UIAlertView处理
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
switch (buttonIndex) {
case 0:
{
//重试()
[selfreVerfifyData];
}
default:
break;
}
}
//------------验证
+ (NSString*)base64forData:(NSData*)theData {
const uint8_t* input = (const uint8_t*)[theData bytes];
NSInteger length = [theData length];
staticchar table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
NSMutableData* data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
uint8_t* output = (uint8_t*)data.mutableBytes;
NSInteger i;
for (i=0; i < length; i += 3) {
NSInteger value = 0;
NSInteger j;
for (j = i; j < (i + 3); j++) {
value <<= 8;
if (j < length) {
value |= (0xFF & input[j]);
}
}
NSInteger theIndex = (i / 3) * 4;
output[theIndex + 0] = table[(value >> 18) & 0x3F];
output[theIndex + 1] = table[(value >> 12) & 0x3F];
output[theIndex + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '=';
output[theIndex + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : '=';
}
return [[[NSStringalloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
}
原创, 原载请注明!