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

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

Flutter HEALTH package get steps from user returns always null?

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.

Firebase analytics is not updating screen name after recent update from my Flutter App

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");

How to solve install status code -100 while working with In-App update in Android?

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.

how to protect my app from share to another?

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));
}

Can't access In-App Products in Play Store

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.

Categories

Resources