Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Johannes Lantz 156 posts 838 karma points c-trib
    Feb 01, 2022 @ 08:45
    Johannes Lantz
    0

    Exchange rate for order vendr

    Hi!

    We have been getting a bunch of exchange rate for order errors now.

    An error occured whilst fetching the exchange rate for order {OrderId}
    
    System.ArgumentNullException: Value cannot be null.
    Parameter name: source
       at System.Linq.Enumerable.First[TSource](IEnumerable`1 source)
       at Vendr.Infrastructure.Services.ExchangeRatesApiCurrencyExchangeRateService.FetchExchangeRate(String fromCurrencyIsoCode, String toCurrencyIsoCode, DateTime date)
       at Vendr.Infrastructure.Services.CurrencyExchangeRateServiceBase.GetExchangeRate(String fromCurrencyIsoCode, String toCurrencyIsoCode, DateTime date)
       at Vendr.Core.Events.Domain.Handlers.Order.SetOrderBaseCurrencyExchangeRate.Handle(OrderFinalized evt)
    

    I am really confused on what this is. And how I would go about resolving it. We are using multiple currencies but we aren't using a 3rd party exchange rates API. Would love some input on this

    //Johannes

  • Matt Brailsford 4124 posts 22215 karma points MVP 9x c-trib
    Feb 01, 2022 @ 09:27
    Matt Brailsford
    1

    Hi Johannes,

    Thanks for raising this, it's just brough something to my attention that we are going to need to resolve.

    Ultimately, Vendr OOTB connects to an exchange rate service (https://exchangeratesapi.io/) to calculate order exchange rates (it should only do this for orders not in a stores base currency). This is predominantly so that the analytics section / commerce dashboard are able to report sales figures in a single currency (the base currency defined on the store). Without this, stores handling multiple currencies couldn't show a single set of values.

    The issue appears to be that this service previously didn't require any authentication to use the free tier however they now require an API key to be passed and so the exchange rate lookup are failing so it looks like we may need to source an alternative, or require folks to register and supply an API key.

    We do have multiple implementations for different providers if you want to sign up to one of those in the meantime (Currency Layer has the same limits on the free tier)

    • ExchangeRatesApiCurrencyExchangeRateService
    • CurrencyLayerCurrencyExchangeRateService
    • FixerCurrencyExchangeRateService

    These can be swapped out via the DI container

    builder.Services.AddUnique<ICurrencyExchangeRateService>(f => new CurrencyLayerCurrencyExchangeRateService(apiKey));
    

    Alternatively you can also implement the ICurrencyExchangeRateService if you have another service you wish to connect to.

    Unfortunately there is just no way of doing these calculations without using a 3rd party service and so we tried to use the path of least resistance for most implementations, but it looks like even that may not be viable anymore.

    Hope this helps

    // Matt

  • Matt Brailsford 4124 posts 22215 karma points MVP 9x c-trib
    Feb 01, 2022 @ 09:41
    Matt Brailsford
    100

    Ok, looks like I've found another free alternative https://exchangerate.host/ if you want to try this. You'll need to define a new Exchange Rate Service like so

    public class ExchangerateHostCurrencyExchangeRateService: CurrencyExchangeRateServiceBase
    {
        private const string API_BASE_URL = "https://api.exchangerate.host";
    
        public override decimal FetchExchangeRate(string fromCurrencyIsoCode, string toCurrencyIsoCode, DateTime date)
        {
            return Get<ExchangerateHostSingleResponse>($"{API_BASE_URL}/{date:yyyy-MM-dd}?base={fromCurrencyIsoCode.ToUpper()}&symbols={toCurrencyIsoCode.ToUpper()}")
                .Rates?.First().Value ?? 0;
        }
    
        public override Dictionary<string, Dictionary<string, decimal>> FetchExchangeRates(string fromCurrencyIsoCode, string[] toCurrencyIsoCodes, DateTime dateFrom, DateTime dateTo)
        {
            var results = new Dictionary<string, Dictionary<string, decimal>>();
    
            LoopByMaxDateRange(dateFrom, dateTo, 365, (startDate, endDate) =>
            {
                var r = Get<ExchangerateHostTimeframeResponse>($"{API_BASE_URL}/timeseries?base={fromCurrencyIsoCode.ToUpper()}&symbols={string.Join(",", toCurrencyIsoCodes.Select(x => x.ToUpper()))}&start_date={startDate:yyyy-MM-dd}&end_date={endDate:yyyy-MM-dd}");
    
                if (r?.Rates != null)
                {
                    foreach (var rate in r.Rates)
                    {
                        results.Add(rate.Key, rate.Value);
                    }
                }
    
            });
    
            return results;
        }
    }
    
    internal class ExchangerateHostResponse
    {
        [JsonProperty(PropertyName = "base")]
        public string Base { get; set; }
    
        [JsonProperty(PropertyName = "date")]
        public DateTime Date { get; set; }
    }
    
    internal class ExchangerateHostSingleResponse : ExchangerateHostResponse
    {
        [JsonProperty(PropertyName = "rates")]
        public Dictionary<string, decimal> Rates { get; set; }
    }
    
    internal class ExchangerateHostTimeframeResponse : ExchangerateHostResponse
    {
        [JsonProperty(PropertyName = "rates")]
        public Dictionary<string, Dictionary<string, decimal>> Rates { get; set; }
    }
    

    And then in your Startup / Composer you'll need to swap out the default implementation like so

    builder.Services.AddUnique<ICurrencyExchangeRateService, ExchangerateHostCurrencyExchangeRateService>();
    

    I'm going to change this to be the new default, but if you want on going stability, you might want to look at one of the paid / registered options.

    Hope this helps

    Matt

  • Johannes Lantz 156 posts 838 karma points c-trib
    Feb 07, 2022 @ 08:52
    Johannes Lantz
    0

    Hi Matt!

    I just implemented the new exchange rate service! Is there a way to double test if it works? Or is it just to leave it for a day and double check if any error has occurred?

    //Johannes

  • Matt Brailsford 4124 posts 22215 karma points MVP 9x c-trib
    Feb 07, 2022 @ 09:11
    Matt Brailsford
    1

    Hi Johannes,

    there is nothing explicit to trigger it so it kind of is wait and see. This said, there is a background task that runs to try and re-populate exchange rates that failed so maybe look back at the orders that failed and see if the values have been populated.

    Failing this, yea, just wait a few days and see if the error continues. It's not detrimental to order taking if you see those errors. As I say, it's mostly for the analytics section.

  • Johannes Lantz 156 posts 838 karma points c-trib
    Feb 10, 2022 @ 16:17
    Johannes Lantz
    0

    Hi Matt!

    I have left it for a while now, and it works like a charm!

    Thank you so much Matt! #H5YR

    //Johannes

  • Matt Brailsford 4124 posts 22215 karma points MVP 9x c-trib
    Feb 10, 2022 @ 16:36
    Matt Brailsford
    1

    Fantastic!

    I implemented as the default and pushed it out in the latest release so hopefully others won't have to face this issue.

    Thanks again for reporting it, and actually coming back to tell me 👍

    Matt

Please Sign in or register to post replies

Write your reply to:

Draft