Implement InApp purchase(non-consumable) in flutter using flutter_inapp_purchase .
I have three plan 1 month, 6 month and 1 year, now i am checking is if 1 month plan then check transactionData and add 30 day to check expire date same for other 2 plan,
but problem is when user is on trial period then how can i check trial period expire date
List<String> storeItems = ['onemonth', 'sixmonth', 'oneyear'];
List<IAPItem> subscriptions = [];
List<PurchasedItem> purchasesSubscriptions = [];
StreamSubscription _purchaseUpdatedSubscription;
StreamSubscription _purchaseErrorSubscription;
StreamSubscription _conectionSubscription;
initPurchaseState(SportsProvider sportsProvider) async {
print("initPurchaseState");
await FlutterInappPurchase.instance.initConnection;
if (!mounted) return;
_conectionSubscription =
FlutterInappPurchase.connectionUpdated.listen((connected) {
print('connected: $connected');
});
_purchaseUpdatedSubscription =
FlutterInappPurchase.purchaseUpdated.listen((productItem) {
var data = jsonDecode(productItem.transactionReceipt);
int purchaseState = data["purchaseState"];
String productId = data["productId"];
String purchaseToken = data["purchaseToken"];
print("Data : ${data.toString()}, ${purchaseState}, ${productId}, ${purchaseToken}");
print('purchase-updated: $productItem');
FlutterInappPurchase.instance.finishTransaction(productItem);
});
_purchaseErrorSubscription =
FlutterInappPurchase.purchaseError.listen((purchaseError) {
print('purchase-error: $purchaseError');
});
subscriptions =
await FlutterInappPurchase.instance.getSubscriptions(storeItems);
//print("subscriptions: ${subscriptions}");
//subscriptions.forEach((f){print("subscription : ${f.freeTrialPeriodAndroid}");});
subscriptions.forEach((f){print("subscriptionPeriodAndroid : ${f.subscriptionPeriodAndroid}");});
subscriptions.forEach((f){print("subscriptionPeriodAndroid : ${f.toString()}");});
subscriptions.forEach((f){print("subscriptionPeriodAndroid : ${f.originalJson}");});
//Here we check for 1 month subscription is available or not by passig productId and days
var isSubscribedForMonth = await FlutterInappPurchase.instance.checkSubscribed(sku:subscriptions[0].productId,duration: Duration(days: 30));
print(isSubscribedForMonth);
//Here we check for 1 month subscription is available or not by passig productId and days
var isSubscribedForSixMonth = await FlutterInappPurchase.instance.checkSubscribed(sku:subscriptions[1].productId,duration: Duration(days: 180));
print(isSubscribedForSixMonth);
//Same for one year
//But how can identify the current subscription is on trial period?
// code for already subscribed
}
Have any way to identify the the current period is trial or regular.
I have tried so may solution but not get any relevant solution for my question,
please help me for the problem
if any more clarification required then just comment
Related
I have two tables for availability for a resource for a week and booked timeslots. I feel the logic of copying the availability into a second MutableList in Kotlin and then looping over the avialable slots and booked slots to remove the booked slots should work. But in my case (code is below), all slots are being removed and as such all slots are visible.
For e.g.
If I have 3 bookable slots for wednesday, say 8 am to 9 am, 9 am to 10.00 am and 10 am to 11.00 am, and the slot at 8 am is now booked, a new user needs to see only the 9 am and 10.00 am slot. But what happens is I see all three slots even if the 8.00 am slot is booked... which is weird... considering that the code seems sound.
Can somebody please guide as to how to do this right?
private fun getUnbookedAppointments(listOfAvailableSlots: MutableList<String>) {
//get appointments that are already scheduled for this day
val newAvailableList: MutableList<String> = listOfAvailableSlots.toMutableList()
FirebaseFirestore.getInstance().collection("Bookings")
.whereEqualTo("providerId", serviceProvider!!.getUID())
.whereEqualTo("date", booking.getdate())
.get()
.addOnSuccessListener {
val existingBookingsList: List<Booking> = it.toObjects(Booking::class.java)
for (booking in existingBookingsList) {
//change availability of the time slots
val startTimeOfExistingBooking = createTime(booking.getstartTime()!!)
val endTimeOfExistingBooking = createTime(booking.getstartTime()!!)
//add duration to time
servDuration = booking.getduration()!!.toInt()
getHrsMins(servDuration)
endTimeOfExistingBooking.add(Calendar.HOUR_OF_DAY, durhrs)
endTimeOfExistingBooking.add(Calendar.MINUTE, durmins)
for (slot in listOfAvailableSlots) {
val startTimeofSlots = createTime(slot)
val endTimeofSlots = createTime(slot)
endTimeofSlots.add(Calendar.HOUR_OF_DAY,1)
if (((startTimeOfExistingBooking.timeInMillis >= startTimeofSlots.timeInMillis) && (startTimeOfExistingBooking.timeInMillis < endTimeofSlots.timeInMillis))
|| ((endTimeOfExistingBooking.timeInMillis <= endTimeofSlots.timeInMillis) && (endTimeOfExistingBooking.timeInMillis > startTimeofSlots.timeInMillis))) {
newAvailableList.remove(slot)
Log.i("/HELLO Size of new after", "Slot removed")
}
}
}
Log.i("BOOK Checked current appointments", "${existingBookingsList.size}")
if(newAvailableList.size > 0){
Log.i("BOOK Checked current appointments", "New list is not empty")
displayAvailableSlotsbyDate(newAvailableList)
} else {
Log.i("BOOK Checked current appointments", "New list is empty")
displayAvailableSlotsbyDate(listOfAvailableSlots)
}
}.addOnFailureListener {
timeSlotsLL!!.removeAllViews()
findViewById<LinearLayout>(R.id.nodateFilter).visibility = VISIBLE
timeSlotsLL!!.visibility = GONE
findViewById<TextView>(R.id.noDatesFiltxt).text = "Sorry, there are no slots available."
Log.i("BOOK Data recovery of slots from Firestore failed", "$it")
}
}
This is the code I am using to convert a string say 8.00 am to a calendar object with Hour of the day at 8.00 am
private fun createTime(time: String): Calendar {
val calendar = Calendar.getInstance()
calendar[Calendar.HOUR_OF_DAY] = time.split(":").toTypedArray()[0].toInt()
calendar[Calendar.MINUTE] =
time.split(":").toTypedArray()[1].split(" ").toTypedArray()[0].toInt()
calendar[Calendar.AM_PM] =
if (time.split(":").toTypedArray()[1].split(" ")
.toTypedArray()[1].lowercase(Locale.getDefault()) == "am"
) Calendar.AM else Calendar.PM
return calendar
}
Actually i want to show weekly notification in flutter, i don't find a way to i'm asking here :-)
So in my app user can schedule timetable and got notification according to that.
And user can schedule notifcation for all 7 days of week(monday, tuesday ... sunday),
now i want to repeat these notification on weekly basis, so if user scheduled notification for monday so that notification will repeat on monday, and if it is scheduled for tuesday it will repeat on tuesday and so on.
static Future showWeeklyNotification({
required int id,
String? title,
String? body,
String? payload,
required int weekday,
required Time scheduleTime,
}) async => _notifications.zonedSchedule(
id,
title,
body,
_scheduleWeekly(scheduleTime , weekday, ),
await _notificationDetails(),
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime,
matchDateTimeComponents: DateTimeComponents.dayOfWeekAndTime,
);
static tz.TZDateTime _scheduleDaily(Time time){
final now = tz.TZDateTime.now(tz.local);
final scheduleDate = tz.TZDateTime(tz.local, now.year, now.month, now.day, time.hour, time.minute, time.second);
return scheduleDate.isBefore(now) ? scheduleDate.add(Duration(days: 1)) : scheduleDate;
}
static tz.TZDateTime _scheduleWeekly(Time time, weekDay){
tz.TZDateTime scheduleDate = _scheduleDaily(time);
print("hour");
print(time.hour);
print(time.minute);
print("weekday");
print(weekDay);
while(scheduleDate.weekday != weekDay){
scheduleDate = scheduleDate.add(const Duration(days: 1));
}
return scheduleDate;
}
I am using this code but it's not working, can anyone help me to find out what's wrong.
If i schedule it for today then it will work, but if i schedule it for next day or previous day
it seems to not work.
Thankyou in advance.
I want to develop a birthday app and it should send a notification every year per person.
I am already using the 'flutter_local_notifications' package and it got the feature to periodically show a notification, but not yearly. At the moment I am just determine the next birthday of each person and everytime a user opens the app, the notifications are scheduled. But if somebody doesn't open up the app in a year, he won't be notificated.
Have anyone a solution for this?
Here is a workaround if you want to implement yearly notifications with 'flutter_local_notifications' package:
import 'package:timezone/data/latest_all.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
List<tz.TZDateTime> scheduledDateTimes = [];
int numberOfYears = 7; // Define how many years you want this notification to show
int notificationID = Random().nextInt(1000000);
DateTime startDate = DateTime.now(); // Define the starting date of this yearly notification
for (int i = 0; i < numberOfYears; i++) {
scheduledDateTimes.add(tz.TZDateTime(tz.local, date.year + i, date.month,
date.day);
}
scheduledDateTimes.asMap().forEach((index, dateTime) async {
await flutterLocalNotificationsPlugin.zonedSchedule(
int.parse("$notificationID$index"),
title,
'Notification title',
dateTime,
notificationDetails,
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime);
});
Also, please take into consideration that number of local notifications you can schedule is limited to 50 for android, and 64 for iOS.
i want to make the notification push on like maybe 5 minute after i run this http function. delete the event_time from the jsaon of notification i will receive the push notification that have timeline now, but if i hard code the event_time the time will show 50 year ago since epoch time , event i put 999 for the year it just increase by 2 year . i dint get it at all. then found Timestapm format in google and firebase so use it and the android got error whil parsing the json payload said cant fine field something . have any idea to format the Zulu RC399 time format?
pp.get('/sendCloudMessageReminderActivation', async (req, res) => {
const x = req.query.userUID;
const xstr = String(x);
const zxcv = admin.firestore().collection('user').doc(xstr);
const xoxo = await zxcv.get();
var today = new Date();
// today.setHours(today.getHours()+1);
today.setHours(today.getHours()+8);
// today.setFullYear(today.getFullYear()+50);
var xoxocode;
console.log(today+'saya saya sebelum cagbge');
// today = ISODateString(today);
const timestamp = admin.firestore.Timestamp.fromDate(today);
console.log(timestamp.nanoseconds +'siniiiiiiiiiiiiiiiiiiiii');
if (xoxo!==null) {
xoxocode= await xoxo.data().cloudMessagingToken;
const message = {
notification: {
title: 'your account is still in active state',
body: 'Tap here to check it out!'
},
android:{
priority:'high',
// important : 'MAX',
notification:{
notification_priority: "PRIORITY_MAX",
event_time: timestamp,// when ever i cahnge this event use ZULU RC339 format i still give my 50 year ago notification. i did my research but cant figure out how to do this ,
default_sound: true,
// notification_priority: PRIORITY_MAX
}
},
token: xoxocode
};
// res.status(200).send('authorized');
const respondfrommesage = admin.messaging().send(message);
console.log(respondfrommesage);
res.status(200).send('message send notification');
}
res.end();
});
Zulu is not a time format. It's a time zone. Zulu is just another name for UTC.
As per this article:
Another name for UTC Time is "Zulu" or "Z Time."
Now, about the timestamp value. If you are creating a Date object and use milliseconds or nanoseconds property of a date, it will always give you the difference in respective property type (mills/nano),
From the time you created the instance and starting time of UNIX which is 1 January 1970.
It has been 50 years as of 1st Jan, 2020. Hence the 50 year difference in the event_time.
If you have any other doubts let me know.
does someone use gameNetwork provided by Google Play? Im having a little problem with it, im trying to fetch personal best score and its not working well.
Here is the code that i use
function loadScoreCallback(event)
oldScore = event.data[5].formattedValue
end
gameNetwork.request( "loadScores",
{
leaderboard =
{
category = "thecategory id",
playerScope = "Global", -- Global, FriendsOnly
timeScope = "AllTime", -- AllTime, Week, Today
range = {1,5},
playerCentered = true,
},
listener = loadScoreCallback
})
I also tried something like
function loadScoreCallback(event)
oldScore = event.data
end
gameNetwork.request( "loadScores",
{
leaderboard =
{
playerID = playerName,
category = "CgkIptXi1qgCEAIQAw",
playerScope = "Global", -- Global, FriendsOnly
timeScope = "AllTime", -- AllTime, Week, Today
playerCentered = true,
},
listener = loadScoreCallback
})
Also didnt work :/
I had some trouble figuring this out, but I found a solution that works for me.
this is the listener that is called when the user submits a score.
local function onGameNetworkRequestResult( event )
if event.type == "setHighScore" then
local function tempScoresFct(event)
if event.data then
local playerRank = event.data[1].rank
local currentValue = event.data[1].value
if lb.tempScore <= event.data[1].value then
native.showAlert("Score submitted", "You score is lower or equal than your current score in the leaderboard("..currentValue.."), nothing changed. Your global, all time rank is:\n" .. playerRank .. "\nTo see how you are doing among your friends and in other time scales, click the button \"show leaderboard\".", {"Ok"})
else
native.showAlert("Score submitted", "Your score was successfully uploaded to the leaderboard. Your global, all time rank is:\n" .. playerRank .. "\nTo see how you are doing among your friends and in other time scales, click the button \"show leaderboard\".", {"Ok"})
end
end
end
gameNetwork.request( "loadScores",
{
leaderboard =
{
category = event.data.category,
playerScope = "Global", -- Global, FriendsOnly
timeScope = "AllTime", -- AllTime, Week, Today
range = {1,1}, --Just get one player
playerCentered = true, -- and this player is the player that is logged in
},
listener = tempScoresFct
})
end
end
and this ist the function that submits the score:
function lb.submitScoreFct(event)
if event.phase == "ended" then
if gameNetwork.request("isConnected") then
--show leaderboards
print("submitting score")
local myCategory
local categoryName
if event.target.category == 1 then
myCategory = "ategoryID" --palo alto
categoryName = "Palo Alto"
elseif event.target.category == 2 then
myCategory = "ategoryID" --groningen
categoryName = "Groningen"
elseif event.target.category == 3 then
myCategory = "ategoryID" --sp2
categoryName = "SP2"
end
--local score = mRand(100,1000)
local score = event.target.score
lb.tempScore = score -- i use this to verify if the uploaded score was higher or lower then the value that is already there
print("Added " .. score .. " to " .. categoryName)
gameNetwork.request( "setHighScore",
{
localPlayerScore = { category=myCategory, value=tonumber(score) },
listener = lb.onGameNetworkRequestResult
})
end
else
print("user needs to log in")
-- Tries to login the user, if there is a problem then it will try to resolve it. eg. Show the log in screen.
gameNetwork.request("login",
{
listener = loginListener,
userInitiated = true -- if that is false, than this process is silent ie dont pop up a log in if the user is not logged in
})
end
return true
end
Oh and one thing I figured out, is that this listener functions are fragile. If there is one error inside, they quit and sometimes dont give any error.
For example if you try to print(event.data.rank) (instedt of print(event.data[1].rank)) there is no error but the function ends at this point and the rest of the function is not executed...
Well so i found a better idea, since not everyone is online all the time i made my own saving system, and it fetches high score from there. But i still dont know why it doesnt work, well at least i solved it with this.