Flutter use api and webhook - flutter

i wanna work with this api in my flutter app
https://dev.chargily.com/docs/epay-api/
i have done the first part 1.Make Payment
but the second part 2. Payment confirmation i don't know how to do it because it contain webhook
(i wanna get the response sent via webhook_url)
this is the first part 1. Make Payment
final response = await dio
.post(
'http://epay.chargily.com.dz/api/invoice',
options: Options(headers: {
'X-Authorization':
'["API-KEY"]',
'Accept': 'application/json',
}),
data: jsonEncode(params),
)
.then((value) async {
log(value.data['checkout_url']);
var url = await value.data['checkout_url'];
work perfectly
i need the second part 2. Payment confirmation

Webhooks are a fancy word to describe a server calling another server's Web Api Endpoint. so, it's not meant to be used in the frontend.
you can't verify / get payment confirmation directly from a flutter app, you have to host your own backend / serverless function to act as a webhook and report back to the frontend environment.

Related

Authentication flow with Oauth2 in flutter communicating with own api

After some hours of research in vain I stay confused how to do the following:
I have a flutter app which authenticates via OAuth2 to Google (google_sign_in) and Facebook. For Facebook this is the code:
final LoginResult loginResult = await FacebookAuth.instance.login();
final userData = await FacebookAuth.instance.getUserData();
print(userData);
Which prints: {email: john.doe#email.com, id: 123456, name: John Doe}
I already have a webpage with OAuth2 authentication built in Flask/Python. Now I want my users to be able to both use Web and App and share the preferences/data/etc.
How would I achieve that? In my Flask webapp I'm just creating a user in my database if it doesn't exist and then use some authentication headers in subsequent calls. So I thought with the app I could…
send what I got from OAuth to the api and create the user if it does not yet exist
return some sort of token (with a TTL?)
verify the tokens being sent by the app
But this is a lot of custom boilerplate code, I'm sure that this is existing somewhere/somehow. Additionally: How can I be sure someone is not "tampering" my app via decompile, proxying or just plainly calls my api and claiming to be someone else?
My security requirements are medium: The app will eventually have messaging but won't be used for things like money transfer.
I'm considering these options:
PKCE but this looks like the OAuth2 flow would go through my flask api and that sounds too complex (I had a hard time already getting OAuth2 to work in flutter alone)
Resource Owner Password Credentials Grant which sounds like I can somehow pass the results of OAuth2 to my api, get back a token and use this in subsequent requests. However this seems like an outdated protocol (top google results are articles from oracle)
firebase implementation: they use the same flow: first OAuth2 authentication and then passing the credentials into their servers api. On the first time they pass the credentials a user is created and stored in the database, etc. But my reverse engineering skills are not good enough to figure out how it's done.
using a webview and use the oauth2 of my flask website. I'm shying back from this because it would be not a nice mobile experience plus I would not know how to read/store these credentials
After a lot of reading I found a good article on auth0 , in essence there are two options:
Resource Owner Password Flow - use this if you totally trust your app, e.g. when you deploy it to a closed group of users for which you have device management in place. This situation doesn't apply for me and also Auth0 doesn't recommend it. Still, it would have been relatively easy to implement.
PKCE (Proof Key for Code Exchange) - use this when the client cannot be trusted (IMO 99.9% of mobile apps). But this needs some fancy protocol between the mobile app and the server and alone by looking at the flowchart diagram I got headaches
As PKCE looks too complicated to implement myself I decided to go with Firebase, which helps small projects such as mine where you don't want to go through the pain to code the whole PKCE flow yourself.
What I did was:
adding firebase authentication to my flask app, using flask-firebase - this was worth it since it decreased the lines of python code by 40%. Because the module lacks good documentation I wrote this blog post which explains how to use it
adding firebase authentication to flutter. This is very well documented e.g. here
The whole flow then works like this:
flutter triggers the oauth flow for e.g. google
flutter gets back the auth details, including email address, name, etc. (depends on oauth provider)
the auth details are sent to firebase which creates the user if it doesn't exist yet, enriches it with a user id and packs it into an encrypted token
the token is sent to flask, which verifies the token against firebase
flask logs the user in (via flask_login) and returns a session cookie
the session cookie is stored in flutter (using requests) and used for subsequent api calls
to preserve the user logged in even after app close, the session is stored in apps preferences (using shared_preferences)
In essence, this is the code needed (google social login example):
Future<String?> signInWithGoogle() async {
final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
final GoogleSignInAuthentication? googleAuth =
await googleUser?.authentication;
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth?.accessToken,
idToken: googleAuth?.idToken,
);
UserCredential userCredentials =
await FirebaseAuth.instance.signInWithCredential(credential);
return userCredentials.user?.getIdToken();
}
…
var cookies = await Requests.getStoredCookies('example.com');
SharedPreferences? prefs;
if (!cookies.keys.contains('session')) {
prefs = await SharedPreferences.getInstance();
if (prefs.containsKey('session')) {
print('cookie not set, load session from prefs');
await Requests.addCookie(
'example.com', 'session', prefs.getString('session')!);
}
}
cookies = await Requests.getStoredCookies('example.com');
if (!cookies.keys.contains('session')) {
print('cookie not set, prefs contain no session, signing in');
String? idToken = await signInWithGoogle();
if (idToken != null) {
await Requests.post('https://example.com/auth/sign-in',
body: idToken,
headers: {'Content-Type': 'application/jwt'},
bodyEncoding: RequestBodyEncoding.PlainText);
var cookies = await Requests.getStoredCookies('example.com');
prefs?.setString('session', cookies['session']!.value);
}
}
var r = await Requests.get('https://example.com/api/something_which_requires_login');
The important part happens with Requests.post: this posts the idToken of firebase to flask, which in turn then verifies the token, calls login_user and returns response with the session cookie header. This cookie is stored by requests and is added to subsequent http requests.
Because this is some mouthful I created this blogpost which explains this in more detail.

Flutter web - dio changes post requests into options requests

I'm using package:dio in a Flutter web application.
Whenever I send a POST request, however, it gets changed into an OPTIONS request. The function that makes the API request looks like this:
Future<LoginResponse> login(LoginRequest request) async {
final dio = Dio(BaseOptions(baseUrl: "http://localhost:8000"));
final response = await dio.post("/login", data: request.toJson());
return LoginResponse.fromJson(jsonDecode(response.data));
}
This code sends an OPTIONS request to http://localhost:8000/login. If I add that endpoint to my server, it works.
If I send the POST request from postman manually it also works.
If I change this code to other methods (e.g. dio.delete(...)) it also maps to an OPTIONS request.
Why is dio rewriting my requests?

How should be the correct http request url in flutter to fetch data from the shopify store?

How should be the correct HTTP request URL in a flutter to fetch data from the Shopify store?
Any idea or material, how to integrate payment method with Shopify in a flutter when the user clicks the buy button?
I am trying with this line for HTTP requests, but not working
getUserData() async {
var response = await http.get(Uri.https(
'{apikey}:{password}#{hostname}.myshopify.com',
'/admin/api/{version}/{resource}.json'));
var jsonData = jsonDecode(response.body);
print(jsonData);
}
getUserData() async {
var response = await http.get(Uri.https(
'c948656b86f9a9e0bf8beffad653e484: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#verdenapp310.myshopify.com',
'/admin/api/2021-07/graphql.json'));
var jsonData = jsonDecode(response.body);
print(jsonData);
}
print output: I/flutter (31610): {errors: Not Found}
You are going to have to do some work. Study the architecture of the Shopify platform and then decide the correct API to use. In your brief example, you are choosing the Admin API, which is completely wrong for retrieving data from a store, unless you are an App installed in the store, to benefit the merchant experience. Usually, Admin API has nothing to do with customers.
Buy Button has a direct line to Shopify checkout. You do not get to install your own payment gateway through that button. The merchant has to select or install the payment gateway they want to use from their Shop. Note that it is possible to provide for custom payment gateways in Shopify but that is far outside the scope of your SO request here. Again, they have documentation for that too.

Running Google Apps Script through https request with Service Account credentials

I'm working on a Flutter app. And I've been trying to run my web-app Google Apps Script through http request since I'm required to use a Service Account and that access isn't supported in the Apps Script API. But I keep getting a 403/Forbidden response to the requests. I have the credentials for the Service Account and I am using its access token in my request but it still doesn't work.
I'm a novice at http requests and new to Google's authentication protocols so I'd appreciate some insight.
Thanks in advance.
Code:
return await driveUtils.getCreds(context).then((creds) async {
final drive_scopes = [drive.DriveApi.DriveReadonlyScope, "https://www.googleapis.com/auth/drive.file"];
final script_scopes = [app_scripts.ScriptApi.ScriptDeploymentsScope];
return await clientViaServiceAccount(creds, script_scopes+drive_scopes).then((AuthClient client) async {
debugPrint("url = " + url);
debugPrint("token = " + client.credentials.accessToken.data);
return await client.get(url,
headers: {
"Authorization": "Bearer ${client.credentials.accessToken.data}"
}
);
}, onError: onClientError);
}, onError: onCredsError);
Background: The script creates a Form and sets it Destination to a Spreadsheet's ID. Hence, the app requires that anyone who runs it to have a Google account to become the owner of the new Form and obtain access to the Sheet.
Update: It seems that Service Accounts can only access scripts that are within the same Google Cloud Project. This is a big issue since the point of the script is to create a central place for acquiring Form creation functionality for my app. And the app is intended to be used by anyone.
Does anyone have any ideas? Assuming a Service Account is the right Google Credentials for my app, I essentially need the ability to:
Create a Form that can be assigned to a user
Designate a user's spreadsheet as the forms response location
Retrieve the forms publishedURL
#Tanaike helped me figure out the issue. In order to make the script visible and able to run with a Service Account I had to change the Share setting for viewing the script. Simple solution

Facebook Webhook Test Button not working as expected

I have successfully added my callback url in my webhooks setup. At that time, the callback url was called successfully with proper logs and everything in the server. But when I click on the TEST button for the respective name (leadgen in this case), nothing AT ALL is being received by the server when in fact it should at least log the very first line. Note that I am using post and get.
Additional note: for NetSuite experienced developers, I am using a suitelet as the callback url for the webhook.
Thanks.
Suitelets that are available externally without login will only work if the User-Agent header in the request mimics a browser. See for example SuiteAnswers # 38695.
I ran into a similar issue, and the workaround was to proxy the request using a Google Cloud Function that simply rewrote the User Agent:
const request = require('request');
exports.webhook = (req, res) => {
request.post(
{
url: process.env.NETSUITE_SUITELET_URL,
body: req.body,
json: true,
headers: {
'User-Agent': 'Mozilla/5',
Authorization: req.headers['authorization'],
},
},
function(error, response, body) {
res.send(body);
}
);
};