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);
});
}
}
Related
I am working on a personal project and I am using flutter to develop an app (cross platform) that reads in the user's health data from google fit (Android) or Apple Health. I am using this package and even the EXACT same code like in the documentation (I am currently only testing on Android):
Future fetchStepData() async {
int? steps;
// get steps for today (i.e., since midnight)
final now = DateTime.now();
final midnight = DateTime(now.year, now.month, now.day);
bool requested = await health.requestAuthorization([HealthDataType.STEPS]);
if (requested) {
try {
steps = await health.getTotalStepsInInterval(midnight, now);
} catch (error) {
print("Caught exception in getTotalStepsInInterval: $error");
}
print('Total number of steps: $steps');
setState(() {
_nofSteps = (steps == null) ? 0 : steps;
_state = (steps == null) ? AppState.NO_DATA : AppState.STEPS_READY;
});
} else {
print("Authorization not granted - error in authorization");
setState(() => _state = AppState.DATA_NOT_FETCHED);
}
}
Then I am calling this function with await and I also have inserted the correct permission in all Android Manifest files:
Also I set up an OAuth2 Client ID for the project and added my google account as a test user.
BUT THE FUNCTION SETS THE VARIABLE STEPS ALWAYS TO NULL? The boolean variable "requested" is true, so it seems like the actual connection is working?
I am really disappointed by myself guys and I really need help - THANK YOU!
I tried adding the correct android permissions, asking for permissions explicitly, different time intervalls but nothing worked for me, I always got a null value back.
My app is developed in flutter and running in IOS and Android for past one year. After recent update in 2022 March 1st week I am not getting updated new user count or the screen name of the pages visited by user only from Android. I am using Mac M1. If I use iphone simulator the screen name appears in analytics (i have suffixed 'ios' and current time to screen name to verify this). But if I use Android simulator page views are not getting updated (probably everything showing under 'not set' or 'others'.
I have added this code in Android manifest.xml as some suggested. but still not working.
This is the code in my initial page.
FirebaseAnalytics analytics = FirebaseAnalytics.instance;
FirebaseAnalyticsObserver observer =
FirebaseAnalyticsObserver(analytics: analytics) ;
String _message = '';
void setMessage(String message) {
// setState(() {
_message = message;
myMessage = message;
// });
}
Future<void> _testSetCurrentScreen() async {
await analytics.setCurrentScreen(
screenName: myScreenName,
screenClassOverride: 'AnalyticsDemo',
);
setMessage('setCurrentScreen succeeded');
myMessage = "loaded $myScreenName" ;
}
_testSetCurrentScreen();
print("1476 $myMessage '_' $myMessage");
I'm working with In-App update from here:
https://developer.android.com/guide/app-bundle/in-app-updates
I have successfully implemented it in my app. My app is already in playstore:
https://play.google.com/store/apps/details?id=com.tekitsolution.remindly
for Testing this implementation, I decreased version in my project and I implemented in Splash Activity.
So, I got the update screen, I click on update and it started downloading. After completing the download, it is again coming same page to download and installErrorCode is getting -100 which means An internal error happened in the Play Store. (Is there a way to understand what install error codes actually mean?)
Flow from update screen:
I printed logs for `state.installStatus()`
PENDING = 1;
DOWNLOADING = 2;
DOWNLOADED = 11;
INSTALLING = 3;
then instead of this (INSTALLED = 4), I'm getting FAILED = 5 with state.installErrorCode -100
Code:
private InstallStateUpdatedListener installStateUpdatedListener = new
InstallStateUpdatedListener() {
#Override
public void onStateUpdate(InstallState state) {
showLog("In-App: " + state.installStatus());
showLog("installErrorCode " + state.installErrorCode());
if (state.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate();
} else if (state.installStatus() == InstallStatus.INSTALLED) {
if (mAppUpdateManager != null) {
showLog("In-App: unregisterListener");
mAppUpdateManager.unregisterListener(installStateUpdatedListener);}
}
}
};
Note: I tried with different devices also.
I am a cordova/phonegap android developer, currently I have some free app and now I have plan to publish the paid app in playstore. But only one can purchase and share it to his/her friends, so they can use without pay. How can I protect it? I refer many things in internet but I am not got any solution.
I found the following cordova plugin
https://github.com/mobilino/Phonegap-android-license-plugin . But I am getting signature random values, no one match with LICENSING & IN-APP BILLING key. Or how can I use this plugin.
AndroidLicensePlugin.check(
function(data) { alert( JSON.stringify(data));},
function(errorString) { alert("error: " + errorString);}
);
Merbin, Not sure if you found your answer but here is what I have done.
When I want to share an Android App that I am selling I go to Google Play Dev and create a promotional code list and provide one of those promo code to friends.
Here is the code I have used with that same plugin.
//---------------------------
//---------------------------
function LicCheck() {
//Running HTTP vs. Native
try {
//Default none or error
setLicKeyValue(Number(99));
AndroidLicensePlugin.check(
function (data) {
licProcessJSON(data);
},
function (errorString) {
console.log('LicCheck() ERROR ' + errorString);
setLicKeyValue(99);
}
);
}
catch (err) {
setLicKeyValue(99);
console.log('LicCheck() - Error - default set to 99 (try later) ' + err)
}
}
//---------------------------
//---------------------------
function licProcessJSON(data) {
var appLicResponseCode = Number(1); //0:owns, 1:do not own
//data = {
// responseCode: 0,
// signedData: "0|-123456798|de.mobilino....", // 6 fields of | delimitered data
// signature: "" // the BASE64 encoded signature from Google
//};
console.log('data.responseCode ' + data.responseCode);
//They own the app
if (data.responseCode === 0) {
console.log('licProcessJSON() - Onwer True');
setLicKeyValue(0);
return;
}
//They do not own the app
if (data.responseCode === 1) {
console.log('licProcessJSON() - Onwer False');
setLicKeyValue(1);
return;
}
console.log('licProcessJSON() - No Data?');
setLicKeyValue(99);
}
//---------------------------------
//---------------------------------
function setLicKeyValue(value) {
localStorage.setItem(_licIndicator, Number(value));
}
I'm working on an Android app developed on Titanium SDK 3.2.0.GA and I'm using the In-App Billing module by Appcelerator.
My implementation of the module it's as follows (omitting functions which only purpose is to display log info):
var IdentifierProduct = '';
var InAppBilling = require('ti.inappbilling');
var publicKey = Alloy.Globals.Android.publicKey_1 + Alloy.Globals.Android.publicKey_2 + Alloy.Globals.Android.publicKey_3 + Alloy.Globals.Android.publicKey_4;
InAppBilling.setPublicKey(publicKey);
function initializeBilling()
{
var synchronousResponse = InAppBilling.checkBillingSupported();
displaySynchronousResponseCodes(synchronousResponse);
}
function requestPurchase(identifier, item_type)
{
// Check if billing for current product type is supported before sending purchase request
var checkBillingResponse = InAppBilling.checkBillingSupported(item_type);
if (checkBillingResponse.responseCode == InAppBilling.RESULT_OK)
{
Ti.API.info('Current product type supported, continuing with request');
var tmpArgs = {
productId: identifier,
productType: item_type,
developerPayload: 'devPayload'
};
Ti.API.info('args for product request\n' + JSON.stringify(tmpArgs));
var synchronousResponse = InAppBilling.requestPurchase({
// productId: 'android.test.purchased',
// productId: 'android.test.canceled',
// productId: 'android.test.refunded',
// productId: 'android.test.item_unavailable',
productId: identifier,
productType: item_type,
developerPayload: 'devPayload'
});
displaySynchronousResponseCodes(synchronousResponse);
}
else
{
Ti.API.info('Current product type not supported, aborting request');
displaySynchronousResponseCodes(checkBillingResponse);
}
}
function ON_BIND_EVENT(e)
{
if (e.result == InAppBilling.SUCCESS) {
NotifyMe('Billing Service Bound');
enableInAppPurchases(true);
//Call
} else {
NotifyMe('Billing Service Bind Failed');
enableInAppPurchases(false);
}
}
InAppBilling.addEventListener(InAppBilling.ON_BIND_EVENT, ON_BIND_EVENT);
function ON_CONNECT_EVENT(e)
{
NotifyMe('CONNECT CALLED');
if(Ti.App.Properties.getBool('transactionsRestores') === null)
{
Ti.API.info('fresh install, lets restore the transactions');
try
{
InAppBilling.restoreTransactions();
}
catch(err)
{
Ti.API.info('Error');
Ti.API.info(JSON.stringify(err));
}
}
}
InAppBilling.addEventListener(InAppBilling.ON_CONNECT_EVENT, ON_CONNECT_EVENT);
function RESPONSE_EVENT(e)
{
// Events with (e.sync == true) are deprecated and will be removed. Use the event object that the methods return.
if(!e.sync){
NotifyMe('RESPONSE CALLED ' + e.requestId + ' ' + e.responseCode);
Ti.API.info('RESPONSE CALLED \n' + 'Request Id:\n' + e.requestId + ' ' + '\nResponse Code:' + ResponseString(e.responseCode));
if(e.responseCode === InAppBilling.RESULT_ERROR)
{
// Error in request
Ti.API.info('Response event error');
Ti.API.info(JSON.stringify(e));
}
else if(e.responseCode === InAppBilling.RESULT_ITEM_UNAVAILABLE)
{
// Item unavailable in request
Ti.API.info('Response event item unavailable');
Ti.API.info(JSON.stringify(e));
}
}
}
InAppBilling.addEventListener(InAppBilling.RESPONSE_EVENT, RESPONSE_EVENT);
function PURCHASE_STATE_CHANGED_EVENT(e)
{
Ti.API.info('PURCHASE_STATE_CHANGED_EVENT Parameters:\n' + JSON.stringify(e) );
NotifyMe('PURCHASE STATE CHANGED CALLED ' + e.signedData + ' ' + e.signature+'\n'+ 'SECURITY RESULT ' + e.result);
Ti.API.info('PURCHASE STATE CHANGED CALLED');
Ti.API.info('Signature Verification Result:\n' + VerificationString(e.result));
Ti.API.info('Signed Data:\n' + e.signedData);
if (e.signedData != null) {
var response = JSON.parse(e.signedData);
/*
* We are not guaranteed to have any orders returned so
* we need to make sure that this one exists before using it.
*
* If there is no notificationId then there is no need to confirmNotifications().
* This happens when restoreTransactions() triggers a PURCHASE_STATE_CHANGED_EVENT.
*/
for(var i = 0; i < response.orders.length; i++)
{
if(typeof response.orders[i] !== 'undefined')
{
setPurchaseFlag(response.orders[i].productId);
}
if (response.orders[i] && response.orders[i].notificationId)
{
Ti.API.info('confirming notification for order ' + JSON.stringify(response.orders[i]));
var synchronousResponse = InAppBilling.confirmNotifications({
notificationIds: [response.orders[i].notificationId]
});
displaySynchronousResponseCodes(synchronousResponse);
}
}
}
}
InAppBilling.addEventListener(InAppBilling.PURCHASE_STATE_CHANGED_EVENT, PURCHASE_STATE_CHANGED_EVENT);
function NOTIFY_EVENT(e)
{
Ti.API.info('NOTIFY CALLED \n' + 'Notify Id:\n' + e.notifyId);
var synchronousResponse = InAppBilling.getPurchaseInformation({
notificationIds: [e.notifyId]
});
displaySynchronousResponseCodes(synchronousResponse);
}
InAppBilling.addEventListener(InAppBilling.NOTIFY_EVENT, NOTIFY_EVENT);
InAppBilling.startBillingService();
The following is a list of the steps I've reviewed to look for errors:
I've checked that my package name is the same as the one in my application draft in Play Store
I checked that my license key is correct
all of my In-App products are in active state
the request sent to the Play Store are with product ids that correspond to my In-App products.
My device account is in registered as a test account so it should let me make a purchase.
I added the In-App Products around two weeks ago and every time I try to request a purchase, all of them are return a not found response, Play Store couldn't take this long to activate the, right?
And yet, I get as a response "the item you are attempting to purchase could not be found".
The response code I get is 2, which I thought it meant RESULT_SERVICE_UNAVAILABLE, but the dialog that appears clearly says the item was not found, am I interpreting the response code wrongly? Or should I look into why the service is unavailable?
I followed the guide to test In-App purchases and I'm getting this error with no clear cause.
EDIT
I've been searching around and found a few questions (here and here) were developers get RESULT_SERVICE_UNAVAILABLE when making too many restoreTransactions() calls. I checked my code and saw that, indeed, I was making a restoreTransactions() call every time I launched the app.
Now I have added a validation on the ON_CONNECT_EVENT to make sure if there were transactions to restore at all, in case it doesn't then I set a flag that prevents the app from making a restoreTransactions() call the next it launches.
The problem, though, still remains, can I contact Google in some way to ask for my In-App services to be enabled again? How long do I have to wait? The users on those questions mention it could take up to 3 weeks, can we accelerate this process?
Unfortunately without In-App Purchases we cannot release our app.
Try publishing your app in alpha or beta. They have changed the way of doing tests and now this is required.
Cheers
I'm a newbie myself, but the methods, properties and events you are using do not appear in the current documentation -- perhaps your code expects an earlier version of InAppBilling.