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