paytabscom / flutter-sdk-bridge

MIT License
12 stars 11 forks source link

Paytab is not allowing me to perform other functions on second payment #16

Closed alihassan143 closed 2 years ago

alihassan143 commented 2 years ago

Hello when the app is restart the payment and other function after payment working correctly but when i do the second payment after one payment successful and after successful payment the function after one payment work perfectly but when i want to initiate the second payment the second payment is done but the event call back function is not performing the flutter give dispose the timer issue [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: setState() called after dispose(): _ServiceDetailsScreenState#74407(lifecycle state: defunct, not mounted) E/flutter (23070): This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback. E/flutter (23070): The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree. E/flutter (23070): This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose(). image image

shadysamir commented 2 years ago

Same issue for me. First time the payment is done. Second time the screen with the code is disposed and I cant use setState or use context.

MuhamedAdly commented 2 years ago

@shadysamir, @alihassan143 We work on reproducing it on our side, if you can download our sample and modify it to produce the above issue it will be helpful for us to fix it.

shadysamir commented 2 years ago

Running the example as is did not produce the issue. I was able to replicate the issue by creating a similar scenario to our app. In our app the user navigates to a payment screen with payment options. Tapping the card payment calls a Future with startCardPayment, after the callback setState there is a button that calls Navigator.pop to take the user back to the screen previous to payment screen with the payment result.

Starting with your example app I built a similar case with the code below. To reproduce the issue:

main.dart:


import 'dart:async';
import 'dart:io' show Platform;

import 'package:flutter/material.dart';
import 'package:flutter_paytabs_bridge/BaseBillingShippingInfo.dart';
import 'package:flutter_paytabs_bridge/IOSThemeConfiguration.dart';
import 'package:flutter_paytabs_bridge/PaymentSdkApms.dart';
import 'package:flutter_paytabs_bridge/PaymentSdkConfigurationDetails.dart';
import 'package:flutter_paytabs_bridge/flutter_paytabs_bridge.dart';

void main() {
  runApp(App());
}

class App extends StatelessWidget {
  const App({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Home(),
    );
  }
}

class Home extends StatelessWidget {
  const Home({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          child: Text("Payment"),
          onPressed: () {
            Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => MyApp(),
                ));
          },
        ),
      ),
    );
  }
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String _instructions = 'Tap on "Pay" Button to try PayTabs plugin';

  @override
  void initState() {
    super.initState();
  }

  Future<PaymentSdkConfigurationDetails> generateConfig() async {
    var billingDetails = BillingDetails("John Smith", "email@domain.com",
        "+97311111111", "st. 12", "ae", "dubai", "dubai", "12345");
    var shippingDetails = ShippingDetails("John Smith", "email@domain.com",
        "+97311111111", "st. 12", "ae", "dubai", "dubai", "12345");
    List<PaymentSdkAPms> apms = [];
    apms.add(PaymentSdkAPms.STC_PAY);
    var configuration = PaymentSdkConfigurationDetails(
        profileId: "*profile id*",
        serverKey: "*server key*",
        clientKey: "*client key*",
        cartId: "12433",
        cartDescription: "Flowers",
        merchantName: "Flowers Store",
        screentTitle: "Pay with Card",
        amount: 20.0,
        showBillingInfo: true,
        forceShippingInfo: false,
        currencyCode: "EGP",
        merchantCountryCode: "EG",
        billingDetails: billingDetails,
        shippingDetails: shippingDetails,
        alternativePaymentMethods: apms);

    var theme = IOSThemeConfigurations();

    theme.logoImage = "assets/logo.png";

    configuration.iOSThemeConfigurations = theme;

    return configuration;
  }

  Future<void> payPressed() async {
    FlutterPaytabsBridge.startCardPayment(await generateConfig(), (event) {
      debugPrint("Mounted: $mounted");
      setState(() {
        if (event["status"] == "success") {
          // Handle transaction details here.
          var transactionDetails = event["data"];
          print(transactionDetails);
        } else if (event["status"] == "error") {
          // Handle error here.
        } else if (event["status"] == "event") {
          // Handle events here.
        }
      });
    });
  }

  Future<void> apmsPayPressed() async {
    FlutterPaytabsBridge.startAlternativePaymentMethod(await generateConfig(),
        (event) {
      setState(() {
        if (event["status"] == "success") {
          // Handle transaction details here.
          var transactionDetails = event["data"];
          print(transactionDetails);
        } else if (event["status"] == "error") {
          // Handle error here.
        } else if (event["status"] == "event") {
          // Handle events here.
        }
      });
    });
  }

  Future<void> applePayPressed() async {
    var configuration = PaymentSdkConfigurationDetails(
        profileId: "*Profile id*",
        serverKey: "*server key*",
        clientKey: "*client key*",
        cartId: "12433",
        cartDescription: "Flowers",
        merchantName: "Flowers Store",
        amount: 20.0,
        currencyCode: "AED",
        merchantCountryCode: "ae",
        merchantApplePayIndentifier: "merchant.com.bunldeId",
        simplifyApplePayValidation: true);
    FlutterPaytabsBridge.startApplePayPayment(configuration, (event) {
      setState(() {
        if (event["status"] == "success") {
          // Handle transaction details here.
          var transactionDetails = event["data"];
          print(transactionDetails);
        } else if (event["status"] == "error") {
          // Handle error here.
        } else if (event["status"] == "event") {
          // Handle events here.
        }
      });
    });
  }

  Widget applePayButton() {
    if (Platform.isIOS) {
      return TextButton(
        onPressed: () {
          applePayPressed();
        },
        child: Text('Pay with Apple Pay'),
      );
    }
    return SizedBox(height: 0);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('PayTabs Plugin Example App'),
      ),
      body: Center(
          child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
            Text('$_instructions'),
            SizedBox(height: 16),
            TextButton(
              onPressed: () {
                payPressed();
              },
              child: Text('Pay with Card'),
            ),
            SizedBox(height: 16),
            TextButton(
              onPressed: () {
                apmsPayPressed();
              },
              child: Text('Pay with Alternative payment methods'),
            ),
            SizedBox(height: 16),
            applePayButton()
          ])),
    );
  }
}
shadysamir commented 2 years ago

The issue persists even in release mode and also on iOS

MuhamedAdly commented 2 years ago

@shadysamir we are currently working on fixing it

alihassan143 commented 2 years ago

The issue is very critical if you change the state it works. Only once in whole app lifecycle if you try another payment it keeps loading and shows error if you destroy the app and open app then only one time you can be able to use paytab successfully

MuhamedAdly commented 2 years ago

Hi @alihassan143, @shadysamir: The issue fixed, please upgrade to 2.1.7

shadysamir commented 2 years ago

@MuhamedAdly fixed in master. Tested against example from master and works perfectly. Still no 2.1.7 release on pub.dev, latest is 2.1.6. Using flutter_paytabs_bridge: 2.1.7 in pubspec fails. For now I used this in pubspec to use master in our project:

flutter_paytabs_bridge:
    git:
      url: https://github.com/paytabscom/flutter-sdk-bridge
      ref: master
MuhamedAdly commented 2 years ago

Sorry @shadysamir, you can use 2.1.7 now

shadysamir commented 2 years ago

The issue persists for startAlternativePaymentMethod()

shadysamir commented 2 years ago

Confirming that the issue is resolved with 2.1.7 for all payment methods