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

来源:互联网 发布:java编程工具哪个好 编辑:程序博客网 时间:2024/05/22 20:58


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 PaymentControllerstatic 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,由客户端判断订单状态,未完成的二次发送至服务器验证。后续补充.....
原创粉丝点击