I am new to flutter and I have just created app that accepts payments from user using flutter_stripe: ^2.1.0 plugin. The amount in cloud function file index.js is fixed but I want to pass the amount that is calculated dynamically. Here is my code.
Future<void> makePayment() async {
final url = Uri.parse(
'https://us-central1-carwashapp-376b6.cloudfunctions.net/stripePayment');
final response =
await http.get(url, headers: {"Content-Type": "application/json"});
paymentIntentData = json.decode(response.body);
await Stripe.instance.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
paymentIntentClientSecret: paymentIntentData['paymentIntent'],
applePay: true,
googlePay: true,
style: ThemeMode.light,
merchantCountryCode: 'US',
merchantDisplayName: 'Kleen My Car',
),
);
setState(() {});
displayPaymentSheet();
}
Future<void> displayPaymentSheet() async {
try {
await Stripe.instance.presentPaymentSheet(
parameters: PresentPaymentSheetParameters(
clientSecret: paymentIntentData['paymentIntent'],
confirmPayment: true));
setState(() {
paymentIntentData = null;
});
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text('Payment succeeded')));
} catch (e) {
print('error error error');
}
}
and here is my index.js file's code
const functions = require("firebase-functions");
const stripe = require("stripe")(functions.config().stripe.testkey);
exports.stripePayment = functions.https.onRequest(async (req, res) => {
const paymentIntent = await stripe.paymentIntents.create(
{
amount: 120,
currency: "USD",
},
function (err, paymentIntent) {
if (err != null) {
console.log(err);
} else {
res.json({
paymentIntent: paymentIntent.client_secret,
});
}
}
);
});
Any kind of help is much appreciated. Thank you so much!
You need to adapt this line:
final response = await http.get(url, headers: {"Content-Type": "application/json"});
(Firstly, it makes no sense to give a content type on a GET, as GETs don't have any content. Remove that header.)
You could change to a POST and add the amount as a parameter, or leave it as a GET and add the amount to the URL.
With a POST, add (for example) body: {'amount': amount.toString()}
With a GET, add it to the URL, as follows:
final uri = Uri.https('us-central1-carwashapp-376b6.cloudfunctions.net', '/stripepayment', {'amount': amount.toString()});
In your cloud function access amount from the req. (For example, in the GET example, it would be req.query.amount as string.)
We also pass up other parameters like email, unique order id (to be used as the idempotency key), etc.
in index.js file change
const paymentIntent = await stripe.paymentIntents.create(
{
amount: 120,
currency: "USD",
},
to
const paymentIntent = await stripe.paymentIntents.create(
{
amount: req.query.amount,
currency: req.query.currency,
},
and deploy your function.
after that, in makepayment function, change your URL to
https://us-central1-carwashapp-376b6.cloudfunctions.net/stripePayment?amount=$amount¤cy=$currency.
In this way, you can pass different amounts every time by changing the value of $amount variable in the URL.
Related
I was using Firebase services without rules for testing like this:
allow read, write: if true; and when I decide to set rules this problem happened suddenly!
allow read, write: if request.auth != null;
FirebaseError: Firebase Storage: The operation 'uploadBytesResumable' cannot be performed on a root reference, create a non-root reference using child, such as .child('file.png'). (storage/invalid-root-operation)
Before changing the rules, I uploaded many images to storage and view them in my app, but now I'm trying with all my information without any changes!!
This is the method which I use to upload the images, I didn't change anything in it before or after changing the rules:
Reference reference = FirebaseStorage.instance.ref();
Future uploadImage() async {
reference.child('Items/${itemNameController.text}.jpg');
await reference.putData(
webImage!,
SettableMetadata(contentType: 'image/jpeg'),
);
downloadUrl = await reference.getDownloadURL();
update();
}
By the way, I'm using Flutter web.
And I'm using this package: firebase_storage: ^11.0.6
Update
I will put here the full code for my operation, the problem is happening when I try to upload to fireStorage, but there is another function to store data in fireStore.
I have tried the solution that typed down by Someone, but still have the same problem when I combine functions (FireStorage - FireStore) with each other:
Database database = Database();
String? id;
Future saveToDb() async {
var formData = formKey.currentState;
if (formData!.validate()) {
await uploadImage().whenComplete(() async {
await addToFireStore();
clearData();
Get.snackbar(
'Adding done',
'Adding item done successfully',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: AppColors.mainColor,
borderRadius: Dimensions.radius20,
duration: const Duration(seconds: 3),
padding: EdgeInsets.fromLTRB(
Dimensions.width30,
Dimensions.height20,
Dimensions.width40,
Dimensions.height20,
),
);
update();
});
}
}
Future uploadImage() async {
await database.reference.child('Items/${itemNameController.text}.jpg').putData(
webImage!,
SettableMetadata(contentType: 'image/jpeg'),
);
downloadUrl = await database.reference.getDownloadURL();
update();
}
Future addToFireStore() async {
for (var i in database.categoriesModel) {
if (database.categoryValue == i.name) {
id = i.id;
}
}
await database.catCollectionRef
.doc(id)
.collection('Items')
.doc(const Uuid().v4())
.set({
'id': const Uuid().v4(),
'Name': itemNameController.text,
'Decryption': itemDescController.text,
'Availability': switchValue.toString(),
'Category': database.categoryValue,
'Quantity type': quantityValue,
'Quantity value': itemQuanController.text,
'Price': itemPriceController.text,
'Image': downloadUrl,
});
update();
}
clearData() {
webImage?.clear();
itemNameController.clear();
itemDescController.clear();
itemQuanController.clear();
itemPriceController.clear();
database.categoryValue = 'Choose one';
switchValue = false;
quantityValue = 'Choose One';
downloadUrl = '';
update();
}
I use the saveToDb() function in the UI button to set the whole operation, but returns the same problem!!!
you're referencing to the root directlt, calling the child() on statement will not change the reference, try:
Reference reference = FirebaseStorage.instance.ref();
Future uploadImage() async {
await reference.child('Items/${itemNameController.text}.jpg').putData(
webImage!,
SettableMetadata(contentType: 'image/jpeg'),
);
downloadUrl = await reference.getDownloadURL();
update();
}
Google pay is completely working on Test mode but after set the live secret key in "v1/payment_intents" in this API so it's not working and getting the below error. I'm using the Stripe plugin.
Future<void> startGooglePay() async {
final googlePaySupported = await Stripe.instance.isGooglePaySupported(IsGooglePaySupportedParams(testEnv:
false));
if (googlePaySupported) {
try {
// 1. fetch Intent Client Secret from backend
final response = await fetchPaymentIntentClientSecret();
print(response);
final clientSecret = response['client_secret'];
// 2.present google pay sheet
await Stripe.instance.initGooglePay(GooglePayInitParams( merchantName: "Test Name",
countryCode: 'US',testEnv: false));
await Stripe.instance.presentGooglePay(PresentGooglePayParams(clientSecret: clientSecret,currencyCode: "GBP"),);
await Stripe.instance.initPaymentSheet(paymentSheetParameters: SetupPaymentSheetParameters(
googlePay: PaymentSheetGooglePay(merchantCountryCode: 'US'),
paymentIntentClientSecret: response['client_secret'],
setupIntentClientSecret:response['client_secret'],
customerEphemeralKeySecret: response['client_secret'],
customerId:PLPrefrence.getPrefValue(key: PLPrefrence.user_code).toString()
));
await Stripe.instance.retrievePaymentIntent(response['client_secret']).then((value){
print(value.id);
print(value.status);
print(value.confirmationMethod.name);
});
} catch (e) {
print('ErrorStripePrint : ${e}');
errorDialog(errorMessage: "$e");
}
} else {
errorDialog(errorMessage: "Google pay is not supported on this device");
}
}
Now here is the API call for payment_intents API
Future<Map<String, dynamic>> fetchPaymentIntentClientSecret() async {
final url = Uri.parse('https://api.stripe.com/v1/payment_intents');
Map<String, dynamic> body = {
'amount':calculateAmount(amount: _plController.isUseWalletbalance.value ?
_plController.processedAmount.value : widget.grandTotal.toString()).toString(),
'currency': "GBP",
'payment_method_types[]': 'card',
};
final response = await http.post(
url,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ${PLKeys.stripeSK_Live}',
},
body: body,
);
return json.decode(response.body);
}
if I set the testEnv true and test secret key in so it's working but set testEnv false and set stripe live mode secret key so it's not working and getting issue as upload image above.
Error Image:-
I have API to send and verify otp code. I need to link that API to my flutter app using Firebase clod function. How is it possible Please help ?
Setup firebase cloud function here
Your js code be like
exports.sendOtp = functions.https.onCall((request, response) => {
const options = JSON.stringify({
mobile: request.text,
});
const headers = {
"Authorization": "Bearer xxxxxxxxxxxxxxxxxxxxxxx”,
};
const res = axios.post(“url”, options, {headers: headers}).then((response) => {
console.log(response);
return response;
});
return res.statusCode;
});
Your service code be like
Future<void> sendOtp(String mobile) async {
HttpsCallable callable =
FirebaseFunctions.instance.httpsCallable('sendOtp');
final resp = await callable.call(<String, dynamic>{
'text': mobile,
});
}
I am developing a flutter application which uses stripe for payments. I am using https://pub.dev/packages/flutter_stripe for this.
Everything is working fine but whenever I initiate payments I always get a webpage middleware (Screenshots attached). What am I doing wrong?
Here is my implementation in Flutter
Future<void> makePayment(String planName, String type) async {
Fluttertoast.showToast(msg: "initiating Payments, Please wait.");
ApiProvider provider = ApiProvider();
final tokenResponse = await provider
.getPaymentToken(PlanPayment(planName: planName, type: type));
if (tokenResponse != null) {`
var _service = locator<NavigationService>();
String secret = tokenResponse.clientSecret;
// make a get call from this url
Map<String, dynamic> paymentIntentData = Map();
await payment.Stripe.instance.initPaymentSheet(
paymentSheetParameters: payment.SetupPaymentSheetParameters(
merchantCountryCode: 'IN',
testEnv: true,
paymentIntentClientSecret: secret,
googlePay: true,
));
try {
// await Stripe.instance.handleCardAction(secret);
await payment.Stripe.instance.presentPaymentSheet().then((value) {});
await payment.Stripe.instance
.confirmPaymentSheetPayment()
.then((value) async {
// await _service.pushNamed(paymentStatus, args: {'isSuccess': true});
});
} catch (e) {
// await _service.pushNamed(paymentStatus, args: {'isSuccess': false});
print("Stripe error" + e.toString());
}
await provider
.confirmPayment(tokenResponse.transactionId)
.then((value) async {
await _service
.pushReplacementNamed(paymentStatus, args: {"isSuccess": value});
});
}
}
`
Maybe you have a webhook put in your account?
1)Provide valid Secret key
2)Provide valid Publisable key
3)Update flutterstripe pacakge
4)provide valid currency code to create stripe account country
ex :- stripe account create india to inr etc..
5)Right Way to implemet
- Main.dart to main method run app to implemet
ex :--
Stripe.publishableKey = "your publishable key ";
- create controller / method
code:-
Map<String, dynamic>? paymentIntentData;
Future<void> makePayment({amount}) async {
try {
paymentIntentData =
await createPaymentIntent(amount: amount, currency: 'INR');
if (paymentIntentData != null) {
await Stripe.instance.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
// applePay: true,
googlePay: const PaymentSheetGooglePay(merchantCountryCode: 'INR'),
merchantDisplayName: "PGA",
customerId: paymentIntentData!['customer'],
paymentIntentClientSecret: paymentIntentData!['client_secret'],
customerEphemeralKeySecret: paymentIntentData!['ephemeralkey'],
));
}
displayPaymentSheet();
} catch (err) {
logger.e(err);
}
}
void displayPaymentSheet() async {
try {
await Stripe.instance.presentPaymentSheet();
Get.snackbar("PaymentInfo", "Payment Successfully");
} on Exception catch (e) {
if (e is StripeException) {
logger.e(e, "Error From Stripe");
} else {
logger.e(e, "Unforeseen error");
}
} catch (e) {
logger.e("exeption === $e");
}
}
var id = "";
createPaymentIntent({amount, currency}) async {
try {
Map<String, dynamic> body = {
'amount': calculateAmount(amount: amount),
'currency': currency,
'payment_method_types[]': 'card'
};
var response = await http.post(
Uri.parse('https://api.stripe.com/v1/payment_intents'),
headers: {
'Authorization':
'Bearer YourSecretKey',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: body,
);
if (response.statusCode == 200) {
var decode = jsonDecode(response.body);
logger.e(decode);
id = decode['id'];
return decode;
}
} catch (e) {
logger.e(e, "error charging user");
}
}
calculateAmount({amount}) {
logger.e(amount.round());
final a = (int.parse(amount.toString())) * 100;
logger.e(a.toString());
update();
return a.toString();
}
6) How to Access Stripe payment :-
ex :- any button click
ontap : (){
makePayment(
amount: "200")
}
I am searching for a way to upload a chunked image in a background task in flutter for Android. I already took a look at flutter_uploader and chunked_uploader plugins but it don't seems they are compatible.
Here is the documentation for the API I am trying to upload to:
piwigo upload async endpoint
Right now I am using a non chunked request with Dio to another endpoint:
void uploadPhotos(List<Asset> photos) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
Map<String, String> queries = {"format":"json", "method": "pwg.images.upload"};
photos.forEach((element) async {
ByteData byteData = await element.getByteData();
List<int> imageData = byteData.buffer.asUint8List();
FormData formData = FormData.fromMap({
"category": widget.category,
"pwg_token": prefs.getString("pwg_token"),
"file": MultipartFile.fromBytes(
imageData,
filename: element.name,
),
"name": element.name,
});
Response response = await API.dio.post("ws.php",
data: formData,
queryParameters: queries,
);
if (response.statusCode == 200) {
print(response.data);
if(json.decode(response.data)["stat"] == "ok") {
SnackBar snackBar = SnackBar(
content: Text("Successfully uploaded ${element.name}"),
duration: Duration(seconds: 2),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
} else {
print("Request failed: ${response.statusCode}");
}
);
}
So, is there a way to use the two plugins for a single request and if not, what should I use to fulfill my needs ?