web
You’re offline. This is a read only version of the page.
close
Skip to main content

Notifications

Announcements

No record found.

Community site session details

Community site session details

Session Id :
Finance | Project Operations, Human Resources, ...
Suggested Answer

How to Implement Xe.com currency exchange rate provider

(0) ShareShare
ReportReport
Posted on by 20

Did someone worked on exchange rate integration using the xe.com with d365fo.
Here I have examples for odanda.com. but I need to implement the similar thing using the XE.com.
I tried following the same concept what Microsoft has suggested, but its not successful
docs.microsoft.com/.../create-exchange-rate-providers
Here I am struck at providing the authentication details to access the xec.om using D365 service call.

I have the same question (0)
  • Martin Dráb Profile Picture
    237,795 Most Valuable Professional on at

    Start with their API specification. It says that they use HTTP Basic Access Authentication, which is a keyword you can use to learn more. For example: StackOverflow: How do you use Basic Authentication with System.Net.Http.HttpClient?.

    If you need more help, please tell us what you did (e.g. show us your code) and explain what problem you have with it. Just saying that you weren't successful doesn't allow us to help you.

  • D365FO Developer Profile Picture
    20 on at

    Hi Martin,

    Thank you for your response.

    I wrote the below logic and it is failing at the authentication(webResponse = httpWebRequest.GetResponse();)

    using Microsoft.Dynamics.ApplicationSuite.FinancialManagement.Currency.Framework;

    using Microsoft.Dynamics.Currency.Instrumentation;

    using System.Collections;

    using System.ComponentModel.Composition;

    /// <summary>

    /// The <c>ExchangeRateProviderOanda</c> class is an exchange rate provider for OANDA.

    /// </summary>

    [ExportMetadataAttribute(enumStr(ExchangeRateProvider), ExchangeRateProvider::XE), ExportAttribute('Microsoft.Dynamics.ApplicationSuite.FinancialManagement.Currency.Framework.IExchangeRateProvider')]

    class FIS_ExchangeRateProviderXE implements IExchangeRateProvider

    {

       private const str ServiceURL = 'xecdapi.xe.com/.../

       private const ExchangeRateProviderId ProviderId = '443fe016-8542-43f9-87a7-3aa4de76c683';

       private const str XEDateFormat = 'yyyy-MM-dd';

       private const str HttpWebRequestMethod = 'GET';

       private const str HttpWebRequestContentType = 'application/xml';

       private const str HttpHeaderAuthorization = 'Authorization';

       private const str KeyTokenPrefix = 'Bearer ';

       private const str XPathQuote = '//response/quotes/quote';

       private const str XPathAverageBid = '//bid';

       private const str XPathAverageAsk = '//ask';

       private const str XPathLowBid = '//low_bid';

       private const str XPathLowAsk = '//low_ask';

       private const str XPathMidpoint = '//midpoint';

       private const str XPathDate = '//quote/date';

       private const str XPathHighBid = '//high_bid';

       private const str XPathHighAsk = '//high_ask';

       private const str QuoteParameterAverages = 'averages';

       private const str QuoteParameterLows = 'lows';

       private const str QuoteParameterMidPoint = 'midpoint';

       private const str QuoteParameterHighs = 'highs';

       IExchangeRateProviderFrameworkFactory factory;

       public ExchangeRateProviderName get_Name()

       {

           return "XE rates";

       }

       public ExchangeRateProviderId get_Id()

       {

           return ProviderId;

       }

       public void set_Factory(IExchangeRateProviderFrameworkFactory _factory)

       {

           factory = _factory;

       }

       public IExchangeRateProviderSupportedOptions GetSupportedOptions()

       {

           IExchangeRateProviderSupportedOptions options = factory.CreateExchangeRateProviderSupportedOptions();

           options.set_doesSupportSpecificCurrencyPairs(true);

           options.set_doesSupportSpecificDates(false);

           options.set_fixedBaseIsoCurrency('');

           options.set_singleRateForDateRange(true);

           options.set_doesSupportPreventImportOnNationalHoliday(false);

           options.set_DoesSupportExchangeRateFromPreviousDay(false);

           return options;

       }

       public IExchangeRateProviderConfigDefaults GetConfigurationDefaults()

       {

           IExchangeRateProviderConfigDefaults configurationDefaults = factory.CreateExchangeRateProviderConfigDefaults();

           configurationDefaults.addNameValueConfigurationPair("@CurrencyExchange:Currency_ConfigField_ServiceTimeout", '5000');

           configurationDefaults.addNameValueConfigurationPair("@CurrencyExchange:Currency_ConfigField_OandaAPIKey", '');

           configurationDefaults.addNameValueConfigurationPair("@CurrencyExchange:Currency_ConfigField_DecimalPlaces", '5');

           configurationDefaults.addNameValueConfigurationPair("@CurrencyExchange:Currency_ConfigField_QuoteType", '1');

           return configurationDefaults;

       }

       public boolean ValidateConfigurationDetail(ExchangeRateProviderPropertyKey _key, ExchangeRateProviderPropertyValue _value)

       {

           boolean result = true;

           switch (_key)

           {

               case "@CurrencyExchange:Currency_ConfigField_DecimalPlaces":

                   int decimals = str2Int(_value);

                   if ((decimals > 12) || (decimals < 1))

                   {

                       CurrencyEventSource eventSource = CurrencyEventSource::Log;

                       eventSource.ImportExchangeRateMark("@CurrencyExchange:Currency_ConfigMessage_DecimalPlacesInvalid");

                       error("@CurrencyExchange:Currency_ConfigMessage_DecimalPlacesInvalid");

                       result = false;

                   }

                   break;

               case "@CurrencyExchange:Currency_ConfigField_OandaAPIKey":

                   if (_value == '')

                   {

                       CurrencyEventSource eventSource = CurrencyEventSource::Log;

                       eventSource.ImportExchangeRateMark("A valid Key from XE Rates is required for this provider to import exchange rates.");

                       warning("A valid Key from XE Rates is required for this provider to import exchange rates.");

                   }

                   break;

           }

           return result;

       }

       public str EnumNameForLookup(ExchangeRateProviderPropertyKey _key)

       {

           if (_key == "@CurrencyExchange:Currency_ConfigField_QuoteType")

           {

               return enumStr(ExchangeRateProviderOANDAQuoteType);

           }

           return '';

       }

       public IExchangeRateResponse GetExchangeRates(IExchangeRateRequest _request, IExchangeRateProviderConfig _config)

       {

           System.Exception exception;

           // cache configuration and request properties locally for performance

           ExchangeRateProviderPropertyValue xeKey = _config.GetPropertyValue(this.get_Id(), "@CurrencyExchange:Currency_ConfigField_OandaAPIKey");

           if (xeKey == '')

           {

               CurrencyEventSource eventSource = CurrencyEventSource::Log;

               eventSource.ImportRatesException("A valid Key from XE Rates is required for this provider to import exchange rates.", "");

               throw error("A valid Key from XE Rates is required for this provider to import exchange rates.");

           }

           int decimalPlaces = str2Int(_config.GetPropertyValue(this.get_Id(), "@CurrencyExchange:Currency_ConfigField_DecimalPlaces"));

           int serviceTimeout = str2int(_config.getPropertyValue(this.get_Id(), "@CurrencyExchange:Currency_ConfigField_ServiceTimeout"));

           boolean singleRateForDateRange = _request.get_SingleRateForDateRange();

           ExchangeRateProviderOANDAQuoteType quoteType = str2Int(_config.getPropertyValue(this.get_Id(), "@CurrencyExchange:Currency_ConfigField_QuoteTypeLocked"));

           str quoteTypeParameterForXE = this.getQuoteTypeParameterForURL(quoteType);

           List rates = new List(Types::Real);

           List dates = new List(Types::Date);

           System.TimeZone localTimeZone = System.TimeZone::get_CurrentTimeZone();

           System.DateTime toUTCDate = localTimeZone.ToUniversalTime(_request.get_ToDate());

           str toDateForRequest = toUTCDate.ToString(XEDateFormat);

           IExchangeRateResponse response = factory.CreateExchangeRateResponse();

           // Iterate over the requested currency pairs. This is only required for providers

           // that support specific currency pairs.

           IEnumerator currencyPairsEnumerator = _request.GetEnumerator();

           while(currencyPairsEnumerator.MoveNext())

           {

               URL xeUrl = ServiceURL;

               // This loop will either execute once if singleRateForDateRange is true; otherwise, it will

               // execute once for each day. If we make a single request for multiple dates

               // then XE will return an average rate for the date range.

               System.DateTime fromDate = _request.get_FromDate();

               int compareResult = fromDate.CompareTo(_request.get_ToDate());

               while (compareResult <= 0)

               {

                   IExchangeRateRequestCurrencyPair currencyPairRequest = currencyPairsEnumerator.Current;

                   IExchangeRateResponseCurrencyPair currencyPairResponse = factory.CreateExchangeRateResponseCurrencyPair();

                   currencyPairResponse.set_FromCurrency(currencyPairRequest.get_FromCurrency());

                   currencyPairResponse.set_ToCurrency(currencyPairRequest.get_ToCurrency());

                   // All rates are requested with a display factor of 1 for this provider. If the rates

                   // internally are represented using a different exchange rate display factor, the

                   // framework will make the necessary adjustments when saving the exchange rates.

                   currencyPairResponse.set_ExchangeRateDisplayFactor(ExchangeRateDisplayFactor::One);

                   // convert to UTC which is required by XE

                   System.DateTime fromUTCDate = localTimeZone.ToUniversalTime(fromDate);

                   str fromDateForRequest = fromUTCDate.ToString(XEDateFormat);

                   // Build the request URL.

                   str xeRequestString;

                   if (singleRateForDateRange)

                   {

                       // getting an average rate for the date range so we invoke the service

                       // only once per currency pair using the from and to date

                       xeRequestString = strFmt(xeUrl,

                                   currencyPairRequest.get_FromCurrency(),

                                   currencyPairRequest.get_ToCurrency(),

                                   fromDateForRequest,

                                   toDateForRequest,

                                   quoteTypeParameterForXE,

                                   decimalPlaces);

                   }

                   else

                   {

                       // invoke the service once for each day.

                       xeRequestString = strFmt(xeUrl,

                                   currencyPairRequest.get_FromCurrency(),

                                   fromDateForRequest,

                                   currencyPairRequest.get_ToCurrency());

                   }

                   // Configure the request for XE.

                   System.Net.HttpWebRequest httpWebRequest = System.Net.WebRequest::CreateHttp(xeRequestString);

                   httpWebRequest.set_Method(HttpWebRequestMethod);

                   httpWebRequest.set_ContentType(HttpWebRequestContentType);

                   httpWebRequest.set_Timeout(serviceTimeout);

                   // Authentication

                   System.Net.WebHeaderCollection webCollection = httpWebRequest.get_Headers();

                   webCollection.Add(HttpHeaderAuthorization, KeyTokenPrefix + xeKey);

                   try

                   {

                       // Invoke the service

                       System.Net.WebResponse webResponse;

                       webResponse = httpWebRequest.GetResponse();

                       // Retrieve the XML response.

                       System.IO.Stream stream = webResponse.GetResponseStream();

                       System.IO.StreamReader streamReader = new System.IO.StreamReader(stream);

                       str XMLOut = streamReader.ReadToEnd();

                       // Parse the XML to retrieve the rate and date.

                       this.processResult(quoteType, singleRateForDateRange, _request.get_FromDate(), XMLOut, rates, dates);

                       ListEnumerator rateEnumerator = rates.getEnumerator();

                       ListEnumerator dateEnumerator = dates.getEnumerator();

                       // Create the Exchange Rate Provider Response.

                       rateEnumerator.moveNext();

                       dateEnumerator.moveNext();

                       CurrencyExchangeRate exchangeRate = rateEnumerator.current();

                       date currentDate = dateEnumerator.current();

                       if (currentDate != dateNull() && exchangeRate)

                       {

                           IExchangeRateResponseExchangeRate exchangeRateResponse = factory.CreateExchangeRateResponseExchangeRate();

                           exchangeRateResponse.set_ValidFrom(currentDate);

                           exchangeRateResponse.set_ExchangeRate(exchangeRate);

                           currencyPairResponse.addExchangeRate(exchangeRateResponse);

                       }

                   }

                   catch(exception)

                   {

                       CurrencyEventSource eventSource = CurrencyEventSource::Log;

                       eventSource.ImportRatesException(exception.Message, Exception.StackTrace);

                   }

                   response.addOrUpdateCurrencyPair(currencyPairResponse);

                   rates = new List(Types::Real);

                   dates = new List(Types::Date);

                   fromDate = fromDate.AddDays(1);

                   if (singleRateForDateRange)

                   {

                       // getting an average rate across the date range so we invoke the service

                       // only once per currency pair

                       compareResult = 1;

                   }

                   else

                   {

                       compareResult = fromDate.CompareTo(_request.get_ToDate());

                   }

               }

           }

           return response;

       }

       private str getQuoteTypeParameterForURL(ExchangeRateProviderOANDAQuoteType _quoteType)

       {

           str quoteTypeParameter;

           switch (_quoteType)

           {

               case ExchangeRateProviderOANDAQuoteType::AverageAsk:

               case ExchangeRateProviderOANDAQuoteType::AverageBid:

                   quoteTypeParameter = QuoteParameterAverages;

                   break;

               case ExchangeRateProviderOANDAQuoteType::LowAsk:

               case ExchangeRateProviderOANDAQuoteType::LowBid:

                   quoteTypeParameter = QuoteParameterLows;

                   break;

               case ExchangeRateProviderOANDAQuoteType::MidPoint:

                   quoteTypeParameter = QuoteParameterMidPoint;

                   break;

               case ExchangeRateProviderOANDAQuoteType::HighAsk:

               case ExchangeRateProviderOANDAQuoteType::HighBid:

                   quoteTypeParameter = QuoteParameterHighs;

                   break;

           }

           return quoteTypeParameter;

       }

       private void readRate(ExchangeRateProviderOANDAQuoteType _quoteType, System.Xml.XmlNode _xmlQuoteNode, List _rates)

       {

           System.Xml.XmlNode xmlRateNode;

           CurrencyExchangeRate exchangeRate;

           str value;

           // Find the exchange rate

           switch (_quoteType)

           {

               case ExchangeRateProviderOANDAQuoteType::AverageBid:

                   xmlRateNode = _xmlQuoteNode.SelectSingleNode(XPathAverageBid);

                   break;

               case ExchangeRateProviderOANDAQuoteType::AverageAsk:

                   xmlRateNode = _xmlQuoteNode.SelectSingleNode(XPathAverageAsk);

                   break;

               case ExchangeRateProviderOANDAQuoteType::LowBid:

                   xmlRateNode = _xmlQuoteNode.SelectSingleNode(XPathLowBid);

                   break;

               case ExchangeRateProviderOANDAQuoteType::LowAsk:

                   xmlRateNode = _xmlQuoteNode.SelectSingleNode(XPathLowAsk);

                   break;

               case ExchangeRateProviderOANDAQuoteType::MidPoint:

                   xmlRateNode = _xmlQuoteNode.SelectSingleNode(XPathMidpoint);

                   break;

               case ExchangeRateProviderOANDAQuoteType::HighBid:

                   xmlRateNode = _xmlQuoteNode.SelectSingleNode(XPathHighBid);

                   break;

               case ExchangeRateProviderOANDAQuoteType::HighAsk:

                   xmlRateNode = _xmlQuoteNode.SelectSingleNode(XPathHighAsk);

                   break;

           }

           if (xmlRateNode)

           {

               value = xmlRateNode.get_InnerText();

               exchangeRate = str2num(value);

               if (exchangeRate)

               {

                   _rates.addEnd(exchangeRate);

               }

           }

       }

       private void processResult(ExchangeRateProviderOANDAQuoteType _quoteType, boolean _singleRateForDateRange, System.DateTime _defaultDate,

       str _xmlString, List _rates, List _dates)

       {

           System.Xml.XmlDocument xmlDom = new System.Xml.XmlDocument();

           System.Xml.XmlNode xmlQuoteNode, xmlDateNode;

           ValidFromDate exchangeDate;

           str value;

           xmlDom.LoadXml(_xmlString);

           // Find the Quote

           xmlQuoteNode = xmlDom.SelectSingleNode(XPathQuote);

           if (xmlQuoteNode)

           {

               this.readRate(_quoteType, xmlQuoteNode, _rates);

               // Find the date of the exchange rate.

               xmlDateNode = xmlQuoteNode.SelectSingleNode(XPathDate);

               if (xmlDateNode || _singleRateForDateRange)

               {

                   if (xmlDateNode)

                   {

                       value = xmlDateNode.get_InnerText();

                   }

                   if (value)

                   {

                       // convert the date from UTC to local timezone.

                       exchangeDate = System.DateTime::Parse(value, System.Globalization.CultureInfo::get_CurrentUICulture(),

                       System.Globalization.DateTimeStyles::AssumeUniversal);

                       if (exchangeDate)

                       {

                           _dates.addEnd(exchangeDate);

                       }

                   }

                   else if (!value && _singleRateForDateRange)

                   {

                       exchangeDate = _defaultDate;

                       _dates.addEnd(exchangeDate);

                   }

               }

           }

       }

    }

  • Suggested answer
    Martin Dráb Profile Picture
    237,795 Most Valuable Professional on at

    It seems to me that most of your code isn't related to the problem in question. My recommendation is testing the basics in isolation and integrate the component with other code only when it works. It'll make running and debugging much easier - and sharing in forums as well. Also, please use Insert > Insert Code (in the rich-formatting view) to paste source code. It'll avoid the double line spacing, among other things.

    I think we can use a class like this:

    using System.Net;
    
    class XEConnectionTest
    {
        public static void main(Args _args)
        {
            str xeUrl = 'https://xecdapi.xe.com/v1/historic_rate.xml/?quote=%2&start=%3&end=%4&fields=%5&decimal_places=%6'';
            str xeKey = '???';
    
            xeRequestString = strFmt(xeUrl, 'USD', mkDate(1,5,2021), 'EUR');
    
            HttpWebRequest httpWebRequest = WebRequest::CreateHttp(xeRequestString);
            httpWebRequest.set_Method('GET');
            httpWebRequest.set_ContentType('application/xml');
            httpWebRequest.set_Timeout(5000);
    
            // Authentication
            System.Net.WebHeaderCollection webCollection = httpWebRequest.get_Headers();
            webCollection.Add('Authorization', 'Bearer '   xeKey);
    
            // Execution
            WebResponse webResponse = httpWebRequest.GetResponse();
        }
    }

    That's much simpler, isn't it?

    I see a few problems:

    1. The value in xeUrl looks strange. For example, you don't have any placeholder for the first value (%1), which is the currency to convert from. Isn't it needed? And you don't provide any values for %5 and %6. Please review the URL generated by your code...
    2. You're using Bearer instead of Basic in the authentication header.
    3. It's not clear if you have a correct value in xeKey. The variable name suggests that you maybe using just the key, which isn't correct. You need both your account ID and the key, encoding with Base64. Please show us your code for this part.

    Also, when you run into a problem, please give us more details than just that is failing. For instance, if you get an exception, please share its type and message with us.

  • Shereen15 Profile Picture
    5 on at

    Hi

    Did you manage to get this working.

    I have to do the same.

    Will you help me please

    Thanks

    Shereen

  • André Arnaud de Calavon Profile Picture
    300,896 Super User 2025 Season 2 on at

    Hi Shereen,

    Microsoft provided documentation how to develop new exchange rate providers to be added in Dynamics 365: Create exchange rate providers - Finance & Operations | Dynamics 365 | Microsoft Docs

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

Responsible AI policies

As AI tools become more common, we’re introducing a Responsible AI Use…

Neeraj Kumar – Community Spotlight

We are honored to recognize Neeraj Kumar as our Community Spotlight honoree for…

Leaderboard > Finance | Project Operations, Human Resources, AX, GP, SL

#1
Martin Dráb Profile Picture

Martin Dráb 663 Most Valuable Professional

#2
André Arnaud de Calavon Profile Picture

André Arnaud de Cal... 540 Super User 2025 Season 2

#3
Sohaib Cheema Profile Picture

Sohaib Cheema 348 User Group Leader

Last 30 days Overall leaderboard

Product updates

Dynamics 365 release plans