Separate price and currency in Windows Store

Usually, having price as a double value is a very bad mistake. However, in some cases this is necessary, for example when your analytic system requires it, like Amazon Mobile Analytic does. Both Android and iOS provide you with a way to get that information, but Windows (Phone) Store only gives you FormattedPrice string.

And obvious way to get price value from it is to use CurrencyFormatter class provided by Microsoft. However, things are not quite that simple. The biggest question is which locale Store used to create FormattedPrice for you. The answer is a little complicated. It pulls currency (and its symbol) from one place, but it takes formatting (including placement of currency symbol relative to value and thousands separator) from another.

If you create CurrencyFormatter with only currency identifier, it will not be able to parse FormattedPrice in all cases, when user have set different regional settings in different places (for example, if he uses USA store with USD as currency, but his price formatting is done in British style) or if he set up some custom formatting.

So instead we need to use the second constructor. It takes a list of languages, and ID of geographical region. It might not be obvious how it can help our cause, but actually specifying this information allows CurrencyFormatter to pull formatting properties set up for user’s language and use them instead of defaults. However, exactly how it does it is not clear from documentation. Our own testing showed that the last parameter (geographical region ID) does not matter – you can always pass in “US”, but the key is to pass user’s language as the second parameter. Take this with a grain of salt, please: until we know the third parameter NEVER does affect parsing, we need to be cautious about it…

The only remaining question is how to get the locale used by Store. The only answer I found works only in C++ using old WinAPI functions, so it’s only relevant for those who target desktop Windows, or Windows Phone 8.1+ and use C++ to interact with Store. Fortunately, that’s just what we do at work 🙂 So, here’s the final code to get price value from FormattedPrice in Windows Store in C++:

// Assuming we're in LoadListingInformationByProductIdsAsync handler 
// and just successfully received ListingInformation
ApplicationModel::Store::ListingInformation^ listInfo = operation->GetResults(); 

// This gets us currency ID used by store, but language settings used for formatting
Windows::Globalization::GeographicRegion^ geographicRegion 
    = ref new Windows::Globalization::GeographicRegion(listInfo->CurrentMarket); 
Platform::String^ currencyID = geographicRegion->CurrenciesInUse->GetAt(0);

// Now, some WinAPI magic: get us user's default locale, which is used by Store to format things
WCHAR wcBuffer[10];
GetUserDefaultLocaleName(wcBuffer, 10);
Platform::String^ userFormatLocale = ref new Platform::String(wcBuffer);

// Use locale returned by GetUserDefaultLocaleName to populate 
// a list of languages for CurrencyFormatter
Platform::Collections::Vector<Platform::String^>^ v 
    = ref new Platform::Collections::Vector<Platform::String^>();
v->Append(userFormatLocale);

// Create CurrencyFormatter using the second form of constructor. 
// Please note that I'm NOT sure about meaning of the 3rd parameter!
Windows::Globalization::NumberFormatting::CurrencyFormatter^ formatter 
   = ref new Windows::Globalization::NumberFormatting::CurrencyFormatter(currencyID, 
         v, ref new Platform::String(L"US"));

// Iterate over products in listInfo
for (auto iter : listInfo->ProductListings)
{
    ApplicationModel::Store::ProductListing^ store_product = iter->Value;
                
    // Use formatter to parse currency value into double. 
    // A Decimal might be preferable for anything serious, but for statistics it will have to do
    Platform::IBox<double>^ result = formatter->ParseDouble(store_product->FormattedPrice);

    // In case formatter failed to parse the price, to a check
    double price = result == nullptr ? 0.0f : result->Value;  
}

There you have it. This solution is not without magic components which may fail, but it seems to work in most cases. Please remember, that you DON’T need this most of the time – a price representation in string format should suffice for almost ANY case! But if you really, really need to parse FormattedPrice into double value – there you have it.

I'm a video games programmer. I have worked in game industry since 2008 on MMOs and mobile games, but now I work for Owlcat Games on great old-school RPGs. In my free time, I like to play rock'n'roll on my guitar and travel.

4 thoughts on “Separate price and currency in Windows Store

  1. Hello, that is nice sample. I have a question on this. When I change the region to UAE, the ParseDouble always return nullptr.
    Could you please help me fix it? Thanks!

    1. I’m sorry, I don’t have access to any Windows Phone devices any longer, so I can’t help you with that. However, you might want to investigate what format the price is returned in from the store (what is exactly in that string), and maybe try to make use of the 3rd parameter of the CurrencyFormatter constructor. It might be worth trying to pass something else than “US” for UAE market.

      1. Hi MaxEd, the implementation is on Windows 8.1 SDK, not Windows Phone. The value of store_product->FormattedPrice is correct, a such like L”$20.99″, but I don’t know what is the reason make it failed. I also tried the 3rd parameter with geographicRegion->CodeTwoLetter or L”AE” but the issue still persist. Please give some idea, Thanks!

        1. I really have no idea how to help you, unfortunately. CurrencyFormatter is magical, in that when something goes wrong, there are zero diagnostics. I mean, generally parseDouble error means it tried to apply some incompatible locale, but where did it got it from, and how to pass a correct one is a mystery that can’t be solved short of decompiling SDK sources (wish it was open-source…). You can play around with setting app’s locale (including Win32 functions) in different ways, or try to pass different combinations of the second and the third parameter, but I guess you already must have done most of that.

Leave a Reply

Your email address will not be published.