IAP实现之一服务器模式 — cocos2dx

11 篇文章 0 订阅
6 篇文章 0 订阅


cocos2dx中的IAP实现:二


C.服务器模式代码实现:添加StoreKit.framework,走完正常的内购前半流程

completeTransaction中处理receipt-data发送至server验证

1.发送数据有几点要注意的:

a.不建议使用AFN方式,虽然封装的比较好,但用不好各种闪退 

b.原生OC NSURLSession 也不建议使用,发送请求另起线程会影响cocos2dx的渲染线程,遇到过回来部分UI画不出来的情况

c.使用cocos2dx的curl库就可以了

2.具体代码实现:

PaymentController.h文件:

/*
*  PaymentController.h
*
*  Created by chiu on 15-06-16.
*  Copyright (c) 2015年 Idlehands. All rights reserved.
*/

#import <UIKit/UIKit.h>

#import <StoreKit/StoreKit.h>

@interface PaymentController : NSObject<SKPaymentTransactionObserver,SKProductsRequestDelegate>

@property  NSString *productID;
@property  bool startContentService;    /* 接收验证控制变量 */

- (void)initProductId:(NSString*) productIdStr;
- (void)purchaseFunc;
- (void)iapDealReceiptFun:(SKPaymentTransaction *)transaction;

@end

PaymentController.cpp文件:

/*
    author: chiu
    project: BYGame
    date: 2016/08/22
 */

#import "PaymentController.h"
#import "CCHelp.h"

@interface PaymentController ()

@end

@implementation PaymentController

static bool hasAddObersver = NO;

- (id)initWithPayConstructor:(NSString *) productIdStr
{
    self = [super init];
    if (self) {
        // Custom initialization
        [self initProductId:productIdStr];
        
        self.startContentService = true;
        
        if (!hasAddObersver) {
            hasAddObersver = YES;
            // 监听购买结果
            [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
        }
    }
    return self;
}

- (void)initProductId:(NSString*) productIdStr
{
    self.productID = productIdStr;
    self.startContentService = true;
}

- (void)purchaseFunc{
    NSString *product = self.productID;
    if([SKPaymentQueue canMakePayments]){
        [self requestProductData:product];
        NSLog(@"允许程序内付费");
    }else{
        NSLog(@"不允许程序内付费");
    }
}

- (void)iapDealReceiptFun:(SKPaymentTransaction *)transaction{

    NSData *receipt;
    receipt = transaction.transactionReceipt;
    
    if (!receipt) {
        /* No local receipt -- handle the error. */
    }
    //因为此处可能会多次调用原因不明所以加判断只调用一次
    else if (receipt && self.startContentService) {
        /**
         服务器要做的事情:
         接收ios端发过来的购买凭证。
         判断凭证是否已经存在或验证过,然后存储该凭证。
         将该凭证发送到苹果的服务器验证,并将验证结果返回给客户端。
         如果需要,修改用户相应的会员权限
         */
        
        NSString *recepitNStr = [receipt base64EncodedStringWithOptions:0];
        CCHelp* cppHelper = GetCCHelp();
        cppHelper->purchaseRecepit([recepitNStr UTF8String]);
        
    }
}

//请求商品
- (void)requestProductData:(NSString *)type{
    NSLog(@"-------------请求对应的产品信息----------------");
    NSArray *product = [[NSArray alloc] initWithObjects:type, nil];
    
    NSSet *nsset = [NSSet setWithArray:product];
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
    request.delegate = self;
    [request start];
    [product release];
}

//收到产品返回信息
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
    
    NSLog(@"--------------收到产品反馈消息---------------------");
    NSArray *product = response.products;
    if([product count] == 0){
        NSLog(@"--------------没有商品------------------");
        return;
    }
    
    NSLog(@"productID:%@", response.invalidProductIdentifiers);
    NSLog(@"产品付费数量:%d",[product count]);
    
    SKProduct *payInfoPorduct = nil;
    for (SKProduct *pro in product) {
        NSLog(@"%@", [pro description]);
        NSLog(@"%@", [pro localizedTitle]);
        NSLog(@"%@", [pro localizedDescription]);
        NSLog(@"%@", [pro price]);
        NSLog(@"%@", [pro productIdentifier]);
        
        if([pro.productIdentifier isEqualToString:self.productID]){
            payInfoPorduct = pro;
        }
    }
    
    if(!payInfoPorduct)
    {
        return;
    }

    NSLog(@"发送购买请求");
    
    if (floor(NSFoundationVersionNumber) < NSFoundationVersionNumber_iOS_5_0)
    {
        NSLog(@"pay_it less 5.0");
        SKPayment* payment = [SKPayment paymentWithProductIdentifier:self.productID];
        [[SKPaymentQueue defaultQueue] addPayment:payment];
    }
    else
    {
        NSLog(@"pay_it 5.0+");
        
        SKMutablePayment* payment = [SKMutablePayment paymentWithProduct:payInfoPorduct];
        payment.quantity = [product count];
        [[SKPaymentQueue defaultQueue] addPayment:payment];
    }
    [request autorelease];
}

- (void)requestProUpgradeProductData
{
    NSLog(@"------请求升级数据---------");
    NSSet *productIdentifiers = [NSSet setWithObject:@"com.productid"];
    SKProductsRequest* productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
    productsRequest.delegate = self;
    [productsRequest start];
    
}

//请求失败
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
    NSLog(@"------------------错误-----------------:%@", error);
    UIAlertView *alerView =  [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Alert",NULL) message:[error localizedDescription]
                                                       delegate:nil cancelButtonTitle:NSLocalizedString(@"Close",nil) otherButtonTitles:nil];
    [alerView show];
    [alerView release];
}

- (void)requestDidFinish:(SKRequest *)request{
    NSLog(@"------------反馈信息结束-----------------");
}

-(void) PurchasedTransaction: (SKPaymentTransaction *)transaction{
    NSLog(@"-----PurchasedTransaction----");
    NSArray *transactions =[[NSArray alloc] initWithObjects:transaction, nil];
    [self paymentQueue:[SKPaymentQueue defaultQueue] updatedTransactions:transactions];
    [transactions release];
}

//监听购买结果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{
    for(SKPaymentTransaction *tran in transaction){
        
        switch (tran.transactionState) {
            case SKPaymentTransactionStatePurchased:
                {
                    NSLog(@"交易完成");
                    [self completeTransaction:tran];
                    self.startContentService = true;
                    [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                }
                break;
            case SKPaymentTransactionStatePurchasing:
                {
                    NSLog(@"商品添加进列表");
                }
                break;
            case SKPaymentTransactionStateRestored:
                {
                    NSLog(@"已经购买过商品");
                    [self restoreTransaction:tran];
                    
                    [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                }
                break;
            case SKPaymentTransactionStateFailed:
                {
                    NSLog(@"交易失败");
                    [self failedTransaction:tran];
                    
                    //self.startContentService = false;
                    
                    [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                }
                break;
            default:
                {
                    [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                }
                break;
        }
    }
}

//交易结束
- (void)completeTransaction:(SKPaymentTransaction *)transaction{
    NSLog(@"交易结束");
    
    //交易结束向服务器发送验证receipt
    [self iapDealReceiptFun:transaction];
    
    //这个千万别忘了 ,要不你就会犯第一条问题(配置消耗型物品重复购买却显示此项目免费恢复)
    // Remove the transaction from the payment queue.
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}

- (void) failedTransaction: (SKPaymentTransaction *)transaction{
    if (transaction.error.code != SKErrorPaymentCancelled)
    {
    }
    switch (transaction.error.code) {
        case SKErrorUnknown:
            NSLog(@"SKErrorUnknown");
            break;
        case SKErrorClientInvalid:
            NSLog(@"SKErrorClientInvalid");
            break;
        case SKErrorPaymentCancelled:
            NSLog(@"SKErrorPaymentCancelled");
            break;
        case SKErrorPaymentInvalid:
            NSLog(@"SKErrorPaymentInvalid");
            break;
        case SKErrorPaymentNotAllowed:
            NSLog(@"SKErrorPaymentNotAllowed");
            break;
        case SKErrorStoreProductNotAvailable:
            NSLog(@"SKErrorStoreProductNotAvailable");
            break;
        default:
            break;
    }
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    
    
}

- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{
    
}

#pragma mark connection delegate
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    NSLog(@"%@",  [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]);
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
    NSLog(@"connectionDidFinishLoading......");
}

- (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(@"test");
}

- (void)dealloc{
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
    [super dealloc];
}

@end


使用NSURLSession方式发送:

        //发送数据字典 后面转Json格式
        NSDictionary *dic = @{@"receipt-data": [receipt base64EncodedStringWithOptions:0],
                              @"order_id":[NSString stringWithFormat:@"%s",PayInstance::GetPayInstance()->getCurOrderIdStr().c_str()],
                              @"user_id":[NSString stringWithFormat:@"%ld",GetUserInstance()->GetMeUserItem()->GetUserID()]};
        
        /* 此处是当前订单号拼接生成验证地址 */
        
        NSString* ocCurOrderIdUrlStr = [NSString stringWithFormat:@"http://xxx.xxxx.com:8003/xxxx.aspx?order_id=%s",
                                                      PayInstance::GetPayInstance()->getCurOrderIdStr().c_str()];
        /* 发送receipt至服务器验证 */
        
        //1,创建会话对象
        NSURLSession *session = [NSURLSession sharedSession];
        //2,根据会话创建task
        NSURL *url = [NSURL URLWithString:ocCurOrderIdUrlStr];
        //3,创建可变的请求对象
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        //4,请求方法改为post
        request.HTTPMethod = @"POST";
        //5,设置请求体-Json格式
        NSData *postData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:nil];
        [request setHTTPBody:postData];
        //6根据会话创建一个task(发送请求)
        NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            
            /* 解析Http回调数据 */
            NSDictionary * callBackDicData = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
            NSLog(@"post dic = %@",dic);
            
            NSString *returnCodeNStr = [callBackDicData objectForKey:@"return_code"];
            int returnCodeInt = [returnCodeNStr intValue];
            if(returnCodeInt == 200 ){
                /* 服务器验证收据成功 并确定支付成功后返回 */
                CCHelp* cppHelper = GetCCHelp();
                cppHelper->purchaseResult(1,1);
            }
            else{
                CCHelp* cppHelper = GetCCHelp();
                cppHelper->purchaseResult(1,-1);
            }
                
            
        }];
        [dataTask resume];


使用cocos2dx方式发送:

void CCHelp::purchaseRecepit(const char* recepitDataStr)
{
    if (recepitDataStr == nullptr) {
        return;
    }
    
    /* 此处是当前订单号拼接生成验证地址 */    
    CHttpRequest* pRequest = new CHttpRequest;
    YYDefineChar(url, 256, "%s","http://xxx.xxx.com:8003/xxx.aspx");
    pRequest->setUrl(url);
    pRequest->setRequestType(CHttpRequest::Type::POST);
    
    YYDefineChar(szPostData,512*20,"receipt-data=%s&order_id=%s&user_id=%ld",recepitDataStr,PayInstance::GetPayInstance()->getCurOrderIdStr().c_str(),GetUserInstance()->GetMeUserItem()->GetUserID());
    
    pRequest->setRequestData(szPostData,strlen(szPostData));
    pRequest->setResponseCallback(CC_CALLBACK_2(CCHelp::onRecepitDataCallback, GetCCHelp()));
    CHttpClient::getInstance()->send(pRequest);
    pRequest->release();
}
onRecepitDataCallback处理服务器验证收据回调

另外注意问题:
1.比第一个文档就多了个接收receipt-data过程处理,支付成功后由服务器加钻石金币处理,不再由客户端通知网站
2.整个服务器验证过程也有掉单的情况,像程序收到receipt-data后网络掉线,程序闪退,重复点击等等,方案是缓存recepit-data,由客户端判断订单状态,未完成的二次发送至服务器验证。后续补充.....
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值