0

I have a game in which you play 1v1 against friends answering questions. Currently there are 5 sets of questions monetised by admob. I plan to add another 10 sets and the option of forfeits, which will all be unlocked via an in-app purchase (also removes ads).

I have the implementation working, but feel like it's not the best way of doing it and could be exploited by editing SharedPreferences.

When a user purchases "premium" via an in-app purchase, I am setting a "has_premium" value in SharedPreferences. In my fragment's OnCreateView() I call querySkuDetailsAsync() and update this preference accordingly to make sure all purchases are acknkowledged and up-to-date.

Wherever I have placed ads, I will check this SharePreference value and then show/hide ads. Likewise, when displaying the RecyclerView of available question sets and forfeits, I again check this SharePreference value. If the user is premium, I enable all items in the RecyclerView. If the user is not premium, then I disable some items and instead launch the BillingFlow when clicked.

Is there a better way of checking that the user has purchased "premium" status than storing the value SharedPreferences?

Muzzle
  • 233
  • 2
  • 11

1 Answers1

0

By moving all billing logic to a BillingRepository, which caches purchases in a local database, then serving all fragments which display ads or premium features via a BillingViewModel, there is no need for any SharedPreferences

I can simply call:

billingViewModel = ViewModelProviders.of(this).get(BillingViewModel::class.java)
    billingViewModel.userPurchasedProLiveData.observe(this, Observer {
        it?.apply {
            showProStatus(entitled)
        }
    })

private fun showProStatus(entitled: Boolean) {
        if (entitled) {
            btnMainMenuPro.visibility = View.GONE
            disableAds()
        } else {
            btnMainMenuPro.visibility = View.VISIBLE
            enableAds()
        }
    }

As per the Google Docs:

Notice that the connection to [playStoreBillingClient] is created using the * applicationContext. This means the instance is not [Activity]-specific. And since it's also * not expensive, it can remain open for the life of the entire [Application]. So whether it is * (re)created for each [Activity] or [Fragment] or is kept open for the life of the application * is a matter of choice.

More info and an example can be found here: https://github.com/android/play-billing-samples/tree/master/TrivialDriveKotlin

Muzzle
  • 233
  • 2
  • 11