Flutter Paystack All Options Implementation
Tags: flutter, paystack, android, beginners
Published: 2022-07-21 08:51:00

## Table of Content

- 🕹 [Introduction](#intro)
- 📡 [Required Paystack Details and Endpoints](#paystack)
- 🖲 [Installing WebView Package](#webview)
- 🛢 [Making API calls in Flutter](#api)
- 📲 [Displaying Paystack pop-up in webView](#popup)
- 💾 [Listening For Navigation url](#url)
- 💙 [Important Notes for Release APK](#releaseApk)


### 🕹 Introduction <a name="intro"></a>
As at the time of writing this article, paystack integration for android and mobile SDK, only supports card payments and bank (with just Zenith as the option).

In this article, you will learn how to include other payment options in your flutter application. 

##
    PS: I'm assuming you are familiar with flutter and 
    paystack with all the necessary keys and how to get them 
    from your paystack dashboard.

Before we continue, please take a quick look at this article from [Paystack](https://paystack.com/docs/guides/using_the_paystack_checkout_in_a_mobile_webview/), that's where I got ideas from 😋. If you want, you could use that article, or continue reading.


### 📡 Required Paystack Details and Endpoints<a name="paystack"></a>
You will need your paystack secret_key (use the test key for testing and live key for prod) as your Bearer token in your Authorization header while making a POST request to the [paystack initialize api]( https://api.paystack.co/transaction/initialize).

Check the below images for better understanding.

#### Setting your authorization header with secret_key
![Postman Authorization Header Bearer token](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6i6x7jlhyzc2vlva4kfi.png)

#### POST body details

![POST body details](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ehhfh408466nodgtzxbn.png)

<a name="callback_url"></a>
##
    email: This is the payers email.

    amount: This is the amount to be paid, please multiply by 
            100, to remove the kobo value
    
    reference: This should be randomly generated as it should 
               be a unique value for each payment.

    callback_url: This is the most important part. If you 
                  already have a callback url, setup from your 
                  paystack dashboard, you don't need this 
                  parameter. If you still go ahead to include 
                  it, it'll override that of the dashboard. 
                  This callback_url is what we will be 
                  listening for from our webview.

    cancel_action: This will be used to listen for when the 
                   user wants to cancel payment, we then pop 
                   the webview screen.


#### Response from paystack API


![Paystack Response](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uxfxzsuhjpsd6wc5hgfm.png)

##
    authorization_url: This is the most important data we will 
                       be needing for our webview, so hold on 
                       to it 😎.


### 🖲 Installing WebView Package<a name="webview"></a>

To install this package, please visit this [link](https://dartlang-pub.appspot.com/packages/webview_flutter), read through the setup and install it.


### 🛢 Making API calls in Flutter <a name="api"></a>

This approach depends on which architecture you're using to build your application, at the end, it's all the same response you will receive. In this article, i'll be keeping it really simple 🥹.

```javascript
var headers = {
  'Authorization': 'Bearer sk_test_039***************',
  'Content-Type': 'application/json',
};
var request = http.Request('POST', Uri.parse('https://api.paystack.co/transaction/initialize'));
request.body = json.encode({
  "email": "g.ikwegbu@gmail.com",
  "amount": "10000",
  "reference": "randomDetails",
  "callback_url": "https://github.com/gikwegbu",
  "metadata": {"cancel_action": "https://www.google.com/"}
});
request.headers.addAll(headers);

http.StreamedResponse response = await request.send();

if (response.statusCode == 200) {
  print(await response.stream.bytesToString());
}
else {
  print(response.reasonPhrase);
}

```
<a name="authUrl"></a>
PS: I personally love using the [flutter dio package](https://dartlang-pub.appspot.com/packages/dio)


##
    Retrieve the authorization_url from the response and store 
    it in a global variable.



### 📲 Displaying Paystack pop-up in webView <a name="popup"></a>

Ok, let's be on the same page here 🙃. We are building an application that allows users make payment for an item via paystack. 

##### 🏳️ The Data Flow: 
when the user clicks on the PAY button, the paystack pops-up, user selects whichever option that's suitable, and proceed. Once the payment is successful, we will be redirected to a [callback_url](#callback_url). Once this happens, we will have to be listening for that callback_url, which when detected, allows us to close the webview widget, and then continue with sending our [reference code](#callback_url) to our server to complete checking out of the PAID item.

##### 🏳️ Implementation: 
To make the process seamless, i'd be using a popup dialog, set it to fullscreen to overlay our current page, add the webview widget.

Below is a code snippet that will help you understand more:

```javascript

// This is called when the PAY button is clicked.
_showPaystack() async {
    var _ref = 'ChargedFromMyApp_${DateTime.now().millisecondsSinceEpoch}';
    Map data = {
      // Removing the kobo by multiplying with 100
      "amount": double.parse('${_amount * 100}').toInt(),
      "email": _user.email,
      "reference": _ref,
      "callback_url": "https://github.com/gikwegbu",
      "metadata": {"cancel_action": "https://www.google.com/"}
    };
    // This awaits the [authorization_url](#authUrl). NB: I'm using the MVVM architecture in my live code, but it's still the same process of retrieving the authURL.
    var authUrl = await _profileCtrl.paystackWebViewChargeCard(data);

    // only pull-up the dialog box when we get the authURL
    if (authUrl != null) {
      return showGeneralDialog(
          context: context,
          barrierDismissible: true,
          barrierLabel:
              MaterialLocalizations.of(context).modalBarrierDismissLabel,
          barrierColor: Colors.black45,
          transitionDuration: const Duration(milliseconds: 200),
          pageBuilder: (BuildContext buildContext, Animation animation,
              Animation secondaryAnimation) {
            return Center(
                child: Container(
              width: MediaQuery.of(context).size.width - 10,
              // height: MediaQuery.of(context).size.height - 80,
              height: MediaQuery.of(context).size.height - 0,
              padding: const EdgeInsets.only(top: 40),
              color: Colors.white,
              child: WebView(
                initialUrl: authUrl.toString(),
                javascriptMode: JavascriptMode.unrestricted,
                userAgent: 'Flutter;Webview',
                navigationDelegate: (navigation) {
                  //Listen for callback URL
                  if (navigation.url == "https://standard.paystack.co/close") {
                    Navigator.of(context).pop(); //close webview

                    // _txnRef is my glabally created variable to handle my reference
                    _txnRef = _ref;
                    _submit();
                  }
                  if (navigation.url ==
                      "github.com/gikwegbu?trxref=$_ref&reference=$_ref") {
                    Navigator.of(context).pop();
                    _txnRef = _ref;
                    _submit();
                  }
                  if (navigation.url == "https://www.google.com/") {
                    Navigator.of(context).pop();
                  }
                  return NavigationDecision.navigate;
                },
              ),
            ));
          });
    }
  }

```

##
    The _submit(), is a function that sends the reference to 
    my backend to complete the purchase transaction on the 
    selected item.



### 💾  Listening For Navigation url <a name="url"></a>
From the above code snippet, you'd have noticed the navigationDelegate section, this is where you'll handle listening for the callback_url and also, in the case the user is making payments with a Card, and the 3Ds Authentication will have to redirect you to the 'https://standard.paystack.co/close'. Other than that, we will listen for our custom callback_url, and then pop our screen.

### 💙 Important Note For Release apk <a name="releaseApk"></a>
The application will actually work while in debug mode, but will through an error or not work at all in released build, as you'd only be seeing a gray overlay.

#### Steps Taken to Resolve this.
1. Add 
```dart
<uses-permission android:name="android.permission.INTERNET" />
```
 in your `android/app/src/main/AndroidManifest.xml`.

2. Add 
```dart
if (Platform.isAndroid) WebView.platform = AndroidWebView();
```
Inside of your `initState(){}` function of your file. This means you'd have to be using a "Stateful" widget.

3. This is the most important. Run 
```dart
flutter run --release
```
This would help you build your app as in 'release mode', but you can still be seeing your debugs in your console. Now watch closely if you see an error like `I/flutter (18782): Another exception was thrown: Instance of 'DiagnosticsProperty<void>'`, if so, then please look for where `Expanded` widget was used in your implementation and remove it.



### Finally

We have successfully integrated our paystack payment system with multiply option into our flutter application. To experience the multiple option, please change from your test secret key to your live secret key, only then will you see the