Unity3d IOS支付驗證處理漏單解決方案

這兩天一直在處理在Unity IOS支付,中間遇到很多問題,今天終於完成了,這裏記錄一下。

1、遊戲內購買類型 
我們知道IOS內購有兩種方式 
1、交付內置型產品的流程。 
1. 程序通過bundle存儲的plist文件得到產品標識符的列表。 
2. 程序向App Store發送請求,得到產品的信息。 
3. App Store返回產品信息。 
4. 程序把返回的產品信息顯示給用戶(App的store界面) 
5. 用戶選擇某個產品 
6. 程序向App Store發送支付請求 
7. App Store處理支付請求並返回交易完成信息。 
8. App獲取信息並提供內容給用戶。 

è¿éåå¾çæè¿°

2、 服務器類型 
1. 程序向服務器發送請求,獲得一份產品列表。 
2. 服務器返回包含產品標識符的列表。 
3. 程序向App Store發送請求,得到產品的信息。 
4. App Store返回產品信息。 
5. 程序把返回的產品信息顯示給用戶(App的store界面) 
6. 用戶選擇某個產品 
7. 程序向App Store發送支付請求 
8. App Store處理支付請求並返回交易完成信息。 
9. 程序從信息中獲得數據,併發送至服務器。 
10. 服務器紀錄數據,並進行審(我們的)查。 
11. 服務器將數據發給App Store來驗證該交易的有效性。 
12. App Store對收到的數據進行解析,返回該數據和說明其是否有效的標識。 
13. 服務器讀取返回的數據,確定用戶購買的內容。 
14. 服務器將購買的內容傳遞給程序。 

 

 

è¿éåå¾çæè¿°

我們項目用的是第二種,用自己的服務器來驗證蘋果交易是否有效。

後端服務器發送憑證失敗的處理 
如果出現網絡問題,導致無法驗證。我們需要持久化保存購買憑證,在用戶下次啓動APP的時候在後臺向服務器再一次發起驗證,直到成功然後移除該憑證。

//Unity裏Application.persistentDataPath 具有讀寫權限
public static string receiptURL = Application.persistentDataPath+"/ReceiptFile";
//通過要驗證的id來刪除本地保存的receipt 
//在接收到後端服務器成功返回充值成功消息後調用
public static void DeleteReceipt(string receiptID)
{
 //如果Receipt目錄不存在,不做任何處理,直接return
      if (!Directory.Exists(receiptURL))
      {
          return;
      }
      string receiptFile = string.Format("{0}/{1}", receiptURL, receiptID);
      //如果要刪除的receipt文件不存在,不做任何處理,直接return
      if (!File.Exists(receiptFile))
      {
          return;
      }
      //刪除驗證文件
      File.Delete(receiptFile);
}
//保存要驗證的交易信息
// @ApplePurchaseRequest request 是要發給後端的proto協議格式
public static void SaveReceipt(ApplePurchaseRequest request)
{
    //判斷要寫入的文件夾是否存在,如果不存在,創建目錄爲receiptURL的文件夾
    if (!Directory.Exists(receiptURL))
    {
        Directory.CreateDirectory(receiptURL);
    }
    //判斷要寫入的文件是否存在,如果不存在,創建路徑爲receiptFile的文件
    string receiptFile = string.Format("{0}/{1}", receiptURL, request.receiptId);
    StreamWriter sw;
    FileInfo t = new FileInfo(receiptFile);
    if (!t.Exists)
    {
        //如果此文件不存在則創建
        sw = t.CreateText();
    }
    else
    {
        //文件存在,打開文件
        sw = t.AppendText();
    }
    //將要保存的文件寫成json格式,轉化爲string保存到文件裏
    Debug.Log("Apple Transaction Receipt save address is :" + receiptFile);
    JsonData data = new JsonData();
    data["count"] = request.count;
    data["receiptData"] = request.receiptData;
    data["receiptId"] = request.receiptId;
    data["cost"] = request.cost;
    data["templateId"] = request.templateId;
    string dataStr = JsonMapper.ToJson(data);
    sw.Write(dataStr);
    sw.Close();
    sw.Dispose();
}
//發送transactionRectipt給後端服務器
private static void SendVerfication(IOSStoreKitResult result)
{
   string msg =string.Format("//////////////////11111111111111///////////////////\n");
   msg += string.Format("IOSStoreKitResult _ProductIdentifier : {0}\n" ,result.ProductIdentifier);
   msg += string.Format("IOSStoreKitResult _State : {0}\n", result.State);
   msg += string.Format("IOSStoreKitResult Receipt : {0}\n", result.Receipt);
   msg += string.Format("IOSStoreKitResult _TransactionIdentifier : {0}\n", result.TransactionIdentifier);
   msg += string.Format("IOSStoreKitResult _ApplicationUsername : {0}\n", result.ApplicationUsername);
   msg += string.Format("////////////////////11111111111111///////////////////////");

   ApplePurchaseRequest apr = new ApplePurchaseRequest();
   apr.count = 1;
   apr.receiptData = result.Receipt;
   apr.receiptId = result.TransactionIdentifier;
   int templeteID = 0;
   float cost = 0;
   foreach (var it in ConfigManager.Instance().ShopConfigData)
   {
       if (it.Value.AppStoreId.ToString() == result.ProductIdentifier)
       {
           templeteID = it.Value.ID;
           cost = it.Value.Price;
           break;
       }
   }
   apr.cost = cost.ToString();
   apr.templateId = templeteID;

   MessagePacket msgPacket = new MessagePacket();
   msgPacket.Body = ProtobufUtility.GetByteFromProtoBuf(apr);
   msgPacket.Code = Utils.BuildProtoCode((int)Module.SHOP, (int)Shop.APPLE_PURCHASE);
   NetMgr.Instance().SendMsg(msgPacket, null);
   //在給後端發送驗證的時候保存到本地,這和收到服務器返回的消息後刪除receipt 成對調用
   SaveReceipt(apr);

   Debug.Log(msg);
}

如果在給發送到後端服務器的時候沒有成功收到返回,在重新啓動遊戲時,去檢查receiptURL目錄下所有文件,把所有文件再重新向後端服務器發送

public IEnumerator CheckReceipt()
    {
        //判斷receiptURL目錄是否存在,如果不存在則不處理
        if (!Directory.Exists(PaymentManager.receiptURL))
        {
            yield return null;
        }
        else
        {
            //得到receiptURL目錄下的所有文件,讀取文件裏有內容,發送給後端服務器再重新驗證
            string[] files = Directory.GetFiles(PaymentManager.receiptURL);
            foreach (string file in files)
            {
                string receiptFile = string.Format("{0}/{1}", PaymentManager.receiptURL, Path.GetFileName(file));
                receiptFile = "file://" + receiptFile;

                WWW www = new WWW(receiptFile);
                yield return www;
                string _result = www.text;

                JsonData data = JsonMapper.ToObject(_result);
                ApplePurchaseRequest apr = new ApplePurchaseRequest();
                apr.cost = data["cost"].ToString();
                apr.count = (int)data["count"];
                apr.receiptData = data["receiptData"].ToString();
                apr.receiptId = data["receiptId"].ToString();
                apr.templateId = (int)data["templateId"];
                MessagePacket msgPacket = new MessagePacket();
                msgPacket.Body = ProtobufUtility.GetByteFromProtoBuf(apr);
                msgPacket.Code = Utils.BuildProtoCode((int)Module.SHOP, (int)Shop.APPLE_PURCHASE);
                NetMgr.Instance().SendMsg(msgPacket, null);
            }
        }
    }

這樣來處理掉單問題。

如果有不正確的地方,希望大家指正。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章