How to integerate Mpesa Api with android

11,160

Solution 1

If you are still interested, Safaricom recently introduced a REST based interface for consumption of MPesa APIs. They have documentation there, and a pretty good tutorial on how to use it can be found here. For using on your app, you will need a back-end interface to act as a mediator to MPesa, as most of their APIs are asynchronous.

Solution 2

M-PESA API Online Checkout Java / Android How to

Following release of M-PESA API, you will need to create a web service client that does SOAP requests and process responses from API calls

I will provide a quick start guide for java developers geared towards integrating existing Java applications.

Pre-requisites

JDK 7 or latest

Instruction steps

Download Full M-PESA API Guide from safaricom website Read through Developers Guide C2B_OnlineCheckout_V2.0.doc Generate JAX-WS artifacts . Your client will use this api to access the published web service. Generate JAX-WS artifacts We will use wsimport tool is used to parse checkout WSDL file and generate required files.

mkdir src

wsimport -XadditionalHeaders -clientjar safaricom-lipanampesa-onlinecheckout-wsdl.jar -s src -p ke.co.makara.integration.mpesa.lnmocheckout http://safaricom.co.ke/mpesa_online/lnmo_checkout_server.php?wsdl

Check wsimport --help to understand above options; Note the package directory for generated artefacts. WSDL is hosted on the lipa na M-PESA endpoint. For this purpose we use the production url. -clientjar option, new in Java 7, simplifies things. You would otherwise have to use -wsdllocation /META-INF/wsdl/Checkout.wsdl option. And copy the wsdl in META-INF after to mitigate the Java 6 wsimport limitation. See limitation below. Optionally create source jar While inside src folder run command.

jar cvf safaricom-lipanampesa-onlinecheckout-source-wsdl.jar ke/

Next we use the web service artifacts to invoke the web service from a web service client.

Generated artifacts

Service Endpoint Interface (SEI) - LNMOPortType.java Service class - LnmoCheckoutService.java If a wsdl:fault is present in the WSDL, an Exception class Java classes mapped from schema types eg ProcessCheckOutRequest.java If a wsdl:message is present in WSDL, asynchronous response beans eg ProcessCheckOutResponse.java

Web Service Client

Next we use the web service artifacts to invoke the web service from a web service client. This can be a servlet invoked from front-end. Out of scope. For simplicity I will create a java class ~ a standalone console

Create a java class say LNMOCheckoutTester In the Java client application, create an instance of the LnmoCheckoutService service

LnmoCheckoutService lnmoCheckoutService = new LnmoCheckoutService(); // lina na mpesa online checkout instance

The Service class will be created during the build. Obtain a proxy to the service from the service using the getLnmoCheckout() method

LNMOPortType soap = lnmoCheckoutService.getLnmoCheckout();

The port carries the protocol binding and service endpoint address information. Configure the service endpoint Configure the request context properties on the javax.xml.ws.BindingProvider interface

((BindingProvider)soap).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, url + "lnmo_checkout_server.php"); // specifying this property on the RequestContext

JAX-WS provides support for the dynamic invocation of service endpoint operations. I store the web service url in database and append the wsdl endpoint to this url. This configures the endpoint at runtime. Compose our client request M-PESA checkout message This is the message payload

ProcessCheckOutRequest checkOutRequest = objFactory.createProcessCheckOutRequest();
  checkOutRequest.setMERCHANTTRANSACTIONID("54635469064");
  checkOutRequest.setREFERENCEID("TD346534GH");
  checkOutRequest.setAMOUNT(13300);
  checkOutRequest.setMSISDN("0721XXXXXX");
  checkOutRequest.setENCPARAMS("");
  checkOutRequest.setCALLBACKURL("https://makara.co.ke:8443/odt/checkout");
  checkOutRequest.setCALLBACKMETHOD("GET");
  checkOutRequest.setTIMESTAMP(String.valueOf(date.getTime()));

Configure request headers Follow business rules in Safaricom document to build the password. See attached code. For String merchantId, String passkey, Date requestTimeStamp; Convert the concatenated string to bytes, Hash the bytes to get arbitary binary data and Convert the binary data to string use base64

CheckOutHeader requestHeader = objFactory.createCheckOutHeader();
  requestHeader.setMERCHANTID(MERCHANT_ID);
  Date timestamp = new Date();
  String encryptedPassword = getEncryptedPassword(MERCHANT_ID, PASSKEY, timestamp);
  requestHeader.setPASSWORD(encryptedPassword.toUpperCase());
  requestHeader.setTIMESTAMP(String.valueOf(timestamp.getTime()));

Invoke the service endpoint with the dispatch stab-based client soap.processCheckOut(checkOutRequest, requestHeader); Process the response message from the service as per your business requirement

  ProcessCheckOutResponse checkOutResponse = new ProcessCheckOutResponse();
  checkOutResponse.getRETURNCODE();
  checkOutResponse.getDESCRIPTION();
  checkOutResponse.getTRXID();
  checkOutResponse.getCUSTMSG();

Tracing SOAP Traffic

One of the usual steps involved in the debugging of Web Services applications is to inspect the request and response SOAP messages Configure client to dump requests and response with JAX-WS System.setProperty("com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump", "true");

Source code

/*
 * LNMOCheckoutTester.java
 *
 * Nov 20, 2016 Joseph Makara -  Created File to tester Lina Na M-PESA Online checkout
 *
 *
 */
package testMe;

import java.io.*;
import java.security.*;
import java.util.*;
import javax.net.ssl.*;
import javax.xml.ws.*;
import ke.co.makara.integration.mpesa.lnmocheckout.*;
import org.apache.commons.codec.binary.*;

/**
 * @author Joseph Makara
 *
 */
public class LNMOCheckoutTester {

 private static final String PASSKEY = "234fdsghfsg5654dgfhgf6dsfdsafsd43dgfhdgfdgfh74567";
 private static final String MERCHANT_ID = "678fsgd54354";
 private static final String REFERENCE_ID = "";
 private static final String ENDPOINT_URL = "https://safaricom.co.ke/mpesa_online/";
 private static final String CALLBACK_URL = "https://makara.co.ke:8443/odt/checkout";
 private static final String CALLBACK_METHOD = "GET";


 static {
  HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
   @Override
   public boolean verify(String hostname, SSLSession session) {
    if (hostname.equals("safaricom.co.ke")) return true;
    return false;
   }
  });
 }

 /**
  * @param args
  */
 public static void main(String[] args) {

  LNMOPortType soap = outBoundLNMOCheckout(ENDPOINT_URL);

  ObjectFactory objFactory = new ObjectFactory();

  CheckOutHeader requestHeader = objFactory.createCheckOutHeader();
  requestHeader.setMERCHANTID(MERCHANT_ID);
  Date timestamp = new Date();
  String encryptedPassword = getEncryptedPassword(MERCHANT_ID, PASSKEY, timestamp);
  requestHeader.setPASSWORD(encryptedPassword);
  requestHeader.setTIMESTAMP(String.valueOf(timestamp.getTime()));

  ProcessCheckOutRequest checkOutRequest = objFactory.createProcessCheckOutRequest();
  checkOutRequest = processCheckOut(timestamp);

  soap.processCheckOut(checkOutRequest, requestHeader);

  ProcessCheckOutResponse checkOutResponse = new ProcessCheckOutResponse();
  checkOutResponse.getRETURNCODE();
  checkOutResponse.getDESCRIPTION();
  checkOutResponse.getTRXID();
  checkOutResponse.getENCPARAMS();
  checkOutResponse.getCUSTMSG();
 }

 private static ProcessCheckOutRequest processCheckOut(Date date){

  ProcessCheckOutRequest checkOutRequest = new ProcessCheckOutRequest();
  checkOutRequest.setMERCHANTTRANSACTIONID("54635469064");
  checkOutRequest.setREFERENCEID("TD346534GH");
  checkOutRequest.setAMOUNT(3.45);
  checkOutRequest.setMSISDN("0721826284");
  checkOutRequest.setENCPARAMS("");
  checkOutRequest.setCALLBACKURL(CALLBACK_URL);
  checkOutRequest.setCALLBACKMETHOD(CALLBACK_METHOD);
  checkOutRequest.setTIMESTAMP(String.valueOf(date.getTime()));

  return  checkOutRequest;
 }

 /**
  * Convert the concatenated string to bytes
  * Hash the bytes to get arbitary binary data
  * Convert the binary data to string use base64
  *
  * @param merchantId
  * @param passkey
  * @param date
  * @return
  */
 private static String getEncryptedPassword(String merchantId, String passkey, Date date) {
  String encodedPassword = null;
  StringBuilder builder = new StringBuilder(merchantId)
  .append(passkey)
  .append(date.getTime());

  try {
   String sha256 = getSHA256Hash(builder.toString());
   return new String(Base64.encodeBase64(sha256.getBytes("UTF-8")));;
  } catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
   ex.printStackTrace();
  }

  return encodedPassword;
 }

 private static LNMOPortType outBoundLNMOCheckout(String url) {
  System.setProperty("com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump", "true");
  LnmoCheckoutService lnmoCheckoutService = new LnmoCheckoutService();
  LNMOPortType soap = lnmoCheckoutService.getLnmoCheckout();

  ((BindingProvider)soap).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
   url + "lnmo_checkout_server.php");

  return soap;
 }

 private static String getSHA256Hash(String input) throws NoSuchAlgorithmException {
  MessageDigest mDigest = MessageDigest.getInstance("SHA-256");
  byte[] result = mDigest.digest(input.getBytes());
  StringBuffer sb = new StringBuffer();
  for (int i = 0; i < result.length; i++) {
   sb.append(Integer.toString((result[i] & 0xff) + 0x100, 16).substring(1));
  }
  return sb.toString();
 }

}

Sample Soap Request

[HTTP request - https://safaricom.co.ke/mpesa_online/lnmo_checkout_server.php]---
Accept: text/xml, multipart/related
Content-Type: text/xml; charset=utf-8
SOAPAction: ""
User-Agent: JAX-WS RI 2.2.9-b130926.1035 svn-revision#5f6196f2b90e9460065a4c2f4e30e065b245e51e
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
    <S:Header>
        <ns2:CheckOutHeader xmlns:ns2="tns:ns">
            <MERCHANT_ID>F2678987M</MERCHANT_ID>
            <PASSWORD>QQFSOITZB5OJMJTW073/SCWLN5WMDL6LO0FP6DJZ8TQ=</PASSWORD>
            <TIMESTAMP>1479116220855</TIMESTAMP>
        </ns2:CheckOutHeader>
    </S:Header>
    <S:Body>
        <ns2:processCheckOutRequest xmlns:ns2="tns:ns">
            <MERCHANT_TRANSACTION_ID>54635469064</MERCHANT_TRANSACTION_ID>
            <REFERENCE_ID>TD346534GH</REFERENCE_ID>
            <AMOUNT>3.45</AMOUNT>
            <MSISDN>0721826284</MSISDN>
            <ENC_PARAMS></ENC_PARAMS>
            <CALL_BACK_URL>https://makara.co.ke:8443/odt/checkout</CALL_BACK_URL>
            <CALL_BACK_METHOD>lnmo</CALL_BACK_METHOD>
            <TIMESTAMP>1479116220855</TIMESTAMP>
        </ns2:processCheckOutRequest>
    </S:Body>
</S:Envelope>

You will only need to contact Safaricom to get Demo test org details (Merchant ID and PassKey)

Client jar

Source code

Solution 3

The M-Pesa API is currently a SOAP API and SOAP isn't native on Android so it's a bunch of work to integrate the API on your app. I wrote Chowder to help developers handle M-Pesa easily and here's all you have to do:

//Add a new dependency
dependencies {
    compile 'com.toe.chowder:chowder:0.7.6'
}

Create a Chowder instance:

//You can create a single global variable for Chowder like this
Chowder chowder = new Chowder(YourActivity.this, PAYBILL_NUMBER, PASSKEY, this);

//You can also add your callback URL using this constructor
Chowder chowder = new Chowder(YourActivity.this, PAYBILL_NUMBER, callbackUrl ,PASSKEY, this);

Process your payment:

//You can then use processPayment() to process individual payments
chowder.processPayment(amount, phoneNumber, productId);

Confirm payment:

chowder.checkTransactionStatus(PAYBILL_NUMBER, transactionId);

There's more sample code and a test Paybill number and passkey here.

Solution 4

Pesa pi cannot be used for the intended purpose. What pesapi does is take confirmation message from your android mobile and pushes that message to your application.

Message in this context is a message that mpesa sends to the registered mobile number of the payee once a payment is done via lipa na mpesa and pay bill.

What Mutwiri was suggesting, is for you to take the API from mpesa site http://www.safaricom.co.ke/business/sme/m-pesa-payment-solutions/m-pesa-api and build a middle ware for your application.

Pointers: Check out the B2C and C2B- paybill and lipa na mpesa documentation.

Share:
11,160
nafees ahmed
Author by

nafees ahmed

Updated on June 11, 2022

Comments

  • nafees ahmed
    nafees ahmed almost 2 years

    I am making loan app. and I am stuck on this How to Integrate MPESA because I live outside Africa.

    Here is the link PESAPI. I am done with step 3 but now I cant understand what to do next.

    Q 1) Can I integrate it with my Loan app android ?

    Q 2) What to do next please guide me I am stuck on this from last one weak.

  • Edijae Crusar
    Edijae Crusar over 7 years
    this can't be used in android applications because some critical java libraries are not found in android
  • Edijae Crusar
    Edijae Crusar over 7 years
    So you charge for this service
  • Edijae Crusar
    Edijae Crusar over 7 years
    did you really test it in an android application?
  • Steve Mwanzia
    Steve Mwanzia over 7 years
    Kinene, For checkout no, unless you will use my servers but if you like it you will get it for your own use
  • Edijae Crusar
    Edijae Crusar over 7 years
    How can one implement your jar in android? i can see an example. how can i download the jar, install it in my project and use it?
  • Patrice Andala
    Patrice Andala almost 6 years
    How does one get this passkey?