need a little help. Im making an android app and integrated Soomla for a simple "No ads" purchase inside my app. Ive a purchase button which should do the actual purchase via google.
What occurs is a popup from google : "Error
authentication needed, You have to login in your google account" .
I think its a small problem, but i dont get what. I am logged in in my google account. Store in Soomla is running ( at least it says so). ive enable test purchase.Im using my phone for the purchase ofc. What i am missing?
public class NoADsButton : MonoBehaviour {
private static bool storeInitialized = false; // prevent store to be initialized twice
void Start () {
if(storeInitialized) return;
SoomlaHighway.Initialize();
StoreEvents.OnSoomlaStoreInitialized += onSoomlaStoreInitialized;
SoomlaStore.Initialize(new SoomlaAssets());
}
public void onSoomlaStoreInitialized() {
storeInitialized = true;
}
public void OnMouseDown(){
StoreInventory.BuyItem("no_ads");
}
And the item ive done as its shown in the soomla example:
public const string NO_ADDS_PRODUCT_ID = "no_ads";
public static VirtualGood NO_ADS_LTVG = new LifetimeVG(
"No Ads", // name
"No More Ads!", // description
"no_ads", // item id
new PurchaseWithMarket(NO_ADDS_PRODUCT_ID, 0.99)); // the way this virtual good is purchased
}
This usually happens when the process of publishing the app for testing wasn't done properly. Try going carefully over Google's instructions, make sure you didn't miss anything. http://developer.android.com/google/play/billing/billing_testing.html#billing-testing-test
Related
I'm working on an app for android phones to be used by multiple users, where they can log in with google or microsoft accounts, to connect the app info to microsoft teams and/or sharepoint if desired.
I'm coding on Android Studio, using MSAL supporting multiple accounts.
Underneath is a method I have to remove accounts from the current PublicClientApplication.MultipleAccountPublicClientApplication. It also returns the result for each removal, if they were removed or not, in a list of booleans.
When testing, all the accounts are removed successfully, but when signing in again and the microsoft sign in intent is opened, the accounts can just be clicked to sign in without password. Signing out seems kind of pointless because of this, since one can just select their user and be logged in again right away. Is it possible to require or force the Microsoft intent to log in with password?
public CompletableFuture<List<Boolean>> signOutAll() {
List<Boolean> removedList = new ArrayList<>();
CompletableFuture<List<Boolean>> future = new CompletableFuture();
for (IAccount account : accountList) {
mPCA.removeAccount(account,
new IMultipleAccountPublicClientApplication.RemoveAccountCallback() {
#Override
public void onRemoved() {
removedList.add(true);
if (accountList.size() == removedList.size()) {
future.complete(removedList);
}
}
#Override
public void onError(#NonNull MsalException exception) {
removedList.add(false);
if (accountList.size() == removedList.size()) {
future.complete(removedList);
}
}
});
}
return future;
}
--
Thank you,
Didrik
This is happening because MSAL automatically refreshes your token after expiration. When user opens your app it checks if that token is already present and valid. So you can remove the token from the Android KeyStore in onStop().
So yes you also need to remove the cache as well to remove the account from the cache, find the account that need to be removed and then call PublicClientApplication.removeAccount()
Set<IAccount> accounts = pca.getAccounts().join();
IAccount accountToBeRemoved = accounts.stream().filter(
x -> x.username().equalsIgnoreCase(
UPN_OF_USER_TO_BE_REMOVED)).findFirst().orElse(null);
pca.removeAccount(accountToBeRemoved).join();
Read more here.
On Android we basically don't have any control on the cookies because they are shared with external Chrome app and because of that it is not accessible. If you want the user to enter the password again then you should do this: AcquireTokenInteractive(scopes).WithPrompt(Prompt.ForceLogin);
I implemented android in app billing, uploaded it in developer console for alpha tests, created an item (one time product for premium version) and tested on my device. Everything worked, but I wanted to test a second time with same device - isn't it possible to undo purchase?
What I tried:
I canceled the purchased item in developer console --> nothing happened on my device, BillingClient.getPurchaseList still returned my premium purchase
I cleared cache with ad command "adb shell pm clear com.android.vending" --> and now it's getting strange:
First, I thought it works and BillingClient.queryPurchases().getPurchaseList didn't return any purchase and my app behaviour changed correctly to basic version. But if I try to buy the item one more time to test the purchase flow again, it says "item already owned". Isn't there any possibility testing it again??
Another strange thing I absolutely don't understand: I didn't do anything, openend my app a few hours later again and it is marked as premium again. What does that mean? Is in a problem in test account or can that also happen in real (canceled) purchases??
Thanks a lot for your help!
If you want to allow an item to be purchased multiple times (i.e in-game currency), you should consume it before buying it again, otherwise the IAB library will return the "Item already owned" error.
To reset a purchase you can use BillingClient#consumeAsync(String purchaseToken).
To get the purchaseToken of a purchase, use BillingClient#queryPurchaseHistoryAsync, this will return the list of current purchases.
If you want to consume all purchases for debugging purposes, you can just use the following code:
client.queryPurchaseHistoryAsync(BillingClient.SkuType.INAPP, new PurchaseHistoryResponseListener() {
#Override
public void onPurchaseHistoryResponse(int responseCode, List<Purchase> purchasesList) {
if (purchasesList != null && !purchasesList.isEmpty()) {
for (Purchase purchase : purchasesList) {
client.consumeAsync(purchase.getPurchaseToken(), new ConsumeResponseListener() {
#Override
public void onConsumeResponse(int responseCode, String purchaseToken) {
if (responseCode == BillingResponse.OK) {
//Item consumed, you may repurchase it now
} else {
// Error, item not consumed. See responseCode for more info
}
}
});
}
}
}
});
Trying to clear cache won't fix the problem because as soon as the IAP library resyncs with GPlay, it will remember the purchases associated with the account of the current user.
I am building a game for Android with Unity 5, and I have imported this plugin
But it's not working properly I followed this tutorial :
When I try to post or show leaderboard , it does not show anything .It does not even throw any error. Sign-in and out are working properly.Please help
Code :
public void LogIn ()
{
Social.localUser.Authenticate ((bool success) =>
{
if (success) {
Debug.Log ("Login Sucess");
} else {
Debug.Log ("Login failed");
}
});
}
public void LogOut ()
{
PlayGamesPlatform.Instance.SignOut ();
}
public void Post ()
{
if (Social.localUser.authenticated) {
Social.ReportScore (5000, LeaderbordId, (bool success) =>
{
if (success) {
((PlayGamesPlatform)Social.Active).ShowLeaderboardUI (LeaderbordId);
} else {
Debug.Log ("Add Score Fail");
}
});
}
}
public void ShowLeader ()
{
Social.ShowLeaderboardUI ();
}
I'm not sure if i got what you want. I think you are trying to open leaderboards everytime you post a score. I'll change your script to make it easier to understand.
When you are using Google Play Services Pluggin you need a few things to get started. Make sure you have this. On the top of the script you'll write:
using GooglePlayGames;
using UnityEngine.SocialPlatforms;
Then you'll need to add this to your start function:
void Start(){
PlayGamesPlatform.Activate();
}
It's easier if you make an function just to show your leaderboards, like your last function for example:
public void ShowLB(){
((PlayGamesPlatform) Social.Active).ShowLeaderboardUI("YOUR_LEADERBOARDS_ID_HERE");
}
Then your Post () function would be like this:
public void Post (){
if (Social.localUser.authenticated) {
Social.ReportScore (5000, LeaderbordId, (bool success) =>
{
if (success) {
ShowLB ();
} else {
Debug.Log ("Add Score Fail");
}
});
}
}
And it seems like in your last function you might want to do this:
public void ShowAchievs(){
Social.ShowAchievementsUI ();
}
Otherwise it should be like this:
public void ShowLB(){
((PlayGamesPlatform) Social.Active).ShowLeaderboardUI("YOUR_LEADERBOARDS_ID_HERE");
}
But it's WAY MORE EASY if you just use your post function after a trigger, like the end of the game, and use something else to show your LB. Like a click on a button or something.
I don't know that's what you wanted.
You must be running your game inside editor , You need to run your game in an actual device or perhaps an emulator ( with a gmail account ... those are rare)
Here is the thing ...
open a console , and type this
adb logcat
or you can narrow the result by filtering only logs from unity
adb logcat -s Unity
Open your game , look at at the console If you get
ShowAchievementsUI not implemented
or
ShowLeaderboardsUI not implemented
That only means that you are using the default configurations (The unity one)
I suggest you replace the defaults with google play's
The default configuration needs to be replaced with a custom configuration
using GooglePlayGames;
using GooglePlayGames.BasicApi;
using UnityEngine.SocialPlatforms;
PlayGamesClientConfiguration config = new PlayGamesClientConfiguration.Builder()
// enables saving game progress.
.EnableSavedGames()
// registers a callback to handle game invitations received while the game is not running.
.WithInvitationDelegate(<callback method>)
// registers a callback for turn based match notifications received while the
// game is not running.
.WithMatchDelegate(<callback method>)
.Build();
PlayGamesPlatform.InitializeInstance(config);
// recommended for debugging:
PlayGamesPlatform.DebugLogEnabled = true;
// Activate the Google Play Games platform
PlayGamesPlatform.Activate();
Any errors will now be shown in the console, actually you dont need all of that of it to work , you only need these
PlayGamesPlatform.DebugLogEnabled = true;
PlayGamesPlatform.Activate();
for logging the errors ,
Then you can authenticate :)
If you open it and suddenly it crashes , it means that you have a problem in your manifest file
you probably going to need this if you using an older version (This get added automatically if you are using the latest version)
<application ... >
...
<meta-data android:name="com.google.android.gms.version" android:value="#integer/google_play_services_version" />
....
</application>
Make sure you update google play services to the latest one via the SDK Manager , that way you can avoid unpleasant situations
Cheers.
Lately, I have been trying to add static interstitial ads into my Unity game. For some reason, I could not get the system to show anything, or even react to me. After trying to work with the base Chartboost plugin, I tried to match a tutorial that I was following and purchased Prime31's Chartboost plugin and have been using that. However, neither the base plugin, nor Prime31's plugin, seem to be allowing me to show any ads. The code is pretty much done inside a single object, and it seems simple enough.
public class Advertisement : MonoBehaviour {
public string chartboostAppID = "5461129ec909a61e38b1505b";
public string chartboostAppSignature = "672b3b34e3e358e7a003789ddc36bd2bc49ea3b5";
// Use this for initialization
void Start () {
DontDestroyOnLoad(this.gameObject);
ChartboostAndroid.init (chartboostAppID, chartboostAppSignature, true);
ChartboostAndroid.cacheInterstitial(null);
}
void OnLevelWasLoaded(int level) {
ChartboostAndroid.cacheInterstitial(null);
if(Application.loadedLevelName == "Network Lobby") {
showAds();
}
}
public static void showAds() {
Debug.Log("Showing ad");
ChartboostAndroid.showInterstitial(null);
}
}
As you can see, it's pretty straightforward. This object is created at the game's splash screen, which appears only once, and it's never destroyed until the program ends. The goal is, whenever I enter the lobby scene, I want to see an ad before going to the lobby's menus. As it is, I do see the log printing "Showing ad", so I know the function is being called. However, nothing appears. Do I need to disable the GUI system first? Is there a step I'm missing?
I have already performed the following steps:
I have created and registered the app with chartboost, as well as double and triple checked the AppID and App Signature.
I have created a publishing campaign and registered it to the app.
I double-checked the orientation and confirmed that it's correct.
I registered this specific device as a test device.
The tutorial showed a call to ChartBoostAndroid.OnStart(), but there was no function like that for me to call. Perhaps that is from an older version?
I emailed Chartboost support and have not heard from them yet. I do not have that much time on this project, so if anyone can offer help, I'd appreciate it.
I'm on the edge of finishing my first app, and one last remaining thing is to implement IAP billing, so that's why I am currently reading quite a lot about the topic (including security concerns like encryption, obfuscation and stuff).
My app is a free version, with the ability to upgrade to full verison via IAP, so there would be just one managed purchase item "premium". I have a few questions about this:
In the Google IAP API example (trivialdrivesample), there's always the IAP check in MainActivity to see if the user bought the premium version, done via
mHelper.queryInventoryAsync(mGotInventoryListener);
My first concern:
This does mean that the user always needs to have an internet/data connection at app-startup, to be able switch to the premium version right? What if the user doesn't have an internet connection? He would go with the lite version I guess, which I would find annoying.
So I thought about how to save the isPremium status locally, either in the SharedPrefs or in the app database. Now, I know you can't stop a hacker to reverse engineer the app, no matter what, even so because I don't own a server to do some server-side validation.
Nevertheless, one simply can't save an "isPremium" flag somewhere, since that would be too easy to spot.
So I was thinking about something like this:
User buys Premium
App gets the IMEI/Device-ID and XOR encodes it with a hardcoded String key, saves that locally in the app database.
Now when the user starts the app again:
App gets encoded String from database, decodes it and checks if decodedString == IMEI. If yes -> premium
If no, then the normal queryInventoryAsync will be called to see if the user bought premium.
What do you think about that approach? I know it's not supersecure, but for me it's more important that the user isn't annoyed (like with mandatory internet connection), than that the app will be unhackable (which is impossible anyway).
Do you have some other tips?
Another thing, which I currently don't have a clue about, is how to restore the transaction status when the user uninstalls/reinstalls the app. I know the API has some mechanism for that, and aditionally my database can be exported and imported through the app (so the encoded isPremium flag would be exportable/importable as well). Ok, I guess that would be another question, when the time is right ;-)
Any thoughts and comments to this approach are welcome, do you think that's a good solution? Or am I missing something/heading into some wrong direction?
I too was making the same investigations, but during my testing I figured out that you do not need to store it, as Google do all the caching you need and I suspect (though I have not investigated it) that they are doing so as securely as possible (seeing as it in their interest too!)
So here is what i do
// Done in onCreate
mHelper = new IabHelper(this, getPublicKey());
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh noes, there was a problem.
Log("Problem setting up In-app Billing: " + result);
} else {
Log("onIabSetupFinished " + result.getResponse());
mHelper.queryInventoryAsync(mGotInventoryListener);
}
}
});
// Called by button press
private void buyProUpgrade() {
mHelper.launchPurchaseFlow(this, "android.test.purchased", 10001,
mPurchaseFinishedListener, ((TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId());
}
// Get purchase response
private IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase)
{
if (result.isFailure()) {
Log("Error purchasing: " + result);
return;
}
else if (purchase.getSku().equals("android.test.purchased")) {
Log("onIabPurchaseFinished GOT A RESPONSE.");
mHelper.queryInventoryAsync(mGotInventoryListener);
}
}
};
// Get already purchased response
private IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result,
Inventory inventory) {
if (result.isFailure()) {
// handle error here
Log("Error checking inventory: " + result);
}
else {
// does the user have the premium upgrade?
mIsPremium = inventory.hasPurchase("android.test.purchased");
setTheme();
Log("onQueryInventoryFinished GOT A RESPONSE (" + mIsPremium + ").");
}
}
};
So what happens here?
The IAB is set up and calls startSetup, on a successful completion (as long as it has been run once with an internet connection and is set up correctly it will always succeed) we call queryInventoryAsync to find out what is already purchased (again if this has been called while online it always works while offline).
So if a purchase is completed successfully (as can only be done while online) we call queryInventoryAsync to ensure that it has been called while online.
Now there is no need to store anything to do with purchases and makes your app a lot less hackable.
I have tested this many ways, flight mode, turning the devices off an on again and the only thing that messes it up is clearing data in some of the Google apps on the phone (Not likely to happen!).
Please contribute to this if you have different experiences, my app is still in early testing stage.
I refactored ne0's answer into a static method, including the comments from snark.
I call this method when my app starts - you'll need to enable your features at the TODO
/**
* This is how you check with Google if the user previously purchased a non-consumable IAP
* #param context App Context
*/
public static void queryPlayStoreForPurchases(Context context)
{
final IabHelper helper = new IabHelper(context, getPublicKey());
helper.startSetup(new IabHelper.OnIabSetupFinishedListener()
{
public void onIabSetupFinished(IabResult result)
{
if (!result.isSuccess())
{
Log.d("InApp", "In-app Billing setup failed: " + result);
}
else
{
helper.queryInventoryAsync(false, new IabHelper.QueryInventoryFinishedListener()
{
public void onQueryInventoryFinished(IabResult result, Inventory inventory)
{
// If the user has IAP'd the Pro version, let 'em have it.
if (inventory.hasPurchase(PRO_VERSION_SKU))
{
//TODO: ENABLE YOUR PRO FEATURES!!
Log.d("IAP Check", "IAP Feature enabled!");
}
else
{
Log.d("IAP Check", "User has not purchased Pro version, not enabling features.");
}
}
});
}
}
});
}
This will work across reboots and without a network connection, provided the user purchased the item.
Since you already know that it's impossible to make it unhackable using this system, I would recommend not attempting to prevent hacking. What you are proposing is known as "Security through obscurity" and is usually a bad idea.
My advice would be to try queryInventoryAsync() first, and only check your 'isPremium' flag if there is no internet connection.
There are also a few potential other ways of going about this, such as having separate free and premium apps, instead of an in app purchase. How other people handle this and the tools Google makes available might warrant an investigation.
queryInventoryAsync will automatically take into account uninstall and reinstalls, as it tracks purchases for the logged in user.
Yes the purchases can be retrieved offline. Also, I'm thinking about counting how many times the user opens the app as a mechanism before showing the billing UI.