天天看点

Unity3d接入IOS内购

利用Unity3d制作完毕游戏发布到appstore,有时会做游戏内购买虚拟物品,也就是内购。

在Ios开发中叫做:In App Purchase,简称IAP

那么如何在unity3d内嵌入IPA呢?几经辗转,多方搜索,摸索出点经验,分享给大家,如有疏漏,还请指教。

当然也有人们写好的插件可用,我觉得自己写的才用着顺手。

一、准备条件:

1、 申请苹果开发者账号。后台先创建证书,在创建应用,填写应用详情,创建测试用的账号,创建内购项目。

这里根据需要创建consumable(每次都需要购买)或者non-consumable的(购买一次一直可用,就是如果买过可以恢复购买)内购项目。

如下我们创建了non-consumable类型,名称“Package_2”,这个名称仅能使用一次,即使删除了也不能够再次利用。

Unity3d接入IOS内购
Unity3d接入IOS内购

创建测试人员账号,可以免费测试购买本开发者账号下面所有应用的物品:

Unity3d接入IOS内购

2、简单了解ios开发的Object-C语言,主要是用来做内购,详细请百度:

3、简单了解IPA

详细请参见:StoreKit Guide(In App Purchase)翻译

http://yarin.blog.51cto.com/1130898/549141

4、了解unity3d与ios通信,详细参见:为iOS创建插件 Building Plugins for iOS

http://game.ceeger.com/Manual/PluginsForIOS.html

二、下面我们单独创建一个例子来演示:

1、 创建工程,切换到ios平台、创建空gameobject,改名为Main,创建点击按钮触发购买的脚本,挂在Main上面。创建平台文件,下面创建子文件夹IOS。

2、 在设置里面修改包名,改为你自己在appstore后台创建的名称

Unity3d接入IOS内购

IPADemo里面编写与ios通信代码以及购买代码,其中内购商品名称修改为自己appstore后台定义的:private stringproduct = "Package_2";

using UnityEngine;

using System.Collections;

using System.Collections.Generic;

using System.Runtime.InteropServices;

public class IPADemo : MonoBehaviour {

         publicList<string> productInfo = new List<string>();

         privatestring product = "Package_2";

         [DllImport("__Internal")]

         privatestatic extern void TestMsg();//测试信息发送

         [DllImport("__Internal")]

         privatestatic extern void TestSendString(string s);//测试发送字符串

         [DllImport("__Internal")]

         privatestatic extern void TestGetString();//测试接收字符串

         [DllImport("__Internal")]

         privatestatic extern void InitIAPManager();//初始化

         [DllImport("__Internal")]

         privatestatic extern bool IsProductAvailable();//判断是否可以购买

         [DllImport("__Internal")]

         privatestatic extern void RequstProductInfo(string s);//获取商品信息

         [DllImport("__Internal")]

         privatestatic extern void BuyProduct(string s);//购买商品

         //测试从xcode接收到的字符串

         voidIOSToU(string s)

         {

                   Debug.Log("[MsgFrom ios]"+s);

         }

         //获取product列表

         voidShowProductList(string s){

                   productInfo.Add(s);

         }

         //获取商品回执

         voidProvideContent(string s)

         {

                   Debug.Log("[MsgFrom ios]proivideContent : "+s);

         }

         voidStart ()

         {

                   InitIAPManager();

         }

         voidUpdate ()

         {                          

         }

         voidOnGUI()

         {                

                   if(Btn("GetProducts")){

                            if(!IsProductAvailable())

                                     thrownew System.Exception("IAP not enabled");

                            productInfo= new List<string>();

                            RequstProductInfo(product);

                   }

                   GUILayout.Space(40);

                   for(inti=0; i<productInfo.Count; i++){

                            if(GUILayout.Button(productInfo[i],GUILayout.Height (100), GUILayout.MinWidth (200))){

                                     string[]cell = productInfo[i].Split('\t');

                                     Debug.Log("[Buy]"+cell[cell.Length-1]);

                                     BuyProduct(cell[cell.Length-1]);

                            }

                   }

         }

         boolBtn(string msg){

                   GUILayout.Space(100);

                   return       GUILayout.Button (msg,GUILayout.Width(200),GUILayout.Height(100));

         }

}

还需要在xcode里面编写内购代码然后复制到平台下,ios文件夹下:

Unity3d接入IOS内购

只能在真机上才能出现内购窗,Unity中运行效果如下:

Unity3d接入IOS内购

3、导出ios工程,在mac上xcode中打开:

Unity3d接入IOS内购

4、加入依赖项,libz,storekit:

Unity3d接入IOS内购

5、在真机上运行,首先获得商品列表,然后点击购买,然后在弹出的账号密码框里面修改为沙盒测试账号。即可测试购买成功。

Unity3d接入IOS内购

 2个.h文件分别为:

1、IAPInterface.h

#import <Foundation/Foundation.h>

@interface IAPInterface : NSObject

@end

2、IAPManager.h

#import <Foundation/Foundation.h>

#import <StoreKit/StoreKit.h>

@interface IAPManager : NSObject<SKProductsRequestDelegate, SKPaymentTransactionObserver>{

    SKProduct *proUpgradeProduct;

    SKProductsRequest *productsRequest;

}

-(void)attachObserver;

-(BOOL)CanMakePayment;

-(void)requestProductData:(NSString *)productIdentifiers;

-(void)buyRequest:(NSString *)productIdentifier;

@end

 2个.m文件分别为:

1/IAPManager.m

#import "IAPManager.h"

@implementation IAPManager

-(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("Main", "ShowProductList", [[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:@"\t"];

}

-(NSString *)transactionInfo:(SKPaymentTransaction *)transaction{

    return [self encode:(uint8_t *)transaction.transactionReceipt.bytes length:transaction.transactionReceipt.length];

    //return [[NSString alloc] initWithData:transaction.transactionReceipt encoding:NSASCIIStringEncoding];

}

-(NSString *)encode:(const uint8_t *)input length:(NSInteger) length{

    static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

    NSMutableData *data = [NSMutableData dataWithLength:((length+2)/3)*4];

    uint8_t *output = (uint8_t *)data.mutableBytes;

    for(NSInteger i=0; i<length; i+=3){

        NSInteger value = 0;

        for (NSInteger j= i; j<(i+3); j++) {

            value<<=8;

            if(j<length){

                value |=(0xff & input[j]);

            }

        }

        NSInteger index = (i/3)*4;

        output[index + 0] = table[(value>>18) & 0x3f];

        output[index + 1] = table[(value>>12) & 0x3f];

        output[index + 2] = (i+1)<length ? table[(value>>6) & 0x3f] : '=';

        output[index + 3] = (i+2)<length ? table[(value>>0) & 0x3f] : '=';

    }

    return [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];

}

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

    UnitySendMessage("Main", "ProvideContent", [[self transactionInfo:transaction] UTF8String]);

}

-(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;

        }

    }

}

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

    NSLog(@"Comblete transaction : %@",transaction.transactionIdentifier);

    [self provideContent:transaction];

    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

}

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

    NSLog(@"Failed transaction : %@",transaction.transactionIdentifier);

    if (transaction.error.code != SKErrorPaymentCancelled) {

        NSLog(@"!Cancelled");

    }

    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

}

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

    NSLog(@"Restore transaction : %@",transaction.transactionIdentifier);

    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

}

@end

2、#import "IAPInterface.h"

#import "IAPManager.h"

@implementation IAPInterface

void TestMsg(){

    NSLog(@"Msg received");

}

void TestSendString(void *p){

    NSString *list = [NSString stringWithUTF8String:p];

    NSArray *listItems = [list componentsSeparatedByString:@"\t"];

    for (int i =0; i<listItems.count; i++) {

        NSLog(@"msg %d : %@",i,listItems[i]);

    }

}

void TestGetString(){

    NSArray *test = [NSArray arrayWithObjects:@"t1",@"t2",@"t3", nil];

    NSString *join = [test componentsJoinedByString:@"\n"];

    UnitySendMessage("Main", "IOSToU", [join UTF8String]);

}

IAPManager *iapManager = nil;

void InitIAPManager(){

    iapManager = [[IAPManager alloc] init];

    [iapManager attachObserver];

}

bool IsProductAvailable(){

    return [iapManager CanMakePayment];

}

void RequstProductInfo(void *p){

    NSString *list = [NSString stringWithUTF8String:p];

    NSLog(@"productKey:%@",list);

    [iapManager requestProductData:list];

}

void BuyProduct(void *p){

    [iapManager buyRequest:[NSString stringWithUTF8String:p]];

}

@end