AppLicensing fails - signature is empty string - android

I've implemented App-Licensing for InApp-Billing and now I'm getting Error.NOT_LICENSED and I have no idea what's wrong. The version I'm working on has not been published to the play store yet.
This is the code I use to start the licensing-process:
private void checkLicense(int retries) {
if (retries != 5) {
String publicKey = "YOUR KEY";
final CdcLicenseCheckerCallback callback = new CdcLicenseCheckerCallback();
String deviceId = mPrefsHandler.getDeviceId();
deviceId = deviceId != null ? deviceId : UUID.randomUUID().toString();
mPrefsHandler.setDeviceId(deviceId);
Crashlytics.setBool("has Device-Id", deviceId != null);
final LicenseChecker checker = new LicenseChecker(this, new ServerManagedPolicy(this,
new AESObfuscator(new byte[] { 1, 1, ... , 1 }, getPackageName(),
deviceId)), publicKey);
checker.checkAccess(callback);
}
}
I used a production variant to figure out what's happening and found that in LicenseValidator.java, the parameter signature for the method verify is an empty string, causing the sig.verify(Base64.decode(signature)) to return ERROR.NOT_LICENSED.
if (!sig.verify(Base64.decode(signature))) {
Log.e(TAG, "Signature verification failed.");
handleInvalidResponse();
return;
}
I only have a hint of an idea that UUID.randomUUID().toString() might be the issue, but I have no idea, whether it actually, nor what to do here.

Found the problem. I hadn't actually published the alpha-version to the playstore, so google play explicitly disallowed the newer version, because it didn't know about it
Solution: Check, if your installed app version is lower or equal to the latest version that was published to google play (alpha, beta or production channel)

Related

How to upgrade/downgrade subscription in an android application using Unity IAP

Right now my application is in Internal testing in the google play console.
I have 3 months,6 months, and 1-year subscriptions in the application.
I already bought 6 months subscription and now I am trying to upgrade my subscription to 12 months.
I have also attached the bit of the code, where I am checking if the user has any existing subscription or not by checking the presence of receipt.
if there is no existing subscription then I am directly buying the subscription
using "BuyProductID(_1YearSubscription);",
else
I am trying to upgrade it to the new subscription using
" SubscriptionManager.UpdateSubscriptionInGooglePlayStore(oldProduct, m_StoreController.products.WithID(_1YearSubscription), (string msg1, string msg2) =>
{
IgoogleplayStoreExtention.UpdateSubscription(oldProduct, m_StoreController.products.WithID(_1YearSubscription), GooglePlayStoreProrationMode.ImmediateWithoutProration);
});"
but I am getting an error that says
"2021-06-17 10:34:09.811 26172 26232 Info Unity Error: the product that will be updated does not have a valid receipt: System.NullReferenceException: Object reference not set to an instance of an object."(this is from android logcat).
what exactly is the issue and how can u fix it??
unity version is 2019.4.18
Thanx for the answer in advance.
public void Buy1YearSubscription()
{
print("trying to buy 12 months of subscription");
bool alreadyHaveSomeSubscription = false;
Product oldProduct = m_StoreController.products.WithID(_1YearSubscription);
foreach (Product p in m_StoreController.products.all)
{
GooglePurchaseData data = new GooglePurchaseData(p.receipt);
if (p.hasReceipt)
{
alreadyHaveSomeSubscription = true;
oldProduct = m_StoreController.products.WithID(data.json.productId);
GooglePurchaseData data2 = new GooglePurchaseData(oldProduct.receipt);
print("Has the reciept of----->" + data2.json.productId);
break;
}
}
if (!alreadyHaveSomeSubscription)
{
print("no previous subscription");
BuyProductID(_1YearSubscription);
}
else
{
print("upgrading to 1year");
SubscriptionManager.UpdateSubscriptionInGooglePlayStore(oldProduct, m_StoreController.products.WithID(_1YearSubscription), (string msg1, string msg2) =>
{
IgoogleplayStoreExtention.UpdateSubscription(oldProduct, m_StoreController.products.WithID(_1YearSubscription), GooglePlayStoreProrationMode.ImmediateWithoutProration);
});
}
}

android in-app billing Security verify method always return false

I have built an android application that is providing to the users the ability to purchase an items, and the google purchase dialog is return payment successfully, but on activity result the application failed in Security.java verify method always return false "sig.verify(Based64.decode(signature))".
any help why it's happening and what I have to do.
I have published the app to the play store and I do not use the android.test.* sku I have my own skus
Did you initialize it correctly?
Get public key:
public PublicKey getAPKKey(String keyFactoryAlgorithm) throws Exception{
byte[] decodedKey = Base64.decode("...your google play public key...", Base64.DEFAULT);
KeyFactory keyFactory = KeyFactory.getInstance(keyFactoryAlgorithm);
return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
}
You can find your public key in the Google Play Developer Console.
Then verify the signature:
// get purchase data
Bundle ownedItems = ... query purchases
String purchaseData = ownedItems.getStringArrayList("INAPP_PURCHASE_DATA_LIST").get(0); // just index 0 for demonstration
String signature = ownedItems.getStringArrayList("INAPP_DATA_SIGNATURE_LIST").get(0); // just index 0 for demonstration
PublicKey pkey = getAPKKey("RSA");
Signature sig = Signature.getInstance("SHA1withRSA");
sig.initVerify(pkey);
sig.update(purchaseData.getBytes());
if(sig.verify(Base64.decode(signature, Base64.DEFAULT))) {
// ok
} else {
// not ok
}

Android - Is it possible to promote to beta through Google Developer API?

Currently if I want to promote my Android app from Alpha to Beta, I do it in Google Developer Console.
I am trying to automate this process using the C# client of Google APIs. Is it possible? If so, I'd love to see a code sample.
Thanks!
I will answer myself, based on the answer I got here: https://github.com/googlesamples/android-play-publisher-api/issues/20#issuecomment-107923343
Something like this works for me:
var service = new AndroidPublisherService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential
});
// Insert new edit
var edit = new AppEdit()
{
ExpiryTimeSeconds = ToEpochTime(DateTime.UtcNow.AddMinutes(30)).ToString()
};
edit = service.Edits.Insert(edit, packageName).Execute();
// Fetching the Alpha track and clearing the version code from it
var tracks = service.Edits.Tracks.List(packageName, edit.Id).Execute().Tracks;
var alphaTrack = tracks.Single(track => track.TrackValue.Equals(EditsResource.TestersResource.GetRequest.TrackEnum.Alpha.ToString(), StringComparison.InvariantCultureIgnoreCase));
var versionCode = alphaTrack.VersionCodes.Single();
alphaTrack.VersionCodes.Clear();
service.Edits.Tracks.Patch(alphaTrack, packageName, edit.Id, EditsResource.TracksResource.PatchRequest.TrackEnum.Alpha).Execute();
// Updating a Beta track with the same version code as in the Alpha track
var betaTrack = new Track {TrackValue = "beta", VersionCodes = new [] { versionCode }};
service.Edits.Tracks.Update(betaTrack, packageName, edit.Id, EditsResource.TracksResource.UpdateRequest.TrackEnum.Beta).Execute();
// Commit the edit
service.Edits.Commit(packageName, edit.Id).Execute();

Google Play Android Developer API from C#/.NET service - (400) Bad Request

I'm trying to access a Purchase Status API from my ASP.NET web server using Google APIs .NET Client Library which is a recommended way for using Purchase API v1.1. However, the Authorization page of this API suggests direct web requests to Google's OAuth2 pages instead of using the corresponding client libraries.
OK, I tried both methods with all variations I could imagine and both of them lead to "The remote server returned an error: (400) Bad Request.".
Now what I've done to get to my point. First I've made all steps 1-8 under the Creating an APIs Console project of the Authorization page. Next I generated a refresh token as described there. During refresh token generation I chose the same Google account as I used to publish my Android application (which is in published beta state now).
Next I've created a console C# application for test purposes in Visual Studio (may be console app is the problem?)
and tried to call the Purchase API using this code (found in some Google API examples):
private static void Main(string[] args)
{
var provider =
new WebServerClient(GoogleAuthenticationServer.Description)
{
ClientIdentifier = "91....751.apps.googleusercontent.com",
ClientSecret = "wRT0Kf_b....ow"
};
var auth = new OAuth2Authenticator<WebServerClient>(
provider, GetAuthorization);
var service = new AndroidPublisherService(
new BaseClientService.Initializer()
{
Authenticator = auth,
ApplicationName = APP_NAME
});
var request = service.Inapppurchases.Get(
PACKAGE_NAME, PRODUCT_ID, PURCHASE_TOKEN);
var purchaseState = request.Execute();
Console.WriteLine(JsonConvert.SerializeObject(purchaseState));
}
private static IAuthorizationState GetAuthorization(WebServerClient client)
{
IAuthorizationState state =
new AuthorizationState(
new[] {"https://www.googleapis.com/auth/androidpublisher"})
{
RefreshToken = "4/lWX1B3nU0_Ya....gAI"
};
// below is my redirect URI which I used to get a refresh token
// I tried with and without this statement
state.Callback = new Uri("https://XXXXX.com/oauth2callback/");
client.RefreshToken(state); // <-- Here we have (400) Bad request
return state;
}
Then I tried this code to get the access token (I found it here: Google Calendar API - Bad Request (400) Trying To Swap Code For Access Token):
public static string GetAccessToken()
{
var request = WebRequest.Create(
"https://accounts.google.com/o/oauth2/token");
request.Method = "POST";
var postData =
string.Format(
#"code={0}&client_id={1}&client_secret={2}&redirect_uri={3}&grant_type=authorization_code",
// refresh token I got from browser
// also tried with Url encoded value
// 4%2FlWX1B3nU0_Yax....gAI
"4/lWX1B3nU0_Yax....gAI",
// ClientID from Google APIs Console
"919....1.apps.googleusercontent.com",
// Client secret from Google APIs Console
"wRT0Kf_bE....w",
// redirect URI from Google APIs Console
// also tried Url encoded value
// https%3A%2F%2FXXXXX.com%2Foauth2callback%2F
"https://XXXXX.com/oauth2callback/");
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byteArray.Length;
using (var dataStream = request.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
}
try
{
// request.GetResponse() --> (400) Bad request again!
using (var response = request.GetResponse())
{
using (var dataStream = response.GetResponseStream())
{
using (var reader = new StreamReader(dataStream))
{
var responseFromServer = reader.ReadToEnd();
var jsonResponse = JsonConvert.DeserializeObject<OAuth2Response>(responseFromServer);
return jsonResponse.access_token;
}
}
}
}
catch (Exception ex) { var x = ex; }
return null;
}
So, to sum up all my long story:
Is it possible at all to pass OAuth2 authorization using either of methods above from a C# Console Application (without user interaction)?
I've double checked the redirect URI (since I saw a lot of discussed troubles because of it here on stackoverflow) and other parameters like ClientID and ClientSecret. What else I could do wrong in the code above?
Do I need to URL encode a slash in the refresh token (I saw that the first method using client library does it)?
What is the recommended way of achieving my final goal (Purchase API access from ASP.NET web server)?
I'll try to answer your last question. If you access your own data account, you dont need to use client id in oAuth2. Let's use service account to access Google Play API.
Create a service account in Google Developer Console > Your project > APIs and auth > Credentials > Create a new key. You will download a p12 key.
Create a C# project. You can choose console application.
Install google play api library from Google.Apis.androidpublisher. Nuget. You can find other library for dotnet in Google APIs Client Library for .NET
Link google api project with your google play account in API access
Authenticate and try to query information. I'll try with listing all inapp item. You can just change to get purchase's status
String serviceAccountEmail = "your-mail-in-developer-console#developer.gserviceaccount.com";
var certificate = new X509Certificate2(#"physical-path-to-your-key\key.p12", "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { "https://www.googleapis.com/auth/androidpublisher" }
}.FromCertificate(certificate));
var service = new AndroidPublisherService(
new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "GooglePlay API Sample",
});
// try catch this function because if you input wrong params ( wrong token) google will return error.
var request = service.Inappproducts.List("your-package-name");
var purchaseState = request.Execute();
// var request = service.Purchases.Products.Get(
//"your-package-name", "your-inapp-item-id", "purchase-token"); get purchase'status
Console.WriteLine(JsonConvert.SerializeObject(purchaseState));
You should do the following in your
private static IAuthorizationState GetAuthorization(WebServerClient client) method:
private IAuthorizationState GetAuthorization(WebServerClient client)
{
IAuthorizationState state = AuthState;
if (state != null)
{
return state;
}
state = new AuthorizationState()
{
RefreshToken = "4/lWX1B3nU0_Ya....gAI",
Callback = new Uri(#"https://XXXXX.com/oauth2callback/")
};
client.RefreshToken(state);
// Store and return the credentials.
HttpContext.Current.Session["AUTH_STATE"] = _state = state;
return state;
}
Let me know if it works for you.
Be aware that we know that the whole OAuth2 flow is awkward today, and we are working to improve it.

Does the android emulator always return null for URL on APKExpansionPolicy check?

I am trying to perform an APKExpansionPolicy check so I can get the URL of the file to download for a custom download. I put up my APK, left it in the unpublished state, uploaded an expansion file, and then test with the emulator to do the LVL call. It returns (no errors) and when i change the options in the google play testing section to always return valid, or false, i see those results as expected. But every time, no matter what I do, the extra strings (url, filename) are not filled in (null).
I'm wondering if I'm trying to do something in the emulator that it just doesn't do. I have also tried different versions of emulator 2.3.3, 4.0, with the same results.
private class MyLicenseCheckerCallback implements LicenseCheckerCallback {
public void allow(int reason) {
if (isFinishing()) {
// Don't update UI if Activity is finishing.
return;
}
// Should allow user access.
String url = policy.getExpansionURL(APKExpansionPolicy.MAIN_FILE_URL_INDEX);
String expansionFileName = policy.getExpansionFileName( APKExpansionPolicy.MAIN_FILE_URL_INDEX );
long expansionFileSize = policy.getExpansionFileSize( APKExpansionPolicy.MAIN_FILE_URL_INDEX );
}
At 2:30am i found the answer...
I'm basically following the Android tutorial on licensing, except i'm using the APKExpansionPolicy.
This didn't work:
policy = new APKExpansionPolicy(this, new AESObfuscator(SALT, getPackageName(), deviceId));
And this does:
policy = new APKExpansionPolicy(getApplicationContext(), new AESObfuscator(SALT, getPackageName(), deviceId));
Google Play now passes back the URL, size, and filename of my expansion. Here is the code in context:
String deviceId = Secure.getString(getContentResolver(), Secure.ANDROID_ID);
// Library calls this when it's done.
mLicenseCheckerCallback = new MyLicenseCheckerCallback();
policy = new APKExpansionPolicy(getApplicationContext(), new AESObfuscator(SALT, getPackageName(), deviceId));
// Construct the LicenseChecker with a policy.
mChecker = new LicenseChecker(this, policy,base64key);
mChecker.checkAccess(mLicenseCheckerCallback);

Categories

Resources