• Unity调用iOS原生内购


    我项目中主要是对消耗品进行内购,非消耗品没进行测试,对iOS商店后台的构建,我这边不说了,下面主要是对Unity怎么实现iOS原生内购功能进行讲解。

    一 在Xcode中编写原生内购代码给Unity调用

     1 IAPManager.h

    #import <Foundation/Foundation.h>
    #import <StoreKit/StoreKit.h>
    
    @interface IAPManager : NSObject<SKProductsRequestDelegate, SKPaymentTransactionObserver>{
        SKProduct *proUpgradeProduct;
        SKProductsRequest *productsRequest;
        NSString *productIndentify;
    }
    
    -(void)attachObserver;
    -(BOOL)CanMakePayment;
    -(void)requestProductData:(NSString *)productIdentifiers;
    -(void)buyRequest:(NSString *)productIdentifier;//保存Unity传递的商品ID
    
    @end

    2 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:@"	"];    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{
        
        NSLog(@"-----------收到产品反馈信息--------------");
        NSArray *products = response.products;
        NSLog(@"产品Product ID:%@",response.invalidProductIdentifiers);
        NSLog(@"产品付费数量: %d", (int)[products count]);
        // populate UI
        
        for (SKProduct *p in products) {
            NSLog(@"product info");
            NSLog(@"SKProduct 描述信息%@", [products description]);
            NSLog(@"产品标题 %@" , p.localizedTitle);
            NSLog(@"产品描述信息: %@" , p.localizedDescription);
            NSLog(@"价格: %@" , p.price);
            NSLog(@"Product id: %@" , p.productIdentifier);
            
            UnitySendMessage("IOSIAPMgr", "ShowProductList", [[self productInfo:p] UTF8String]);
        }
        
        for(NSString *invalidProductId in response.invalidProductIdentifiers){
            NSLog(@"Invalid product id:%@",invalidProductId);
        }
        
        // [request autorelease];
    }
    
    -(void)buyRequest:(NSString *)productIdentifier{
        //  NSArray* transactions=[SKPaymentQueue defaultQueue].transactions;
        //  if(transactions.count>0)
        // {
        // for(SKPaymentTransaction *tran in transactions)
        // {
        //   NSLog(@"**************************************************************%@",tran.transactionState);
        //检查是否有完成的交易
        //   SKPaymentTransaction* transaction =[transactions firstObject];
        // if(tran.transactionState==SKPaymentTransactionStatePurchasing)
        //   {
        //   NSLog(@"----------------------%@",tran.transactionState);
        //   [[SKPaymentQueue defaultQueue] finishTransaction:tran];
        // return;
        //  }
        //}
        
        // }
        productIndentify=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:@"	"];
    }
    
    -(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("IOSIAPMgr", "ProvideContent", [[self transactionInfo:transaction] UTF8String]);
    }
    
    //沙盒测试环境验证
    #define SANDBOX @"https://sandbox.itunes.apple.com/verifyReceipt"
    //正式环境验证
    #define AppStore @"https://buy.itunes.apple.com/verifyReceipt"
    /**
     *  验证购买,避免越狱软件模拟苹果请求达到非法购买问题
     *
     */
    -(void)verifyPurchaseWithPaymentTransaction{
        //从沙盒中获取交易凭证并且拼接成请求体数据
        NSURL *receiptUrl=[[NSBundle mainBundle] appStoreReceiptURL];
        NSData *receiptData=[NSData dataWithContentsOfURL:receiptUrl];
        
        NSString *receiptString=[receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];//转化为base64字符串
        
        NSString *bodyString = [NSString stringWithFormat:@"{"receipt-data" : "%@"}", receiptString];//拼接请求数据
        NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
        
        
        //测试的时候填写沙盒路径,上APPStore的时候填写正式环境路径
        NSURL *url=[NSURL URLWithString:SANDBOX];
        NSMutableURLRequest *requestM=[NSMutableURLRequest requestWithURL:url];
        requestM.HTTPBody=bodyData;
        requestM.HTTPMethod=@"POST";
        //创建连接并发送同步请求
        NSError *error=nil;
        NSData *responseData=[NSURLConnection sendSynchronousRequest:requestM returningResponse:nil error:&error];
        if (error) {
            NSLog(@"验证购买过程中发生错误,错误信息:%@",error.localizedDescription);
            return;
        }
        NSDictionary *dic=[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:nil];
        NSLog(@"%@",dic);
        if([dic[@"status"] intValue]==0){
            NSLog(@"购买成功!");
            NSDictionary *dicReceipt= dic[@"receipt"];
            
            NSLog(@"--------------%@",dicReceipt);
            
            //NSDictionary *dicInApp=[dicReceipt[@"in_app"] firstObject];
            for(NSDictionary  *tmp in dicReceipt[@"in_app"])
            {
                
                
                // NSLog(@"+++++++++++%@",dicInApp);
                NSString *productIdentifier= tmp[@"product_id"];//读取产品标识
                NSLog(@"+++++++++++++++++++++++++++++++++++++%@",productIdentifier);
                NSLog(@"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%@",productIndentify);
                //如果是消耗品则记录购买数量,非消耗品则记录是否购买过
                NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
                if ([productIdentifier isEqualToString:productIndentify]) {
                    NSInteger purchasedCount=[defaults integerForKey:productIdentifier];//已购买数量
                    [[NSUserDefaults standardUserDefaults] setInteger:(purchasedCount+1) forKey:productIdentifier];
                    UnitySendMessage("IOSIAPMgr", "BuyProcuctSucessCallBack",productIdentifier.UTF8String);
                    break;
                }else{
                    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:productIdentifier];
                    UnitySendMessage("IOSIAPMgr", "BuyProcuctSucessCallBack",productIdentifier.UTF8String);
                    break;
                }
                
            }
        }else{
            NSLog(@"购买失败,未通过验证!");
        }
    }
    //监听购买结果
    - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{
        for(SKPaymentTransaction *tran in transaction){
            switch (tran.transactionState) {
                case SKPaymentTransactionStatePurchased:{
                    NSLog(@"交易完成");
                    // 发送到苹果服务器验证凭证
                    [self verifyPurchaseWithPaymentTransaction];
                    [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                    
                }
                    break;
                case SKPaymentTransactionStatePurchasing:
                    NSLog(@"商品添加进列表");
                    
                    break;
                case SKPaymentTransactionStateRestored:{
                    NSLog(@"已经购买过商品");
                    
                    [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                }
                    break;
                case SKPaymentTransactionStateFailed:{
                    NSLog(@"交易失败");
                    UIAlertView *mBoxView = [[UIAlertView alloc]
                                             initWithTitle:@"交易提示"
                                             message:@"交易失败"
                                             delegate:nil
                                             cancelButtonTitle:@"Cancel"
                                             otherButtonTitles:@"OK", nil];
                    [mBoxView show];
                    [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                }
                    break;
                default:
                {
                    [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                }
                    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

    3  IAPInterface.h

    #import <Foundation/Foundation.h>
    
    @interface IAPInterface : NSObject
    
    @end

    4 IAPInterface.mm  Untiy主要是调用这个接口去调用IAPMananger

    #import "IAPInterface.h"
    #import "IAPManager.h"
    
    @implementation IAPInterface
    #if defined (__cplusplus)
    extern "C"
    {
    #endif
        IAPManager *iapManager = nil;
        //初始化商品信息
        void InitIAPManager(){
            iapManager = [[IAPManager alloc] init];
            [iapManager attachObserver];
            
        }
        //判断是否可以购买
        bool IsProductAvailable(){
            return [iapManager CanMakePayment];
        }
        //获取商品信息
        void RequstProductInfo(char *p){
            NSString *list = [NSString stringWithUTF8String:p];
            NSLog(@"商品列表:%@",list);
            [iapManager requestProductData:list];
        }
        //购买商品
        void BuyProduct(char *p){
            [iapManager buyRequest:[NSString stringWithUTF8String:p]];
        }
    
    #if defined (__cplusplus)
    }
    #endif
    
    @end

    二 Unity调用Xcode主要是通过 [DllImport("__Internal")]去调用,代码如下:

      using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    
    public class IOSIAPMgr : MonoBehaviour
    {
            
             public List<string> productInfo = new List<string>();
            
             private static IOSIAPMgr _instance;
             public static IOSIAPMgr Instance{
                 get{
                   //  if(_instance==null)
                    // {
                        // GameObject go=new GameObject("IOSIAPMgr");
                       //  DontDestroyOnLoad (go);
                       //  _instance=go.AddComponent<IOSIAPMgr>();
                         
                   //  }
                     return _instance;
                 }
             }
        private string _goodsID = "";
        void Awake()
        {
            _instance = this;
            Init ();
            StartCoroutine (InitProductInfo ());
            DontDestroyOnLoad (this.gameObject);
    
        }
        private IEnumerator InitProductInfo()
        {
            yield return new WaitForSeconds(5f);
            IOSIAPMgr.Instance.RequstALLProductInfo();
        }
        
         
             [DllImport("__Internal")]
             private static extern void InitIAPManager();//初始化
            
             [DllImport("__Internal")]
             private static extern bool IsProductAvailable();//判断是否可以购买
            
             [DllImport("__Internal")]
             private static extern void RequstProductInfo(string s);//获取商品信息
            
             [DllImport("__Internal")]
             private static extern void BuyProduct(string s);//购买商品
            
             //测试从xcode接收到的字符串
             void IOSToU(string s)
             {
                 Debug.Log("[MsgFrom ios]"+s);
             }
            
             //获取product列表
             void ShowProductList(string s){
                 productInfo.Add(s);
             }
            
             //获取商品回执
             void ProvideContent(string s)
             {
                 Debug.Log("[MsgFrom ios]proivideContent : "+s);
             }
             public  void Init ()
             {
                Debug.Log ("初始化InitIAPManager");
                InitIAPManager();
                 
             }
    
            public bool IsProductVailable()
            {
                return IsProductAvailable();
            }
    
            public void RequstALLProductInfo()
            {
                if(IsProductAvailable())
                {
                 RequstProductInfo("com.smallMBag");
                 RequstProductInfo("com.MidMBag");
                 RequstProductInfo("com.BigMBag");
     
                 RequstProductInfo("com.SpecialM");
                 RequstProductInfo("com.SmallFBag");
                 RequstProductInfo("com.Weapon");
                 //RequstProductInfo("com.SingleFuHuoSHi");
                  
                 RequstProductInfo("com.BigFBag");
                }
              
            }
             
            /// <summary>
            /// 购买商品成功的回调
            /// </summary>
            /// <param name="str">String.</param>
           public void BuyProcuctSucessCallBack(string str)
           {
                 
             
                  
                   
           }
            /// <summary>
            /// 购买商品失败调回调
            /// </summary>
            /// <param name="str">String.</param>
            public void BuyProcuctFailedCallBack(string str)
            {
               
            }
    
    
    
          
          
    }

    三 注意事项
      1 iOS 11的系统对沙盒测试有bug,第一次会让你输入沙盒账号和密码,第二次也会再让你输入一次,最后购买的时候,直接购买失败(仅对沙盒测试,正式上线的时候没有这个问题)。

      2 iOS内购的时候非消耗品的交易一直会存在账单里面而且不会被清除。

      3 使用沙盒账号测试的时候要退出登录苹果手机的账号,不然也对导致购买失败。

      4 在内购商品之前也调用请求商品信息。

  • 相关阅读:
    (转)android
    (转)Eclipse 扩大内存
    (转)setTextColor()的参数设置方式
    RelativeLayout里的gravity不能居中的解决方法
    hdu1010 Tempter of the Bone
    ubuntu系统下如何禁用笔记本触摸板
    十滴水,半成品,大多数关卡可以过去,不过也有几个过不去,仍在调试中,不断更新
    HDU1007 TLE代码和AC代码对比
    hdu1013
    1005 矩阵快速幂
  • 原文地址:https://www.cnblogs.com/weiqiangwaideshijie/p/9103407.html
Copyright © 2020-2023  润新知