mpesa_flutter_plugin 0.0.4

  • Readme
  • Changelog
  • Example
  • Installing
  • 78

Mpesa plugin #

Use this plugin to implement Lipa Na MPESA Online.

Getting Started #

Credentials #

  1. Create an account on the Safaricom Developer Portal
  2. Create a Lipa na MPESA Online App
  3. Get your keys -> ConsumerKey and ConsumerSecret

Usage #

This plugin requires good understanding of the MPESA C2B concept, in as much as it will help you complete the process, you will also need to get things right in order to have it serve you right. With that said,

These two places will help you get started on a better gear.

  1. Safaricom API Tutorial
  2. Safaricom Developer Portal Docs

From here, it's now simpler to have it on your app.

  1. You will need to set the keys before initiating the payment.
    import 'package:mpesa_flutter_plugin/mpesa_flutter_plugin.dart';
    
    void main(){
    MpesaFlutterPlugin.setConsumerKey(<your-consumer-key>);
    MpesaFlutterPlugin.setConsumerSecret(<your-consumer-secret>);
    runApp(new MyApp());
    
    }
  1. Initiate the payment.

  dynamic transactionInitialisation;
 //Wrap it with a try-catch
  try {
  //Run it
  transactionInitialisation =
          await MpesaFlutterPlugin.initializeMpesaSTKPush(
                  businessShortCode: <your-code>,
                  transactionType: TransactionType.CustomerPayBillOnline,
                  amount: <amount-in-string-format>,
                  partyA: <user's-phone-to-request-payment>,
                  partyB: <your-code>,
                  callBackURL: <url-to-receive-payment-results>,
                  accountReference: <could-be-order-number>,
                  phoneNumber: <user's-phone-to-request-payment>,
                  baseUri: <live-or-sandbox-base-url>,
                  transactionDesc: <short-description>,
                  passKey: <your-passkey>);
                  
  } catch (e) {
  //you can implement your exception handling here.
  //Network unreachability is a sure exception.
  print(e.getMessage();
  }

With that you are pretty much done. Here is a breakdown of the params required :

  1. businessShortCode & partyB which you can apply from the developer portal mentioned in credentials, alternatively, use 174379 for test purposes.
  2. amount amount you expect from customer, in Ksh, string.
  3. phoneNumber & partyA the user's phone number to request payment from.
  4. callBackURL is where the payment results will be POSTed to you.
  5. accountReference what are the payments for? a short ref like users' order, account number...will be displayed to the user when requesting completion of payment.
  6. transactionDesc brief description of the tansaction. Not actually a description, a descriptive word. (Sometimes optional)
  7. passKey obtained from portal, will be blended with a few more things to generate your final password later, alternatively, use bfb279f9aa9bdbcf158e97dd71a467cd2e0c893059b10f78e6b72ada1ed2c919 for test purposes.

Docs at a glance. #

When you place the request, and network does it's job well, the MPESA payment processor will validate your parameters and send your an acknowledgement or an error response immediately: case success: expect something of this sort in transactionInitialisation var.

  {
      "MerchantRequestID": "1466 - 405147 - 1",
      "CheckoutRequestID" : "ws_CO_DMZ_370754209_06062019172849964",
      "ResponseCode" : "0",
      "ResponseDescription": "Success.Request accepted for processing",
      "CustomerMessage" : "Success.Request accepted for processing"
  }

It means just that, accepted for processing, yet to be processed.

else, if some of your params are not accepted, expect something of this sort:

  {
      "requestId": "751-526141-1",
      "errorCode": "400.002.02",
      "errorMessage": "Bad Request - Invalid Amount"
  }

After this, the payment processor will proceed to seek the user and hopefully find, hopefully accepts the payment by giving correct pin, and hopefully they have enough amount ... case success, expect something like this on your backend, where the callBackURL points to:

  { "Body":
      { "stkCallback":
              { "MerchantRequestID": "1466-405147-1",
              "CheckoutRequestID": "ws_CO_DMZ_370754209_06062019172849964",
              "ResultCode": 0,
              "ResultDesc": "The service request is processed successfully.",
              "CallbackMetadata": {
                              "Item":
                                      [
                                          {
                                          "Name": "Amount",
                                          "Value": 100.00
                                          },
                                          
                                          {
                                          "Name": "MpesaReceiptNumber",
                                          "Value": "NF68F38A1G"
                                          },
                                          
                                          {
                                          "Name": "Balance"
                                          },
                                          
                                          {
                                          "Name": "TransactionDate",
                                          "Value": 20190606172857
                                          },
                                          
                                          {
                                          "Name": "PhoneNumber",
                                          "Value": 254710xxx574
                                          }
                                      ]
              
                          }

                }

        }

}

case failure of a transaction, this is a sample of your result:

{
    "Body":
        {
        "stkCallback":
                {
                "MerchantRequestID":"24963-1092493-1",
                "CheckoutRequestID":"ws_CO_DMZ_511000437_07062019123449116",
                "ResultCode":2001,
                "ResultDesc":"[MpesaCB - ]The initiator information is invalid."

                }

        }

}

That's what in the docs in summary.

Plugin In Action #

Contributing #

Pull Requests are welcomed to this plugin project on GitHub.

0.0.1 #

  • Basic functioning of STK push.

0.0.2 #

  • Bug fixes

0.0.3 #

  • Added support for iOS
  • Migrated android version to support androidx
  • Changed following properties:
    1. String amount to double amount
    2. String callbackUrl to Uri callbackUrl
    3. String baseUrl to Uri baseUri
    4. Removed enableDebugModeWithLogging method.

0.0.4 #

  • Removed unnecessary dependencies in android.

example/README.md


import 'package:flutter/material.dart';
import 'dart:async';

import 'package:mpesa_flutter_plugin/mpesa_flutter_plugin.dart'; //Import the plugin
import './global_keys.dart';

void main() {
  /*Set Consumer credentials before initializing the payment.
    You can get  them from https://developer.safaricom.co.ke/ by creating
    an account and an app.
     */
  MpesaFlutterPlugin.setConsumerKey(mConsumerKey);
  MpesaFlutterPlugin.setConsumerSecret(mConsumerSecret);
  //MpesaFlutterPlugin.enableDebugModeWithLogging(true);
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  Future<void> startCheckout({String userPhone, int amount}) async {
    //Preferably expect 'dynamic', response type varies a lot!
    dynamic transactionInitialisation;
    //Better wrap in a try-catch for lots of reasons.
    try {
      //Run it
      transactionInitialisation =
          await MpesaFlutterPlugin.initializeMpesaSTKPush(
              businessShortCode: "174379",
              transactionType: TransactionType.CustomerPayBillOnline,
              amount: amount.toString(),
              partyA: userPhone,
              partyB: "174379",
              callBackURL: "https://url-to-post",
              accountReference: "shoe",
              phoneNumber: userPhone,
              baseUrl: "https://sandbox.safaricom.co.ke/",
              transactionDesc: "purchase",
              passKey: mPasskey);

      print("TRANSACTION RESULT: " + transactionInitialisation.toString());

      /*Update your db with the init data received from initialization response,
      * Remaining bit will be sent via callback url*/
      return transactionInitialisation;
    } catch (e) {
      //For now, console might be useful
      print("CAUGHT EXCEPTION: " + e.toString());
    }
  }

  List<Map<String, dynamic>> itemsOnSale = [
    {
      "image": "image/shoe.jpg",
      "itemName": "Breathable Oxford Casual Shoes",
      "price": 1
    }
  ];

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
          primaryColor: Colors.brown[450], primarySwatch: Colors.brown),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Mpesa Payment plugin'),
        ),
        body: ListView.builder(
          itemBuilder: (BuildContext context, int index) {
            return Card(
              elevation: 4.0,
              child: Container(
                decoration: ShapeDecoration(
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(10.0),
                    ),
                    color: Colors.brown),
                height: MediaQuery.of(context).size.height * 0.35,
                //color: Colors.brown,
                child: Column(
                  children: <Widget>[
                    Container(
                      decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(10.0)),
                      child: Image.asset(
                        itemsOnSale[index]["image"],
                        fit: BoxFit.cover,
                      ),
                      height: MediaQuery.of(context).size.height * 0.25,
                      width: MediaQuery.of(context).size.width * 0.95,
                    ),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                      children: <Widget>[
                        Text(
                          itemsOnSale[index]["itemName"],
                          overflow: TextOverflow.ellipsis,
                          style: TextStyle(fontSize: 14.0, color: Colors.black),
                        ),
                        Text(
                          "Ksh. " + itemsOnSale[index]["price"].toString(),
                          style: TextStyle(
                              fontSize: 18.0,
                              color: Colors.white,
                              fontWeight: FontWeight.bold),
                        ),
                        RaisedButton(
                            shape: RoundedRectangleBorder(
                                borderRadius: BorderRadius.circular(10.0)),
                            onPressed: () {
                              startCheckout(
                                  userPhone: "2547xxxxxxxx",
                                  amount: itemsOnSale[index]["price"]);
                            },
                            child: Text("Checkout"))
                      ],
                    )
                  ],
                ),
              ),
            );
          },
          itemCount: itemsOnSale.length,
        ),
      ),
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  mpesa_flutter_plugin: ^0.0.4

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:mpesa_flutter_plugin/mpesa_flutter_plugin.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
63
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
80
Overall:
Weighted score of the above. [more]
78
Learn more about scoring.

We analyzed this package on Oct 11, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.5.1
  • pana: 0.12.21
  • Flutter: 1.9.1+hotfix.4

Platforms

Detected platforms: Flutter

References Flutter, and has no conflicting libraries.

Maintenance issues and suggestions

Support latest dependencies. (-10 points)

The version constraint in pubspec.yaml does not support the latest published versions for 1 dependency (intl).

Package is pre-v0.1 release. (-10 points)

While nothing is inherently wrong with versions of 0.0.*, it might mean that the author is still experimenting with the general direction of the API.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.1.0 <3.0.0
flutter 0.0.0
intl ^0.15.8 0.15.8 0.16.0
Transitive dependencies
collection 1.14.11 1.14.12
meta 1.1.7
path 1.6.4
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
flutter_test