using System; using System.IO; using System.Text; namespace UnityEngine.Purchasing { /// /// Records processed transactions on the file system /// for de duplication purposes. /// internal class TransactionLog { private readonly ILogger logger; private readonly string persistentDataPath; public TransactionLog(ILogger logger, string persistentDataPath) { this.logger = logger; if (!string.IsNullOrEmpty(persistentDataPath)) { this.persistentDataPath = Path.Combine(Path.Combine(persistentDataPath, "Unity"), "UnityPurchasing"); } } /// /// Removes all transactions from the log. /// public void Clear() { Directory.Delete(persistentDataPath, true); } public bool HasRecordOf(string transactionID) { if (string.IsNullOrEmpty(transactionID) || string.IsNullOrEmpty(persistentDataPath)) { return false; } return Directory.Exists(GetRecordPath(transactionID)); } public void Record(string transactionID) { // Consumables have additional de-duplication logic. if (!(string.IsNullOrEmpty(transactionID) || string.IsNullOrEmpty(persistentDataPath))) { var path = GetRecordPath(transactionID); try { Directory.CreateDirectory(path); } catch (Exception recordPathException) { // A wide variety of exceptions can occur, for all of which // nothing is the best course of action. logger.LogException(recordPathException); } } } private string GetRecordPath(string transactionID) { return Path.Combine(persistentDataPath, ComputeHash(transactionID)); } /// /// Compute a 64 bit Knuth hash of a transaction ID. /// This should be more than sufficient for the few thousand maximum /// products expected in an App. /// internal static string ComputeHash(string transactionID) { var hash = 3074457345618258791ul; for (var i = 0; i < transactionID.Length; i++) { hash += transactionID[i]; hash *= 3074457345618258799ul; } var builder = new StringBuilder(16); foreach (var b in BitConverter.GetBytes(hash)) { builder.AppendFormat("{0:X2}", b); } return builder.ToString(); } } }