Wait for specific key-value pair to change in HTTP response? - flutter

I'm building a mobile application with Flutter and using Firebase(firestore) to store data. When a user creates an account, I need to make POST request to an external website using that websites api, which will then return me a response that tells me if the user info is still registering (i.e. either provisioning or operational). Once it is operational, I will then get that data and store it in the users account information on firestore. However, it may take some up to 10 minutes for that information to be in the operational status. Can I use "retry" to repeatedly fetch this resource and check if the status has changed?

You can try using Timer to make that request periodically and check for status.
import 'dart:async';
main() {
const timeout = const Duration(seconds: 10);
new Timer.periodic(timeout, (Timer t) {...});
// Run your API request and logic here ^
}
If the response is as expected, just cancel the Timer and proceed with your application flow.

Related

How to get firestore query from cache when no connectivity in flutter?

In my home page , i need to get current user data every time when user open app.
But i want to get this data from cache.
As per the documentation offline Persistence is by default in android and iOS.
UserRef.doc(FirebaseAuth.instance.currentUser.uid).get()
.then((DocumentSnapshot snapshot) {
if(snapshot.exists){
print('this is exists');
userExistence=true;
setState(() {
});
}
these code i run in initState but it is not getting from offline cache
When the app is not connected/cannot connect to the database server, the get() call will already return the results from the local cache. This may take some time if this is the first time you call Firestore, as the client in that case will try to reach the server first - and only returns results from the cache once that connection fails.
If you already know that the client doesn't have an internet connection, you can speed up the get call by specifying that it must return results from the cache:
get(GetOptions(source: Source.cache))
Also see the documentation for DocumentReference.get().

Flutter. How to check that autorenewal subscription is still valid

My app has a 1 month autorenewal subscription. When the user clicks on a "Buy a subscription" button I am saving date of purchase to shared preferences.
Then, after 1 month, I need to check is this subscription is still valid.
So how can I implement it?
==== UPDATE from 11.03.2020
Hi, I can see this post still reading by people who looking for a method of how to work with subscription in Flutter.
During 2019 I made two apps with thousands installs where users can buy a renewable subscription on the 2 platforms.
Until February 2020 I used for this package from Flutter team https://pub.dev/packages/in_app_purchase, BUT - there is no way to get info about the user to unsubscribe in iOS. This is not the plugin issue, but the iOS approach for the process. We should implement our own backend for security reasons (by the way Google also recommends to do the same, but still left the way to check state directly from the app).
So, after some researches, I found guys who made backend and plugin and it is free until you have less than 10 000 USD revenue for the month.
https://www.revenuecat.com/
https://pub.dev/packages/purchases_flutter
I've implemented this plugin in my apps and it works like a charm. There is some good approaches that allow you to get a subscription state at any point in the app. I'm going to make an example and article, but not sure about the timing.
====
UPDATE from 15.07.2019. Just to save time. The answer below was given for an outdated plugin for payments. After that Flutter team made plugin
https://pub.dev/packages/in_app_purchase
and I recommend using it.
=====
The best way is to use a secure backend server for receiving Real-time Developer Notifications.
But, it is possible to check status directly in the application.
So, when user tries to get access to some paid functionality you can check whether his subscription is active or not. Below is the example:
Create somewhere the file with the class
import 'dart:io' show Platform;
import 'package:flutter/services.dart';
import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart';
import 'dart:async';
class SubcsriptionStatus {
static Future<bool> subscriptionStatus(
String sku,
[Duration duration = const Duration(days: 30),
Duration grace = const Duration(days: 0)]) async {
if (Platform.isIOS) {
var history = await FlutterInappPurchase.getPurchaseHistory();
for (var purchase in history) {
Duration difference =
DateTime.now().difference(purchase.transactionDate);
if (difference.inMinutes <= (duration + grace).inMinutes &&
purchase.productId == sku) return true;
}
return false;
} else if (Platform.isAndroid) {
var purchases = await FlutterInappPurchase.getAvailablePurchases();
for (var purchase in purchases) {
if (purchase.productId == sku) return true;
}
return false;
}
throw PlatformException(
code: Platform.operatingSystem, message: "platform not supported");
}
}
Import it where you need to check subscription status and use in Constructor. For example:
class _SubscriptionState extends State<Subscription> {
bool userSubscribed;
_SubscriptionState() {
SubcsriptionStatus.subscriptionStatus(iapId, const Duration(days: 30), const
Duration(days: 0)).then((val) => setState(() {
userSubscribed = val;
}));
}
}
In variable userSubscribed will be the state - true or false.
(Please note you have to add flutter_inapp_purchase to your project).
There's a few ways of doing this, but I would not do this on the mobile device.
On Device like you asked for
Install Flutter Cache Manager, on start set a cache key value 'Subscription' to true with maxAgeCacheObject: Duration (days: 30). On every start check if that key still exists in the cache. If it does then it's still valid otherwise it has expired.
Suggested solution using FirebaseFunction
I would suggest setting up a backend to manage all this. This is not a task for the mobile device. You can have a Cloud Function from firebase where you pass a unique device id and it'll return whether the subscription is still valid or not. A serverless function should work for that. Pseudo steps:
(On Device)When the app starts up generate a guid and make an http post request with your guid.
(Server)In your serverless function save the date the request is made to your db along with the uniqueId that you sent. If your id is already in the DB then check if it's expired (date added - current date) < 30days. Return true or false from the function. True if still valid, false if not valid.
(On Device) When you receive true from your function then save the generated id locally on disk and continue with what you want to do. If it's false then lock the user out or show the subscription that you want to take care of.
Yeah, as to the vulnerabilities, there is one way to take the issue of users changing their time manually just to deceive the app, the solution I thought of is letting the DateTime come from the server this way, whether users change the Date and Time or not you end up with the correct time frame. I hope this helps.
As to checking whether subscription has expired, just follow the step #awaik gave and in addition, you can make request to your api to store the dateTime when the subscription was purchased and when you expect the subscription to expire. I suggest you save the purchase date and expected date in your server rather than on user device as clearing the cache or data directory of the app will lead to loss of the saved data. Goodluck.

How to handle async API calls in Actions on Google

I'm using actions-on-google github nodejs app with DialogFlow. I'm trying to figure out how to make an async api call that takes longer than 5 seconds and return the response to the user when the response is ready, considering that actions on google returns Malformat error if a response is not received from the intent within 5 seconds.
This is a simple code snippet of my code:
app.intent('first-intent', async (conv: any) => {
conv.ask('Please wait while we make our long api call...');
await myPrivateFunction();
})
// I have put API_RESPONSE_RECEIVED as Events in DialogFlow
app.intent('second-intent', (conv: any) => {
console.log('This is second-intent');
var response = conv.data.apiResponse;
conv.ask(response);
})
function myPrivateFunction(): Promise<void> {
utils.apiCall().then(apiResponse => {
console.log('api response received');
conv.data.apiResponse = apiResponse;
conv.followup('API_RESPONSE_RECEIVED');
});
}
In my Firebase logs I can see "Please wait while we make our long api call..." and "api response received", but not "This is second-intent". If I put conv.followup('API_RESPONSE_RECEIVED') outside the api call right after conv.ask('Please wait while we make our long api call...'), I see "This is second-intent". So, app.followup looks OK and apparently the problem is about how I'm handling the promise, but I don't know how to fix it.
I'm using TypeScript targeting es5 in my development environment. So, I can use await/async on my api call. However, using await causes that malformat error since the api call takes longer than 5 seconds.
We don't really have a good way to handle this right now, but we have a couple of approaches that sorta work based on your needs.
Notifications are currently available for the Assistant on smartphones, and they're coming for speakers. In some cases, it might make sense to say that you're working on the problem and you'll send a notification when you have it, and then resume the conversation from the notification.
Another approach is to use the Media Response to play a bit of "hold music". Under this scheme, you would start the async call, but also immediately send back the "hold music response". When the long async call completes, it would save the result in a local cache. At the end of the segment of music, your webhook will get a notice that the music has completed. If you have the result available in the local cache, you can report it at that time, otherwise you would play more hold music and repeat this process.

SAP GATEWAY & UI5: How can reset the CSRF token?=

I need to reset the CSRF token in an OData model. Based on the UI5 documentation I am trying to do that with refreshSecurityToken(fnSuccess?, fnError?, bAsync?) function. (click here for reference)
I wrote the following code:
var oDataModel = this.getOwnerComponent().getModel("ZMDM_ODATA_FILE_SRV");
oDataModel.setTokenHandlingEnabled(true);
oDataModel.refreshSecurityToken(function() {
var token = oDataModel.getSecurityToken();
console.log(token);
// can upload the file if token reset
});
The problem is that this token is not reset for 30 minutes and that is our session timeout. Actually it is valid during the session lifetime. I even checked the following link:
https://blogs.sap.com/2014/08/26/gateway-protection-against-cross-site-request-forgery-attacks/
Actually many people had this problem, but I couldn't find a clear solution for resetting the token. I did all the required steps in the front-end for the sending a Head request for resting the token. I think something is missing regarding the back-end gateway settings or ABAP coding.
What do I have to do?
You can delete a CSRF Token (per user/token) via transaction SM05.
seems like you need to set a interval in your front-end application to fetch and update the token more often. But that's a paradox: if your back-end sets the timeout for 30 minutes, why would you keep it live for more time?
SecurityToken timeout is important to make sure the active session is being used and that no individual "forgot" it and left the system open and unwatched/unused.
But if you really need to keep your front-end session always available and force the back-end to be too, you can setInterval() to fetch the CSRF and update the application:
var oDataModel = this.getOwnerComponent().getModel("ZMDM_ODATA_FILE_SRV");
oDataModel.setTokenHandlingEnabled(true);
var fnRefreshToken = oDataModel.refreshSecurityToken(function() {
var token = oDataModel.getSecurityToken();
console.log(token);
// can upload the file if token reset
});
window.setInterval(function(){
fnRefreshToken;
}, 1800000); // where 1.800.000 miliseconds represents 30 minutes
And then you should store your new token in the token variable and allow upload if token is reset.
Kindly regards,
Henrique Mattos

How can I approve an XMPP subscription request 'later' in Smack ("Add later" functionality)?

Let us suppose that Alice sends a subscription request to Bob using the next code.
public bool AddBuddy(string jid) {
var roster = conn.Roster;
// 2` param is nickname
roster.CreateEntry(jid, null, null);
roster.SetSubscriptionMode(Roster.SubscriptionMode.Manual);
Presence subscribe = new Presence(Presence.Type.Subscribe);
subscribe.To = jid;
conn.SendPacket(subscribe);
}
When Bob has logged, it receives a popup where tell you if you want to added or not in the next method.
public void ProcessPacket (Packet p0)
{
Presence presence = p0.JavaCast<Presence> ();
var a = presence;
}
But I need to implement a "Add Later" functionality. I have no idea how to save the messages in the server and also how to receive all of them
You can delay the subscription as long as you want, there is no need to save the subscriptions packets on the server. And in order to query the deferred subscriptions requests, simply query the roster for subscriptions not in mode both.
One remark regarding your code: Roster.createEntry(String, String, String[] will automatically send the presence packat. No need to send it again.
No need to save anything on the server as it maintains pending subscribe requests automatically, ie. whenever you login to the server at a later time, the subscribe request will be pushed to you again (if it wasn't dealt with before). Therefore, just save the subscribe request locally in your application in a list or something whenever you receive it from the server, and show that local request list to the user as a "Friend request page" for the user to accept/reject. If your application's lifecycle restarts, it will be receiving all pending subsribe presences again from the server (or whenever it re-logins). Let me know if this works for you.