Unity苹果内购

喜欢而已 提交于 2020-01-26 06:00:42

Unity内购IAP的二三事

Unity端

这里需要使用Unity内置的IAP插件,下载IAP插件的方式:Window–>Services–>In-App Purchasing(这个就是unity内置的内购插件),点击把OFF->ON
在这里插入图片描述
点击之后会有个询问是否是对13岁以下儿童设计的游戏,按照情况勾选,之后点击continue进入安装界面,点击input安装

下面的Options是谷歌商店支付相关的,可以空着不用管它。
下载好插件之后会包含IAP内购的Demo,可以点击查看效果,此Demo在Unity上运行点击购买会出现一个fakeStore界面,在真机测试中会弹出正常IP的支付界面。

此时当你下载好IAP插件点击Window后会发现多出来一个UnityIAP选项,这时插件就已经安装好了。
当安装好插件后,有两种方式实现IAP内购:

一、使用IAPButton按钮
这种方法可以在层级界面点击右键可以看到UnityIAP->IAPButton,在要生成支付按钮的位置生成它,它就是一个挂载IAPButton脚本的Button按钮。
在这里插入图片描述
Product ID是设置在Unity端你要购买物品的ID,它是与你在APPStore上设置的购买项目在Unity端的映射,这个ID可以自己设定,点击下面IAP Catalog可以设定这个映射关系。
在这里插入图片描述
ID就是上文中所说的Product ID,Type是在APPStore生成内购物品时的哪几种类型,在下面是物品描述,在下面的Store ID Overrides就是在各个平台的商店里物品的正式ID了,第一个就是苹果APPStore的。再然后最下面Apple Configuration选择的是购买物品的费用,和你APPStore选一样的就行了。

IAPButton图中ButtonType有两种,当前的是交易类型按钮,还有一种是Restore按钮,用来查询和重置非消耗项目物品时使用。

在下面有两个On Purchase Complete和On Purchase Failed,这两个分别可以添加回调成功和失败后要执行的事件,使用方法就跟Button按钮最下面的On Click一模一样。

然后运行点击这个按钮就可以触发内购了。(这种方式的OK了,就是这么智能。。。。但是这种方法要设定好这个IAPButton,我们的项目先进行的安卓端的测试,UI界面已经都设置好了,要使用的话我需要把之前的的Button都改一下,好像挺麻烦的,所以用下面代码控制更灵活一点)

二、使用代码控制
下面就来用代码实现IAP内购
下面我先贴一下代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Purchasing;
 
/// <summary>
/// 这是通用方式,通过读取catalog里面的信息,获取所有商品信息
/// </summary>
public class ApplePayModule: MonoBehaviour, IStoreListener
{
    private static ApplePayModule m_instance;
    public static ApplePayModule Instance { get { return m_instance; } }
    private static IStoreController m_StoreController;
    private static IExtensionProvider m_StoreExtensionProvider;
    
    bool blnRestore = false;//用来表示
    bool blnPressRestore = false;//用来区分是否按了	restore 按钮

    //catalog 里面设置的id,和这边一一对应,这个id是unity端的一个映射,在catalog里面可以对应不同平台的真实的 商品id
    private string[] kProducts = new string[] {
        "buycoins0",
        "buycoins1",
    };
    void Start()
    {
        if (m_instance == null)
        {
            m_instance = this;
        }
#if UNITY_EDITOR
#elif UNITY_ANDROID
#elif UNITY_IPHONE
        InitializePurchasing();
#endif

    }
    private bool IsInitialized()
    {
        return m_StoreController != null && m_StoreExtensionProvider != null;
    }
    /// <summary>
    /// 初始化内购项目,主要是从catalog中获取商品信息,设置给 UnityPurchasing
    /// </summary>
    void InitializePurchasing()
    {
        if (IsInitialized())
        {
            Debug.Log("购买项目=======================>>>>>>>>>>>>>>>>>>>>>>>初始化失败");
            return;
        }
        Debug.Log("开始初始化物品");
        StandardPurchasingModule module = StandardPurchasingModule.Instance();
        module.useFakeStoreUIMode = FakeStoreUIMode.StandardUser;
        ConfigurationBuilder builder = ConfigurationBuilder.Instance(module);
        //通过编辑器中的Catalog添加,方便操作
        ProductCatalog catalog = ProductCatalog.LoadDefaultCatalog();
        //下面是IAP最新封装好的读取IAP Catalog里映射表的方法和下面foreach中代码是等效的
        //IAPConfigurationHelper.PopulateConfigurationBuilder(ref builder, catalog); 
        // Debug.Log(catalog.allProducts.Count);
        foreach (var product in catalog.allProducts)
        {
            if (product.allStoreIDs.Count > 0)
            {
                // Debug.Log("product:" + product.id);
                var ids = new IDs();
                foreach (var storeID in product.allStoreIDs)
                {
                    ids.Add(storeID.id, storeID.store);
                    // Debug.Log("stordId:" + storeID.id  + ", " + storeID.store);
                }
                builder.AddProduct(product.id, product.type, ids);
            }
            else
            {
                builder.AddProduct(product.id, product.type);
            }
        }
        UnityPurchasing.Initialize(this, builder);
    }
    //供外部调用,当按 Restore 按钮时触发
    public void OnRestore()
    {
        if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.WindowsEditor)
        {
            Debug.Log("Restore success!");
        }
        else
        {
            blnRestore = true;
            RestorePurchases();
            blnPressRestore = true;
        }
    }
    /// <summary>
    /// 供外部调起苹果支付
    /// </summary>
    /// <param name="str"></param>
    public void ApplePay(string str)
    {
        BuyProductID(str);
    }

    //这里是通过商品id购买物品
    void BuyProductID(string productId)
    {
        if (IsInitialized())
        {
            Debug.Log("Buy ProductID: " + productId);
            Product product = m_StoreController.products.WithID(productId);
           
            if (product != null && product.availableToPurchase)
            {
                Debug.Log(string.Format("Purchasing product asychronously: '{0}'", product.definition.id));
                m_StoreController.InitiatePurchase(product);
            }
            else
            {
                Debug.Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
            }
        }
        else
        {
            Debug.Log("购买物品时========================================没出初始化");
        }
    }
    //真是的发起Restore请求
    public void RestorePurchases()
    {
        if (!IsInitialized())
        {
            Debug.Log("没出初始化");
            return;
        }
        if (Application.platform == RuntimePlatform.IPhonePlayer || Application.platform == RuntimePlatform.OSXPlayer)
        {
            // Debug.Log("RestorePurchases started ...");
            var apple = m_StoreExtensionProvider.GetExtension<IAppleExtensions>();
            apple.RestoreTransactions(HandleRestored);
            
        }
        else
        {
            Debug.Log("RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform);
        }

    }
    //如果restore之后,会返回一个状态,如果状态为true,那边以前购买的非消耗物品都会回调一次 ProcessPurchase 然后在这里个回调里面进行处理
    void HandleRestored(bool result)
    {
        //返回一个bool值,如果成功,则会多次调用支付回调,然后根据支付回调中的参数得到商品id,最后做处理(ProcessPurchase)
        // Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore.");
        blnRestore = false;
        if (result)
        {
            Debug.Log("Restore success!");
        }
        else
        {
            Debug.Log("Restore Failed!");
        }
    }
    //初始化
    public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    {
        //初始化成功
        Debug.Log("OnInitialized: =========================>>>>>>>>>>>>>>>>>>>>>>>>>>PASS");
        m_StoreController = controller;
        m_StoreExtensionProvider = extensions;
    }
    public void OnInitializeFailed(InitializationFailureReason error)
    {
        //初始化失败
        Debug.Log("OnInitializeFailed InitializationFailureReason:" + error);
        if (error == InitializationFailureReason.AppNotKnown)
        {
            //
        }
        else if (error == InitializationFailureReason.NoProductsAvailable)
        {
            Debug.Log("NoProducts========================================Available");
        }
        else if (error == InitializationFailureReason.PurchasingUnavailable)
        {
            //
        }
    }
    //购买成功后的回调,包括restore的商品
    public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    {
       
        string str = args.purchasedProduct.receipt;
        //下面是我们公司客户端购买成功后通知服务器的方法,这里填你自己的逻辑
        GameManager.GetManager().GetGameModuleManager().GetModule<PayModule>().NetIOSPayTradeQuery(str);
        Debug.Log(str);
        return PurchaseProcessingResult.Complete;
    }
    //购买失败回调,根据具体情况给出具体的提示
    public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
    {
        //支付失败
        Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
        if (failureReason == PurchaseFailureReason.UserCancelled)
        {
            TipManager.Instance.ShowSimpleTextTip("用户取消交易");//用户取消交易
        }
        else if (failureReason == PurchaseFailureReason.ExistingPurchasePending)
        {
            TipManager.Instance.ShowSimpleTextTip("上一笔交易还未完成");//上一笔交易还未完成
        }
        else if (failureReason == PurchaseFailureReason.PaymentDeclined)
        {
            TipManager.Instance.ShowSimpleTextTip("拒绝付款");//拒绝付款
        }
        else if (failureReason == PurchaseFailureReason.ProductUnavailable)
        {
            TipManager.Instance.ShowSimpleTextTip("商品不可用");//商品不可用
        }
        else if (failureReason == PurchaseFailureReason.PurchasingUnavailable)
        {
            TipManager.Instance.ShowSimpleTextTip("支付不可用");//支付不可用
        }
        else
        {
            TipManager.Instance.ShowSimpleTextTip("未知错误");//未知错误
        }
    }

}

在上面的代码里也需要设定IAP Catalog中设定Unity和具体平台的映射关系:Window->UnityIAP->IAPCatalog 点开和那个按钮设定是一样的。(当然也可以手动添加,这里就不具体给出代码了)
在外部调用ApplePay(string str)方法时传入你设定在IAPCatalog里的Product ID就可以调起支付购买所对应APPStore上设置的物品了。
方法:ProcessPurchase(PurchaseEventArgs args)---->>>>>是购买成功后APPStore给的回调,args是它给返回的用以二次验证的信息,网络游戏的话在这里把它里面的receipt传到服务器端,然后服务器再到苹果给的验证平台上进行二次验证。

iOS平台进行打包

这里我们的项目使用了ShareSDK和UpSDK,所以打包之前我将这两个都删除掉然后重新导入package包,ShareSDK这里有两个控制使用平台的脚本ShareSDK.cs和ShareSDKDevInfo.cs暂时取出,两个package包导入成功后,把这两个替换回我们原来的。
然后就悬着iOS平台打包。这里打包完成后会少一个ShareSDK的依赖库,打完包需要在Xcode上进行是把它这个iOS的依赖库文件夹拖到Xcode上。

一、在AppStoreConnect上添加APP项目
先申请添加一个APP项目,这个项目的Bundle在下面生成证书的时候会用到。

在这个APP项目里添加需要购买的项目,信息填完之后最下面的屏幕截图一定要有,测试用的时候可以找一张大小适当的图片代替,(正式发布可能需要正式的截图以及描述)。这里添加的ID就是UnityIAPCatalog里添加的那个,一定要一致。

然后最下面有个添加测试员账号,用以添加沙箱测试用的账号,账号的邮箱可以随便写一个你能记得住的不存在的邮箱就可以。

二、配置Xcode环境
这里有个相关的操作博客:https://www.jianshu.com/p/b1b77d804254

三、打包测试
打开Xcode选择Unity iOS平台打包好的文件夹,会自动加载项目,然后需要把项目的bundleID写和APPStore,以及上一步生成的证书绑定的一致,不然程序无法链接到APPStore上的那个APP项目,从而无法初始化要购买的项目,还有苹果系统7.0以前使用的内购方式不一致,需要选择一下苹果运行的系统,现在最低也是8.0,如何你不改的会他会自动识别unity端打包的时候设定的最小支持的版本导则无法支付成功。
将在第二步中添加的测试设备连接到电脑上。
然后就是添加一些依赖库(这些依赖库并不是苹果内购IAP所需要的依赖库,而是使用其他插件时所需要的依赖库,如果其他插件不需要的话,直接打包运行)。

Xcode也可以看到过程日志,如果有错误就分局错误进行分析解决把。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!