🎉 PhaJay is service now. Get Started

v1
ເຊື່ອມຕໍ່ Payment QR
ໂຄ້ດຕົວຢ່າງ
Flutter

Flutter

Flutter ເປັນ UI Framework ທີ່ພັດທະນາ ແລະ ສະໜັບສະໜູນໂດຍ Google ເພື່ອສ້າງອິນເຕີເຟດຜູ້ໃຊ້ (UI) ຂອງແອັບພລິເຄຊັນສຳລັບຫຼາຍ Platform ດ້ວຍ Code ພື້ນຖານດຽວ. ປັດຈຸບັນ Flutter ຮອງຮັບການພັດທະນາແອັບພລິເຄຊັນຢູ່ຫຼາຍ Platform ເຊັ່ນ: iOS, Android, Web, macOS, Windows ແລະ Linux.

Flutter ໃຊ້ພາສາ Dart ໃນການຂຽນໂປຣແກຣມ. Dart ເປັນພາສາ Open Source ທີ່ພັດທະນາໂດຍ Google, ເປັນພາສາທີ່ຖືກອອກແບບ ແລະ ປັບປຸງໃຫ້ເໝາະກັບການສ້າງ UI ແລະ ຈຸດແຂງຫຼາຍຢ່າງຂອງ Dart ຖືກນຳມາໃຊ້ໃນ Flutter. ຕົວຢ່າງ: ການປ້ອງກັນຄ່າວ່າງເປົ່າ (Null Safety) ຊ່ວຍໃຫ້ຊອກຫາ ແລະ ແກ້ໄຂ Bug ໄດ້ງ່າຍຂຶ້ນ. ຄວາມສາມາດນີ້ຊ່ວຍໃຫ້ນັກພັດທະນາຫຼຸດເວລາ ແລະ ນຳເວລາທີ່ເຫຼືອໄປສຸມໃສ່ສ່ວນທີ່ສຳຄັນທີ່ສຸດໃນການພັດທະນາແອັບພລິເຄຊັນຂອງພວກເຂົາ.

ການເຊື່ອມຕໍ່ Payment ດ້ວຍ Flutter

ທ່ານສາມາດນຳໃຊ້ Flutter ເພື່ອເຊື່ອມຕໍ່ກັບ PhaJay Payment Support ຂອງພວກເຮົາ. ກ່ອນອື່ນໝົດ ເຮົາຕ້ອງສ້າງໂປຣເຈັກ Flutter. ໂປຣເຈັກນີ້ຈະເປັນລະບົບ e-Commerce ໂດຍສະເພາະໜ້າລາຍລະອຽດສິນຄ້າ ທີ່ຈະສະແດງຮູບພາບ, ລາຍລະອຽດ, ລາຄາ, ປະເພດ ແລະ ອື່ນໆ. ນອກຈາກນີ້ ຍັງມີການສັ່ງຊື້ ເຊິ່ງຈະສົ່ງຄຳສັ່ງໄປສ້າງ QR Payment ຢູ່ທະນາຄານການຄ້າ ແລະ ສາມາດໃຊ້ແອັບທະນາຄານການຄ້າ (BCEL One) ສະແກນເພື່ອຊຳລະ. ຫຼັງຈາກນັ້ນ ລະບົບຈະໄດ້ຮັບ Callback ຈາກທະນາຄານ ທີ່ມີລາຍລະອຽດຂອງການຊຳລະນັ້ນ ແລະ ເຮົາຈະສະແດງຂໍ້ຄວາມຢືນຢັນການຊຳລະ. Source Code ທັງໝົດສາມາດເບິ່ງໄດ້ທີ່ລິ້ງນີ້ Github (opens in a new tab)

ຖ້າຕ້ອງການອ່ານລາຍລະອຽດເພີ່ມເຕີມກ່ຽວກັບ PhaJay Payment Support ສາມາດເບິ່ງໄດ້ທີ່ນີ້ Introduction

ເລີ່ມໂປຣເຈັກ Flutter

ສ້າງ Flutter App ເພື່ອສ້າງສະພາບແວດລ້ອມໃນການຮຽນຮູ້ການໃຊ້ PhaJay Payment Support ກັບ Flutter. ລາຍລະອຽດເພີ່ມເຕີມກ່ຽວກັບການສ້າງໂປຣເຈັກ Flutter ສາມາດເບິ່ງໄດ້ທີ່ ຄູ່ມືການນຳໃຊ້ Flutter (opens in a new tab) ເຊິ່ງເປັນວິທີທີ່ດີທີ່ສຸດເພື່ອເລີ່ມຕົ້ນສ້າງແອັບພລິເຄຊັນໃໝ່ດ້ວຍ Flutter.

  flutter create lailaopayment_app
  cd lailaopayment_app
  flutter run

ແຖວທີ 1 ຄຳສັ່ງນີ້ຈະສ້າງໂປຣເຈັກ Flutter ຊື່ lailaopayment_app

ແຖວທີ 2 ແມ່ນການເຂົ້າໄປຍັງໂຟນເດີ lailaopayment_app

ແຖວທີ 3 ຄຳສັ່ງເພື່ອແລ່ນໂປຣເຈັກໃນໂໝດ Debug ເປັນຄັ້ງທຳອິດ

ຕໍ່ໄປເຮົາຈະຕິດຕັ້ງ Library ທີ່ຈະໃຊ້ເພື່ອສ້າງ App ໃຫ້ງ່າຍຂຶ້ນ.

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

ຄຳສັ່ງຂ້າງເທິງແມ່ນການເພີ່ມ Library ຕ່າງໆລົງໃນໂປຣເຈັກ ເຊິ່ງແຕ່ລະອັນມີຄວາມໝາຍດັ່ງນີ້:

Libraryລາຍລະອຽດ
httphttp ແມ່ນຕົວເຊື່ອມຕໍ່ຂອງ Flutter ກັບເຊີເວີ ເພື່ອສົ່ງ request ໄປຫາເຊີເວີ ແລະ ຮັບຂໍ້ມູນ response ກັບມາຍັງ frontend ແລະ ເປັນສ່ວນສຳຄັນໃນການສື່ສານກັບ RESTful API.
socket_io_clientsocket.io ໃຊ້ສຳລັບເຊື່ອມຕໍ່ກັບເຊີເວີ WebSocket ແລະ Socket.IO. ເປັນ client ສຳລັບສ້າງແອັບພລິເຄຊັນແບບ real-time ເຊິ່ງສາມາດສົ່ງຂໍ້ມູນລະຫວ່າງ client ແລະ ເຊີເວີ ໂດຍບໍ່ຕ້ອງໂຫຼດໜ້າໃໝ່ ຫຼື ສົ່ງ request ໃໝ່.
url_launcherurl_launcher ແມ່ນ Library ທີ່ໃຊ້ໃນ Flutter ເພື່ອເປີດ URL ຈາກພາຍໃນແອັບຂອງເຮົາດ້ວຍແອັບພາຍນອກ
company_infocompany_info ແມ່ນ Library ທີ່ລວມໂລໂກ້ຂອງແອັບ ຫຼື ບໍລິສັດຕ່າງໆໃນລາວ

ຕໍ່ໄປເລີ່ມຂຽນ Code ໂດຍເລີ່ມຈາກ:

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://portal.phajay.co/?key=$keyDev";
  static const String endPoint =
      "https://portal.phajay.co/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),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ));
  }
}

ອັບເດດຫຼ້າສຸດ: 11 ເດືອນກ່ອນ