Android Get user's currency

Discussion in 'Programming' started by Speederman, Feb 6, 2017.

Tags:
  1. Speederman

    Speederman Member

    Joined:
    Jun 23, 2016
    Posts:
    46
    Hello, everybody. Is there any way to know which currency a user has when they purchase an item? I mean, iap_product_details returns some info about the item, like the price (as a string) and the title, but I need the ISO 4217 currency code (USD, EUR,...) to send the transaction properly to my analytics provider. Google Play returns that value to the app as price_currency_code, but I can't find any way of accessing that value in GMS' documentation. Does someone know how to do it?

    Thanks in advance.
     
    andev likes this.
  2. Shawn Basnett

    Shawn Basnett Discount Dev

    Joined:
    Jan 5, 2017
    Posts:
    104
    Bumping this.
     
  3. Speederman

    Speederman Member

    Joined:
    Jun 23, 2016
    Posts:
    46
    Re-bumping... Please, this is important. If someone can give me any clue, I'll be really happy...
     
  4. Speederman

    Speederman Member

    Joined:
    Jun 23, 2016
    Posts:
    46
    Finally I got it working on Android... These are the steps I followed:

    First of all I edited the GetSkuDetails.java file inside the GooglePlayServicesIAPExtension so whenever we request the items details, it automatically sends the currency code also. This is the full modified file:
    Code:
    package ${YYAndroidPackageName};
    
    import android.os.Bundle;
    import android.os.RemoteException;
    import android.util.Log;
    
    import java.util.Arrays;
    import java.util.ArrayList;
    
    import ${YYAndroidPackageName}.BillingRequest;
    import ${YYAndroidPackageName}.RunnerBilling;
    import com.yoyogames.runner.RunnerJNILib;
    
    import com.android.vending.billing.IInAppBillingService;
    
    /**
     * Wrapper class that sends a RESTORE_TRANSACTIONS message to the server.
     */
    class GetSkuDetails extends BillingRequest
    {
        private static final int EVENT_OTHER_SOCIAL = 70;
        String[] mProductIds;
        IRunnerBilling.IBillingCallback mCallback;
    
        public GetSkuDetails(String[] productIds, IRunnerBilling.IBillingCallback callback)
        {
            mProductIds = productIds;
            mCallback = callback;
        }
    
        @Override
        protected void run(IInAppBillingService billingService) throws RemoteException
        {            
            ArrayList<String> skuList = new ArrayList<String>(Arrays.asList(mProductIds));
            ArrayList<String> responseList = new ArrayList<String>();
    
            // getSkuDetails() has a 20 item limit. Not that Google mention this in their documentation at all...
            int responseCode = RunnerBilling.BILLING_RESPONSE_RESULT_OK;
            for (int i = 0; i < skuList.size(); i += 20)
            {
                Log.i("yoyo", "BILLING: getSkuDetails() at index " + i);
    
                ArrayList<String> limitedSkuList = new ArrayList<String>();
                limitedSkuList.addAll(skuList.subList(i, (i + 20) > skuList.size() ? skuList.size() : i + 20));
                    
                Bundle querySkus = new Bundle();
                querySkus.putStringArrayList("ITEM_ID_LIST", limitedSkuList);
                Bundle skuDetails = billingService.getSkuDetails(RunnerBilling.API_VERSION, getPackageName(), "inapp", querySkus);
    
                if (!skuDetails.containsKey("DETAILS_LIST")) {
    
                    responseCode = getResponseCodeFromBundle(skuDetails);
                    if (responseCode != RunnerBilling.BILLING_RESPONSE_RESULT_OK) {
                    
                        Log.i("yoyo", "BILLING: getSkuDetails() failed at index " + i + " with response " + RunnerBilling.getResponseDesc(responseCode));                
                    }
                    else {
                        Log.i("yoyo", "BILLING: getSkuDetails() returned a bundle with neither an error nor a detail list at index " + i + ".");                                    
                    }
                }
                else {
                    ArrayList<String> detailsList = skuDetails.getStringArrayList("DETAILS_LIST");
                    responseList.addAll(detailsList);
                
                    if (!detailsList.isEmpty()) {
                       String item0 = detailsList.get(0);
                
                       int dsMapIndex = RunnerJNILib.jCreateDsMap(null, null, null);
                       RunnerJNILib.DsMapAddString( dsMapIndex, "BILLING_ITEM_DETAILS", item0 );
                       RunnerJNILib.CreateAsynEventWithDSMap(dsMapIndex, EVENT_OTHER_SOCIAL);
                       //Log.i("yoyo", "BILLING: BILLING_ITEM_DETAILS " + i + ": " + item0);
                    }
                }
            }
            mCallback.onComplete(responseCode, responseList);
        }
    }
    
    The last thing to do is to prepare GMS to receive the data. I created an asynchronous social event and added this code:
    Code:
    if ds_map_exists(async_load, "BILLING_ITEM_DETAILS") //This is the data we get from the extension.
        {
         var details_billing = ds_map_find_value(async_load, "BILLING_ITEM_DETAILS"); //We receive the details of the first item (just enough)
         if !is_undefined(details_billing) and is_string(details_billing) and string_length(details_billing)>10 //Checking...
            {
             var map_item0 = json_decode(details_billing); //and turn them into a map.
             if ds_exists(map_item0, ds_type_map)
               {
                if !ds_map_empty(map_item0)
                 {
                    var currency_billing = map_item0[? "price_currency_code"]; //This key holds the ISO 4217 currency code.
                    if !is_undefined(currency_billing) and is_string(currency_billing)
                       {
                        global.store_currency = currency_billing; //The value can be stored to send it to our analytics provider.
                       }
                 }
                ds_map_destroy(map_item0);
               }
            }
        }
    
     
    Last edited: Feb 20, 2018
    Death, silengames, Moar.Of.Me and 2 others like this.
  5. Drewster

    Drewster Member

    Joined:
    Jun 21, 2016
    Posts:
    222
    Hey Speederman, this is actually very helpful.

    The fact the currency is missing in IAPs is a seriously missing bit. Doing anything smart with analytics really requires it.

    Did you happen to make a fixed one for the iOS as well?
     
  6. Speederman

    Speederman Member

    Joined:
    Jun 23, 2016
    Posts:
    46
    Yes, indeed. I created an iOS extension that retrieves that info also. I called it CurrencyRequestExt.

    This is the CurrencyRequestExt.h file:
    Code:
    // Block definition
    #import <StoreKit/StoreKit.h>
    
    @interface currencyRequestExt : NSObject <SKProductsRequestDelegate>
    {
        NSString *CurrencyCode;
    }
    
    - (void) getCurrencyIOS:(char *)_itemTienda;
    - (void) productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response;
    @end
    
    And here you have the CurrencyRequestExt.mm file:
    Code:
    #import "currencyRequestExt.h"
    
    extern "C" void dsMapClear(int _dsMap );
    extern "C" int dsMapCreate();
    extern "C" void dsMapAddInt(int _dsMap, char* _key, int _value);
    extern "C" void dsMapAddString(int _dsMap, char* _key, char* _value);
    extern "C" int dsListCreate();
    extern "C" void dsListAddInt(int _dsList, int _value);
    extern "C" void dsListAddString(int _dsList, char* _value);
    
    extern "C" void createSocialAsyncEventWithDSMap(int dsmapindex);
    
    @implementation currencyRequestExt
    
    - (void) getCurrencyIOS:(char *)_itemTienda
    {
         NSString *itemString = [NSString stringWithUTF8String:_itemTienda];
         NSSet *PRODUCT_ID = [NSSet setWithObjects: itemString, nil];
         SKProductsRequest *request= [[SKProductsRequest alloc] initWithProductIdentifiers:PRODUCT_ID];
         request.delegate = self;
         [request start];
    }
    
    - (void) productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
    {
      NSArray *myProducts = response.products;
      for (SKProduct* product in myProducts) {
      NSLocale* storeLocale = product.priceLocale;
      CurrencyCode = (NSString*)CFLocaleGetValue((CFLocaleRef)storeLocale, kCFLocaleCurrencyCode);
      NSLog(@"Currency Code = %@", CurrencyCode);
      }
     
      [request release];
     
      if (CurrencyCode != nil) {
      int dsMapIndex = dsMapCreate();
      dsMapAddString(dsMapIndex, (char *)"storeCurrency", (char *)[CurrencyCode UTF8String]);
      createSocialAsyncEventWithDSMap(dsMapIndex);
      }
    }
    @end
    
    All you have to do is to create the extension with those 2 files and add a function that accesses to the external getCurrencyIOS one.

    Captura de pantalla 2017-08-16 a las 22.35.00.png

    Keep in mind that you must call it passing the id of any of your store iap items as an argument. For example: getCurrencyIOS("super_booster").

    And this is the asynchronous social event to receive the value:
    Code:
    var ios_currency = ds_map_find_value(async_load, "storeCurrency");
    if !is_undefined(ios_currency) and is_string(ios_currency) and string_length(ios_currency)>2
           global.store_currency = ios_currency;
    
     
    Last edited: Aug 16, 2017
    Rob and silengames like this.
  7. Drewster

    Drewster Member

    Joined:
    Jun 21, 2016
    Posts:
    222
    speeder man -- thank you! You really saved me a lot of hassle!
    I am really surprised nobody has chased this down previously. If you want to do anything useful with analytics, you want this info.

    By the way, your Aliens in Chains looks really good!

    The video on the website should probably auto-play.
     
  8. Speederman

    Speederman Member

    Joined:
    Jun 23, 2016
    Posts:
    46
    You're welcome. A good review for Aliens in Chains on the stores could be enough reward... :p

    And thank you for the video suggestion. We are going to make a better video and we'll surely activate the auto play then...
     
  9. signal

    signal Guest

    Thanks for doing the grunt work on this!

    Lol at the YoYo dev calling out Google for lack of documentation..

    Oh and your game looks very cool.. downloaded and ready to play.
     
  10. Speederman

    Speederman Member

    Joined:
    Jun 23, 2016
    Posts:
    46
    Thank you and thank you... 5 stars are welcome on the store... ;)
     
    Last edited: Jan 2, 2019
  11. clee2005

    clee2005 Member

    Joined:
    Jun 25, 2016
    Posts:
    213
    @Speederman thank you very much for your extension! Exactly what I needed as well. I downloaded and enjoyed your game! Rated it 5 stars... well deserved.

    Cheers,
    Chris
     
  12. Speederman

    Speederman Member

    Joined:
    Jun 23, 2016
    Posts:
    46
    You're welcome and thank you for your 5 stars rating... ;)
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice