Unity3d发布IOS(包含u3d自带IAP内购)的流程-小白篇(三)-u3d配置ios内购部分

  上一篇地址:https://www.cnblogs.com/yzxhz/p/9572272.html

  在上一篇说道ios内购网页端的配置,配置好证书appID配置文件以及真机调试设备后,

  这篇文章还需要用到:内购产品的ID,内购产品的公共秘钥

  接下来就进入u3d操作环节。

  u3d可以与ios的代码进行相互调用,这样就可以用oc代码编写ios内购,然后再从unity调用,方法网上有,不过对于我这种看不懂oc的菜鸟来说,实在是为难我了。。。不过好在unity提供了一个IAP插件,解决了这个大问题。

  首先,想办法获取这个插件,可以去Windows=>Generel=> Asset Store  里面搜索 “Unity IAP”,图中第一个免费的就是。

或者也可以在Windows=>Generel=> Services 

里面找到上图红圈部分(注意后面这里,无论是插件在方法1找到还是2找到的,都要把上图这里的状态改为ON),点进去后

这里没有导入的话显示的是Import,我已近导入了,就显示的是Update,点击导入即可。

上述两种方法都要把Services的内购状态改为ON,还有都要登录账号才能操作。

 

接下来,上代码:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Text;
using LitJson;
using UnityEngine;
using UnityEngine.Purchasing;
using UnityEngine.Networking;

namespace TianBoWang.Function
{
    [Serializable]
    public class Products
    {
        public string id;
        public int productType;

    }

    /// <summary>
    /// 购买管理
    /// </summary>
    public class PurchaseManager : MonoBehaviour, IStoreListener
    {
        public List<Products> products = new List<Products>();
        public string publicKey;
        ConfigurationBuilder builder;
        private IStoreController m_Controller;
        private IAppleExtensions m_AppleExtensions;
        private int productIndex;
        private static bool isInited = false;
        private bool isInitFailed = false;

        void Awake()
        {

            if (!isInited)
            {
                InitPurchase();
            }
        }

        /// <summary>
        /// 初始化
        /// </summary>
        void InitPurchase()
        {

            var module = StandardPurchasingModule.Instance();
            builder = ConfigurationBuilder.Instance(module);

            for (int i = 0; i < products.Count; i++)
            {
                builder.AddProduct(products[i].id, (ProductType)products[i].productType);
            }

            UnityPurchasing.Initialize(this, builder);
        }
        /// <summary>
        /// 初始化成功
        /// </summary>
        /// <param name="controller">Controller.</param>
        /// <param name="extensions">Extensions.</param>
        public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
        {

            m_Controller = controller;
            m_AppleExtensions = extensions.GetExtension<IAppleExtensions>();
            m_AppleExtensions.RegisterPurchaseDeferredListener(OnDeferred);

            isInited = true;

        }
        /// <summary>
        /// iOS 网络延迟错误
        /// </summary>
        /// <param name="item">Item.</param>
        private void OnDeferred(Product item)
        {
            // Debug.Log("网络连接不稳");
        }

        /// <summary>
        /// 初始化失败
        /// </summary>
        /// <param name="error">Error.</param>
        public void OnInitializeFailed(InitializationFailureReason error)
        {
            isInitFailed = true;
            Debug.Log("IAPInitializeFailed!!!" + "Reason:" + error);
        }

        /// <summary>
        /// 恢复购买
        /// </summary>
        public void RestorePurchases()
        {

            if (Application.platform == RuntimePlatform.IPhonePlayer ||
                Application.platform == RuntimePlatform.OSXPlayer)
            {

                if (!isInited)
                {
                    //loading.SetActive(false);
                    InitPurchase();
                }

                StartCoroutine("InitAndRestore");
            }

        }

        IEnumerator InitAndRestore()
        {

            if (isInitFailed || !isInited)
            {
                //初始化失败
                StopCoroutine("InitAndRestore");

            }
            yield return new WaitUntil(() => { return m_Controller != null && m_AppleExtensions != null; });

            m_AppleExtensions.RestoreTransactions((result) =>
            {
                // The first phase of restoration. If no more responses are received on ProcessPurchase then 
                // no purchases are available to be restored.
                Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore.");

                if (result)
                {
                    //产品已经restore,不过官方的解释是恢复过程成功了,并不代表所购买的物品都恢复了
                }
                else
                {
                    // 恢复失败
                }

                StopCoroutine("InitAndRestore");
            });

        }

        /// <summary>
        /// 购买产品  购买的第几个    按钮点击
        /// </summary>
        /// <param name="index">Index.</param>
        public void OnPurchaseClicked(int index)
        {

            if (Application.platform == RuntimePlatform.IPhonePlayer ||
                Application.platform == RuntimePlatform.OSXPlayer)
            {

                if (!isInited)
                    InitPurchase();
                StartCoroutine("InitAndPurchase", index);
            }
        }
        IEnumerator InitAndPurchase(int index)
        {

            if (isInitFailed || !isInited)
            {
                //初始化失败
                StopCoroutine("InitAndPurchase");

            }

            yield return new WaitUntil(() => { return m_Controller != null && m_AppleExtensions != null; });

            m_Controller.InitiatePurchase(products[index].id);
            StopCoroutine("InitAndPurchase");
        }


        /// <summary>
        /// 购买成功回调
        /// </summary>
        /// <returns>The purchase.</returns>
        /// <param name="e">E.</param>
        public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e)
        {
            //使用id判断是否是当前购买的产品,我这里只有一个产品,所以就是products[0]
            if (e.purchasedProduct.definition.id == products[0].id)
            {
                string transactionReceipt = m_AppleExtensions.GetTransactionReceiptForProduct(e.purchasedProduct);
                StartCoroutine("CheckRecipe", transactionReceipt);//使用苹果的服务器进行验证订单是否有效
            }

            return PurchaseProcessingResult.Complete;
        }


        public void OnPurchaseFailed(Product i, PurchaseFailureReason p)
        {
            //购买失败的逻辑
        }



        HttpWebRequest request;
        IEnumerator CheckRecipe(string s)
        {
            JsonData json = new JsonData();
            json["receipt-data"] = s;
            json["password"] = publicKey;

            Uri urlReal = new Uri("https://buy.itunes.apple.com/verifyReceipt");//正式验证网址
                                                                                //   Uri urlSandBox = new Uri("https://sandbox.itunes.apple.com/verifyReceipt");//沙箱测试验证网址
            using (UnityWebRequest www = new UnityWebRequest(urlReal, "POST"))
            {
                byte[] postBytes = Encoding.UTF8.GetBytes(json.ToJson());
                www.uploadHandler = (UploadHandler)new UploadHandlerRaw(postBytes);
                www.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
                www.SetRequestHeader("Content-Type", "application/json");
                www.timeout = 20;//20秒后超时
                yield return www.Send();
                if (www.isNetworkError)
                {
                    //Debug.Log("网络错误:"+www.error);
                }
                else
                {
                    if (www.responseCode == 200)
                    {
                        JsonData resoultJson = JsonMapper.ToObject(www.downloadHandler.text);
                        if (resoultJson["status"].ToString() == "0")
                        {
                            //验证成功的逻辑
                        }
                        else
                        {
                            //验证失败的逻辑
                        }
                    }
                }
            }

            StopCoroutine("CheckRecipe");
        }
    }
}

2018/12/21补充:

m_Controller.products.WithID(/*<Product>.id*/).metadata.localizedPriceString;

使用这种方法获取iap后台的价格以及货币符号

使用方法:我这里使用的是LitJson插件, 将代码挂到任意物体

有几个需要购买的产品就在size里面写几。

id就是每个要购买的产品的id(苹果后台获取)

product Type代表类型,(0表示消耗品,1表示费消耗品,2表示订阅)

下面的public key是苹果后台产品的公共秘钥

然后在按钮点击的时候调用其中的购买方法( void OnPurchaseClicked(int i) ) 参数i代表在面板上加的第几个产品、,以及恢复购买方法( void RestorePurchases())即可。

其中有几处需要自己写逻辑的地方,我已经代码注释标明了,例如购买成功、失败之后要执行的逻辑等。

在二次验证的时候有一些坑:

1.二次验证向服务器发送不能使用WWW通讯。

2.不要使用C#的HttpWebRequest,这个鬼东西当你在协程中使用的时候,网络不好就会出现程序假死!要使用unity内置的 UnityWebRequest。

3.测试的时候用沙箱验证网址测试,送审的时候别忘了使用正式验证网址,不然被打回。

在二次验证中返回的正确/错误代码以及意思:

0 验证成功
21000 App Store不能读取你提供的JSON对象
21002 receipt-data域的数据有问题
21003 receipt无法通过验证
21004 提供的shared secret不匹配你账号中的shared secret
21005 receipt服务器当前不可用
21006 receipt合法,但是订阅已过期。服务器接收到这个状态码时,receipt数据仍然会解码并一起发送
21007 receipt是Sandbox receipt,但却发送至生产系统的验证服务
21008 receipt是生产receipt,但却发送至Sandbox环境的验证服务
原文地址:https://www.cnblogs.com/yzxhz/p/9618665.html