Callback with Stripe and Paymentsheet - flutter

I am implementing the Paymentsheet method of Stripe in my application, but i can't find a callback or something else to know if the payment has been confirmed or there have been problems.
Is this information available with Paymentsheet? If not, how can i pay using stripe sdk and receive a callback of the call

You need to use Stripe webhooks to be notified when the payment succeeds. More specifically you should listen to the payment_intent.succeeded event. You can learn more about this in this doc.

I tried with try and catch only because using webhook it tooks some time for our mobile UI. Though, Webhook is the best option. Whatever I did here is some time of patch.
Future<void> _presentPaymentSheet(
BuildContext context, String? clientSecret) async {
try {
await Stripe.instance.presentPaymentSheet();
// ==> Here, I assume payment success
state = NetworkState.success;
} on Exception catch (stripeException) {
if (stripeException is StripeException) {
state = NetworkState.error;
if (stripeException.error.code == FailureCode.Canceled) {
context.showSnackBar(AppStrings.errorFromStripe +
(stripeException.error.localizedMessage ?? ''));
context.pop();
} else {
debugLog('Error7: $stripeException');
context.showSnackBar(AppStrings.errorFromStripe +
(stripeException.error.localizedMessage ?? ''));
}
}
}
}

Related

can't load iap products in local, it waits forever

I'm using the official in_app_purchase plugin, version ^1.0.4, and I'm following the official guide from Google insert my first iap (https://codelabs.developers.google.com/codelabs/flutter-in-app-purchases#0).
My consumable iap product is active on Play console with name "pacchetto_25", I've already submitted to the alpha channel my app and is accepted, the tester email is correctly configured in the Tester Group and in Licence Testing.
Now I'm trying to load the iap products in my app, the code is the same of the guide:
Future<void> loadPurchases() async {
final available = await _iap.isAvailable();
if (!available) {
print("STORE NOT AVAILABLE");
return;
} else {
print("STORE AVAILABLE");
const ids = <String>{
"pacchetto_25",
};
final response = await _iap.queryProductDetails(ids);
response.notFoundIDs.forEach((element) {
print('Purchase $element not found');
});
response.productDetails.forEach((element) {
print("Purchase $element found");
});
// products =
// response.productDetails.map((e) => PurchasableProduct(e)).toList();
}
}
In my console I have the "STORE AVAILABLE" message, but then nothing else. If I put same debug point it does not stops on them, this problem appear after this line:
final response = await _iap.queryProductDetails(ids);
Do someone know what's happening? I've no errors in my console and the code after loadPurchases() is not executed, it's like is waiting forever... Any ideas?
Solved! If you have the same issue DON'T put
implementation("com.android.billingclient:billing:4.0.0")
in your build.gradle

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));
}
}
}
}
}
},
);
}

Open an app in flutter based on the url provided in the email sent for verification

So I have been working on a flutter app and once the user registers through my app they are sent an email for the verification of their account. Once the url in the link is tapped they are verified. Now after their verification,the users must be redirected to the app. I looked into firebase dynamic links but in all of the articles,they were trying to share their app by generating a link. Is there a way I can implement this? Thanks in advance!
Use this package
https://pub.dev/packages/uni_links
For getting the link when the app is started.
This is the case in which your app was in the closed state.
Future<Null> initUniLinks() async {
// Platform messages may fail, so we use a try/catch PlatformException.
try {
String initialLink = await getInitialLink();
// Parse the link and warn the user, if it is not correct,
// but keep in mind it could be `null`.
} on PlatformException {
// Handle exception by warning the user their action did not succeed
// return?
}
}
For listening to link clicks. This is for the case when your app is already open and you click the link.
_sub = getLinksStream().listen((String link) {
// Parse the link and warn the user, if it is not correct
}, onError: (err) {
// Handle exception by warning the user their action did not succeed
});
// NOTE: Don't forget to call _sub.cancel() in dispose()
}
Usually, you need to implement both of them together since your app may be in the closed state or open state while you click the link.
Future<Null> initUniLinks() async {
try {
String initialLink = await getInitialLink();
} on PlatformException {
}
_sub = getLinksStream().listen((String link) {}, onError: (err) {});
}}

Flutter getting an exception when calling queryPastPurchases (in_app_purchase plugin): Cannot find receipt for the current main bundle

I'm trying to utilise the in_app_purchase plugin (https://pub.dev/packages/in_app_purchase) for my Flutter App.
Calling a queryProductDetails(ids) works just fine,
but when calling the queryPastPurchases() I get the following exception:
PlatformException (PlatformException(storekit_no_receipt, Cannot find receipt for the current main bundle., null))
Seems like this is the source for the exception:
https://github.com/flutter/plugins/blob/master/packages/in_app_purchase/ios/Classes/FIAPReceiptManager.m
Worth mentioning:
I've setup an IAP in App-Store-Connect.
This IAP hasn't been sent for Apple approval yet.
App is in development, tested using TestFlight with beta testers.
Another issue is that this exception doesn't get cought in the try-catch statement (see code below), but only by the environment when checking the "All Exceptions" option in VSCode...
Tested on Emulator and real device.
[UPDATE]:
Added a breakpoint to the objective-c code of the plugin, and saw that the receiptURL refers to "sandboxReceipt". So maybe it's because I don't use sandbox users?
Future<void> _getPastPurchases() async {
try {
final QueryPurchaseDetailsResponse response = await InAppPurchaseConnection.instance.queryPastPurchases();
if (response.error != null) {
// Handle the error
print(response.error.toString());
}
print('response = ' + response.toString());
for (PurchaseDetails purchase in response.pastPurchases) {
if (Platform.isIOS) {
//InAppPurchaseConnection.instance.completePurchase(purchase);
}
}
setState(() {
_purchases = response.pastPurchases;
});
} on PlatformException catch(err) {
print(err.toString());
}
catch (err) {
print(err.toString());
}
}

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".