Home Blog Page 27

Stripe Payment Gateway In Flutter

0
FLUTTER STRIPE PAYMENT INTEGRATION

What is Stripe’s role in flutter?

For smooth payment experiences in native Android and iOS apps, leverage the Stripe Flutter SDK. Will provide UI screens and components that are reliable and flexible and can be used right out of the box to collect payment from your users/customers.

The Stripe payment gateway is used by millions of businesses, including Shopify and some of our largest online companies like Amazon, Google, and others. They provide a simple and efficient way for businesses and their clients all over the world to receive and transfer payments. They also offer further services like making invoices, managing recurring payments, delivering diagrammatic and intelligent financial reports, among others.

flutter stripe
flutter stripe

Flutter Stripe Example

Flutter Stripe Payment integration example
Flutter Stripe Payment integration example

Use of the Stripe ?

Anywhere in the globe, you may process payments and pay out merchants with the use of Stripe’s tools and programmable APIs. With Stripe, platforms and marketplaces can simply handle their payment processing while still adhering to compliance standards, from confirming the legitimacy of your merchants to managing payments.

Stripe SDK

Using the Stripe SDK, you can quickly build a payment flow in your Android app. They provide UI components that are reliable and flexible so you can start collecting payment information from your users straight away. We also make the low-level APIs that support those UIs public so you may design completely original experiences.

This API tour includes important information to help you comprehend the APIs, such as the fundamental concepts that underlie all of our APIs. the path that a successful payment takes.

Stripe payment Features 

Simplified Security: Stripe make it easy for you to acquire PCI compliant sensitive data, such credit card numbers, and do so while still being secure. This indicates that instead of going through your server, the sensitive data is transferred straight to Stripe.

Apple Pay: offer a seamless Apple Pay connection.

Payment options: Adding extra payment options will increase your company’s global reach and checkout conversion.

SCA-Ready: To meet with Strong Customer Authentication regulations in Europe, the SDK is SCA-Ready and automatically conducts native 3D Secure authentication when necessary.

Native User Interface: On Android and iOS, we offer native panels and components for securely collecting payment information.


Setup in Stripe console

To get started, we would need to generate an API key from Stripe. To do this, A Stripe account would need to be created.

Click below link to create Stripe account :

https://dashboard.stripe.com/dashboard

Once you’ve completed that, access your dashboard.

stripe dashboard

For testing and integration, switch on Test mode.

All you need to do to launch your app is follow Stripe’s instructions and switch from Test mode to Live mode on your dashboard.

then choose the API Keys option under Developers on the screen’s left side to view your Publishable and Secret API keys.

Finally You have Stripe account and API key.

Implementation Stripe in Flutter

Step 1: Your requirements should contain the following packages

flutter_stripe: ^any
http: ^any

Step 2: Import the Stripe Payment library

import 'package:stripe_payment/stripe_payment.dart';

Step 3: Run flutter packages from your app’s root directory.

Step 4:  Version Minimum Requirement

In android module of flutter project minimun sdk version should be 19 & above

//Your android/app/build.gradle file needs to be modified.

minSdkVersion 19
compileSdkVersion 28

Step 5: Activate MultiDex

//Set MultiDex in the app in the build.gradle file .

android {
    compileSdkVersion 28
    defaultConfig {
        /// TODO: Specify your own unique Application ID 
        applicationId "com.example.flutterAppName"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
        multiDexEnabled true  // add this line
    }
 }

Step 6: incorporate multidex dependencies

dependencies {
    Implementation
     "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.android.support:multidex:1.0.3'
}

Step 7: Activate Android X

Add this to your gradle.properties file :

org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true

Step 8: Change Theme.Appcompat in style.xml file (android/app/src/main/res/values/styles.xml).

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
   <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
       <!-- Show a splash screen on the activity. Automatically removed when
            the Flutter engine draws its first frame -->
       <item name="android:windowBackground">@drawable/launch_background</item>
   </style>
   <!-- Theme applied to the Android Window as soon as the process has started.
        This theme determines the color of the Android Window while your
        Flutter UI initializes, as well as behind your Flutter UI while its
        running.

        This Theme is only used starting with V2 of Flutter's Android embedding. -->
   <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
       <item name="android:windowBackground">?android:colorBackground</item>
   </style>
</resources>

Step 9: Using FlutterFragmentActivity instead of FlutterActivity in MainActivity.kt

(android/app/src/main/kotlin/com/example/appname/MainActivity.kt)

package com.flutter.stripe.example  //your package name

import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.android.FlutterFragmentActivity

class MainActivity: FlutterFragmentActivity() {}

flutter_stripe: We will utilize the flutter stripe package since it spares us the burden of having to design our own payment modal by providing us with a beautiful one that already has all the information and customization we need. Additionally, it includes dark mode and allows you to scan your card to access it’s information. Visit their instructions here to set up the additional configurations required for the flutter stripe package on Android.

http : We’ll be using http to connect with the Stripe API; if you’d like, you may use dio or any other package to handle your API requests instead.


Code implement

Create a dart filename HomePage.dart with a payment button that opens the payment modal.

This is how we handling  payments:

 Future<void> openPaymentSheetWidget() async {
   try {
     paymentIntentData = await callPaymentIntentApi('200', 'INR');
     await Stripe.instance
         .initPaymentSheet(
       paymentSheetParameters: SetupPaymentSheetParameters(
         appearance: PaymentSheetAppearance(
           primaryButton: const PaymentSheetPrimaryButtonAppearance(
             colors: PaymentSheetPrimaryButtonTheme(
               light: PaymentSheetPrimaryButtonThemeColors(
                 background: Colors.blue,
               ),
             ),
           ),
           colors: PaymentSheetAppearanceColors(background: blueShade50),
         ),
         paymentIntentClientSecret: paymentIntentData!['client_secret'],
         style: ThemeMode.system,
         merchantDisplayName: 'Merchant Display Name',
       ),
     )
         .then((value) {
       showPaymentSheetWidget();
     });
   } catch (exe, s) {
     debugPrint('Exception:$exe$s');
   }
 }

Now, let’s implement this step for stripe integration

Step 1) : Create Payment Intent 

 To begin, we define a function called makingPaymentIntentApi that accepts the currency and payment amount.

makingPaymentDataIntentApi(String amount, String currency) async {
   try {
     Map<String, dynamic> body = {
       'amount': calculateAmount(amount),
       'currency': currency,
       'payment_method_types[]': 'card'
     };
     debugPrint("Body : $body");
     var response = await http.post(
         Uri.parse('https://api.stripe.com/v1/payment_intents'),
         body: body,
         headers: {
           'Authorization': 'Add_Your_Authorization_Token_Here',
           'Content-Type': 'application/x-www-form-urlencoded'
         });
     return jsonDecode(response.body);
   } catch (err) {
     debugPrint('callPaymentIntentApi Exception: ${err.toString()}');
   }
 }

In order to keep the value of the amount when it is converted to a double by flutter stripe, we make a post request to Stripe with a body that contains the currency we are paying in and the amount multiplied by 100. Stripe replies by returning a payment intent. This will serve as the initialization for our payment sheet in STEP 2).

Step 2) : Initialise the payment sheet

A payment sheet is started. The payment sheet modal, where we will input our card information and make a payment, will be created using this.

The client secret we acquired from the payment intent in the previous step is what we enter here. Several settings are available to you, including style, which enables us to choose the dark theme.

Step 3) : show Payment Sheet

This step is show the payment sheet

showPaymentSheetWidget() async {
   try {
     await Stripe.instance.presentPaymentSheet(
         // ignore: deprecated_member_use
         parameters: PresentPaymentSheetParameters(
       clientSecret: paymentIntentData!['client_secret'],
       confirmPayment: true,
     ));
     setState(() {
       paymentIntentData = null;
     });
     ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
         backgroundColor: Colors.blue,
         content: Text(
           "Payment Successfully Completed ",
           style: TextStyle(color: Colors.white),
         )));
   } on StripeException catch (e) {
     debugPrint('StripeException:  $e');
     showDialog(
         context: context,
         builder: (_) => const AlertDialog(
               content: Text("Get Stripe Exception"),
             ));
   } catch (e) {
     debugPrint('$e');
   }
 }

The integration of Stripe into your Flutter application is complete.

Now, run your code and get output.

Github code link for reference:

https://github.com/kevalv001/deep_link_app.git

flutter_stripe package used in this application, for this contribution. In this article , I have explained the basic architecture of stripe payment integration in flutter. You can change this code according to your choice, and this was a small introduction of stripe payment.

Thank you !! Have a fun …..

Convert minutes to hours – For All Languages

0
convert minutes to hours

To get Total Hours & Minutes when you have total time duration in minutes, All you need to do is:

Divide(/) Time Minutes by 60 to get Hours & then Math.floor() to remove the decimal places & to get minutes perform modules (%) on Minutes as shown below:

JavaScript get hours from minutes

const min = 129;

const toHours = Math.floor(min/60);
const toMinutes = min%60; // Will give reminder that is equal to minutes

console.log(toHours," Hours ,",toMinutes," Minutes"); // 2  Hours , 9  Minutes

Convert minutes to hours in java

int minutes = 120;
int hours = minutes / 60;
int remainingMinutes = minutes % 60;
System.out.println(hours + " hours and " + remainingMinutes + " minutes");

Convert minutes to hours in python

minutes = 130
hours = minutes // 60
remaining_minutes = minutes % 60
print(f"{hours} hours and {remaining_minutes} minutes")

Convert minutes to hours in dart

In dart to perform division we use ~/.

int minutes = 140;
int hours = minutes ~/ 60;
int remainingMinutes = minutes % 60;
print("$hours hours and $remainingMinutes minutes");

Flutter Web – XMLHttpRequest error  DIO library Web Issue Fixed

0
flutter web XMLhttpRequest Error

Flutter Web – XMLHttpRequest error – Fix Solution

DioError [DioErrorType.response]: XMLHttpRequest error

I was making a API call using Flutter Dio package, Here my Flutter code was running completely fine in mobile devices, but when I run the same code on web browser (chrome) I was experiencing “DioError [DioErrorType.response]: XMLHttpRequest error”. Then with lot of research I found out the working solution for above Flutter Web – XMLHttpRequest error.

Solution Steps:

  1. Go to flutter SDK path-> flutter\bin\cache
  2. Find a file by name flutter_tools.stamp, take a backup of it in some other location so that you can retain a copy then simple delete the original one.
  3. Next Go to flutter\packages\flutter_tools\lib\src\web and open chrome.dart file in any editor.
  4. Search for line '--disable-extension' then Comment it out.
  5. and and just below it add --disable-web-security.
  6. Now simple clear your IDE cache if android Studio and restart the IDE.

Now, Install the flutter app on you device and now you can make Internet API call.

Don’t Worry if you see this warning ‘You are using an unsupported comment-line flag: –disable-web-security. Stability and security will suffer’. This will only be soon on your local system.

Refer The Screenshot below to fix Flutter Web XMLHttpRequest Error

Step 1 : Go to flutter SDK path-> flutter\bin\cache

Here Delete the File by name flutter_tools.stamp

delete flutter_tools stamp file
delete flutter_tools stamp file

Step 2: add --disable-web-security in chrome.dart file as show below

flutter\packages\flutter_tools\lib\src\web
flutter chrome dart file edit
flutter chrome.fart add --disable-web-security extensions line

Step 3: Restart your IDE

Flutter Gradient Animation to Widget – Source Code

0
Flutter Gradient Animation to Widget

Here is a Basic Animation in flutter example, I had a container to which I wanted to give a border animation by using decoration> gradient property of container widget and apply animation to it. Here is the result of below flutter animation source code.

Video Tutorial

Flutter Animation to container gradient
Flutter Animation to container gradient

Source Code

import 'package:flutter/material.dart';
void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  late AnimationController _animationController;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    _animationController = AnimationController(vsync: this,duration: Duration(seconds: 1));

    _animationController..addStatusListener((status) {
      if(status == AnimationStatus.completed) _animationController.forward(from: 0);
    });

    _animationController.addListener(() {
      setState(() {

      });
    });

    _animationController.forward();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          width: 180,
          height: 80,
          decoration: BoxDecoration(
              boxShadow: const[
                BoxShadow(offset: Offset(1,1),blurRadius: 20,color: Colors.black)
              ],
            gradient: SweepGradient(
              startAngle: 4,
              colors: [Colors.red,Colors.white],
              transform: GradientRotation(_animationController.value*6)
            )
          ),
          child: Padding(
            padding: EdgeInsets.all(8.0),
            child: Container(
              color: Colors.white,
              alignment: Alignment.center,
              child: Text("Proto Coders Point"),
            ),
          ),
        ),
      ),
    );
  }
}

Flutter Dio – Make HTTP POST/GET Request using Dio Package

0
flutter dio
what is dio in flutter

Most of us are unaware of what Dio in Flutter is. I’m going to talk something about dio in this flutter article, that may be useful to many of us in the future in flutter app development. A networking library called flutter dio was created by Flutter China. Things that dio package supports may be accomplished using the standard http library provided by the flutter sdk, but dio may be preferable because it is easier to learn quickly and make internet calls.

So let’s begin with Dio.

What is dio in flutter

library dio Null safety Dio is a strong HTTP client for Dart that supports Interceptors, global configuration, FormData, file downloading, etc. Dio is also highly user-friendly. a free software undertaking approved by https://flutterchina.club .

Why to use Dio for Internet calls?

Dio makes networking in Flutter feel simple and gently manages several edge circumstances. Dio makes it simpler to manage several network requests running simultaneously while providing the security of a sophisticated error handling method. Additionally, it enables you to avoid the boilerplate code required to track any file upload progress using the http package.

So let’s begin with Dio. In order to begin using Dio, you must first add dependency :

dependencies:
dio: ^any

Then import it after installing the package using the terminal’s command line:

To set up the package: flutter pub get or hit pub get button on android studio

Now , import "package:dio/dio.dart" into your dart code;

How to Make Internet Calls in flutter using Dio Package

Making a GET request using Dio package:

Here, create & use a Dio instance, then wait for the desired response from the api’s.

Response? response;
Dio dio = Dio();
response = await dio.get("base_url/test?id=1&name=jhon");
print(“Response :: ${response.data.toString()}”);

A POST request using Dio Package:

Await the request you wish to post after receiving the request using a Dio instance.

response = await dio.post("base_url/test", data: {"id": 1, "name": "jhon"});

Executing many queries at once / multiple api call in flutter:

It frequently happens that you must access multiple APIs at once, which can be perplexing at times. In these situations, dio can be used quickly to obtain results from multiple APIs.

response = await Future.wait([dio.post("/info"), dio.get("/token")]);


In order to download a file using dio in flutter:

Dio can be the greatest option for downloading any type of content. Although the standard http library included in the flutter SDK can be used, it is not particularly intuitive, thus dio can be used instead.

response = await dio.download("https://www.google.com/", "./xx.html","./xx.pdf""./xx.jpeg",);

Receive a stream response:

Receiving a series of events is possible with the help of a stream. Therefore, you must set responseType to “stream” in order to get response stream with the flow of your code.

Response<ResponseBody> response  = await Dio().get<ResponseBody>(url,
 options: Options(responseType: ResponseType.stream), 
/// here, change responseType to bytes
);
print(“Response ::${response.data.stream}”);

Obtain a response in bytes:

Set responseType to “bytes” in order to receive a response in bytes.

Response<List<int>> response  = await Dio().get<List<int>>(url,
 options: Options(responseType: ResponseType.bytes), 
/// here, change responseType to bytes
);
print(“Response :: ${response .data}”); 

Sending FormData in flutter:

Use the instance FormData from the map to transmit the form data, then choose the destination and watch for the post.

FormData formData = FormData.fromMap({
    "name": "jhon",
    "age": 20,
  });
response = await dio.post("/info", data: formData);
print(“Response FormData :: ${formData}”);

Using FormData to upload several files to a server:

Using MultiPartFile and then waiting for it step by step or vice versa might save you important time when you need to upload many files to the server using your formData.

FormData.fromMap({
    "name": "jhon",
    "age": 20,
    "file": await MultipartFile.fromFile("./text.txt",filename: "dataFile.txt"),
    "files": [
      await MultipartFile.fromFile("./text1.txt", filename: "myFile.txt"),
      await MultipartFile.fromFile("./text2.txt", filename: "myFiles.txt"),
    ]
});
response = await dio.post("/info", data: formData);

It is important to note that the HttpClient-initiated request is still used by dio internally. As a result, the callback function onHttpClientCreate can set the proxy, request authentication, certificate verification, and other parameters to those of the HttpClient.

(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
client.findProxy = (uri) {
      return "PROXY 192.168.1.2:8888";
    };
    ///Here , Verification certificate
httpClient.badCertificateCallback=(X509Certificate cert, String host, int port){
      if(cert.pem==PEM){
      return true; 
///here, If the certificate is consistent, data is allowed to be sent
     }
     return false;
    };   
  };

The current dio instance will call onHttpClientCreate when a new HttpClient needs to be created, so changing the configuration of the HttpClient through this callback will affect the entire dio instance. You can start a fresh dio instance if you want to ask for a different proxy or certificate verification policy for a certain application.

Following the uploading process with audio:

After using await in the post request, the function onSendProgress can be helpful for monitoring the uploading process of the response.

response = await dio.post(
  "http://www.dtworkroom.com/doris/1/2.0.0/test",
  data: {"aa": "bb" * 22},
  onSendProgress: (int sent, int total) {
    print("Value :: $sent");
    print("Value :: $total");
  },
);

Data post-binary by stream:

/// Binary data
List<int> postData = <int>[...];
await dio.post(
  url,
  data: Stream.fromIterable(postData.map((e) => [e])), 
///create a Stream<List<int>>
  options: Options(
    headers: {
      Headers.contentLengthHeader: postData.length, 
///Here, set content-length
    },
  ),
);

Many more functionalities offered by dio through the dio API include like :

baseUrl: It makes a basic url request, which typically includes the sub route. comparable to “https://www.google.com/api/.”

connectionTimeout: It contains the timeout for opening a url in milliseconds.

receiveTimeout: The Dio will issue a DioError with a DioErrorType if the duration between two events in the response stream exceeds the receiveTimeout. A RECEIVE TIMEOUT.

dio.options.baseUrl = "Your URL Here ";
dio.options.connectTimeout = 3000; 
dio.options.receiveTimeout = 2000;

BaseOptions options = BaseOptions(
    baseUrl: "Your URL Here ",
    connectTimeout: 3000,
    receiveTimeout: 2000,
);

Dio dio = Dio(options);

ResponseType: 

The following details are included in a response to a request.

{
  // Response body. may have been transformed, please refer to [ResponseType].
  T? data;
  // Response headers.
  Headers? headers;
  // The corresponding request info.
  Options? request;
  // Http status code.
  int?statusCode;
  // Whether redirect 
  bool? isRedirect;  
  // redirect info    
  List<RedirectInfo>? redirects ;
  // Returns the final real request uri (maybe redirect). 
  Uri ? realUri;    
  // Custom field that you can retrieve it later in `then`.
  Map<String, dynamic>? extra;
}

Verifying the Response :

We also have a unique key stored in our shared preferences that we must use to verify our responses, just as it was with the Request. The user is no longer active, according to the error message we issue if we cannot locate the requested header or if the key differs from the one that is saved.

We can develop a method for the response in the same way that we did for the request. The main distinction is that we are now working with a Response object, which includes both the statusCode and the original Request data in addition to some of the headers and data from the Request.

We also have a dynamic type for the function’s return, which can be any of the following:

If we wish to proceed with the request, we must pass the Response object.

If we throw an error after validating the return data, it will be a DioError.

This means that we may return the following DioError object if the boolean value for the header isUserActive is false:

dynamic responseInterceptor(Response options) async {
  if (options.headers.value("verify_Token") != null) {

    /// Compare it to the Shared Preferences key if the header is present.
    SharedPreferences prefs = await SharedPreferences.getInstance();
    var verifyToken = prefs.get("Verify_Token");
    
    // if the value is the same as the header, continue with the request
    if (options.headers.value("verify_Token") == verify_Token) {
      return options;
    }
  }

  return DioError(request: options.request, message: "Something Wrong");
}

Defining the PUT request :

Using a PUT request, you can change the data already on the API server.

Future<UserInfo?> updateUserValue({
  required UserInfoValue ? userInfoValue,
  required String ? idValue,
}) async {
  UserInfoValue ? updatedUserValue;

  try {
    Response response = await _dio.put(
      _baseUrl + '/users/$idValue',
      data: UserInfoValue.toJson(),
    );

     print(“updated Value : ${response.data}”);

    updatedUserValue = userInfoValue.fromJson(response.data);
  } catch (exe) {
    print(“Exception :: $exe”);
  }

  return updatedUserValue;
}

The DELETE request’s :

Using a DELETE request, you can remove some data from the server.

Future<void> deleteValue({required String ? idValue}) async {
  try {
    await _dio.delete(_baseUrl + '/users/$idValue');
    print(“User deleted successfully…..”);
  } catch (exe) {
    print(“Exception :: $exe”);
  }
}

Services communicate with the backend using an API class. A dedicated Dio client is used when the token has to be refreshed; otherwise, one of the two Dio clients in the class is used for all requests.

class Api {
  final dio = createDio();
  final tokenDio = Dio(BaseOptions(baseUrl: Globals().Url));

  Api._internal();

  static final _singleton = Api._internal();

  factory Api() => _singleton;

  static Dio createDio() {
    var dio = Dio(BaseOptions(
      baseUrl: Globals().Url,
      receiveTimeout: 5000, 
      connectTimeout: 5000,
      sendTimeout: 5000,
    ));

    dio.interceptors.addAll({
      AppInterceptors(dio),
    });
    return dio;
  }
}

class AppInterceptors extends Interceptor {
  final Dio ? dio;

  AppInterceptors(this.dio);

  @override
  void onRequest(
      RequestOptions options, RequestInterceptorHandler handler) async {
    var accessToken = await TokenRepository().getAccessToken();

    if (accessToken != null) {
      var expiration = await TokenRepository().getAccessTokenRemainingTime();

      if (expiration.inSeconds < 60) {
        dio.interceptors.requestLock.lock();

        // Call the refresh endpoint to get a new token
        await UserService()
            .refresh()
            .then((response) async {
          await TokenRepository().persistAccessToken(response.accessToken);
          accessToken = response.accessToken;
        }).catchError((error, stackTrace) {
          handler.reject(error, true);
        }).whenComplete(() => dio.interceptors.requestLock.unlock());
      }

      options.headers['Authorization'] = “Bearer $accessToken”;
    }

    return handler.next(options);
  }
}

Error handling :

It is simpler to detect and manage various mistakes when exceptions are used. Using the error interceptor shown below, we can handle various HTTP status codes and turn them into the relevant exceptions.

class ErrorHandlingClass extends Interceptor {
  final Dio ? dio;

  ErrorHandlingClass(this.dio);

  @override
  void onError(DioError ? err, ErrorInterceptorHandler ? handler) {
    switch (e.type) {
      case DioErrorType.connectTimeout:
      case DioErrorType.sendTimeout:
      case DioErrorType.receiveTimeout:
        throw DeadlineExceededException(e.requestOptions);
      case DioErrorType.response:
        switch (e.response?.statusCode) {
          case 400:
            throw BadRequestException(e.requestOptions);
          case 401:
            throw UnauthorizedException(e.requestOptions);
          case 404:
            throw NotFoundException(e.requestOptions);
          case 409:
            throw ConflictException(e.requestOptions);
          case 500:
            throw InternalServerErrorException(e.requestOptions);
        }
        break;
      case DioErrorType.cancel:
        break;
      case DioErrorType.other:
        throw NoInternetConnectionException(e.requestOptions);
    }

    return handler.next(e);
  }
}

class BadRequestException extends DioError {
  BadRequestException(RequestOptions  r) : super(requestOptions: r);

  @override
  String toString() {
    return 'Invalid request';
  }
}

class InternalServerErrorException extends DioError {
  InternalServerErrorException(RequestOptions r) : super(requestOptions: r);

  @override
  String toString() {
    return 'Unknown error, please try again …..';
  }
}

class ConflictException extends DioError {
  ConflictException(RequestOptions r) : super(requestOptions: r);

  @override
  String toString() {
    return 'Conflict occurred';
  }
}

class UnauthorizedException extends DioError {
  UnauthorizedException(RequestOptions r) : super(requestOptions: r);

  @override
  String toString() {
    return 'Access denied';
  }
}

class NotFoundException extends DioError {
  NotFoundException(RequestOptions r) : super(requestOptions: r);

  @override
  String toString() {
    return 'The requested info could not be found';
  }
}

class NoInternetConnectionException extends DioError {
  NoInternetConnectionException(RequestOptions r) : super(requestOptions: r);

  @override
  String toString() {
    return 'No internet connection found , please try again.';
  }
}

class DeadlineExceededException extends DioError {
  DeadlineExceededException(RequestOptions r) : super(requestOptions: r);

  @override
  String toString() {
    return 'The connection has timed out, please try again…..';
  }
}

We log the following while testing it with the JSON Placeholder website:

GET “https:// url”
Headers: headers
requiresToken: true
queryParameters:
—> END GET
<— 200 for success
Headers:
connection: [keep-alive]
set-cookie: [cookieKey=cookieValue; expires=Fri, 04-Sep-20 10:34:19 GMT; path=/; path ; HttpOnly]
cache-control: [public, max-age=1400]
transfer-encoding: [chunked]
date: [Thu, 09 Sep 2020 10:00:00 GMT]
content-encoding: [gzip]
vary: [Origin, Accept-Encoding]
age: [4045]
cf-cache-status: [HIT]
expect-ct: [max-age=604800, report-uri=“https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct”]
content-type: [application/json; charset=utf-8]
pragma: [no-cache]
server: [cloudflare]
x-powered-by: [Express]
access-control-allow-credentials: [true]
cf-ray: [51178cd29f9b724b-AMS]
etag: [W/“53-hfEnumeNh6YirfjyjaujcOPPT+s”]
via: [1.1 vegur]
x-content-type-options: [nosniff]
expires: [Thu, 05 Sep 2019 14:34:19 GMT]
Response: {userId: 2, id: 2, title: dioExample, completed: false}
<— END HTTP

To add to that, the logs are now displayed via the print method. Since these logs will also be shown in the production app, anyone who connects the phone while this app is open and launches flutter logs will be able to view the complete output, which may not be optimal. DebugPrint is a superior substitute that we may utilize.

Interceptors :

By using then or catchError, you can stop Dio requests, answers, and errors before they are processed. In a real-world setting, interceptors are helpful for JSON Web Tokens (JWT)-based authorisation, parsing JSON, managing problems, and quickly troubleshooting Dio network requests.

By replacing the callbacks at onRequest, onResponse, and onError, you may run the interceptor.

For the purposes of our example, we will construct a straightforward interceptor for logging various requests. a new class called Logging that extends Interceptor should be created:

void onResponse(Response ? response, ResponseInterceptorHandler ?handler) {
 
   print( “Response is :: [${response.statusCode}] => PATH:     ${response.requestOptions.path}”);
   
 return super.onResponse(response, handler);
  }

  void onError(DioError ? e, ErrorInterceptorHandler ? handler) {

    print(“Error is [${err.response?.statusCode}] => PATH: ${err.requestOptions.path}”);

    return super.onError(e, handler);
  }
}

Conclusion

Dio makes networking in Flutter feel simple and gently manages several edge circumstances. Dio makes it simpler to manage several network requests running simultaneously while providing the security of a sophisticated error handling method. Additionally, it enables you to avoid the boilerplate code required to track any file upload progress using the http package. Furthermore, the Dio package allows you to perform a number of further sophisticated changes that go beyond what we have discussed here.

ThankYou for reading the content!!!!!Have a Wonderful Day 🙂🙂……..

PayPal Payment Integration in Flutter

0

Flutter PayPal Payment Gateway Integration

At the time of writing, PayPal does not provide an SDK/package for their Paypal payment Integration in Flutter 

We will cover how to integrate PayPal into a flutter project using web-view. We must take a few actions to do this.

What is PayPal ?

PayPal is a payment platform featuring a website and a phone app that allows for online money transactions between parties.

PayPal is a highly secure banking service that employs some of the most cutting-edge end-to-end encryption technology available. You should also activate two-factor authentication and erase any inactive bank accounts or email addresses.

Because PayPal is not presently providing an SDK/package for their payment gateway integration in Flutter, I successfully implemented PayPal using WebView. I’ve used several of PayPal’s payment Web services that are available for integration in websites all around the world, so I decided to test it in a flutter to reach my aim.

First, we’ll make a PayPal application.

In this example, PayPal Sandbox is used to test the payment integration.

What is the PayPal sandbox?

The PayPal sandbox is a virtual testing environment that is segregated from the actual PayPal production environment. The PayPal sandbox mimics the functionality accessible on PayPal’s production servers. While several PayPal features, such as account termination, monthly statement creation, preserving shipping preferences, and PayPal Shops support, are not available in the paypal sandbox, it does have the same PayPal API feature set as the live environment. You may test your PayPal procedures in the sandbox. Processes behave precisely as they do on production servers in the sandbox.

PayPal developer dashboard

Now. Go to the PayPal developer account dashboard to start utilizing PayPal with Flutter.

Account for PayPal sandbox testing

You’ll see a sidebar in the SANDBOX option where you may pick the accounts and obtain this screen.

Make a PayPal application.

Select My Apps & Credentials from the left panel as shown in this image, then click the “Build App” button to create an app in PayPal.

Include appName, appType, and sandbox account information.

Include this page in your app’s Name, AppType, and Sandbox Business Accounts, as instructed by PayPal, and then click the “Create App” button to acquire the app credentials.

This screen contains the secret id and client id.

After successfully constructing the application, you must retrieve the secret id and client id by clicking on the application detail.

Implementation of Paypal In Flutter App:

Download/Install dependencies.

Add the required dependencies in pubspec.yaml file as shown below.

http_auth:^any
http:^any
webview_flutter:^any

Code implement

To begin, we’ll develop a PaypalHomeScreen and populate it with our simple sample UI and Navigation to pay the payment .

 final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     backgroundColor: Colors.white,
     key: _scaffoldKey,
     appBar: AppBar(
       centerTitle: true,
       backgroundColor: Colors.white,
       title: const Text(
         'Paypal Payment',
         style: TextStyle(
           fontSize: 18.0,
           color: Colors.black,
           fontWeight: FontWeight.bold,
         ),
       ),
     ),
     body: SizedBox(
         width: MediaQuery.of(context).size.width,
         child: Column(
           crossAxisAlignment: CrossAxisAlignment.center,
           mainAxisAlignment: MainAxisAlignment.center,
           children: <Widget>[
             Column(
               children: const [
                 Text(
                   "Items in your Cart",
                   style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
                 ),
                 ListTile(
                   title: Text(
                     "Product: One plus 10",
                     style:
                         TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
                   ),
                   subtitle: Text(
                     "Quantity: 1",
                     style:
                         TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
                   ),
                   trailing: Text(
                     "\$100",
                     style:
                         TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
                   ),
                 )
               ],
             ),
             RaisedButton(
               color: Colors.red,
               onPressed: () {
                 // make PayPal payment
                 Navigator.of(context).push(
                   MaterialPageRoute(
                     builder: (BuildContext context) => Payment(
                       onFinish: (number) async {
                         // payment done
                         final snackBar = SnackBar(
                           content: const Text("Payment done Successfully"),
                           duration: const Duration(seconds: 5),
                           action: SnackBarAction(
                             label: 'Close',
                             onPressed: () {
                               // Some code to undo the change.
                             },
                           ),
                         );
                         _scaffoldKey.currentState!.showSnackBar(snackBar);
                       },
                     ),
                   ),
                 );
               },
               child: const Text(
                 'Pay with Paypal',
                 textAlign: TextAlign.center,
                 style: TextStyle(color: Colors.white),
               ),
             ),
           ],
         )),
   );
 }

The next step is to establish the PayPalService page, from which we will use the PayPal APIs; for now, because we are testing, we will use the URL https://api.sandbox.paypal.com . If you wish to make an actual transaction, replace that with https://api.paypal.com .

Note : In testing mode, a sandbox account is used, but in live mode, a genuine PayPal account is used.

We will now create a PaypalService.dart screen.

PaypalService.dart

class PaypalServices {
 String domain = "https://api.sandbox.paypal.com";

 /// for testing mode
//String domain = "https://api.paypal.com"; /// for production mode

 /// Change the clientId and secret given by PayPal to your own.
 String clientId =
     'Here add clientId';
 String secret =
     'Here add secretId';

 /// for obtaining the access token from Paypal
 Future<String?> getAccessToken() async {
   try {
     var client = BasicAuthClient(clientId, secret);
     var response = await client.post(
         Uri.parse('$domain/v1/oauth2/token?grant_type=client_credentials'));
     if (response.statusCode == 200) {
       final body = convert.jsonDecode(response.body);
       return body["access_token"];
     }
     return null;
   } catch (e) {
     rethrow;
   }
 }

 // for generating the PayPal payment request
 Future<Map<String, String>?> createPaypalPayment(
     transactions, accessToken) async {
   try {
     var response = await http.post(Uri.parse("$domain/v1/payments/payment"),
         body: convert.jsonEncode(transactions),
         headers: {
           "content-type": "application/json",
           'Authorization': 'Bearer ' + accessToken
         });

     final body = convert.jsonDecode(response.body);
     if (response.statusCode == 201) {
       if (body["links"] != null && body["links"].length > 0) {
         List links = body["links"];

         String executeUrl = "";
         String approvalUrl = "";
         final item = links.firstWhere((o) => o["rel"] == "approval_url",
             orElse: () => null);
         if (item != null) {
           approvalUrl = item["href"];
         }
         final item1 = links.firstWhere((o) => o["rel"] == "execute",
             orElse: () => null);
         if (item1 != null) {
           executeUrl = item1["href"];
         }
         return {"executeUrl": executeUrl, "approvalUrl": approvalUrl};
       }
       return null;
     } else {
       throw Exception(body["message"]);
     }
   } catch (e) {
     rethrow;
   }
 }

 /// for carrying out the payment process
 Future<String?> executePayment(url, payerId, accessToken) async {
   try {
     var response = await http.post(url,
         body: convert.jsonEncode({"payer_id": payerId}),
         headers: {
           "content-type": "application/json",
           'Authorization': 'Bearer ' + accessToken
         });

     final body = convert.jsonDecode(response.body);
     if (response.statusCode == 200) {
       return body["id"];
     }
     return null;
   } catch (e) {
     rethrow;
   }
 }
}

To submit the order and payment details to PayPal, we will now create a payment screen.

payment screen

GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
 String? checkoutUrl;
 String? executeUrl;
 String? accessToken;
 PaypalServices services = PaypalServices();

 // You may alter the default value to whatever you like.
 Map<dynamic, dynamic> defaultCurrency = {
   "symbol": "USD ",
   "decimalDigits": 2,
   "symbolBeforeTheNumber": true,
   "currency": "USD"
 };

 bool isEnableShipping = false;
 bool isEnableAddress = false;

 String returnURL = 'return.example.com';
 String cancelURL = 'cancel.example.com';

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

   Future.delayed(Duration.zero, () async {
     try {
       accessToken = await services.getAccessToken();

       final transactions = getOrderParams();
       final res =
           await services.createPaypalPayment(transactions, accessToken);
       if (res != null) {
         setState(() {
           checkoutUrl = res["approvalUrl"];
           executeUrl = res["executeUrl"];
         });
       }
     } catch (ex) {
       final snackBar = SnackBar(
         content: Text(ex.toString()),
         duration: const Duration(seconds: 10),
         action: SnackBarAction(
           label: 'Close',
           onPressed: () {
             // Some code for undoing the alteration.
           },
         ),
       );
       _scaffoldKey.currentState!.showSnackBar(snackBar);
     }
   });
 }

 // item name, price and quantity here
 String itemName = 'One plus 10';
 String itemPrice = '100';
 int quantity = 1;

 Map<String, dynamic> getOrderParams() {
   List items = [
     {
       "name": itemName,
       "quantity": quantity,
       "price": itemPrice,
       "currency": defaultCurrency["currency"]
     }
   ];

   // Checkout Invoice Specifics
   String totalAmount = '100';
   String subTotalAmount = '100';
   String shippingCost = '0';
   int shippingDiscountCost = 0;
   String userFirstName = 'john';
   String userLastName = 'smith';
   String addressCity = 'USA';
   String addressStreet = "i-10";
   String addressZipCode = '44000';
   String addressCountry = 'Pakistan';
   String addressState = 'Islamabad';
   String addressPhoneNumber = '+1 223 6161 789';

   Map<String, dynamic> temp = {
     "intent": "sale",
     "payer": {"payment_method": "paypal"},
     "transactions": [
       {
         "amount": {
           "total": totalAmount,
           "currency": defaultCurrency["currency"],
           "details": {
             "subtotal": subTotalAmount,
             "shipping": shippingCost,
             "shipping_discount": ((-1.0) * shippingDiscountCost).toString()
           }
         },
         "description": "The payment transaction description.",
         "payment_options": {
           "allowed_payment_method": "INSTANT_FUNDING_SOURCE"
         },
         "item_list": {
           "items": items,
           if (isEnableShipping && isEnableAddress)
             "shipping_address": {
               "recipient_name": userFirstName + " " + userLastName,
               "line1": addressStreet,
               "line2": "",
               "city": addressCity,
               "country_code": addressCountry,
               "postal_code": addressZipCode,
               "phone": addressPhoneNumber,
               "state": addressState
             },
         }
       }
     ],
     "note_to_payer": "Contact us for any questions on your order.",
     "redirect_urls": {"return_url": returnURL, "cancel_url": cancelURL}
   };
   return temp;
 }

 @override
 Widget build(BuildContext context) {
   print(checkoutUrl);

   if (checkoutUrl != null) {
     return Scaffold(
       appBar: AppBar(
         backgroundColor: Theme.of(context).backgroundColor,
         leading: GestureDetector(
           child: const Icon(Icons.arrow_back_ios),
           onTap: () => Navigator.pop(context),
         ),
       ),
       body: WebView(
         initialUrl: checkoutUrl,
         javascriptMode: JavascriptMode.unrestricted,
         navigationDelegate: (NavigationRequest request) {
           if (request.url.contains(returnURL)) {
             final uri = Uri.parse(request.url);
             final payerID = uri.queryParameters['PayerID'];
             if (payerID != null) {
               services
                   .executePayment(executeUrl, payerID, accessToken)
                   .then((id) {
                 widget.onFinish!(id);
                 Navigator.of(context).pop();
               });
             } else {
               Navigator.of(context).pop();
             }
             Navigator.of(context).pop();
           }
           if (request.url.contains(cancelURL)) {
             Navigator.of(context).pop();
           }
           return NavigationDecision.navigate;
         },
       ),
     );
   } else {
     return Scaffold(
       key: _scaffoldKey,
       appBar: AppBar(
         leading: IconButton(
             icon: const Icon(Icons.arrow_back),
             onPressed: () {
               Navigator.of(context).pop();
             }),
         backgroundColor: Colors.black12,
         elevation: 0.0,
       ),
       body: const Center(child: CircularProgressIndicator()),
     );
   }
 }

Your desired output is 👍

flutter paypal github complete code

https://github.com/kevalv001/paypal_demo.git

Conclusion

This was a brief overview of the PayPal services; I covered the service in the post; you may modify the code to suit your needs. I believe you now have enough information from this blog to experiment with PayPal in your flutter projects.

Thank you very much!!Have a Good day…..