Unity呼叫iOS原生內購
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 <Foundation/Foundation.h>
#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 <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>
@interface IAPManager : NSObject<SKProductsRequestDelegate, SKPaymentTransactionObserver>{
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的名字。
這個裡面