Stripe Flutter can't display payment in IOS simulator - ios

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

Related

No payment sheet has been initialized yet

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.

React Native: RNIap.getPurchaseHistory().then runs infinately

The project is at this Github Repository. The file with the code is at components/Soundboard.js
This code was working previously, but now it looks like the promise is running forever. It looks like neither the resolve function, nor the reject function are executing because if I uncomment all the commented lines below and call the function askForPurchase() the only things printed to the console are
an object that looks like "_40": 0, "_55": {"_40": 0, "_55": null, "_65": 0, "_72": null}, "_65": 3, "_72": null} for the line console.log(RNIap.getPurchaseHistory())
and then the word end.
The buyProduct() function also is no longer initializing an IAP.
const buyProduct = function(){
RNIap.requestPurchase("1985162691", false).then(purchase => {
store.dispatch(setPurchases(purchase))
await RNIap.finishTransaction(purchase, false) //developerPayloadAndroid?: string Should I use this argument? I don't get how to use it
}).catch((error) => {
console.log(error.message);
})
}
const askForPurchase = function(){
if (!store.getState().purchase){
//console.log(RNIap.getPurchaseHistory())
RNIap.getPurchaseHistory().then(purchase => {
//console.log(`test1`)
store.dispatch(setPurchases(purchase))
if (purchase.length == 0){
//console.log(`test if`)
buyProduct()
}else{
//console.log(`test else`)
RNIap.getAvailablePurchases()
}
}, reason => {
console.log(reason)
})
//console.log(`end`)
}
}
EXTRA
This code was working a few months ago and I even pulled a commit(1b9cb81f229680e173ce910892dddedc632c1651, comment: "Made the seal pic more cartoony") from that time to test out. After pulling this commit, I deleted my node_modules and pods, and cleaned my build folder, but the askForPurchase() and buyProduct() functions no longer work in that commit either.
I am testing this on a real iPhone SE running ios 13.6.1
I created a sandbox tester if you need to test it out, but I don't think you'll need it
email: rniapsandbox#gmail.com
pw: Somepassword1
hello #Sam problem is async await problem they are not able to get value because they are not waiting to get data before getting data its firing without data and it was returning promise so you have to use async function
so your code be like
const buyProduct = async()=>{
await RNIap.requestPurchase("1985162691", false).then(purchase => {
store.dispatch(setPurchases(purchase))
await RNIap.finishTransaction(purchase, false) //developerPayloadAndroid?: string Should I use this argument? I don't get how to use it
}).catch((error) => {
console.log(error.message);
})}
const askForPurchase = async()=>{
if (!store.getState().purchase){
//console.log(await RNIap.getPurchaseHistory())
await RNIap.getPurchaseHistory().then(purchase => {
//console.log(`test1`)
store.dispatch(setPurchases(purchase))
if (purchase.length == 0){
//console.log(`test if`)
buyProduct()
}else{
//console.log(`test else`)
RNIap.getAvailablePurchases()
}
}, reason => {
console.log(reason)
})
//console.log(`end`)
}}
You will need to change from
console.log(RNIap.getPurchaseHistory())
to
console.log(await RNIap.getPurchaseHistory())

How to send a message directly from my Flutter app to WhatsApp using UrlLauncher?

I am building an app that has to be able to take an order and send it to a specific WhatsApp number. What exactly am I supposed to do? I can open WhatsApp but I can't figure out how to send a message when opening it.
title: new Text("WhatsApp"),
trailing: new Icon(Icons.message),
onTap: () async {
int phone = 962770593839;
var whatsappUrl = "whatsapp://send?phone=$phone";
await UrlLauncher.canLaunch(whatsappUrl) != null
? UrlLauncher.launch(whatsappUrl)
: print(
"open WhatsApp app link or do a snackbar with
notification that there is no WhatsApp installed");
},
I expect that when I input a TextField and press send that saved String will be able to be sent to the WhatsApp number after launching WhatsApp.
Use the plugin.
url_launcher
Using the following link : https://api.whatsapp.com/send?phone=XXXXXXXXXXX (In place of the Xs type the phone number of the person you want to contact, including the country code, but without the + sign.)
RaisedButton(
onPressed: () async => await launch(
"https://wa.me/${number}?text=Hello"),,
child: Text('Open Whatsapp'),
),
Alternatively
You can use this other plugin.
whatsapp_unilink
The whatsapp_unilink package helps you build HTTP links and provides you with an idiomatic Dart interface that:
import 'package:whatsapp_unilink/whatsapp_unilink.dart';
import 'package:url_launcher/url_launcher.dart';
launchWhatsApp() async {
final link = WhatsAppUnilink(
phoneNumber: '+001-(555)1234567',
text: "Hey! I'm inquiring about the apartment listing",
);
await launch('$link');
}
Try flutter_open_whatsapp plugin.You Directly Send Message to Number
FlutterOpenWhatsapp.sendSingleMessage("918179015345", "Hello");
Link
Open in WhatsApp
You can do it like this.
onPressed: () async {
for (var msg in msgList) {
if (msg["phone"] != null) {
var url = "${baseURL}91${msg['phone']}&text=${msg['messages']}";
print(url);
AndroidIntent intent = AndroidIntent(
action: 'action_view',
data: Uri.encodeFull(url),
package: "com.whatsapp.w4b");
intent.launch();
}
}
},
child: Icon(Icons.send),
),
Use whatsapp_share package
This launches whatsApp with respective number and prefills the text field.
for text and link
Future<void> share() async {
await WhatsappShare.share(
text: 'Whatsapp share text',
linkUrl: 'https://flutter.dev/',
phone: '911234567890',
);
}
Share images, files
_image.path is the file path which you wanna share to whatsapp
Future<void> shareFile() async {
await WhatsappShare.shareFile(
text: 'Whatsapp share text',
phone: '911234567890',
filePath: "${_image.path}",
);
}
Complete example here
I use url_launcher: ^6.1.7 like this.
void launchWhatsapp(
String phone,
String message,
) async {
final url = 'https://wa.me/967$phone?text=$message';
await launchUrlString(
url,
mode: LaunchMode.externalApplication,
);
}
what I want to refer here is the launch model default set to
LaunchMode.platformDefault
and that opens a web page and not WhatsApp when I try to launch WhatsApp
so set the launch model to
model: LaunchMode.externalApplication
has fixed the issue

Fetch not working on react native when run on Expo from iphone

So it's actually been on and off the past few days, the fetch function for POST method (for login purposes) on my app would suddenly stop working when I'm not even making any changes to the code.
I spent hrs tracking down the bug the first time this happened but realized that the code is still running fine on Android simulator that I run from Android studio.
This problem only happens on my iphone device when I run the code via expo.
is there any sort of convention on how to tackle this issue?
I've been googling for a few days now but no one forum actually agreed on a solution, some works but says it's unstable and some just doesn't work.
I'm pretty new to expo, react native, and everything in general, so please enlighten me using "english" lol
EDIT: since I was asked about the code, here it is, I hope it helps:
export function login(user, callback){
var endpoint = "oauth/token";
const { username, password } = user;
//from https://scotch.io/tutorials/how-to-encode-and-decode-strings-with-base64-in-javascript
var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t="";var n,r,i,s,o,u,a;var f=0;e=Base64._utf8_encode(e);while(f<e.length){n=e.charCodeAt(f++);r=e.charCodeAt(f++);i=e.charCodeAt(f++);s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},decode:function(e){var t="";var n,r,i;var s,o,u,a;var f=0;e=e.replace(/[^A-Za-z0-9+/=]/g,"");while(f<e.length){s=this._keyStr.indexOf(e.charAt(f++));o=this._keyStr.indexOf(e.charAt(f++));u=this._keyStr.indexOf(e.charAt(f++));a=this._keyStr.indexOf(e.charAt(f++));n=s<<2|o>>4;r=(o&15)<<4|u>>2;i=(u&3)<<6|a;t=t+String.fromCharCode(n);if(u!=64){t=t+String.fromCharCode(r)}if(a!=64){t=t+String.fromCharCode(i)}}t=Base64._utf8_decode(t);return t},_utf8_encode:function(e){e=e.replace(/\r\n/g,"\n");var t="";for(var n=0;n<e.length;n++){var r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r)}else if(r>127&&r<2048){t+=String.fromCharCode(r>>6|192);t+=String.fromCharCode(r&63|128)}else{t+=String.fromCharCode(r>>12|224);t+=String.fromCharCode(r>>6&63|128);t+=String.fromCharCode(r&63|128)}}return t},_utf8_decode:function(e){var t="";var n=0;var r=c1=c2=0;while(n<e.length){r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r);n++}else if(r>191&&r<224){c2=e.charCodeAt(n+1);t+=String.fromCharCode((r&31)<<6|c2&63);n+=2}else{c2=e.charCodeAt(n+1);c3=e.charCodeAt(n+2);t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);n+=3}}return t}};
let header = {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": ("Basic " + Base64.encode(username+":"+password))
};
let userData = {
"grant_type": "password",
"username": username,
"password": password
};
return fetchAPI(endpoint,'POST', header, userData)
.then((tokenJson) => {
alert("\nAccess Token:\n\n" + tokenJson.access_token + "");
callback(true, {exists : true, token : tokenJson.access_token}, null);
})
.catch((error) => {
alert(error);
callback(false, null, error);
});
}
And here's the fetchAPI function (a generic fetch function) called from that function:
export function fetchAPI(endpoint, method, header, data) {
let url = 'http://10.64.2.149:8081/' + endpoint;
let options = {
method: method,
headers: header,
body: stringify(data) //from qs-stringify package
};
return fetch(url, options)
.then(response => {
return response.json()
.then((json) => { ...//not relevant from this point on
The fetch 'POST' seems to always get stuck at response.json(), it just never resolves until it returns "network request failed". But as I said only happens sometimes when I run it on iphone via expo. It is fine on Android sim.
Thanks! :)

React-Native Firebase Phone Auth freezes iPhone

I want to sign the user in by phoneNumber in react-native. On Android this code snippet works neat. But on iOS always when I start this function the emulator freezes. I am trying to use this code Auth-Example I am using the react-native-firebase library.
openPhoneVerification() {
firebase
.auth()
.signInWithPhoneNumber(this.state.phoneNumber)
.then(confirmResult =>
this.props.navigation.navigate("PhoneVerification", {
confirmResult
})
)
// Wrong number - https://firebase.google.com/docs/reference/js/firebase.auth.Auth#signInWithPhoneNumber
.catch(error =>
Alert.alert(
"Number not valid",
"Please check your number and type in again",
[
{
text: "OK",
style: "cancel"
}
],
{ cancelable: true }
)
);
}
Solution for anyone encountering the same issue:
Make sure that the name given to your APN matches the app name

Resources