Unity在5.x以后的版本,都附带了各种平台的IAP(内购),网上一搜Unity IAP,就一大堆如何如何接入的教程,据说还挺方便的。本人也是用Unity 5.x,也曾经用了一下Unity的IAP,那为何现在还要讨论调用iOS原生的IAP呢?
在这里不得不吐槽一下Unity的IAP,虽然它目的是更加方便的让游戏接入支付,但接入过程,感觉也不是那么的顺利的。可能本人水平问题,接入这东西整整花费了一周时间(服务器+客户端)。首先文档挺简略的,另外,就是网上教程都是单机向,没涉及服务器验证。
还有的是,IAP是集成到Service上的,也就是说,用Unity IAP就得开Service,就得实时联网,倘若断网什么的,就会报一大堆的错误!即使是导入了package的。。。当然对于电信的同学来说,应该不存在这问题,本人也是在电信环境下,工作的很好的。但最近换了移动网络,一直连不上Service,我不得不一些宏把调用IAP的代码屏蔽掉。。。
说到导入package,我简直无力吐槽,它就不会给你分一下平台,各种平台的东西都导进来,我本来只想接iOS的,结果包里面包含了啥安卓,Tizen,小米,乱七八糟。。。
还有的是,如果协同工作,一起工作的小伙伴,还必须加入项目的组织,这对于独立开发者来说,挺不方便的。
鉴于以上种种槽点,我把Unity IAP怒删了。。。重新接入iOS原生的IAP。可能是因为只有老版本的Unity,才需要用原生的。文章也挺少的了。只有以下两篇文章:
https://www.cnblogs.com/weiqiangwaideshijie/p/9103407.html
https://blog.csdn.net/dingxiaowei2013/article/details/52988354
上面两篇文章写的两位大兄弟,其实代码差不多了,我也抄了大部分。实际调试过程中,发现不少问题,首先代码有点不简洁,另外就是支付回执处理有问题。这里把我的贴出来分享一下。(代码是通的,Unity部分涉及到许多游戏逻辑,就不贴了。)
IAPUnity.m
#import "IAPManager.h"
#import
#if defined(__cplusplus)
extern "C" {
#endif
// 判定商品是否有效
bool IsProductAvailable(){
return [[IAPManager shareInstance] CanMakePayment];
}
// 请求获得商品信息
void RequestProductInfo(char* p){
NSString* list = [NSString stringWithUTF8String:p];
[[IAPManager shareInstance] requestProductData:list];
}
// 购买商品
void BuyProduct(char* p){
[[IAPManager shareInstance] buyRequest:[NSString stringWithUTF8String:p]];
}
// 处理一些未完成支付
void HandlePaymentQueue(){
[[IAPManager shareInstance] handlePaymentQueue];
}
#if defined(__cplusplus)
}
#endif
这里面的函数,主要是供Unity调用的。购买商品调BuyProduct就可以了。注意的是,调BuyProduct之前最好调一下 HandlePaymentQueue,用来关闭一些未完成订单,以免购买相同的商品的时候,苹果弹出说商品已购买,需要恢复之类的提示。RequestProductInfo 其实这个是用来做验证用的了,调不调也行。
IAPManager.h
#import
#import
@interface IAPManager : NSObject
SKProduct *proUpgradeProduct;
SKProductsRequest *productsRequest;
}
+(id)shareInstance;
-(void)attachObserver;
-(BOOL)CanMakePayment;
-(void)requestProductData:(NSString *)productIdentifiers;
-(void)buyRequest:(NSString *)productIdentifier;
-(void)handlePaymentQueue;
-(NSString*) transactionInfo:(SKPaymentTransaction*)transaction;
@end
这个只是一个头文件。
IAPManager.m
#import "IAPManager.h"
static IAPManager* instance = nil;
@implementation IAPManager
+(id)shareInstance{
if (instance == nil){
instance = [[[self class] alloc] init];
[instance attachObserver];
}
return instance;
}
// 处理一些未完成的支付
-(void)handlePaymentQueue{
NSArray* transactions = [SKPaymentQueue defaultQueue].transactions;
if (transactions.count > 0) {
// NSLog(@"transactions.count = %lu", (unsigned long)transactions.count);
for(SKPaymentTransaction* transaction in transactions){
if (transaction.transactionState == SKPaymentTransactionStatePurchased){
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
}
}
}
-(void) attachObserver{
NSLog(@"attachObserver");
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
-(BOOL) CanMakePayment{
return [SKPaymentQueue canMakePayments];
}
-(void) requestProductData:(NSString* )productIdentifiers{
NSArray* idArray = [productIdentifiers componentsSeparatedByString:@"\t"];
NSSet* idSet = [NSSet setWithArray:idArray];
[self sendRequest:idSet];
}
-(void) sendRequest:(NSSet* )idSet{
SKProductsRequest* request = [[SKProductsRequest alloc] initWithProductIdentifiers:idSet];
request.delegate = self;
[request start];
}
-(void) productsRequest:(SKProductsRequest*)request didReceiveResponse:(SKProductsResponse*)response{
NSArray* products = response.products;
for (SKProduct* p in products){
UnitySendMessage("xxx", "AddProduct", [[self productInfo:p] UTF8String]);
}
for (NSString* invalidProductId in response.invalidProductIdentifiers){
NSLog(@"Invalid product id: %@", invalidProductId);
}
// [request autorelease];
}
-(void) buyRequest:(NSString*)productIdentifier{
SKPayment* payment = [SKPayment paymentWithProductIdentifier:productIdentifier];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
-(NSString*) productInfo:(SKProduct*)product{
NSArray* info = [NSArray arrayWithObjects:product.localizedTitle,
product.localizedDescription,
product.price,
product.productIdentifier,
nil];
return [info componentsJoinedByString:@","];
}
// 处理支付队列
-(void) paymentQueue:(SKPaymentQueue*)queue updatedTransactions:(NSArray*)transactions{
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];
break;
default:
break;
}
}
}
// 支付动作转字符串
-(NSString*) transactionInfo:(SKPaymentTransaction*)transaction{
NSArray* info = [NSArray arrayWithObjects:transaction.payment.productIdentifier,
transaction.transactionIdentifier,
[transaction.transactionReceipt base64Encoding],
nil];
return [info componentsJoinedByString:@","];
}
// 支付完成回调
-(void) completeTransaction:(SKPaymentTransaction*)transaction{
NSLog(@"Complete transaction: transactionIdentifier = %@\n", transaction.transactionIdentifier);
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
// 直接调用Unity
UnitySendMessage("xxxx",
"OnSucess",
[[self transactionInfo:transaction] UTF8String]);
}
// 支付失败回调
-(void) failedTransaction:(SKPaymentTransaction*)transaction{
NSLog(@"Failed transaction: %@", transaction.transactionIdentifier);
if (transaction.error.code != SKErrorPaymentCancelled) {
NSLog(@"!Cancelled");
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
UnitySendMessage("xxxx", "OnFailed", "");
}
-(void) restoreTransaction:(SKPaymentTransaction*) transaction{
NSLog(@"Restore transaction: %@", transaction.transactionIdentifier);
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
@end
这里面主要注册一些Unity的回调函数。xxx是对应Unity里面绑定支付代码的某个GameObject的名字。
这个里面