上一篇地址: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環境的驗證服務 |