main.dart
WidgetsFlutterBinding.ensureInitialized();
Stripe.publishableKey = stripePublishableKey;
Stripe.merchantIdentifier = 'emailID';
await Stripe.instance.applySettings();
Initiate Payment
Future<void> initPaymentSheet({
required String? price,
}) async {
try {
await Stripe.instance.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
// Main params
paymentIntentClientSecret: clientSecret,
customerId: customerId,
customerEphemeralKeySecret: ephemeralKey,
// Merchant Name
merchantDisplayName: 'TEST',
// Extra params
applePay: const PaymentSheetApplePay(
merchantCountryCode: 'US',
),
googlePay: const PaymentSheetGooglePay(
merchantCountryCode: 'US',
testEnv: true,
),
style: ThemeMode.system,
),
);
} catch (e) {
rethrow;
}
}
/// Display the payment sheet.
Future<void> confirmPayment({required String price}) async {
try {
await initPaymentSheet(price: price);
// Present Payment sheet to user for payment
await Stripe.instance.presentPaymentSheet();
// Book the request.
bookingRequest();
} on Exception catch (e) {
if (e is StripeException) {
// displaySnackBar(
// 'Error!', 'Error from Stripe: ${e.error.localizedMessage}');
log('Error from Stripe: ${e.error.localizedMessage}');
print('Error from Stripe: ${e.error.localizedMessage}');
displaySnackBar('Error!', 'Something went wrong!, \nPlease try again.');
} else {
log('Error from Stripe: $e');
displaySnackBar('Error!', 'Something went wrong!, \nPlease try again.');
}
}
}
This confirmPayment() is called on Button click.
It will wait for Init the payment sheet.
After that presentPaymentSheet called to present the Payment sheet.
In Android, It's working fine.
In iOS, it's showing error like: No payment sheet has been initialized yet
Already Raised the Issue on the plugin Repo.
https://github.com/flutter-stripe/flutter_stripe/issues/850
✅ Solved:
Okay, the problem was in our backend response
when we were generating EphemeralKey, our backend dev was returning ephemeralKey["id"] instead of ephemeralKey["secret"] as a ephemeralKey.
I changed the EphemeralKey
from this
paymentIntentDTO.setEphemeralKey(ephemeralKey.getId());
to this
paymentIntentDTO.setClientSecret(paymentIntent.getClientSecret());
see ephemeralKey is different.
ephemeralKey["id"] is work fine in android app for stripe payment but on IOS ephemeralKey["id"] will not working
it was showing Error from Stripe: No payment sheet has been initialised yet
i changed ephemeralKey["id"] to ephemeralKey["secret"] as a ephemeralKey and now it working fine on both device.
Related
I'm working Flutter Stripe with this library : https://pub.dev/packages/flutter_stripe.
This is the function from my bloc to create successfully payment intent and display the payment sheet.
paymentSink.add(Status.Loading);
logData(key, 'Stripe Pay');
//create payment intent
var response = await paymentRepository.createPaymentIntent(200, 'USD');
paymentSink.add(Status.Successful);
var paymentIntentData = json.decode(response.body);
await stripe.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
applePay: true,
googlePay: true,
style: ThemeMode.dark,
testEnv: true,
merchantDisplayName: 'Flutter Stripe Store Demo',
customerId: paymentIntentData!['customer'],
paymentIntentClientSecret: paymentIntentData['client_secret'],
customerEphemeralKeySecret: paymentIntentData['ephemeralKey'],
));
//display payment sheet
await displayPaymentSheet();
if(isSuccessPaid){
logData(key, 'isSuccessPaid: $isSuccessPaid');
//handle result after payment successfully
}
and this is the display payment sheet function
try {
await stripe.presentPaymentSheet().then((value) {
isSuccessPaid = true;
}).onError((error, stackTrace){
GetIt.I<AppSnackBar>().show(error.toString());
logData(key,error.toString());
});
} catch (e) {
GetIt.I<AppSnackBar>().show(e.toString());
logData(key, '$e');
}
The problem is Android simulator works perfectly like this
but the IOS simulator can't display it and the terminal shows the errors like this
I don't know this is the platform error or just the simulator problem.Anyone got problem like this?Please help.Thanks!
Update Thursday 14 Apr 2022
For someone got same problem like me , i just want to share basic answer like this:
await stripe.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
applePay: true,
googlePay: true,
testEnv: true,
merchantCountryCode: 'US',
merchantDisplayName: 'Prospects',
customerId: paymentIntentData!['customer'] ?? 'Customer',
paymentIntentClientSecret: paymentIntentData!['client_secret'],
));
customerId and paymentIntentClientSecret mustn't be null so check null safety carefully and the response data must has the client_secret field
I am trying to set up with apple sign in with the following code. When tapping the button nothing happens.
I get the following error:
NoSuchMethodError (NoSuchMethodError: The method '[]' was called on null.
Receiver: null
Tried calling: []("User"))
The new user appears in firebase authentication but other than that the app does not work.
I have enabled sign in with apple in xcode and on developer.apple.com. I have also tried redownloading my provisioning profiles in xcode but no luck.
I have enabled apple sign in in firebase.
Does anyone know how to fix this?
mixin AuthenticationApple {
static String generateNonce([int length = 32]) {
const charset =
'0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._';
final random = Random.secure();
return List.generate(length, (_) => charset[random.nextInt(charset.length)])
.join();
}
/// Returns the sha256 hash of [input] in hex notation.
static String sha256ofString(String input) {
final bytes = utf8.encode(input);
final digest = sha256.convert(bytes);
return digest.toString();
}
static Future<User> signInWithApple({BuildContext context}) async {
User user;
// To prevent replay attacks with the credential returned from Apple, we
// include a nonce in the credential request. When signing in with
// Firebase, the nonce in the id token returned by Apple, is expected to
// match the sha256 hash of `rawNonce`.
final rawNonce = generateNonce();
final nonce = sha256ofString(rawNonce);
// Request credential for the currently signed in Apple account.
final appleCredential = await SignInWithApple.getAppleIDCredential(
scopes: [
AppleIDAuthorizationScopes.email,
AppleIDAuthorizationScopes.fullName,
],
nonce: nonce,
);
// Create an `OAuthCredential` from the credential returned by Apple.
final oauthCredential = OAuthProvider('apple.com').credential(
idToken: appleCredential.identityToken,
rawNonce: rawNonce,
);
try {
// Sign in the user with Firebase. If the nonce we generated earlier does
// not match the nonce in `appleCredential.identityToken`, sign in will fail.
final UserCredential userCredential =
await FirebaseAuth.instance.signInWithCredential(oauthCredential);
user = userCredential.user;
} on FirebaseAuthException catch (e) {
if (e.code == 'account-exists-with-different-credential') {
ScaffoldMessenger.of(context).showSnackBar(
AppWidget.customSnackBar(
content: 'The account already exists with a different credential.',
),
);
} else if (e.code == 'invalid-credential') {
ScaffoldMessenger.of(context).showSnackBar(
AppWidget.customSnackBar(
content: 'Error occurred while accessing credentials. Try again.',
),
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
AppWidget.customSnackBar(
content: 'Error occurred using Apple Sign-In. Try again.',
),
);
}
return user;
}
}
I just surpassed this issue by using iOS 13.0 for simulator. It seems there's an issue with higher versions when using the simulator but not with physical devices.
You can download and install simulators all the way back to iOS 13.0 (which is the minimum version for Apple Sign-In. In Xcode:
Xcode > Preferences.
Select the "Components" tab.
Mark the simulators you want.
Press "Check and Install Now".
I use '#react-native-firebase/messaging' module to send notifications. On Android everything works fine, Following is the error log I get when I try const fcmToken = await firebase.messaging().getToken(); on ios device.
NativeFirebaseError: [messaging/unknown] The operation couldn’t be completed. (com.firebase.iid error 1001.)
I have already implemented "react-native-permissions" to grant notification permission.
My AppDelegate.m contains :
if ([FIRApp defaultApp] == nil) {
[FIRApp configure];
}
Should I add anything else to it?
Any help or suggestion will be very helpful.
Thanks in advance
You need to check and ask messaging permission before get the fcm token in iOS
/**
* Check is notification showing permission enabled if not ask the permission.
*/
async checkFcmPermission() {
firebase
.messaging()
.hasPermission()
.then(enabled => {
if (enabled) {
// User has permissions
this.getFcmToken(); // const fcmToken = await firebase.messaging().getToken();
} else {
// User doesn't have permission
firebase
.messaging()
.requestPermission()
.then(() => {
// User has authorized
this.getFcmToken(); // const fcmToken = await firebase.messaging().getToken();
})
.catch(error => {
// User has rejected permissions
console.log(
'PERMISSION REQUEST :: notification permission rejected',
);
});
}
});
}
There are a lot of solutions to this issue. I post my answer since some StackOverflow users requested.
Here is my code
const sleep = ms => {
return new Promise(resolve => setTimeout(resolve, ms));
};
async function requestPermissionForNotification() {
try {
const permission = await requestNotifications(["alert", "badge", "sound"]);
if (permission.status === "granted") {
setupNotification();
}
} catch (e) {
console.log(e)
}
};
async function setupNotification() {
try {
await sleep(5000)
// TODO no need token for now, Will be used for future releases
const enabled = await firebase.messaging().hasPermission();
await requestNotifications(['alert', 'badge', 'sound']);
await firebase.messaging().registerForRemoteNotifications();
const token = await firebase.messaging().getToken();
firebase.messaging().onMessage(async (remoteMessage) => {
});
} catch (e) {
console.log(e)
}
}
And Also I have added following to Info.plist
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
To sum up, I use react permissions to grant permission and the sleep method is to wait for firebase to be ready. Finally uninstalled app couple time to get a result.
When It started working, I couldn't dare to touch the code since I spent days on this.
I'm working on in-app purchases (non-consumable subscriptions) using the official flutter in_app_purchase package. I start listening to purchaseUpdatedStream as soon as the user opens a page where he can buy a subscription. Once I validate a successful purchase I call completePurchase() method, but purchaseUpdatedStream keeps sending me events with old completed subscriptions even when the subscription is expired. What do I do wrong?
My success purchases processing code:
Future<void> _handleSuccess(PurchaseDetails lastPurchase) async {
var verificationData = lastPurchase.verificationData;
if (Platform.isIOS) {
verificationData = await InAppPurchaseConnection.instance.refreshPurchaseVerificationData();
}
try {
if (!await _apiService.verifySubscription(verificationData)) {
if (Platform.isIOS) {
await InAppPurchaseConnection.instance.completePurchase(lastPurchase);
}
purchasesLogInfo.add('Serverside receipt verification failed');
_purchasingStatusSink.add(PurchasingState.internalError);
throw Exception(
'Purchase with id ${lastPurchase.purchaseID} for product with id ${lastPurchase.productID} verification failed.');
}
} catch (e) {
_purchasingStatusSink.add(PurchasingState.verificationApiConnectionError);
rethrow;
}
purchasesLogInfo.add('Serverside receipt verification successful');
if (Platform.isIOS) {
await InAppPurchaseConnection.instance.completePurchase(lastPurchase);
}
await updateUserStatus();
_purchasingStatusSink.add(PurchasingState.successPayment);
}
I'm New to flutter and dart mobile app development. how to implement forgot password and send verification mail in flutter/dart development or is there any way to implement to send mail.
I don't think there is any way to send an email from your flutter application. This is something I would definitely implement on a backend server.
I would implement a 'forgot password' button in flutter, which triggers a http call to the backend which then triggers the password generation and email sending.
Yes there are some ways. The most common would be to use firebase as a backend server handling these requests.
You could do it like this
Add these packages to your flutter apps pubspec.yaml file
// latest version
firebase_core: ^1.17.0
firebase_auth: ^3.3.18
On forgot password button click
once you've completed the logic necessary before making the request, call this function
sendResetEmail(String email, BuildContext context) async {
final FirebaseAuth _auth = FirebaseAuth.instance;
try {
await _auth.sendPasswordResetEmail(email: email);
Timer(
const Duration(seconds: 3),
() => CustomWidgets().moveToPage(
page: const Home(), context: context, replacement: true),
);
} catch (e) {
// error handling here
}
}
This will send an email from firebase to the selected email to reset password.
On email verification request
Once the logic is over, call this function.
bool _isEmailVerified = false;
Timer? timer;
final FirebaseAuth _auth = FirebaseAuth.instance;
the initstate method
#override
void initState() {
_isEmailVerified = _auth.currentUser!.emailVerified;
if (!_isEmailVerified) {
sendEmailVerificationForUser();
timer = Timer.periodic(const Duration(seconds: 5), (timer) {
emailVerificationStatus(context);
});
}
email verification check function
emailVerificationStatus(BuildContext context) async {
try {
await _auth.currentUser!.reload();
setState(() {
_isEmailVerified = _auth.currentUser!.emailVerified;
});
} catch (e) {
// handle the error here
}
setState(() {
_isEmailVerified = _auth.currentUser!.emailVerified;
});
if (_isEmailVerified) {
timer?.cancel();
// and move to next page
}}
send email verification function
Future sendEmailVerificationForUser() async {
try {
await FirebaseAuth.instance.currentUser!.sendEmailVerification();
} catch (e) {
// error handling}