Google charged me $5000 in a month for using Google Place API - flutter

My project is under development, Vuejs and Flutter in the frontend and Laravel is in the backend.
I am using Google place autocomplete API in this project, I made a big mistake due to not having enough knowledge of using google apiz.
After trial finished google charged $5000.
My mistakes were 2 things;
Requesting unnecessary data and not providing sessionToken
I am posting my edited codes here if anyone can check it please, I'm afraid to make mistake again.
vuejs code
mounted() {
this.$refs.focusable.focus();
let $vm = this;
var autocomplete = new google.maps.places.Autocomplete(
document.getElementById("autocomplete"),
{
componentRestrictions: { country: "ca" },
fields: ["address_components", "formatted_address", "geometry"],
types: ["address"],
bounds: new google.maps.LatLngBounds(
new google.maps.LatLng(49.246292, -123.116226)
),
//strictbounds: true,
}
);
google.maps.event.addListener(autocomplete, "place_changed", function () {
var data = autocomplete.getPlace();
let latlng = {
lat: data.geometry.location.lat(),
lng: data.geometry.location.lng(),
};
data.address_components.forEach((component) => {
if (component.types.indexOf("administrative_area_level_1") > -1) {
if ($vm.checkState(component.short_name)) {
$vm.validAddress(data, latlng);
} else {
$vm.invalidAddress(data);
}
}
});
});
},
According to google docs link I don't need to provide sessionToken for Autocomplete widget. and I think my code is correct here.
and this is my Flutter code
TextFormField(
autofocus: true,
controller: _addressController,
decoration: InputDecoration(hintText: 'Postal code'),
onChanged: (val) {
final sessionToken = Uuid.v4();
findPlace(val, sessionToken);
},
),
//get a place
void findPlace(String placeName, String sessionToken) async {
if (placeName.length > 1) {
String autoComplete =
"$googleApi?input=$placeName&types=address&components=country:ca&key=$mapKey&sessiontoken=$sessionToken";
var res = await http.get(Uri.parse(autoComplete));
Map data = jsonDecode(res.body);
var predictions = data['predictions'];
var placesList = (predictions as List)
.map((e) => PlacePredictions.fromJson(e))
.toList();
setState(() {
sToken = sessionToken;
placePredictionList = placesList;
});
}
}
// get place details using place_id
addressDetails(placeId, sessionToken) async {
String fields = 'address_component,formatted_address,geometry';
String placeDetailsUrl =
"$googleApiDetails?place_id=$placeId&fields=$fields&key=$mapKey&sessiontoken=$sessionToken";
var res = await http.get(Uri.parse(placeDetailsUrl));
Map data = jsonDecode(res.body);
}
In the above Flutter code there is a confusion for me is this sessiontoken or sessionToken I don't know how to check it and there is no any information in the Google docs.
Help really appreciated.

Can't post as a comment as I don't have the required rep.
But you can cap your API usage so things like these won't happen.
Read more here: https://cloud.google.com/apis/docs/capping-api-usage
In the Google Cloud console, go to the APIs & Services Dashboard page.
From the projects list, select a project or create a new one.
Click the name of the API you're interested in.
Click Quotas. If the Quotas tab is not present in the tab nav, it means the API you've selected doesn't have quotas defined.
To find the quota you want to cap, enter the appropriate properties and values in the filter_list Filter field. For example, to find the Subnetworks quota, enter Quota:Subnetworks.
Click the checkbox next to the quota you want to cap, and then click create EDIT QUOTAS.
Complete the quota change form, including the new limit that you want to set.
Click SUBMIT REQUEST.

Related

How can I get the chat room online members without reload the page in converse js

I am using converse.js and I am trying to get the users who joined the chat room, I am able to get the users but when a new user joins I can not get the new user on my console log until I reload the page, Below I have created a plugin for getting the users.
export const moderationActions = () => {
window.converse.plugins.add('moderation-actions', {
dependencies: [],
initialize: function () {
const _converse = this._converse;
_converse.api.listen.on(
'getToolbarButtons',
async (toolbar_el: any, buttons: any) => {
toolbar_el.model.occupants.models.map((occupant: any) => {
console.log(occupant.get('nick'), occupant.get('show')),
console.log(occupant);
});
},
);
},
});
};
There are a few events related to users like membersFetched but don't know how can I get the users without reloading the page
Whenever someone joins or leaves a room, a new occupant model is added or removed from the occupants collection on the room model (available via the .occupants attribute).
Whenever a model is added or removed from a collection, an add or remove event is triggered which you can listen to.
So you can try something like this:
window.converse.plugins.add('num-occupants', {
initialize: function () {
const _converse = this._converse;
const room = await api.rooms.get('jid');
let num_occupants = room.occupants.length;
room.occupants.on('add', () => (num_occupants = room.occupants.length));
room.occupants.on('remove', () => (num_occupants = room.occupants.length));
}
});

Braintree Google Pay sandbox opens live domain

I'm trying to integrate Google Pay at my page, as expected doing that first at Sandbox environment but I face a problem that when I click the Google Pay button it opens the live domain and asks me to enter a real card, although I setup up all related to Sandbox environment.
Here is the code following BT documentation.
var createGooglePaymentComponent = function(clientInstance){
var button = document.querySelector('#google-pay-button');
var paymentsClient = new google.payments.api.PaymentsClient({
environment: 'TEST' // Or 'PRODUCTION'
});
braintree.googlePayment.create({
client: clientInstance,
googlePayVersion: 2,
}, function (googlePaymentErr, googlePaymentInstance) {
paymentsClient.isReadyToPay({
apiVersion: 2,
apiVersionMinor: 0,
allowedPaymentMethods: googlePaymentInstance.createPaymentDataRequest().allowedPaymentMethods,
}).then(function(response) {
if (response.result) {
button.addEventListener('click', function (event) {
event.preventDefault();
var paymentDataRequest = googlePaymentInstance.createPaymentDataRequest({
transactionInfo: {
currencyCode: 'USD',
totalPriceStatus: 'FINAL',
totalPrice: '100.00',
}
});
var cardPaymentMethod = paymentDataRequest.allowedPaymentMethods[0];
cardPaymentMethod.parameters.billingAddressRequired = true;
paymentsClient.loadPaymentData(paymentDataRequest).then(function(paymentData) {
googlePaymentInstance.parseResponse(paymentData, function (err, result) {
if (err) {
// Handle parsing error
}
// Send result.nonce to your server
});
}).catch(function (err) {
});
});
}
}).catch(function (err) {
});
});
};
Here is a screenshot of what I get:
Any idea why does that happen?
Using Google Pay in the TEST environment will return a TEST payment credential which won't actually charge the payment method that you provide. It's understandable that you don't want to be using real payment details.
If you want to be able to choose from a list of predefined test cards, follow the instructions here: https://developers.google.com/pay/api/web/guides/resources/test-card-suite
In short, you will need to join the googlepay-test-mode-stub-data Google Group which will then display a list of test accounts when accessing the Google Pay payment sheet with that user.

Accepting payments in Flutter Web

I am creating an application in Flutter Web that needs to collect payment information to create subscription charges. My plan was to use Stripe to do this, however, after making all the necessary methods for Stripe, I found that Stripe only accepts card data through Stripe Elements. Upon further research, I saw that essentially all payment platforms do this to maintain PCI compliance.
Is there any method of embedding Stripe elements(or any equivalent) into my application or is there an easier method of accepting payments with Flutter Web?
There's an unofficial Flutter Stripe package that might be what you're after: https://pub.dev/packages/stripe_payment
There's a new package called stripe_sdk, that appears to have Web support. I haven't tried it yet, but it says Web support in the description and has a web demo aswell :)
Also the old packages for mobile won't work for web, because they rely on WebView, which is not supported (and wouldn't make much sense) on web.
In case you're using Firebase as a backend, there's a stripe payments extension you can install in Firebase which makes it easy. How it works is you add a checkout_session in to a user collection and keep listening on the document. Stripe extension will update the document with a unique payments url and we just open that URL in a new tab to make the payment in the tab. We're using it in our web app, and it's working.
Something like :
buyProduct(Product pd) async {
setState(() {
loadingPayment = true;
});
String userUid = FirebaseAuth.instance.currentUser!.uid;
var docRef = await FirebaseFirestore.instance
.collection('users')
.doc(userUid)
.collection('checkout_sessions')
.add({
'price': pd.priceId,
'quantity': pd.quantity,
'mode': 'payment',
'success_url': 'https://yourwebsite/purchase-complete',
'cancel_url': 'https://yourwebsite/payment-cancelled',
});
docRef.snapshots().listen(
(ds) async {
if (ds.exists) {
//check any error
var error;
try {
error = ds.get('error');
} catch (e) {
error = null;
}
if (error != null) {
print(error);
} else {
String url = ds.data()!.containsKey('url') ? ds.get('url') : '';
if (url != '') {
//open the url in a new tab
if (!isStripeUrlOpen) {
isStripeUrlOpen = true;
setState(
() {
loadingPayment = false;
},
);
launchUrl(Uri.parse(url));
}
}
}
}
}
},
);
}

Can I create follow-up actions on Actions on Google?

I know that I can deep link into my Google Home application by adding to my actions.json.
I also know that I can parse raw string values from the app.StandardIntents.TEXT intent that's provided by default, which I am currently doing like so:
if(app.getRawInput() === 'make payment') {
app.ask('Enter payment information: ');
}
else if(app.getRawInput() === 'quit') {
app.tell('Goodbye!');
}
But does Actions on Google provide direct support for creating follow-up intents, possibly after certain user voice inputs?
An example of a conversation flow is:
OK Google, talk to my app.
Welcome to my app, I can order your most recent purchase or your saved favorite. Which would you prefer?
Recent purchase.
Should I use your preferred address and method of payment?
Yes.
OK, I've placed your order.
My previous answer won't work after testing.
Here is a tested version.
exports.conversationComponent = functions.https.onRequest((req, res) => {
const app = new ApiAiApp({request: req, response: res});
console.log('Request headers: ' + JSON.stringify(req.headers));
console.log('Request body: ' + JSON.stringify(req.body));
const registerCallback = (app, funcName)=>{
if (!app.callbackMap.get(funcName)){
console.error(`Function ${funcName} required to be in app.callbackMap before calling registerCallback`);
return;
}
app.setContext("callback_followup", 1, {funcName});
}
const deRegisterCallback = (app)=>{
const context = app.getContext("callback_followup");
const funcName = app.getContextArgument("callback_followup", "funcName").value;
const func = app.callbackMap.get(funcName);
app.setContext("callback_followup", 0);
return func;
}
app.callbackMap = new Map();
app.callbackMap.set('endSurvey', (app)=>{
if (app.getUserConfirmation()) {
app.tell('Stopped, bye!');
}
else {
app.tell('Lets continue.');
}
});
app.callbackMap.set('confirmationStartSurvey', (app)=>{
const context = app.getContext("callback_follwup");
if (app.getUserConfirmation()) {
registerCallback(app, 'endSurvey');
app.askForConfirmation('Great! I\'m glad you want to do it!, do you want to stop?');
} else {
app.tell('That\'s okay. Let\'s not do it now.');
}
});
// Welcome
function welcome (app) {
registerCallback(app, 'confirmationStartSurvey');
const prompt = "You have one survey in your task list, do you want to proceed now?";
app.askForConfirmation(prompt);
}
function confirmationCalbackFollowup (app) {
const context = app.getContext("callback_followup");
if (! context){
console.error("ERROR: confirmationCallbackFollowup should always has context named callback_followup. ");
return;
}
const callback = deRegisterCallback(app);
return callback(app);
}
const actionMap = new Map();
actionMap.set(WELCOME, welcome);
actionMap.set('confirmation.callback.followup', confirmationCalbackFollowup );
app.handleRequest(actionMap);
});
The previous solution won't work because app is generated everytime the action function is called. I tried to save a callback function into app.data but it won't be existing next intent coming. So I changed another way. Register the callback function to app.callbackMap inside the function. so it will be there anyway.
To make it work, one important thing is Api.Ai need to have context defined in the intent. See the Api.Ai Intent here.
Make sure you have event, context, and action of course. otherwise, this intent won't be triggered.
Please let me know if you can use this solution. sorry for my previous wrong solution.
thanks
Can you give an example of a conversation flow that has what you are trying to do?
If you can use API.AI, they have Follow Up intents in the docs.
I do not think your code
if(app.getRawInput() === 'make payment') {
app.ask('Enter payment information: ');
}
else if(app.getRawInput() === 'quit') {
app.tell('Goodbye!');
}
is a good idea. I would suggest you have two different intent to handle "Payment information" and "Quit".

Soundcloud API sc.stream (track not loading on mobile sometime) - working on desktop

We are currently using the soundcloud API SDK for streaming and it does work on desktop but not 100% on mobile. (using responsive html. same api of course)
Sometime track is not lauch ? sometime it is.
I do not have specific error but on chrome network this line is show in red ??
http://api.soundcloud.com/tracks/146926142/stream?client_id=XXXXX
Redirect
We use a function to stream the track.
function streamTrack(id) {
var defer = $q.defer();
// Stream the track
SC.stream('/tracks/' + id, {
useHTML5Audio: false,
waitForWindowLoad: true,
onfinish: _scope.next,
whileplaying: function () {
var _this = this;
// Since we are in a callback, we need to tell angularJS to apply the change
if (timeout1) $timeout.cancel(timeout1);
timeout1 = $timeout(function () {
// Update the progress bar
_scope.progress = (_this.position / currentTrackDuration * 100) + '%';
_scope.timer = moment(_this.position).format('mm:ss');
$rootScope.$broadcast('trackRunning', { timerunning: _scope.timer });
});
}
}, function (sound) {
if (sound) {
defer.resolve(sound);
} else {
defer.reject();
}
});
return defer.promise;
}
If somebody has an idea pls.
Best Regards
Xavier