Why does Product.metadata.localizedPrice return a decimal?
Wouldn’t floating-point errors cause a problem?
I thought the standard was to use the actual minimum value of a currency, so cents for US dollars, pennies for British pounds, and so on.
Is there a better source for this value that would return the actual amount of the minimum value in cents, pennies … etc? or should I just round(price * 100) for all currencies and call it a day?
My guess is that it’s just for convenience. Not all currencies are two decimal places. For example some are three decimal places or might not use decimals at all (JPY). If localized price was an int then you’d need another value for the number of decimal places. I guess they used a decimal to avoid floating point issues even with large numbers (where some countries might be 90,000 units for 1 USD). I normally use localizedPriceString to display the price to the user. I’ve never really seen the need to use localizedPrice.
It would be fine if it was just to display information to the user, but I need it for analytics, and our analytics service expects an int, as is standard anyway.
I don’t see why an integer describing the actual minimum amount of a currency wouldn’t work, and you wouldn’t need a second integer denoting decimals because there is no such a thing as half a dollar; only 50 cents.
I’ve never use the localizedPrice for analytics. Mainly because the app store has better analytic tools e.g. if I sell three $1USD items in different parts of the world I can see it as $3USD total. If I was tracking currency then I’d really want a common currency format but localizedPrice will return the price for the region it was purchase in. So for the same $1USD product I might have:
- 1 US purchase => localizedPrice = $1.00 USD => x100 => 100 US cents
- 1 Japan purchase => localizedPrice ~= 150 Yen => (no multiplication) => 150 yen
- 1 Low currency purchase with 3 digits => localizedPrice = 90,000.123 => x1000 => 90,000123
So for three purchase I’d end up 90,000,274 total which doesn’t mean anything. I’d need to store the total for each region instead e.g. int column + local column. Then have some tool covert those currencies to a common format like USD before display it. But most currency converters work in full units like USD to Yen. So I’d have to know how many decimal places each currency requires to convert the base back to decimal.
Either that or I’d need to convert it on the client side to USD before sending the analytics, maybe with just an estimate of the conversion rates at the time for each local. Personally I’d prefer it if Unity just had a common currency state like usdPrice. Either that or I’d not send the price at all and just record the number of purchase and keep another record for the price changes over time.
Hi,
I can add a few more details that might help.
product.metadata.localizedPriceandproduct.metadata.localizedPriceStringare great for UI displays- If you are using Unity Analytics your revenue reports will automatically show your purchase as USD.
- The IAP SDK automatically sends transaction events to Unity Analytics (assuming you have the Analytics Package installed, have sought user consent and started if)
- The Analytics backend automatically converts your purchase from local currency to USD based on the conversion rate on that day.
- You can drill into your Analytics data in SQL to reveal the localized purchase or converted purchase details.
- The Analytics SDK has a public method to convert floating point currency to a long containing the min currency units. It can handle currencies that don’t have two currency units.
Here’s a code snippet that might help:
public void BrowseStore()
{
GameManager.Instance.Log("Browsing Store...");
foreach (var product in controller.products.all)
{
GameManager.Instance.Log($"localizedTitle - {product.metadata.localizedTitle}" +
$":: localizedDescription - {product.metadata.localizedDescription} " +
$":: localizedPriceString - {product.metadata.localizedPriceString}" +
$":: localizedPrice - {product.metadata.localizedPrice}" +
$":: currencyCode - {product.metadata.isoCurrencyCode}" );
long minCurrencyUnits = AnalyticsService.Instance.ConvertCurrencyToMinorUnits(
product.metadata.isoCurrencyCode,
System.Convert.ToDouble(product.metadata.localizedPrice));
Debug.Log($"minCurrencyUnits = {minCurrencyUnits}");
}
}
and a Unity Analytics SQL Data Explorer query
select EVENT_JSON:productCategory as productCategory
, EVENT_JSON:transactionVector as transactionVector
, EVENT_JSON:realCurrencyType::STRING as currencyCode
, EVENT_JSON:realCurrencyAmount::INTEGER as localCurrencyAmount
, EVENT_JSON:convertedProductAmount::INTEGER as convertedProductAmount
from events
where event_date > CURRENT_DATE - 7
and event_name = 'transaction'
and PRODUCT_CATEGORY = 'REAL_CURRENCY'
limit 10 ;
Agreed, but my company wants the analytics submitted from the client on a cross-platform SDK, not much I can do.
The SDK expects both an ISO 4217 formatted currency code (which Unity provides of course) & an integer amount, all I need to care about is supplementing the correct values, I assume the SDK does the conversion on its end.
Thank you for the detailed answer, we are using GameAnalytics, so I can’t -of course- rely on the Unity analytics service here.
But
This function will probably fix my problems, I’ll try to extract it from the AnalayticsService SDK into its own file.
Tho it seems like it would be better to have this function as part of the IAP SDK rather than the Analytics SDK; no?
