当前位置: 代码迷 >> 综合 >> IOS开发中,IAP(In-App Purchase, 程序内置收费)相关总结, 及备忘
  详细解决方案

IOS开发中,IAP(In-App Purchase, 程序内置收费)相关总结, 及备忘

热度:49   发布时间:2023-12-10 17:59:52.0

  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];

}

 

 原创, 原载请注明!