public static GloebitTransaction Get(string transactionIDStr)
        {
            m_log.InfoFormat("[GLOEBITMONEYMODULE] in Transaction.Get");
            GloebitTransaction transaction = null;

            lock (s_transactionMap) {
                s_transactionMap.TryGetValue(transactionIDStr, out transaction);
            }

            if (transaction == null)
            {
                m_log.DebugFormat("[GLOEBITMONEYMODULE] Looking for prior transaction for {0}", transactionIDStr);
                GloebitTransaction[] transactions = GloebitTransactionData.Instance.Get("TransactionID", transactionIDStr);

                switch (transactions.Length)
                {
                case 1:
                    transaction = transactions[0];
                    m_log.DebugFormat("[GLOEBITMONEYMODULE] FOUND TRANSACTION! {0} {1} {2}", transaction.TransactionID, transaction.PayerID, transaction.PayeeID);
                    lock (s_transactionMap) {
                        s_transactionMap[transactionIDStr] = transaction;
                    }
                    return(transaction);

                case 0:
                    m_log.DebugFormat("[GLOEBITMONEYMODULE] Could not find transaction matching tID:{0}", transactionIDStr);
                    return(null);

                default:
                    throw new Exception(String.Format("[GLOEBITMONEYMODULE] Failed to find exactly one transaction for {0}", transactionIDStr));
                }
            }

            return(transaction);
        }
        // Creates a new transaction
        // First verifies that a transaction with this ID does not already exist
        // --- If existing txn is found, returns null
        // Creates new Transaction, stores it in the cache and db
        public static GloebitTransaction Create(UUID transactionID, UUID payerID, string payerName, UUID payeeID, string payeeName, int amount, int transactionType, string transactionTypeString, bool isSubscriptionDebit, UUID subscriptionID, UUID partID, string partName, string partDescription, UUID categoryID, uint localID, int saleType)
        {
            // Create the Transaction
            GloebitTransaction txn = new GloebitTransaction(transactionID, payerID, payerName, payeeID, payeeName, amount, transactionType, transactionTypeString, isSubscriptionDebit, subscriptionID, partID, partName, partDescription, categoryID, localID, saleType);

            // Ensure that a transaction does not already exist with this ID before storing it
            string             transactionIDstr = transactionID.ToString();
            GloebitTransaction existingTxn      = Get(transactionIDstr);

            if (existingTxn != null)
            {
                // Record in DB store with this id -- return null
                return(null);
            }
            // lock cache and ensure there is still no existing record before storing this txn.
            lock (s_transactionMap) {
                if (s_transactionMap.TryGetValue(transactionIDstr, out existingTxn))
                {
                    return(null);
                }
                else
                {
                    // Store the Transaction in the fast access cache
                    s_transactionMap[transactionIDstr] = txn;
                }
            }

            // Store the Transaction to the persistent DB
            GloebitTransactionData.Instance.Store(txn);

            return(txn);
        }
        /**************************************************/
        /******* ASSET STATE MACHINE **********************/
        /**************************************************/

        public static bool ProcessStateRequest(string transactionIDstr, string stateRequested, IAssetCallback assetCallbacks, GloebitAPIWrapper.ITransactionAlert transactionAlerts, out string returnMsg)
        {
            bool result = false;

            // Retrieve asset
            GloebitTransaction myTxn = GloebitTransaction.Get(UUID.Parse(transactionIDstr));

            // If no matching transaction, return false
            // TODO: is this what we want to return?
            if (myTxn == null)
            {
                returnMsg = "No matching transaction found.";
                return(false);
            }

            // Attempt to avoid race conditions (not sure if even possible)
            bool alreadyProcessing = false;

            lock (s_pendingTransactionMap) {
                alreadyProcessing = s_pendingTransactionMap.ContainsKey(transactionIDstr);
                if (!alreadyProcessing)
                {
                    // add to race condition protection
                    s_pendingTransactionMap[transactionIDstr] = myTxn;
                }
            }
            if (alreadyProcessing)
            {
                returnMsg = "pending";  // DO NOT CHANGE --- this message needs to be returned to Gloebit to know it is a retryable error
                return(false);
            }

            // Call proper state processor
            switch (stateRequested)
            {
            case "enact":
                result = myTxn.enactHold(assetCallbacks, transactionAlerts, out returnMsg);
                break;

            case "consume":
                result = myTxn.consumeHold(assetCallbacks, transactionAlerts, out returnMsg);
                if (result)
                {
                    lock (s_transactionMap) {
                        s_transactionMap.Remove(transactionIDstr);
                    }
                }
                break;

            case "cancel":
                result = myTxn.cancelHold(assetCallbacks, transactionAlerts, out returnMsg);
                if (result)
                {
                    lock (s_transactionMap) {
                        s_transactionMap.Remove(transactionIDstr);
                    }
                }
                break;

            default:
                // no recognized state request
                returnMsg = "Unrecognized state request";
                result    = false;
                break;
            }

            // remove from race condition protection
            lock (s_pendingTransactionMap) {
                s_pendingTransactionMap.Remove(transactionIDstr);
            }
            return(result);
        }