After exploring other options I learned that the only way to solve problem for my use case of accepting card payments in my Flutter web app is via Flutter_stripe and flutter_stripe_web packages.
I implemented both to find out that Stripe.PaymentSheet is NOT supported for web. :( From Stripe support, I learned that instead of PaymentSheet, I should use Stripe.PaymentElement while rest of the code remain same i.e. PaymentIntent and backend function etc
I have tried following document (though a bit unclear in context with Flutter/dart) but I am just unable to fill the missing blocks.
Could you guys take a look and let me know what am I doing wrong or what could be the right way of implementing PaymentElement instead of Sheets
Thanks in advance.
My PaymentFile
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:http/http.dart' as http;
class StripePayments {
Future<void> initPaymentSheet(context,
{required String email, required int amount}) async {
try {
// 1. create payment intent on the server
final response = await http.post(
Uri.parse(
'https://mycloudFunctionUrl/stripePaymentIntentRequest'),
body: {
'email': email,
'amount': amount.toString(),
});
if (response.statusCode != 200) {
throw Exception('Failed to create payment intent: ${response.body}');
}
final jsonResponse = jsonDecode(response.body);
log('Payment intent response: $jsonResponse');
// 2. Verify the keys and data
if (jsonResponse['paymentIntent'] == null ||
jsonResponse['customer'] == null ||
jsonResponse['ephemeralKey'] == null) {
throw Exception('Invalid response from server: $jsonResponse');
}
// 3. initialize the payment sheet
await Stripe.instance.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
paymentIntentClientSecret: jsonResponse['paymentIntent'],
merchantDisplayName: 'Cruise-App',
customerId: jsonResponse['customer'],
customerEphemeralKeySecret: jsonResponse['ephemeralKey'],
style: ThemeMode.light,
),
);
// 4. Present the payment sheet
await Stripe.instance.presentPaymentSheet();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Payment completed!')),
);
} catch (e) {
if (e is StripeException) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error from Stripe: ${e.error.localizedMessage}'),
),
);
} else if (e is StripeConfigException) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Stripe configuration error: ${e.message}'),
),
);
} else {
print(e.toString());
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: $e')),
);
}
}
}
}
This is how my backend / Cloud Function looks like
const functions = require("firebase-functions");
const cors = require("cors")({ origin: true });
const stripe = require("stripe")("sk_test_my_secret_key");
exports.stripePaymentIntentRequest = functions.region("europe-west3").https.onRequest((req, res) => {
cors(req, res, async () => {
try {
let customerId;
// Gets the customer whose email id matches the one sent by the client
const customerList = await stripe.customers.list({
email: req.body.email,
limit: 1
});
// Checks if the customer exists, if not creates a new customer
if (customerList.data.length !== 0) {
customerId = customerList.data[0].id;
} else {
const customer = await stripe.customers.create({
email: req.body.email
});
customerId = customer.id; // Changed customer.data.id to customer.id
}
// Creates a temporary secret key linked with the customer
const ephemeralKey = await stripe.ephemeralKeys.create(
{ customer: customerId },
{ apiVersion: '2020-08-27' }
);
// Creates a new payment intent with amount passed in from the client
const paymentIntent = await stripe.paymentIntents.create({
amount: parseInt(req.body.amount),
currency: 'nok',
customer: customerId,
});
res.status(200).send({
paymentIntent: paymentIntent.client_secret,
ephemeralKey: ephemeralKey.secret,
customer: customerId,
success: true,
});
} catch (error) {
res.status(404).send({ success: false, error: error.message });
}
});
});
This is the documentation I was referred to by Stripe Team but their support says on Discord, "We don't know dart or flutter. Unsure how people do that who have already done it"
Please suggest ! Thank you again
you can directly use PaymentElement widget. I don't have much knowledge on this but this may help you to get started.
var paymentIntentResult = await createPaymentIntent('100','JYP');
var paymentIntentSecret = paymentIntentResult['client_secret'];
PaymentElement(
autofocus: true,
enablePostalCode: true,
onCardChanged: (_) {},
clientSecret: paymentIntentSecret ?? '',
)
createPaymentIntent(String amount, String currency) async {
try {
//Request body
Map<String, dynamic> body = {
'amount': calculateAmount(amount),
'currency': currency,
};
//Make post request to Stripe
var response = await http.post(
Uri.parse('https://api.stripe.com/v1/payment_intents'),
headers: {
'Authorization':
'Bearer [your_key]',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: body,
);
return json.decode(response.body);
} catch (err) {
throw Exception(err.toString());
}
and use this to make the call
OutlinedButton(
onPressed: () async {
try {
await WebStripe.instance
.confirmPaymentElement(ConfirmPaymentElementOptions(
redirect: PaymentConfirmationRedirect.ifRequired,
confirmParams: ConfirmPaymentParams(return_url: ''),
));
} on Exception catch (e) {
print(e.toString());
}
},
child: Text('Go'));