public Transaction GetTransaction(string paymentId)
        {
            string url = BeanstreamUrls.GetPaymentUrl
                .Replace("{v}", String.IsNullOrEmpty(_configuration.Version) ? "v1" : "v"+_configuration.Version)
                .Replace("{p}", String.IsNullOrEmpty(_configuration.Platform) ? "www" : _configuration.Platform)
                .Replace("{id}", paymentId);

            HttpsWebRequest req = new HttpsWebRequest () {
                MerchantId = _configuration.MerchantId,
                Passcode = _configuration.PaymentsApiPasscode,
                WebCommandExecutor = _webCommandExecuter
            };

            string response = req.ProcessTransaction (HttpMethod.Get, url);

            return JsonConvert.DeserializeObject<Transaction>(response);
        }
        /// <summary>
        /// Query for transaction data. You must specify a start date and an end date, as well as search criteria.
        /// You also specify the start row and end row for paging since the search will limit the number of returned results to 1000.
        /// </summary>
        /// <param name="reportName">Report name. </param>
        /// <param name="startDate">Start date.</param>
        /// <param name="endDate">End date.</param>
        /// <param name="startRow">Start row.</param>
        /// <param name="endRow">End row.</param>
        /// <param name="criteria">Criteria.</param>
        public List<TransactionRecord> Query(DateTime startDate, DateTime endDate, int startRow, int endRow, params Criteria[] criteria)
        {
            if (endDate == null || startDate == null)
                throw new ArgumentNullException ("Start Date and End Date cannot be null!");
            if (endDate < startDate)
                throw new ArgumentException ("End Date cannot be less than Start Date!");
            if (endRow < startRow)
                throw new ArgumentException ("End Row cannot be less than Start Row!");
            if (endRow - startRow > 1000)
                throw new ArgumentException ("You cannot query more than 1000 rows at a time!");

            string url = BeanstreamUrls.ReportsUrl
                .Replace ("{v}", String.IsNullOrEmpty (_configuration.Version) ? "v1" : "v" + _configuration.Version)
                .Replace ("{p}", String.IsNullOrEmpty (_configuration.Platform) ? "www" : _configuration.Platform);

            HttpsWebRequest req = new HttpsWebRequest () {
                MerchantId = _configuration.MerchantId,
                Passcode = _configuration.ReportingApiPasscode,
                WebCommandExecutor = _webCommandExecuter
            };

            var query = new
            {
                name = "Search",
                start_date = startDate,
                end_date = endDate,
                start_row = startRow,
                end_row = endRow,
                criteria = criteria
            };

            string response = req.ProcessTransaction (HttpMethod.Post, url, query);
            Console.WriteLine ("\n\n"+response+"\n\n");

            Records records = JsonConvert.DeserializeObject<Records>(response);

            return records.records;
        }
        private ProfileResponse CreateProfile(Token token, Card card, Address billingAddress, CustomFields customFields, string language, string comment)
        {
            if (token == null && card == null) {
                Gateway.ThrowIfNullArgument (null, "Card and Token both null!");
            }

            if (token == null) {
                Gateway.ThrowIfNullArgument (card, "card");
                Gateway.ThrowIfNullArgument (card.Number, "card.number");
                Gateway.ThrowIfNullArgument (card.Name, "card.name");
                Gateway.ThrowIfNullArgument (card.ExpiryMonth, "card.expiryMonth");
                Gateway.ThrowIfNullArgument (card.ExpiryYear, "card.expiryYear");
            }
            if (card == null) {
                Gateway.ThrowIfNullArgument (token, "token");
                Gateway.ThrowIfNullArgument (token.Name, "token.name");
                Gateway.ThrowIfNullArgument (token.Code, "token.code");
            }

            string url = BeanstreamUrls.BaseProfilesUrl
                .Replace ("{v}", String.IsNullOrEmpty (_configuration.Version) ? "v1" : "v" + _configuration.Version)
                .Replace ("{p}", String.IsNullOrEmpty (_configuration.Platform) ? "www" : _configuration.Platform);

            HttpsWebRequest req = new HttpsWebRequest () {
                MerchantId = _configuration.MerchantId,
                Passcode = _configuration.ProfilesApiPasscode,
                WebCommandExecutor = _webCommandExecuter
            };

            var profile = new {
                card = card,
                token = token,
                billing = billingAddress,
                custom = customFields
            };

            string response = req.ProcessTransaction (HttpMethod.Post, url, profile);

            return JsonConvert.DeserializeObject<ProfileResponse>(response);
        }
        /// <summary>
        /// Add a new card to the profile. It gets appended to the end of the list of cards.
        /// Make sure your Merchant account can support more cards. The default amount is 1.
        /// You can change this limit in the online Members area for Merchants located at:
        /// https://www.beanstream.com/admin/sDefault.asp
        /// and heading to Configuration -> Payment Profile Configuration
        /// </summary>
        /// <returns>The response</returns>
        /// <param name="profileId">Profile identifier.</param>
        /// <param name="card">Card.</param>
        public ProfileResponse AddCard(string profileId, Card card)
        {
            string url = BeanstreamUrls.CardsUrl
                .Replace ("{v}", String.IsNullOrEmpty (_configuration.Version) ? "v1" : "v" + _configuration.Version)
                .Replace ("{p}", String.IsNullOrEmpty (_configuration.Platform) ? "www" : _configuration.Platform)
                .Replace ("{id}", profileId);

            HttpsWebRequest req = new HttpsWebRequest () {
                MerchantId = _configuration.MerchantId,
                Passcode = _configuration.ProfilesApiPasscode,
                WebCommandExecutor = _webCommandExecuter
            };

            // the json wants to be wrapped in a 'card' group
            var c = new {
                card
            };

            string response = req.ProcessTransaction (HttpMethod.Post, url, c);
            return JsonConvert.DeserializeObject<ProfileResponse>(response);
        }
        /// <summary>
        /// Updates the card on the profile.
        /// </summary>
        /// <returns>The result of the update</returns>
        /// <param name="profileId">Profile identifier.</param>
        /// <param name="card">Card.</param>
        public ProfileResponse UpdateCard(string profileId, Card card)
        {
            string url = BeanstreamUrls.CardsUrl
                .Replace ("{v}", String.IsNullOrEmpty (_configuration.Version) ? "v1" : "v" + _configuration.Version)
                .Replace ("{p}", String.IsNullOrEmpty (_configuration.Platform) ? "www" : _configuration.Platform)
                .Replace ("{id}", profileId)
                         + "/" + card.Id;

            HttpsWebRequest req = new HttpsWebRequest () {
                MerchantId = _configuration.MerchantId,
                Passcode = _configuration.ProfilesApiPasscode,
                WebCommandExecutor = _webCommandExecuter
            };

            if (card.Number.Contains ("X") )
                card.Number = null; // when a card is returned from the server the number will be masked with X's. We don't want to submit that back.

            // the json wants to be wrapped in a 'card' group
            var c = new {
                card
            };

            string response = req.ProcessTransaction (HttpMethod.Put, url, c);
            return JsonConvert.DeserializeObject<ProfileResponse>(response);
        }
        /// <summary>
        /// Updates the profile. You must first retrieve the profile using ProfilesAPI.GetProfile(id)
        /// </summary>
        /// <returns>The profile response.</returns>
        /// <param name="profile">Profile.</param>
        public ProfileResponse UpdateProfile(PaymentProfile profile)
        {
            string url = BeanstreamUrls.BaseProfilesUrl
                .Replace("{v}", String.IsNullOrEmpty(_configuration.Version) ? "v1" : "v"+_configuration.Version)
                .Replace("{p}", String.IsNullOrEmpty(_configuration.Platform) ? "www" : _configuration.Platform)
                +"/"+profile.Id;

            HttpsWebRequest req = new HttpsWebRequest () {
                MerchantId = _configuration.MerchantId,
                Passcode = _configuration.ProfilesApiPasscode,
                WebCommandExecutor = _webCommandExecuter
            };

            var updateProfile = new {

                billing = profile.Billing,
                custom = profile.CustomFields,
                language = profile.Language,
                comment = profile.Comment
            };

            string response = req.ProcessTransaction (HttpMethod.Put, url, updateProfile);
            return JsonConvert.DeserializeObject<ProfileResponse>(response);
        }
        /// <summary>
        /// Retrieve a profile using the profile's ID. If you want to modify a profile
        /// you must first retrieve it using this method.
        /// </summary>
        /// <returns>The profile.</returns>
        /// <param name="profileId">Profile identifier.</param>
        public PaymentProfile GetProfile(string profileId)
        {
            string url = BeanstreamUrls.BaseProfilesUrl
                .Replace("{v}", String.IsNullOrEmpty(_configuration.Version) ? "v1" : "v"+_configuration.Version)
                .Replace("{p}", String.IsNullOrEmpty(_configuration.Platform) ? "www" : _configuration.Platform)
                +"/"+profileId;

            HttpsWebRequest req = new HttpsWebRequest () {
                MerchantId = _configuration.MerchantId,
                Passcode = _configuration.ProfilesApiPasscode,
                WebCommandExecutor = _webCommandExecuter
            };

            string response = req.ProcessTransaction (HttpMethod.Get, url);
            PaymentProfile profile = JsonConvert.DeserializeObject<PaymentProfile>(response);
            profile.Id = profileId;
            return profile;
        }
        /// <summary>
        /// Removes the card from the profile.
        /// Card IDs are their index in getCards(), starting a 1 and going up: 1, 2, 3, 4...
        /// </summary>
        /// <returns>The card.</returns>
        /// <param name="profileId">Profile identifier.</param>
        /// <param name="cardId">Card identifier.</param>
        public ProfileResponse RemoveCard(string profileId, int cardId)
        {
            string url = BeanstreamUrls.CardsUrl
                .Replace ("{v}", String.IsNullOrEmpty (_configuration.Version) ? "v1" : "v" + _configuration.Version)
                .Replace ("{p}", String.IsNullOrEmpty (_configuration.Platform) ? "www" : _configuration.Platform)
                .Replace ("{id}", profileId)
                +"/"+cardId;

            HttpsWebRequest req = new HttpsWebRequest () {
                MerchantId = _configuration.MerchantId,
                Passcode = _configuration.ProfilesApiPasscode,
                WebCommandExecutor = _webCommandExecuter
            };

            string response = req.ProcessTransaction (HttpMethod.Delete, url);
            return JsonConvert.DeserializeObject<ProfileResponse>(response);
        }
        /// <summary>
        /// Gets the cards contained on this profile.
        /// It is possible for a profile not to contain any cards if it was created using a Legato token (single-use token)
        /// </summary>
        /// <returns>The cards.</returns>
        /// <param name="profileId">Profile identifier.</param>
        public IList<Card> GetCards(string profileId)
        {
            string url = BeanstreamUrls.CardsUrl
                .Replace ("{v}", String.IsNullOrEmpty (_configuration.Version) ? "v1" : "v" + _configuration.Version)
                .Replace ("{p}", String.IsNullOrEmpty (_configuration.Platform) ? "www" : _configuration.Platform)
                .Replace ("{id}", profileId);

            HttpsWebRequest req = new HttpsWebRequest () {
                MerchantId = _configuration.MerchantId,
                Passcode = _configuration.ProfilesApiPasscode,
                WebCommandExecutor = _webCommandExecuter
            };

            string response = req.ProcessTransaction (HttpMethod.Get, url);
            ProfileCardsResponse cardResponse = JsonConvert.DeserializeObject<ProfileCardsResponse>(response);

            if (cardResponse.Cards != null) {
                return cardResponse.Cards;
            } else
                return new List<Card>(); // empty list with no cards
        }
        /// <summary>
        /// Make a credit card payment.
        /// </summary>
        /// <returns>he payment result</returns>
        /// <param name="paymentRequest">Payment request.</param>
        public PaymentResponse MakePayment(PaymentRequest paymentRequest)
        {
            Gateway.ThrowIfNullArgument (paymentRequest, "paymentRequest");

            string url = BeanstreamUrls.BasePaymentsUrl
                            .Replace("{v}", String.IsNullOrEmpty(_configuration.Version) ? "v1" : "v"+_configuration.Version)
               					    .Replace("{p}", String.IsNullOrEmpty(_configuration.Platform) ? "www" : _configuration.Platform);

            HttpsWebRequest req = new HttpsWebRequest () {
                MerchantId = _configuration.MerchantId,
                Passcode = _configuration.PaymentsApiPasscode,
                WebCommandExecutor = _webCommandExecuter
            };

            string response = req.ProcessTransaction (HttpMethod.Post, url, paymentRequest);

            return JsonConvert.DeserializeObject<PaymentResponse>(response);
        }
        /// <summary>
        /// Void the specified paymentId.
        /// 
        /// Voids generally need to occur before end of business on the same day that the transaction was processed.
        /// 
        /// Voids are used to cancel a transaction before the item is registered against a customer credit card account. 
        /// Cardholders will never see a voided transaction on their credit card statement. As a result, voids can only 
        /// be attempted on the same day as the original transaction. After the end of day (roughly 11:59 pm EST/EDT), 
        /// void requests will be rejected from the API if attempted.
        /// </summary>
        /// <returns>The return result</returns>
        /// <param name="paymentId">Payment identifier from a previous transaction.</param>
        public PaymentResponse Void(string paymentId, double amount)
        {
            Gateway.ThrowIfNullArgument (paymentId, "paymentId");

            string url = BeanstreamUrls.VoidsUrl
                .Replace("{v}", String.IsNullOrEmpty(_configuration.Version) ? "v1" : "v"+_configuration.Version)
                .Replace("{p}", String.IsNullOrEmpty(_configuration.Platform) ? "www" : _configuration.Platform)
                .Replace("{id}", paymentId);

            HttpsWebRequest req = new HttpsWebRequest () {
                MerchantId = _configuration.MerchantId,
                Passcode = _configuration.PaymentsApiPasscode,
                WebCommandExecutor = _webCommandExecuter
            };

            var VoidPayment = new
            {
                merchant_id = _configuration.MerchantId,
                amount = amount
            };

            string response = req.ProcessTransaction (HttpMethod.Post, url, VoidPayment);
            Console.WriteLine (response);
            return JsonConvert.DeserializeObject<PaymentResponse>(response);
        }
        /// <summary>
        /// Return a previous swipe payment that was not made through Beanstream. Use this if you would like to
        /// return a payment but that payment was performed on another payment service.
        /// 
        /// You must have this capability enabled on your account by calling Beanstream support. It is dangerous to
        /// have it enabled as the API will not check if you have a transaction ID.
        /// </summary>
        /// <returns>The return result</returns>
        /// <param name="returnRequest">Return request.</param>
        public PaymentResponse UnreferencedReturn(UnreferencedSwipeReturnRequest returnRequest)
        {
            Gateway.ThrowIfNullArgument (returnRequest, "returnRequest");

            string url = BeanstreamUrls.ReturnsUrl
                .Replace("{v}", String.IsNullOrEmpty(_configuration.Version) ? "v1" : "v"+_configuration.Version)
                .Replace("{p}", String.IsNullOrEmpty(_configuration.Platform) ? "www" : _configuration.Platform)
                .Replace("{id}", "0"); // uses ID 0 since there is no existing payment ID for this transaction

            HttpsWebRequest req = new HttpsWebRequest () {
                MerchantId = _configuration.MerchantId,
                Passcode = _configuration.PaymentsApiPasscode,
                WebCommandExecutor = _webCommandExecuter
            };
            returnRequest.MerchantId = _configuration.MerchantId.ToString();

            string response = req.ProcessTransaction (HttpMethod.Post, url, returnRequest);
            return JsonConvert.DeserializeObject<PaymentResponse>(response);
        }
        /// <summary>
        /// Get a particular card on a profile.
        /// Card IDs are their index in getCards(), starting a 1 and going up: 1, 2, 3, 4...
        /// </summary>
        /// <returns>The card.</returns>
        /// <param name="profileId">Profile identifier.</param>
        /// <param name="cardId">Card identifier.</param>
        public Card GetCard(string profileId, int cardId)
        {
            string url = BeanstreamUrls.CardsUrl
                .Replace ("{v}", String.IsNullOrEmpty (_configuration.Version) ? "v1" : "v" + _configuration.Version)
                .Replace ("{p}", String.IsNullOrEmpty (_configuration.Platform) ? "www" : _configuration.Platform)
                .Replace ("{id}", profileId)
                +"/"+cardId;

            HttpsWebRequest req = new HttpsWebRequest () {
                MerchantId = _configuration.MerchantId,
                Passcode = _configuration.ProfilesApiPasscode,
                WebCommandExecutor = _webCommandExecuter
            };

            string response = req.ProcessTransaction (HttpMethod.Get, url);

            ProfileCardsResponse cardResponse = JsonConvert.DeserializeObject<ProfileCardsResponse>(response);
            if (cardResponse.Cards == null)
                return null;
            cardResponse.Cards [0].id = cardId; // give it the ID since that is not persisted nor retrieved
            return cardResponse.Cards[0];
        }