Membuat Payment Gateway menggunakan Flutter

Midtrans memungkinkan menerima pembayaran online secara asli di aplikasi mobile, midtrans dapat digunakan di berbagai platform, sistem midtrans kopatible dengan berbagai API dan plug-in untuk proses integrasi yang mudah, jadi dengan menggunakan midtrans anda tidak perlu lagi cek pembayaran dan buat laporan keuangan secara manual, dengan midtrans tersedia 20 metode pebayaran, anda bisa melayani kebutuhan pelanggan diseluruh daerah indonesia.

Transaksi flow

  1. Checkout: Pelanggan mengklik tombol Checkout pada aplikasi Host dan aplikasi membuat permintaan ke Merchant Server
  2. Token request: Merchant Server membuat permintaan ke server Midtrans dengan Informasi Pemesanan.
  3. Token response: Midtrans merespons dengan token transaksi yang valid ke server Merchant
  4. Token response: Server pedagang meneruskan token ke Mobile SDK
  5. Get transaction options: SDK Seluler meminta informasi pembayaran / pedagang berdasarkan token
  6. Transaction options response: SDK Seluler membuat opsi pembayaran dan informasi pembayaran untuk melakukan pembayaran
  7. Pay: Pelanggan memilih metode pembayaran dan rincian pembayaran dan mengklik “Bayar”
  8. Charge: SDK Seluler mengirimkan permintaan Tagihan ke Midtrans Backend untuk Pemrosesan pembayaran.
  9. Charge response: SDK Seluler menerima respons dari Midtrans Backend dan memicu pengendali pada Aplikasi Seluler dengan status berhasil / gagal / tertunda
  10. Charge notification: Midtrans Backend mengirimkan pemberitahuan ke Merchant backend yang mengkonfirmasi penyelesaian transaksi.

Server Side

Bagian variable server_key anda masukan server key yang anda dapat dari Midtrans

<?php

$server_key = "YOUR_SERVER_KEY";

$is_production = false;

$api_url = $is_production ? 
  'https://app.midtrans.com/snap/v1/transactions' : 
  'https://app.sandbox.midtrans.com/snap/v1/transactions';


if( !strpos($_SERVER['REQUEST_URI'], '/charge') ) {
  http_response_code(404); 
  echo "wrong path, make sure it's `/charge`"; exit();
}

if( $_SERVER['REQUEST_METHOD'] !== 'POST'){
  http_response_code(404);
  echo "Page not found or wrong HTTP request method is used"; exit();
}

$request_body = file_get_contents('php://input');
header('Content-Type: application/json');

$charge_result = chargeAPI($api_url, $server_key, $request_body);

http_response_code($charge_result['http_code']);

echo $charge_result['body'];


function chargeAPI($api_url, $server_key, $request_body){
  $ch = curl_init();
  $curl_options = array(
    CURLOPT_URL => $api_url,
    CURLOPT_RETURNTRANSFER => 1,
    CURLOPT_POST => 1,
    CURLOPT_HEADER => 0,


    CURLOPT_HTTPHEADER => array(
      'Content-Type: application/json',
      'Accept: application/json',
      'Authorization: Basic ' . base64_encode($server_key . ':')
    ),
    CURLOPT_POSTFIELDS => $request_body
  );
  curl_setopt_array($ch, $curl_options);
  $result = array(
    'body' => curl_exec($ch),
    'http_code' => curl_getinfo($ch, CURLINFO_HTTP_CODE),
  );
  return $result;
}

Jalankan code diatas menggunakan Heroku, kemudian anda copy URL untuk diterapkan pada client side bagian mobile.

Client Side

Bagian gradle anda masukan repositories

allprojects {
    repositories {
        ...
        maven { url "http://dl.bintray.com/pt-midtrans/maven" }
    }
}

Buat project baru kemudian bagian dependencies anda masukan 2 environment, mode sanbox dan mode production

sandboxImplementation 'com.midtrans:uikit:1.21.2-SANDBOX'
productionImplementation 'com.midtrans:uikit:1.21.2'

Anda buatkan varian dengan productFlavors

productFlavors {
//      mode pengembang
        sandbox {
            dimension = "mode"
            applicationId "com.kodetr.kodetr_app.sandbox"
            resValue "string", "app_name", "Kiostr SandBox"
            buildConfigField "String", "MERCHANT_BASE_URL", "YOUR_URL_SERVER"
            buildConfigField "String", "MERCHANT_CLIENT_KEY", "YOUR_CLIENT_KEY"
        }

//      mode produksi
        production {
            dimension = "mode"
            applicationId "com.kodetr.kodetr_app"
            resValue "string", "app_name", "Kiostr Production"
            buildConfigField "String", "MERCHANT_BASE_URL", "YOUR_URL_SERVER"
            buildConfigField "String", "MERCHANT_CLIENT_KEY", "YOUR_CLIENT_KEY"
        }
    }

Class Product

Selanjutnya Anda buat class model sebagai penampung dari data product

class Product{
  String name, image;
  int  price;
  bool userLiked;
  double discount;

  Product({
    this.name,
    this.price,
    this.discount,
    this.image,
    this.userLiked
  });
}

Class Items

Menampilkan hasil dari data item yang di tampung pada data list

import 'package:flutter/material.dart';
import './Product.dart';
import '../shared/colors.dart';
import '../shared/styles.dart';

Widget productItem(Product product,
    {double imgWidth, onLike, onTapped, bool isProductPage = false}) {

  return Container(
    width: 180,
    height: 200,
    margin: EdgeInsets.only(left: 20, bottom: 30),
    child: Stack(
      children: <Widget>[
        Container(
            width: 180,
            height: 300,
            child: RaisedButton(
                color: white,
                elevation: (isProductPage) ? 20 : 12,
                shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(5)),
                onPressed: onTapped,
                child: Hero(
                    transitionOnUserGestures: true,
                    tag: product.name,
                    child: Image.asset(product.image,
                        width: (imgWidth != null) ? imgWidth : 100)))),
        Positioned(
          bottom: 10,
          left: 10,
          child: (!isProductPage)
              ? Column(
                  mainAxisAlignment: MainAxisAlignment.start,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Text(product.name, style: nameText),
                    Text("Rp "+product.price.toString(), style: priceText),
                  ],
                )
              : Text(' '),
        ),
        Positioned(
            top: 10,
            left: 10,
            child: (product.discount != null)
                ? Container(
                    padding:
                        EdgeInsets.only(top: 5, left: 10, right: 10, bottom: 5),
                    decoration: BoxDecoration(
                        color: Colors.red[600],
                        borderRadius: BorderRadius.circular(50)),
                    child: Text(product.discount.toString() + '%',
                        style: TextStyle(
                            color: Colors.white, fontWeight: FontWeight.w700)),
                  )
                : SizedBox(width: 0))
      ],
    ),
  );
}

Class HomePage

Merupakan bagian dari menu utama dan sekaligus menampilkan berupa data dari list Computers dan list dari data handphones

import 'package:flutter/material.dart';
import '../shared/styles.dart';
import '../shared/colors.dart';
import '../shared/fryo_icons.dart';
import './ProductPage.dart';
import './Product.dart';
import './Items.dart';

class HomePage extends StatefulWidget {

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    final _tabs = [
      storeTab(context),
      Text('Feed'),
      Text('Keranjang'),
      Text('Akun'),
    ];

    return Scaffold(
        backgroundColor: bgColor,
        appBar: AppBar(
          centerTitle: true,
          elevation: 0,
          backgroundColor: primaryColor,
          title:
              Text('KiosTR', style: logoWhiteStyle, textAlign: TextAlign.center),
          actions: <Widget>[
            IconButton(
              padding: EdgeInsets.all(0),
              onPressed: () {},
              iconSize: 21,
              icon: Icon(Fryo.heart_1),
            ),
            IconButton(
              padding: EdgeInsets.all(0),
              onPressed: () {},
              iconSize: 21,
              icon: Icon(Fryo.alarm),
            )
          ],
        ),
        body: _tabs[_selectedIndex],
        bottomNavigationBar: BottomNavigationBar(
          items: <BottomNavigationBarItem>[
            BottomNavigationBarItem(
                icon: Icon(Fryo.home),
                title: Text(
                  'Home',
                  style: tabLinkStyle,
                )),
            BottomNavigationBarItem(
                icon: Icon(Fryo.bookmark),
                title: Text(
                  'Feed',
                  style: tabLinkStyle,
                )),
            BottomNavigationBarItem(
                icon: Icon(Fryo.cart),
                title: Text(
                  'Keranjang',
                  style: tabLinkStyle,
                )),
            BottomNavigationBarItem(
                icon: Icon(Fryo.smile),
                title: Text(
                  'Akun',
                  style: tabLinkStyle,
                ))
          ],
          currentIndex: _selectedIndex,
          type: BottomNavigationBarType.fixed,
          fixedColor: Color(0xff597de9),
          onTap: _onItemTapped,
        ));
  }

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }
}

Widget storeTab(BuildContext context) {

  // will pick it up from here
  // am to start another template
  List<Product> computers = [
    Product(
        name: "ASUS Laptop E402WA",
        image: "images/5.png",
        price: 10000,
        userLiked: true,
        discount: 30),
    Product(
        name: "MacBook Pro",
        image: "images/6.jpg",
        price: 20000,
        userLiked: false,
        discount: 20),
    Product(
      name: "Acer Aspire E5575",
      image: 'images/7.jpeg',
      price: 7000,
      userLiked: false,
    ),
    Product(
        name: "Alienware",
        image: "images/8.jpeg",
        price: 20000,
        userLiked: true,
        discount: 10)
  ];

  List<Product> handphones = [
    Product(
        name: "SAMSUNG Galaxy A30",
        image: "images/1.png",
        price: 10000,
        userLiked: true,
        discount: 5),
    Product(
        name: "Hp Oppo A39",
        image: "images/2.jpeg",
        price: 3000,
        userLiked: false,
        discount: 30),
    Product(
        name: "Iphone 11 Pro",
        image: "images/3.jpeg",
        price: 20000,
        userLiked: false),
    Product(
        name: "Vivo Y91C",
        image: "images/4.jpeg",
        price: 5000,
        userLiked: true,
        discount: 20)
  ];

  return ListView(children: <Widget>[
    deals('Komputer', onViewMore: () {}, items: <Widget>[
      productItem(computers[0], onTapped: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) {
              return new ProductPage(
                productData: computers[0],
              );
            },
          ),
        );
      }, onLike: () {}),
      productItem(computers[1], onTapped: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) {
              return new ProductPage(
                productData: computers[1],
              );
            },
          ),
        );
      }, imgWidth: 250, onLike: () {
        
      }),
      productItem(computers[2], onTapped: () {
         Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) {
              return new ProductPage(
                productData: computers[2],
              );
            },
          ),
        );
      }, imgWidth: 200, onLike: () {
       
      }),
      productItem(computers[3], onTapped: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) {
              return new ProductPage(
                productData: computers[3],
              );
            },
          ),
        );
      }, onLike: () {
        
      }),
    ]),
    deals('Handphone', onViewMore: () {}, items: <Widget>[
      productItem(handphones[0], onTapped: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) {
              return new ProductPage(
                productData: handphones[0],
              );
            },
          ),
        );
      }, onLike: () {}, imgWidth: 60),
      productItem(handphones[1], onTapped: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) {
              return new ProductPage(
                productData: handphones[1],
              );
            },
          ),
        );
      }, onLike: () {}, imgWidth: 75),
      productItem(handphones[2], onTapped: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) {
              return new ProductPage(
                productData: handphones[2],
              );
            },
          ),
        );
      }, imgWidth: 110, onLike: () {}),
      productItem(handphones[3], onTapped: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) {
              return new ProductPage(
                productData: handphones[3],
              );
            },
          ),
        );
      }, onLike: () {}),
    ])
  ]);
}

Widget sectionHeader(String headerTitle, {onViewMore}) {
  return Row(
    mainAxisAlignment: MainAxisAlignment.spaceBetween,
    crossAxisAlignment: CrossAxisAlignment.start,
    children: <Widget>[
      Container(
        margin: EdgeInsets.only(left: 15, top: 10),
        child: Text(headerTitle, style: h4),
      ),
      Container(
        margin: EdgeInsets.only(left: 15, top: 2),
        child: FlatButton(
          onPressed: onViewMore,
          child: Text('Lihat Semua ›', style: contrastText),
        ),
      )
    ],
  );
}

Widget headerCategoryItem(String name, IconData icon, {onPressed}) {
  return Container(
    margin: EdgeInsets.only(left: 15),
    child: Column(
      mainAxisAlignment: MainAxisAlignment.start,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: <Widget>[
        Container(
            margin: EdgeInsets.only(bottom: 10),
            width: 86,
            height: 86,
            child: FloatingActionButton(
              shape: CircleBorder(),
              heroTag: name,
              onPressed: onPressed,
              backgroundColor: white,
              child: Icon(icon, size: 35, color: Colors.black87),
            )),
        Text(name + ' ›', style: categoryText)
      ],
    ),
  );
}

Widget deals(String dealTitle, {onViewMore, List<Widget> items}) {
  return Container(
    margin: EdgeInsets.only(top: 5),
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: <Widget>[
        sectionHeader(dealTitle, onViewMore: onViewMore),
        SizedBox(
          height: 250,
          child: ListView(
            scrollDirection: Axis.horizontal,
            children: (items != null)
                ? items
                : <Widget>[
                    Container(
                      margin: EdgeInsets.only(left: 15),
                      child: Text('Tidak ada item tersedia',
                          style: taglineText),
                    )
                  ],
          ),
        )
      ],
    ),
  );
}

Gambar dibawah menunjukan hasil output dari code dart diatas

Class ProductPage

Menampilkan hasil detail peritem dari produk yang dipilih dari list data, sekaligus melakukan pembayaran dengan menggunakan payment gateway

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import './Product.dart';
import '../shared/styles.dart';
import '../shared/colors.dart';
import './Items.dart';
import '../shared/buttons.dart';
import 'package:smooth_star_rating/smooth_star_rating.dart';

const CHANNEL = "com.kodetr.kodetr_app.channel";
const KEY_NATIVE = "showPaymentGateway";

class ProductPage extends StatefulWidget {
  final String pageTitle;
  final Product productData;

  ProductPage({Key key, this.pageTitle, this.productData}) : super(key: key);

  @override
  _ProductPageState createState() => _ProductPageState();
}

class _ProductPageState extends State<ProductPage> {

  static const platform = const MethodChannel(CHANNEL);

  double _rating = 4;
  int _quantity = 1;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: bgColor,
        appBar: AppBar(
          elevation: 0,
          backgroundColor: bgColor,
          centerTitle: true,
          leading: BackButton(
            color: darkText,
          ),
          title: Text(widget.productData.name, style: h4),
        ),
        body: ListView(
          children: <Widget>[
            Container(
              margin: EdgeInsets.only(top: 20),
              child: Center(
                child: Stack(
                  children: <Widget>[
                    Align(
                      alignment: Alignment.center,
                      child: Container(
                      margin: EdgeInsets.only(top: 100, bottom: 100),
                      padding: EdgeInsets.only(top: 100, bottom: 50),
                      width: MediaQuery.of(context).size.width * 0.85,
                      child: Column(
                        mainAxisAlignment: MainAxisAlignment.start,
                        crossAxisAlignment: CrossAxisAlignment.center,
                        children: <Widget>[
                          Text(widget.productData.name, style: h5),
                          Text("Rp "+widget.productData.price.toString(), style: h3),
                          Container(
                            margin: EdgeInsets.only(top: 5, bottom: 20),
                            child: SmoothStarRating(
                              allowHalfRating: false,
                              onRatingChanged: (v) {
                                setState(() {
                                  _rating = v;
                                });
                              },
                              starCount: 5,
                              rating: _rating,
                              size: 27.0,
                              color: Colors.orange,
                              borderColor: Colors.orange,
                            ),
                          ),
                          Container(
                            margin: EdgeInsets.only(top: 10, bottom: 25),
                            child: Column(
                              children: <Widget>[
                                Container(
                                  child: Text('Jumlah', style: h6),
                                  margin: EdgeInsets.only(bottom: 15),
                                ),
                                Row(
                                  mainAxisAlignment: MainAxisAlignment.center,
                                  crossAxisAlignment: CrossAxisAlignment.center,
                                  children: <Widget>[
                                    Container(
                                      width: 55,
                                      height: 55,
                                      child: OutlineButton(
                                        onPressed: () {
                                          setState(() {
                                            _quantity += 1;
                                          });
                                        },
                                        child: Icon(Icons.add),
                                      ),
                                    ),
                                    Container(
                                      margin:
                                          EdgeInsets.only(left: 20, right: 20),
                                      child: Text(_quantity.toString(), style: h3),
                                    ),
                                    Container(
                                      width: 55,
                                      height: 55,
                                      child: OutlineButton(
                                        onPressed: () {
                                          setState(() {
                                           if(_quantity == 1) return;
                                             _quantity -= 1; 
                                          });
                                        },
                                        child: Icon(Icons.remove),
                                      ),
                                    )
                                  ],
                                )
                              ],
                            ),
                          ),
                          Container(
                            width: 180,
                            child: froyoOutlineBtn('Beli', () {
                                _showNativeView();
                            }),
                          )
                        ],
                      ),
                      decoration: BoxDecoration(
                          color: white,
                          borderRadius: BorderRadius.circular(10),
                          boxShadow: [
                            BoxShadow(
                                blurRadius: 15,
                                spreadRadius: 5,
                                color: Color.fromRGBO(0, 0, 0, .05))
                          ]),
                    ),
                    ),
                    Align(
                        alignment: Alignment.center,
                      child: SizedBox(
                        width: 200,
                        height: 160,
                        child: productItem(widget.productData,
                            isProductPage: true,
                            onTapped: () {},
                            imgWidth: 250,
                            onLike: () {}),
                      ),
                    )
                  ],
                ),
              ),
            )
          ],
        ));
  }

  Future<Null> _showNativeView() async {
    await platform.invokeMethod(KEY_NATIVE, {
      "name": widget.productData.name,
      "price": widget.productData.price,
      "quantity": _quantity,
    });
  }
}

Gambar dibawah menunjukan hasil output dari code dart diatas


Resource


Jika anda sudah mengikuti artikel ini sesuai intruksi dari vidio maka anda berhasil membuat aplikasi payment gateway menggunakan `midtrans.

Demikian yang dapat saya sampaikan dari artikel ini semoga bermanfaat, jika ada yang ditanyakan silahkan di kolom komentar dibawah, selamat mencoba.

Share Comments
comments powered by Disqus