Try ERPNext Buy Support Partners Foundation

M-pesa Integration

First, a few pointers.
https://www.safaricom.co.ke/personal/m-pesa

https://developer.safaricom.co.ke/docs#introduction

I’m incorporating this into ERPNext. How would I achieve the below in erpnext? I suppose the below will still work(currently getting an ImportError)

def authenticate(self):

"""To make Mpesa API calls, you will need to authenticate your app. This method is used to fetch the access token

required by Mpesa. Mpesa supports client_credentials grant type. To authorize your API calls to Mpesa,

you will need a Basic Auth over HTTPS authorization token. The Basic Auth string is a base64 encoded string

of your app's client key and client secret.

    **Args:**

        - env (str): Current app environment. Options: sandbox, production.

        - app_key (str): The app key obtained from the developer portal.

        - app_secret (str): The app key obtained from the developer portal.

        - sandbox_url (str): Base Mpesa sandbox url.

        - production_url (str): Base Mpesa production url.

    **Returns:**

        - access_token (str): This token is to be used with the Bearer header for further API calls to Mpesa.

    """

mpesa_settings = self.get_mpesa_settings()

base_mpesa_url = mpesa_settings["base_mpesa_url"]

authenticate_uri = "/oauth/v1/generate?grant_type=client_credentials"

authenticate_url = "{0}{1}".format(base_mpesa_url, authenticate_uri)

try:

    r = requests.get(authenticate_url, auth=HTTPBasicAuth(

        mpesa_settings["api_key"], mpesa_settings["api_secret"]))

except Exception as e:

    r = requests.get(authenticate_url, auth=HTTPBasicAuth(

        mpesa_settings["api_key"], mpesa_settings["api_secret"]), verify=False)

self.token = r.json()['access_token']

return r.json()['access_token']
1 Like

For now started with this

@frappe.whitelist(allow_guest=True)

def stk_push(self, business_shortcode=None, amount=None, reference_code=None,

        phone_number=None, description=None):

"""This method uses Mpesa's Express API to initiate online payment on behalf of a customer..

**Args:**

- business_shortcode (int): The short code of the organization.

- passcode (str): Get from developer portal

- amount (int): The amount being transacted

- callback_url (str): A CallBack URL is a valid secure URL that is used to receive notifications from M-Pesa API.

- reference_code: Account Reference: This is an Alpha-Numeric parameter that is defined by your system as an Identifier of the transaction for CustomerPayBillOnline transaction type.

- phone_number: The Mobile Number to receive the STK Pin Prompt.

- description: This is any additional information/comment that can be sent along with the request from your system. MAX 13 characters

**Returns:**

- CustomerMessage (str):

- CheckoutRequestID (str):

- ResponseDescription (str):

- MerchantRequestID (str):

- ResponseCode (str):

"""

mpesa_settings = self.get_mpesa_settings()

authentication_token = self.authenticate()

time = str(datetime.datetime.now()).split(".")[0].replace(

    "-", "").replace(" ", "").replace(":", "")

password = "{0}{1}{2}".format(str(business_shortcode), str(mpesa_settings["passcode"]), time)

encoded = base64.b64encode(bytes(password, encoding='utf8'))

payload = {

    "BusinessShortCode": business_shortcode,

    "Password": encoded.decode("utf-8"),

    "Timestamp": time,

    "TransactionType": "CustomerPayBillOnline",

    "Amount": amount,

    "PartyA": int(phone_number),

    "PartyB": business_shortcode,

    "PhoneNumber": int(phone_number),

    "CallBackURL": mpesa_settings["redirect_to"],

    "AccountReference": reference_code,

    "TransactionDesc": description

    }

headers = {'Authorization': 'Bearer {0}'.format(

    authentication_token), 'Content-Type': "application/json"}

base_mpesa_url = mpesa_settings["base_mpesa_url"]

mpesa_url = "{0}{1}".format(

    base_mpesa_url, "/mpesa/stkpush/v1/processrequest")

try:

    r = requests.post(mpesa_url, headers=headers, json=payload)

except Exception as e:

    r = requests.post(mpesa_url, headers=headers, json=payload, verify=False)

return r.json()

The imports

from future import unicode_literals

import frappe

import json,datetime

import requests

from requests.auth import HTTPBasicAuth

import base64

from frappe import _

from frappe.utils.password import get_decrypted_password

from six.moves.urllib.parse import urlencode

from frappe.model.document import Document

from frappe.utils import get_url, call_hook_method, cint, get_timestamp

from frappe.integrations.utils import make_get_request, make_post_request, create_request_log

This will be called on the POS and payment entry(of type receive).

1 Like

Did this work for you?

So far, I can send stk push and receive replies from Payment Entry. I’m in the process of incorporating the same on the POS.

This is cool. I together with a few colleagues would be interested in starting a Kenya/EAC community chapter where we can collaborate on this and more stuff specific to this. What do you guys think?

This would be a welcome move.
Been looking for a way to organize such a community.
I initially started by selling ERPNext to partners/implementers I knew from my Dynamics days.
Let’s do it.

A great move there!

How do we go about this?

Created a Telegram group here: https://t.me/erpnext_eastern_africa_community

We can start there.

Thank you! This should work

I will join, from Kigali