iOS游戏IAP数据服务器验证

我上一篇博客《为iOS应用/游戏内建购买项目(IAP)》对于CP有自己游戏服务器的从安全性来讲并不适用,有自己服务器则需要进一步对数据进行校验。

如果CP具有自己的游戏服务器, Apple建议在服务器端存储产品ID,而不要将其存储在客户端本地。 这样就可以在不升级程序的前提下添加新的产品,这只需要我们向客户端发送哪个产品的ID。

如果要申请商品ID,可参考《为iOS应用/游戏内建购买项目(IAP)
更多详细流程可参考这篇文章:《Store Kit Guide(In App Purchase)翻译

IAP充值数据校验流程如图:

这里写图片描述

而以下代码就是要实现上图数据校验流程的:

//
//  ViewController.m
//
//

#import "ViewController.h"


@interface ViewController ()

@end

@implementation ViewController

#pragma mark 数据初始化

- (void)viewDidLoad {
    [super viewDidLoad];

    //1
    [self setOnListner];


    UIButton * btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [btn setFrame:CGRectMake(100, 300, self.view.bounds.size.width/2, 50)];
    [btn setTitle:@"buy" forState:UIControlStateNormal];
    btn.backgroundColor = [UIColor purpleColor];
    [btn addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchDown];

    [self.view addSubview:btn];

}

//1,对交易进行监听和设置可购买
-(void)setOnListner{

    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    NSString * des;
    if([SKPaymentQueue canMakePayments]){
        des = @"可购买内置项目!";
    }else{
        des = @"不可购买内置项目!";
    }
    NSLog(@"%@",des);

}


-(void)click{
    [self reqBuyData];
}

//2,请求商品信息
-(void)reqBuyData{

    // 所注册的商品ID数组
    NSArray * arr = [[NSArray alloc] initWithObjects:@"com.xx.gold100",@"com.xx.gold200", nil];
    NSSet *set = [NSSet setWithArray:arr];
    SKProductsRequest * req = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
    req.delegate = self;
    [req start];
}




#pragma mark SKProductsRequestDelegate代理实现

//3, 得到商品信息,并自动购买第一个
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{

    NSArray * myProducts = response.products;
    //如果是无效的商品将会出现在这里,如AppleID账号没完善银行信息之类也会导致
    NSArray * dArr = response.invalidProductIdentifiers;


    NSLog(@"接受到产品返回信息%lu", (unsigned long)myProducts.count);
    NSLog(@"接受到无效的产品返回信息%lu", (unsigned long)dArr.count);

    if(myProducts.count < 1){
        NSLog(@"无有效商品信息!");
        return;
    }

    SKProduct * buyProduct;
    for (SKProduct * product in myProducts) {
        NSLog(@"---pay : %@", product.localizedDescription);
        NSLog(@"product title:%@", product.localizedTitle);
        NSLog(@"price:%@", product.price);

        buyProduct = product;
    }
    //这里我直接对最后一个商品进行下单购买,
    // 游戏中应该把商品列表列出来给玩家选择,然后把要买的add到[SKPaymentQueue defaultQueue],到add这里开始发送购买请求
    SKPayment * pay = [SKPayment paymentWithProduct:buyProduct];
    [[SKPaymentQueue defaultQueue] addPayment:pay];

}

//SKProductsRequestDelegate代理实现,请求商品有错log
-(void)request:(SKRequest *)request didFailWithError:(NSError *)error{
    NSLog(@"---------error log----%@", error.localizedDescription);

}
//SKProductsRequestDelegate代理实现
-(void)requestDidFinish:(SKRequest *)request{
    NSLog(@"信息请求结束");
}






#pragma mark  SKPaymentTransactionObserver代理实现

//4,SKPaymentTransactionObserver代理实现,也就是购买的结果
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{

    NSString * des = @"";

    for(SKPaymentTransaction * trans in transactions){

        switch (trans.transactionState) {
            case SKPaymentTransactionStatePurchased:

                des = @"客户端本地购买完成!将向服务器验证数据!";
                //购买成功,那么将要向cp游戏服务器发送订单,cp游戏服务器再去通过iap服务器验证数据,再返回结果到客户端
                //5,
                [self completeTransaction:trans];
                //pop出用过的交易
                [[SKPaymentQueue defaultQueue] finishTransaction:trans];
                break;

            case SKPaymentTransactionStateFailed:
                des = @"购买失败!";
                [self failedTransaction:trans];
                //pop出用过的交易
                [[SKPaymentQueue defaultQueue] finishTransaction:trans];
                break;

            case SKPaymentTransactionStateRestored:
                des = @"购买重复!";
                //pop出用过的交易
                [[SKPaymentQueue defaultQueue] finishTransaction:trans];
                break;

            case SKPaymentTransactionStatePurchasing:
                //des = @"购买ing!";
                break;
            default:
                break;
        }

        UIAlertView * a = [[UIAlertView alloc] initWithTitle:@"result" message:des delegate:self cancelButtonTitle:@"ok" otherButtonTitles:nil, nil];
        if (![des isEqual: @""]) {
            [a show];
        }

    }
}

#pragma mark 自定义的函数,由本地购买成功后验证数据和打印信息用

//5,购买成功后调用,
-(void)completeTransaction:(SKPaymentTransaction*) transaction{

    //模拟向游戏服务器验证数据
    [self verifyTransaction:transaction];


}


//6,
//假设以下函数是游戏服务器的
//这里应该交给游戏服务器去验证,然后把结果返回客户端,直接现在这里模拟游戏服务器吧
-(void)verifyTransaction:(SKPaymentTransaction*) transaction{

    //沙盒测试用url
    NSString * urlStr = @"https://sandbox.itunes.apple.com/verifyReceipt";
    NSURL * url = [NSURL URLWithString:urlStr];

    NSMutableURLRequest * req = [NSMutableURLRequest  requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                    timeoutInterval:5000];
    req.HTTPMethod = @"POST";

    //要求Base64编码
    NSString * transactionReceipt = [transaction.transactionReceipt base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    NSString * jsonStr = [NSString stringWithFormat:@"{\"receipt-data\":\"%@\"}", transactionReceipt];
    NSData  * jsonData = [jsonStr dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];

    [req setHTTPBody:jsonData];

    NSData * result = [NSURLConnection sendSynchronousRequest:req returningResponse:nil error:nil];


    //这个验证函数里应该是服务器的,得到下面结果后返回给本客户端就OK啦
    if (result == nil) {
        NSLog(@"验证到有误数据");
    }else{
        NSDictionary * dic = [NSJSONSerialization JSONObjectWithData:result options:NSJSONReadingAllowFragments error:nil];

        /*应该返回的json
        receipt =     {
                .......
         };
         status = 0;
         */
        //status == 0 才算成功
        if (dic!= nil && [dic[@"status"] == 0]) {
            NSLog(@"数据验证成功!购买成功!");
            NSLog(@"返回数据:");
            NSLog(@"%@",dic);

        }else{
            NSLog(@"验证到有误数据");
            if (dic != nil) {
                NSLog(@"%@", dic);
            }
        }
    }

}

//给交易失败后调用
-(void)failedTransaction:(SKPaymentTransaction*)transaction{

    NSString * errorDes;
    NSInteger errorCode = transaction.error.code;

    NSLog(@"payment error ; %@", transaction.error.localizedDescription);

    switch (errorCode) {
        case SKErrorUnknown:
            errorDes = @"unknown";
            break;
        case SKErrorPaymentCancelled:
            errorDes = @"payment cancel";
            break;
        case SKErrorPaymentInvalid:
            errorDes = @"payment invalid";
            break;
        case SKErrorPaymentNotAllowed:
            errorDes = @"not allowed";
            break;
        case SKErrorClientInvalid:
            errorDes = @"client invalid";
            break;
        default:
            break;
    }
    UIAlertView * alert = [[UIAlertView alloc] initWithTitle:@"title" message:errorDes delegate:self cancelButtonTitle:@"ok" otherButtonTitles:nil, nil];
    [alert show];
}


@end

运行就可以得到结果了:

这里写图片描述

查看打印数据:

这里写图片描述

iOS 16上的内购项目(IAP)经历超时问题时,可能会导致用户无法完成购买或受阻于支付过程。这可能是由于多种因素引起的,包括网络连接问题、服务器故障或应用程序错误等。以下是一些可能的解决方法: 1. 检查网络连接:首先,请确保您的设备已正确连接至互联网。尝试连接其他应用或使用浏览器验证网络连接是否正常。 2. 重启设备和应用程序:关闭并重新启动设备,然后重新打开应用程序。这有助于刷新应用程序的状态并清除可能存在的临时错误。 3. 更新应用程序和iOS版本:检查应用商店或系统设置中是否有可用的应用程序或iOS更新。更新通常包括修复错误和增强功能,可能有助于解决超时问题。 4. 清除应用程序缓存:在iOS设置中找到应用程序,并尝试清除其缓存。这有时可以解决应用程序运行过程中的问题。 5. 取消和重新购买:如果内购项目超时,尝试取消并重新提交购买请求。确保您的支付信息正确并且账户余额充足。 6. 联系技术支持:如果问题仍然存在,请联系应用程序的开发者或苹果的技术支持团队。提供详细的问题描述和相关的错误信息,以便他们可以帮助您解决问题。 总结而言,iOS 16上的IAP超时问题可能是由于网络连接、设备或应用程序错误引起的。通过检查网络连接、重启设备和应用程序、更新软件、清除缓存、重新购买或联系技术支持,您可以尝试解决这个问题。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值