iOS游戏开发三:苹果内支付实现 In-app

首先你必须熟悉内支付的流程,可以到网上搜搜看。

前几天因为需要做了个苹果的内支付,当中遇到了不少苦头,感觉网上的各种博客对内支付这一块都是点到即止,没有真真正正实现自己的需求。正所谓辛苦就有所得,通过东接西搬,终于内力小成,特与大家分享一下自己的经验。这里我假设你已经把商品的ID什么的都已经注册成功了,测试账号也有了。

下面直接贴代码了~~~~

//
//  IAPHelperViewController.h
//  

#import <UIKit/UIKit.h>
#import <StoreKit/StoreKit.h>

@interface IAPHelperViewController : UIViewController<SKProductsRequestDelegate,SKPaymentTransactionObserver>
{
    NSString* kMyInAppIdentifier;
    BOOL receiptValid;
    UIActivityIndicatorView* spinner;
}

+ (IAPHelperViewController *) sharedIAPHelper;

- (void) requestProUpgradeProductData;

- (void) requestProductData;

- (void) buyGolds:(int)buyType;

- (BOOL) canInAppPayment;

- (void) paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions;

- (void) purchasedTransaction:(SKPaymentTransaction *)transaction;

- (void) completeTransaction:(SKPaymentTransaction *)transaction;

- (void) failedTransaction:(SKPaymentTransaction *)transaction;

- (void) paymentQueueRestoreCompletedTransactionsFinished: (SKPaymentTransaction *)transaction;

- (void) paymentQueue:(SKPaymentQueue *) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error;

- (void) restoreTransaction:(SKPaymentTransaction *)transaction;

- (void) recordTransaction:(SKPaymentTransaction *)product;

- (void) verifyReceiptFromMyServer:(SKPaymentTransaction *)transaction;

@end

//
//  IAPHelperViewController.m
//  

#import "IAPHelperViewController.h"
#include "cocos2d.h"
using namespace cocos2d;

const static int IAP1p99 = flag1;//主要是标记用户点击了那个商品
const static int IAP2p99 = flag2;
const static int IAP3p99 = flag3;
const static int IAP4p99 = flag4;
const static int IAP5p99 = flag5;
#define MyServerURL        @"your server address , not apple"
#define ProductOne         @"product  1 id"
#define ProductThree       @".......  2 .."
#define ProductFive        @".......  3 .."
#define ProductFifteen     @".......  4 .."
#define ProductFifty       @"......   5 .."
#define MyFrint            NSLog(@"\n%s %d\n",__FUNCTION__, __LINE__)


@interface IAPHelperViewController ()

@end

@implementation IAPHelperViewController

//定义单例变量
static IAPHelperViewController* singleton = nil;
//单例模式
+ (IAPHelperViewController *) sharedIAPHelper
{
    MyFrint;
    if (singleton == nil) {
        singleton = [[IAPHelperViewController alloc] initWithNibName:nil bundle:nil];
    }
    return singleton;
}
//初始化 这里应该根据自己的情况初始化 我用的是C++写的界面 OC不鸟~~~~~~
- (id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    MyFrint;  
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        receiptValid = NO;
    }
    return self;
}
//加载视图
- (void) viewDidLoad
{
    MyFrint;
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor orangeColor];
}
//请求升级数据
- (void) requestProUpgradeProductData
{
    MyFrint;
    NSSet* productIdentifiers = [NSSet setWithObjects:ProductOne, ProductThree, ProductFive, ProductFifteen, ProductFifty, nil];
    SKProductsRequest* request = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
    request.delegate = self;
    [request start];
}
//点击商品按钮
- (void) buyGolds:(int)buyType
{
    MyFrint;//根据点击标记 找到对应的商品IDs
    switch (buyType) {
        case IAP1p99: { kMyInAppIdentifier = ProductOne;     break; }
        case IAP2p99: { kMyInAppIdentifier = ProductThree;   break; }
        case IAP3p99: { kMyInAppIdentifier = ProductFive;    break; }
        case IAP4p99: { kMyInAppIdentifier = ProductFifteen; break; }
        case IAP5p99: { kMyInAppIdentifier = ProductFifty;   break; }
        default: break;
    }
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    if ([self canInAppPayment]) {
        NSLog(@"允许应用内支付");
        //首先向AppStore请求恢复交易
        [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
    } else {
        NSLog(@"不允许应用内支付");
    }
}
//判断是否可以应用内支付
- (BOOL) canInAppPayment
{
    MyFrint;//你应该根据是否想让越狱的iOS设备允许内支付 
    if ([[NSFileManager defaultManager] fileExistsAtPath:@"/Applications/Cydia.app"]){
        NSLog(@"Jailbroken detected!");
        return NO;
    }
    if ([SKPaymentQueue canMakePayments]) {
        return YES;
    } else {
        return NO;
    }
}
//输入正确的帐号后进入 (如果是测试 必须必须使用你注册的沙河账号和密码)
- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentTransaction *)transaction
{
    MyFrint;
    [self requestProductData];
}
//请求购买的商品信息
- (void) requestProductData
{
    MyFrint;
    NSSet * set = [NSSet setWithObjects:kMyInAppIdentifier, nil];//可以添加多个商品ID
    SKProductsRequest* request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
    request.delegate = self;
    [request start];
}
#pragma mark SKProductsRequestDelegate 请求协议 ----------------------------------------------------------
//收到产品信息
- (void) productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
    //    NSArray *myProduct = response.products;
    //    NSLog(@"产品付费数量: %d", [myProduct count]);
    //    NSLog(@"产品Product ID:%@",response.invalidProductIdentifiers);
    //    SKPayment *payment = nil;
    //    // populate UI
    //    for(SKProduct *product in myProduct)
    //    {
    //        NSLog(@"\n\nproduct info");
    //        NSLog(@"SKProduct 描述信息%@", [product description]);
    //        NSLog(@"产品标题 %@" , product.localizedTitle);
    //        NSLog(@"产品描述信息: %@" , product.localizedDescription);
    //        NSLog(@"价格: %@" , product.price);
    //        NSLog(@"Product id: %@\n\n" , product.productIdentifier);
    //        payment = [SKPayment paymentWithProduct:product];
    //    }
    MyFrint;
    SKPayment* payment = [SKPayment paymentWithProduct:[response.products lastObject]];
    NSLog(@"\nPayment:\n productIdentifier=%@, \nrequestData=%@, \nquanlity=%d, \n",payment.productIdentifier,[payment.requestData description],payment.quantity);
    [[SKPaymentQueue defaultQueue] addPayment:payment];
    [request release];//请求已经生效
}
//请求错误信息
- (void) request:(SKRequest *)request didFailWithError:(NSError *)error
{
    MyFrint;
    UIAlertView *alerView =  [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Alert",NULL)
                                                        message:[error localizedDescription]
                                                       delegate:nil
                                              cancelButtonTitle:NSLocalizedString(@"Close",nil)
                                              otherButtonTitles:nil];
    [alerView show];
    [alerView release];
    [request release];//请求失效
}
- (void) purchasedTransaction:(SKPaymentTransaction *)transaction
{
    MyFrint;
}
- (void) requestDidFinish:(SKRequest *)request
{
    MyFrint;
}
//然后当用户输入正确的appStore帐号密码后进入(再次说明 如果是测试 必须是你注册的测试账号 不能使用真实的AppleID 否则后果~~~~)
//方法在新交易被创建或更新时都会被调用
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    MyFrint;
    for ( SKPaymentTransaction* transaction in transactions )
    {
        switch (transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchased:
            {//交易完成
                [self completeTransaction:transaction];
            }
                break;
            case SKPaymentTransactionStateFailed:
            {//交易失败
                [self failedTransaction:transaction];
            }
                break;
            case SKPaymentTransactionStateRestored:
            {//已经购买过该商品
                [self restoreTransaction:transaction];
            }
            case SKPaymentTransactionStatePurchasing:
            {//商品添加进列表
                NSLog(@"商品添加进列表==");
                break;
            }
            default:
                break;
        }
    }
}
//玩家等待 这里我想加个转动条 但没成功OC不鸟呀
- (void) activityIndicatorRun
{
    spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
    spinner.color = [UIColor whiteColor];
    spinner.frame = CGRectMake(100, 180.0f, 137.0f, 137.0f);
    [spinner startAnimating];
}
//玩家结束等待
- (void) activityIndicatorStop
{
    [spinner stopAnimating];
}
//observer在用户成功购买后提供相应的product
- (void)completeTransaction:(SKPaymentTransaction *)transaction
{
    MyFrint;
    [self activityIndicatorRun];
    [self verifyReceiptFromMyServer:transaction];//这个方法是内支付最重要的方法
    [self activityIndicatorStop];
    if (receiptValid) {
        [self recordTransaction:transaction];
        //提示服务器刷新数据  ---
        CCNotificationCenter::sharedNotificationCenter()->postNotification(kGNetworkNotificationName_Shop_BuyCredit_Successed, NULL);
    } else {
        [self failedTransaction:transaction];
    }
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
//处理还原购买  结束交易
- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{
    MyFrint;
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
//处理失败购买 结束交易
- (void) failedTransaction: (SKPaymentTransaction *)transaction
{
    MyFrint;
    if (transaction.error.code != SKErrorPaymentCancelled) {
        NSLog(@"购买失败");
    } 
<span style="white-space:pre">	</span>//根据项目的实际运行情况看 下面的完全多余.
<pre code_snippet_id="105137" snippet_file_name="blog_20131209_2_9929406" name="code" class="objc">    //else {
    //    NSLog(@"用户取消购买");
    //}


    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
//取消登陆帐号密码操作
- (void) paymentQueue:(SKPaymentQueue *) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
    MyFrint;
}
//用户成功购买
- (void) recordTransaction:(SKPaymentTransaction *)product
{
    MyFrint;
    UIAlertView* view = [[UIAlertView alloc] initWithTitle:@"购买结果" message:@"购买成功" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles: nil];
    [view show];
    [view release];
}
//向服务器请求验证
- (void) verifyReceiptFromMyServer:(SKPaymentTransaction *)transaction
{
    MyFrint;
    //初始化为合法
    if ([transaction.payment.productIdentifier isEqualToString:kMyInAppIdentifier]) {
        //base64 encode the receipt data for sending
        if ([transaction.transactionReceipt base64Encoding] != nil) {
            //将数据http post请求发送到我的服务器
            //data=base64(json(post))
            
            //字典内容
            NSDictionary* postDict = [[NSDictionary alloc] initWithObjectsAndKeys:@"ios",@"mode",
                                      @"verify",@"method",
                                      [NSDictionary dictionaryWithObjectsAndKeys:[self base64Encode:transaction.transactionReceipt],@"receipt",@"YES",@"test", nil],@"params",
                                      @"1136",@"uid", nil];
            //字典转化为json数据
            NSError* error = nil;
            NSData* jsonData = [NSJSONSerialization dataWithJSONObject:postDict options:NSJSONWritingPrettyPrinted error:&error];
            if([jsonData length]>0 && error == nil){
                
            }else if([jsonData length] == 0 && error == nil){
                NSLog(@"No data returned after serialization.");
                receiptValid = NO;
            }else if(error != nil){
                NSLog(@"An error happened = %@",error);
                receiptValid = NO;
            }
            //json数据base64
            NSString* base64 = [self base64Encode:jsonData];
            if ([base64 length]==0 || [base64 isEqualToString:@""]) {
                NSLog(@"Base64 error");
                receiptValid = NO;
            }
            
            NSURL* url = [NSURL URLWithString:MyServerURL];
            NSMutableURLRequest* theRequest = [NSMutableURLRequest requestWithURL:url];
            
            [theRequest setHTTPMethod:@"POST"];
            [theRequest setValue:[NSString stringWithFormat:@"%d", [jsonData length]] forHTTPHeaderField:@"Content-Length"];
            [theRequest setHTTPBody:[[NSString stringWithFormat:@"data=%@", base64] dataUsingEncoding:NSUTF8StringEncoding]];
            
            
            //发送同步请求  因为你将使用自己的服务器返回的数据来判断是否成功
            NSURLResponse* response = [[NSURLResponse alloc] init];
            NSError*       myerror =  [[NSError alloc] init];
            NSMutableData* receiveData = (NSMutableData *)[NSURLConnection sendSynchronousRequest:theRequest returningResponse:&response error:&myerror];
            
            //NSURLConnection* theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
            
            //接收到服务器返回的数据
            NSString *receiptStatus = [[NSString alloc] initWithBytes: [receiveData mutableBytes] length:[receiveData length] encoding:NSUTF8StringEncoding];
            NSLog(@"\nreceiptStatus=%@",receiptStatus);
            NSString* str = @"\"status\":0";
            
            if ([receiptStatus length]==0 || [receiptStatus isEqualToString:@""]) {
                receiptValid = NO;
            }
            
            if ([receiptStatus rangeOfString:str].location != NSNotFound) {
                NSLog(@"Yes Yes Yes!!!");
                receiptValid = YES;
            } else {
                receiptValid = NO;
            }
            [receiptStatus release];
            [myerror release];
            [response release];
            [postDict release];
        } else {
            NSLog(@"当向服务器发送验证数据时出错!!!");
            receiptValid = NO;
        }
    }
    else
    {
        NSLog(@"无效的商品ID");
        receiptValid = NO;
    }
}
//将NSData64位编码
- (NSString*) base64Encode:(NSData *)theData
{
    const uint8_t* input = (const uint8_t*)[theData bytes];
    NSInteger length = [theData length];
    
    static char 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 [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
}
//收到内存警告释放资源
- (void) didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
//回收内存
-(void) dealloc
{
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];//解除监听
    [spinner release]; spinner = nil;
    [super dealloc];
}
@end


你可以 将上面的.m后缀改成.mm,这样C嘎嘎就可以调用了(有个单例)。这里 我主要是实现了我自己应用或游戏的需求,仅供你参考。谢谢大家 大笑~~~~

   如果你有更好的内支付码码 请你不要吝啬告诉我 QQ:1065874255 多谢.
<p><span style="font-size:18px; color:#999999"><strong>
</strong></span></p>

你可能感兴趣的:(游戏,应用,苹果,in-app,内支付)