🎉 Lailaolab Payment Support is service now. Get Start

Flutter

Flutter is a UI Framework developed and supported by Google to create user interface (UI) applications for multiple platforms with a single code base. Currently, Flutter supports the development of applications on multiple platforms such as IOS, Android, Web, MacOS, Windows and Linux.

Flutter uses the Dart programming language to write. Dart is an Open Source language developed by Google, Dart is a programming language designed and optimized for UI creation and many of its strengths are used in Flutter. For example: Null Safety, helps to find and fix bugs more easily. This capability allows developers to reduce time and focus the rest of their time on the most important parts of their application development.

Connect to Payment Support Using Flutter

You can use Flutter to connect to Lailao Payment Support. First of all, we need to create a Flutter project. This project will create an e-Commerce system, especially the product details page that will display images, details, prices, categories, etc. There is also a provision to send an order to create a QR Payment at a commercial bank and can use the commercial banking app (BCEL One) to scan for payment. After that, the system will receive a callback from the bank detailing the payment and we will display a message confirming the payment. All Source Code is available at this link Github (opens in a new tab)

If you want to read more details about Lailao Payment Support, you can see here Introduction

Start Flutter Project

Create a Flutter App to create an environment to learn how to use Lailao Payment Support with Flutter. More details on creating a Flutter project can be found at Flutter User Guide (opens in a new tab) and is the best way to start creating a new single-page application in Flutter.

  flutter create lailaopayment_app
  cd lailaopayment_app
  flutter run

Line 1 This command will create a Flutter project named lailaopayment_app

Line 2 is moved to the lailaopayment_app folder

Line 3 commands to run the Debug system first

Next we will install Libray which will be used to create App more easily.

  flutter pub add http
  flutter pub add socket_io_client
  flutter pub add url_launcher
  flutter pub add company_info

The command above is to add various libraries to the project, each of which has the following meaning:

LibraryDetails
httphttp is our flutter link to the server to send requests to the server and receive response data back to the frontend and an important part of communicating with RESTful APIs.
socket_io_clientsocket.io is used for connecting to WebSocket and Socket.IO server. It is a client used for creating real-time applications by which data can be sent between the client and the server without reloading the page or sending new requests.
url_launcherurl_launcher is a library used in Flutter to open URLs from within our app with external apps
company_infocompany_info is a library that includes logos of apps or companies in Laos

Next start to write the code by starting from:

main.dart

main.dart
  import 'package:carshop/screens/home_screen.dart';
import 'package:flutter/material.dart';
 
void main() {
  runApp(const MyApp());
}
 
class MyApp extends StatelessWidget {
  const MyApp({super.key});
 
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomeScreen(),
    );
  }
}

home_screen.dart

home_screen.dart
import 'package:carshop/screens/payment_screens.dart';
import 'package:flutter/material.dart';
 
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
 
@override
State<HomeScreen> createState() => _HomeScreenState();
}
 
class _HomeScreenState extends State<HomeScreen> {
List product = [
  {
    "brand": "APPLE",
    "name": "iPhone-15 pro max ",
    "price": 100,
    "image":
        "https://www.pngmart.com/files/15/Apple-iPhone-12-Transparent-Images-PNG.png"
  },
  {
    "brand": "APPLE",
    "name": "iPhone-14 pro max",
    "price": 200,
    "image":
        "https://www.pngmart.com/files/15/Apple-iPhone-12-Transparent-Images-PNG.png"
  },
  {
    "brand": "APPLE",
    "name": " iPhone-11 pro max ",
    "price": 300,
    "image":
        "https://www.pngmart.com/files/15/Apple-iPhone-12-Transparent-Images-PNG.png"
  },
  {
    "brand": "APPLE",
    "name": "iPhone-13 pro max",
    "price": 400,
    "image":
        "https://www.pngmart.com/files/15/Apple-iPhone-12-Transparent-Images-PNG.png"
  },
  {
    "brand": "APPLE",
    "name": "iPhone-12 pro max",
    "price": 500,
    "image":
        "https://www.pngmart.com/files/15/Apple-iPhone-12-Transparent-Images-PNG.png"
  },
  {
    "brand": "APPLE",
    "name": "iPhone-12",
    "price": 600,
    "image":
        "https://www.pngmart.com/files/15/Apple-iPhone-12-Transparent-Images-PNG.png"
  }
];
 
@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      centerTitle: true,
      title: const Text("ລາຍການລົດສີ້ນຄ້າ"),
    ),
    body: Padding(
      padding: const EdgeInsets.all(10.0),
      child: ListView.builder(
        itemCount: product.length,
        shrinkWrap: true,
        itemBuilder: ((context, index) {
          return InkWell(
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: ((context) => PaymentScreen(
                        dataProduct: product[index],
                      )),
                ),
              );
            },
            child: Padding(
              padding: const EdgeInsets.only(top: 7),
              child: Container(
                  width: MediaQuery.of(context).size.width,
                  height: 70,
                  decoration: BoxDecoration(
                      color: Colors.white,
                      borderRadius: BorderRadiusDirectional.circular(10),
                      boxShadow: [
                        BoxShadow(
                          color: Colors.grey.shade100,
                          spreadRadius: 0.1,
                          offset: const Offset(
                            0,
                            0.5,
                          ),
                        ),
                      ]),
                  child: ListTile(
                    dense: true,
                    leading: Image(
                      image: NetworkImage("${product[index]['image']}"),
                    ),
                    title: Row(
                      children: [
                        const Icon(Icons.smartphone),
                        const SizedBox(width: 10),
                        Text(product[index]['brand'] ?? ""),
                      ],
                    ),
                    subtitle: Row(
                      children: [
                        const Icon(Icons.vibration),
                        const SizedBox(width: 10),
                        Text(product[index]['name'] ?? ""),
                      ],
                    ),
                    trailing: Text("${product[index]['price']} KIP"),
                  )),
            ),
          );
        }),
      ),
    ),
  );
}
}

payment_screens.dart

payment_screens.dart
// ignore_for_file: must_be_immutable, prefer_typing_uninitialized_variables
 
import 'dart:convert';
import 'package:carshop/screens/payment_success.dart';
import 'package:company_info/company_info.dart';
import 'package:company_info/logo_enum.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:qr_flutter/qr_flutter.dart';
import 'package:socket_io_client/socket_io_client.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:socket_io_client/socket_io_client.dart' as IO;
 
class PaymentScreen extends StatefulWidget {
  var dataProduct;
  PaymentScreen({super.key, this.dataProduct});
 
  @override
  State<PaymentScreen> createState() => _PaymentScreenState();
}
 
class _PaymentScreenState extends State<PaymentScreen> {
  IO.Socket? socket;
  var qrIB;
  static const String keyDev =
      "\$2b\$10\$fwHDiyPBpPf9YLwcLWfNV.O6427H0C8zEzV0Opo6p.uqtwAUYX6GG";
  // "\$2b\$10\$q8fTfBbezQ3t6Mq1fjcZ1u4oWcMprn0.ENGzZUTyOKhp75tfVGog2";
  static const String urlSocket =
      "https://payment-Support.lailaolab.com/?key=$keyDev";
  static const String endPoint =
      "https://payment-Support.lailaolab.com/v1/api/payment";
 
  Future<void> generateIBQr() async {
    var bodyData = {
      "amount": widget.dataProduct['price'],
      "description": "Take test from Lailaolab",
    };
    var response = await http.post(Uri.parse("$endPoint/generate-ib-qr"),
        headers: <String, String>{
          'Content-Type': 'application/json',
          "secretKey": keyDev
        },
        body: jsonEncode(bodyData));
    if (response.statusCode == 200) {
      var responsePayment = jsonDecode(response.body);
      setState(() {
        qrIB = responsePayment['qrCode'];
      });
    }
  }
 
  Future<void> generateBcelQr() async {
    var bodyData = {
      "amount": widget.dataProduct['price'],
      "description": "Take test from Lailaolab",
    };
    var response = await http.post(Uri.parse("$endPoint/generate-bcel-qr"),
        headers: <String, String>{
          'Content-Type': 'application/json',
          "secretKey": keyDev
        },
        body: jsonEncode(bodyData));
    print(response.body);
    if (response.statusCode == 200) {
      var responsePayment = jsonDecode(response.body);
      await launchUrl(
        Uri.parse(responsePayment['link']),
        mode: LaunchMode.externalApplication,
      );
    }
  }
 
  Future<void> generateJDBQr() async {
    var bodyData = {
      "amount": widget.dataProduct['price'],
      "description": "Take test from Lailaolab",
    };
    var response = await http.post(Uri.parse("$endPoint/generate-jdb-qr"),
        headers: <String, String>{
          'Content-Type': 'application/json',
          "secretKey": keyDev
        },
        body: jsonEncode(bodyData));
    if (response.statusCode == 200) {
      var responsePayment = jsonDecode(response.body);
      await launchUrl(
        Uri.parse(responsePayment['appLink']),
        mode: LaunchMode.externalApplication,
      );
    }
  }
 
  @override
  void initState() {
    super.initState();
    _socketConnect();
  }
 
  _socketConnect() async {
    socket =
        IO.io(urlSocket, OptionBuilder().setTransports(['websocket']).build());
    socket!.onConnect((_) {
      print('connected');
    });
    socket!.on("join::$keyDev", (data) async {
      print("===>>>${data}");
      if (data != null) {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: ((context) => PaymentSuccess(
                  dataResponse: data,
                )),
          ),
        );
        socket!.clearListeners();
      }
    });
  }
 
  @override
  Widget build(BuildContext context) {
    print(widget.dataProduct);
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: const Text("ເລືອກທະນາຄານ"),
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            BankWidget(
              company: const companyInfo(
                logoType: LogoType.bcel,
                showName: true,
                width: 30,
                height: 30,
                nameStyle: TextStyle(color: Colors.white),
              ),
              onTap: () => generateBcelQr(),
            ),
            BankWidget(
              company: const companyInfo(
                logoType: LogoType.jdb,
                showName: true,
                width: 30,
                height: 30,
                nameStyle: TextStyle(color: Colors.white),
              ),
              onTap: () => generateJDBQr(),
            ),
            BankWidget(
              company: const companyInfo(
                logoType: LogoType.ib,
                showName: true,
                width: 30,
                height: 30,
                nameStyle: TextStyle(color: Colors.white),
              ),
              onTap: () => generateIBQr(),
            ),
            const SizedBox(height: 20),
            qrIB != null
                ? QrImage(
                    data: '$qrIB',
                    version: QrVersions.auto,
                    size: 200.0,
                  )
                : const SizedBox.shrink()
          ],
        ),
      ),
    );
  }
}
 
class BankWidget extends StatelessWidget {
  Function() onTap;
  Widget company;
  BankWidget({Key? key, required this.onTap, required this.company})
      : super(key: key);
 
  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: onTap,
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 5),
        child: Container(
          width: double.infinity,
          height: 60,
          decoration: BoxDecoration(
            boxShadow: const [
              BoxShadow(color: Colors.grey, offset: Offset(0, 0.5))
            ],
            color: Colors.blue,
            borderRadius: BorderRadius.circular(40),
          ),
          child: Center(
            child: company,
          ),
        ),
      ),
    );
  }
}

payment_success.dart

payment_success.dart
// ignore_for_file: must_be_immutable, prefer_typing_uninitialized_variables
 
import 'package:flutter/material.dart';
 
class PaymentSuccess extends StatefulWidget {
  var dataResponse;
  PaymentSuccess({super.key, this.dataResponse});
 
  @override
  State<PaymentSuccess> createState() => _PaymentSuccessState();
}
 
class _PaymentSuccessState extends State<PaymentSuccess> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          centerTitle: true,
          title: const Text("ສຳເລັດ"),
        ),
        body: Center(
          child: Container(
            width: MediaQuery.of(context).size.width * 0.8,
            height: 400,
            decoration: BoxDecoration(
                color: Colors.white,
                borderRadius: BorderRadius.circular(20),
                boxShadow: [
                  BoxShadow(
                    color: Colors.grey.shade400,
                    offset: const Offset(0, 0.5),
                  )
                ]),
            child: Column(
              children: [
                const SizedBox(height: 10),
                const Image(
                  width: 120,
                  height: 120,
                  image: AssetImage("assets/images/success.png"),
                ),
                const SizedBox(height: 20),
                const Text(
                  "ຊຳລະເງີນສຳເລັດ",
                  style: TextStyle(color: Colors.black),
                ),
                const SizedBox(height: 30),
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 20),
                  child: Row(
                    children: [
                      const Text("ຈຳນວນເງີນ"),
                      const Spacer(),
                      Text(
                        "${widget.dataResponse['amount'] ?? ""} KIP",
                        style: const TextStyle(fontSize: 10),
                      ),
                    ],
                  ),
                ),
                const SizedBox(height: 10),
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 20),
                  child: Row(
                    children: [
                      const Text("ຊື່"),
                      const Spacer(),
                      Text(
                        "${widget.dataResponse['name'] ?? ""}",
                        style: const TextStyle(fontSize: 10),
                      ),
                    ],
                  ),
                ),
                const SizedBox(height: 10),
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 20),
                  child: Row(
                    children: [
                      const Text("uuid:"),
                      const Spacer(),
                      Text(
                        "${widget.dataResponse['uuid'] ?? ""}",
                        style: const TextStyle(fontSize: 10),
                      ),
                    ],
                  ),
                ),
                const SizedBox(height: 10),
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 20),
                  child: Row(
                    children: [
                      const Text("ວິນທີ່"),
                      const Spacer(),
                      Text(
                        "${widget.dataResponse['txtime'] ?? ""}",
                        style: const TextStyle(fontSize: 10),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ));
  }
}