fix: process transaction callback
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
index bedf5e5..c43cb79 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
@@ -142,23 +142,6 @@
 			frm: cur_frm
 		})
 	},
-
-	request_for_payment: function (frm) {
-		frm.save().then(() => {
-			frappe.dom.freeze();
-			frappe.call({
-				method: 'create_payment_request',
-				doc: frm.doc,
-			})
-				.fail(() => {
-					frappe.dom.unfreeze();
-					frappe.msgprint('Payment request failed');
-				})
-				.then(() => {
-					frappe.msgprint('Payment request sent successfully');
-				});
-		});
-	}
 })
 
 $.extend(cur_frm.cscript, new erpnext.selling.POSInvoiceController({ frm: cur_frm }))
@@ -218,5 +201,22 @@
 			}
 			frm.set_value("loyalty_amount", loyalty_amount);
 		}
+	},
+
+	request_for_payment: function (frm) {
+		frm.save().then(() => {
+			frappe.dom.freeze();
+			frappe.call({
+				method: 'create_payment_request',
+				doc: frm.doc,
+			})
+				.fail(() => {
+					frappe.dom.unfreeze();
+					frappe.msgprint('Payment request failed');
+				})
+				.then(() => {
+					frappe.msgprint('Payment request sent successfully');
+				});
+		});
 	}
 });
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
index 4780688..1cff3c6 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
@@ -279,8 +279,7 @@
    "fieldtype": "Check",
    "label": "Is Return (Credit Note)",
    "no_copy": 1,
-   "print_hide": 1,
-   "set_only_once": 1
+   "print_hide": 1
   },
   {
    "fieldname": "column_break1",
@@ -461,7 +460,7 @@
   },
   {
    "fieldname": "contact_mobile",
-   "fieldtype": "Small Text",
+   "fieldtype": "Data",
    "hidden": 1,
    "label": "Mobile No",
    "read_only": 1
@@ -1579,10 +1578,9 @@
   }
  ],
  "icon": "fa fa-file-text",
- "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-09-07 12:43:09.138720",
+ "modified": "2020-09-28 16:51:24.641755",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Invoice",
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
index d79cdaa..dced7b0 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
@@ -6,6 +6,7 @@
 class MpesaConnector():
 	def __init__(self, env="sandbox", app_key=None, app_secret=None, sandbox_url="https://sandbox.safaricom.co.ke",
 		live_url="https://safaricom.co.ke"):
+		"""Setup configuration for Mpesa connector and generate new access token."""
 		self.env = env
 		self.app_key = app_key
 		self.app_secret = app_secret
@@ -38,6 +39,7 @@
 					remarks=None, queue_timeout_url=None,result_url=None):
 		"""
 		This method uses Mpesa's Account Balance API to to enquire the balance on a M-Pesa BuyGoods (Till Number).
+
 		Args:
 			initiator (str): Username used to authenticate the transaction.
 			security_credential (str): Generate from developer portal.
@@ -73,6 +75,7 @@
 				 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
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py
index 0d3912e..5d32a1c 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py
@@ -2,9 +2,7 @@
 from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 
 def create_custom_pos_fields():
-	"""
-		Create custom fields corresponding to POS Settings and POS Invoice
-	"""
+	"""Create custom fields corresponding to POS Settings and POS Invoice."""
 	pos_field = {
 		"POS Invoice": [
 			{
@@ -14,6 +12,13 @@
 				"hidden": 1,
 				"insert_after": "contact_email"
 			},
+			{
+				"fieldname": "Mpesa Receipt Number",
+				"label": "mpesa_receipt_number",
+				"fieldtype": "Data",
+				"read_only": 1,
+				"insert_after": "company"
+			}
 		]
 	}
 	if not frappe.get_meta("POS Invoice").has_field("request_for_payment"):
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.js b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.js
index 239a0bc..7742a45 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.js
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.js
@@ -15,7 +15,6 @@
 	},
 
 	setup_account_balance_html: function(frm) {
-		console.log(frm.doc.account_balance)
 		$("div").remove(".form-dashboard-section.custom");
 		frm.dashboard.add_section(
 			frappe.render_template('account_balance', {
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
index 3af0baa..8fc05c6 100644
--- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
@@ -9,10 +9,9 @@
 import frappe
 from frappe.model.document import Document
 from frappe import _
-from frappe.utils import call_hook_method
+from frappe.utils import call_hook_method, fmt_money
 from frappe.integrations.utils import create_request_log, create_payment_gateway
 from frappe.utils import get_request_site_address
-from frappe.utils import get_request_site_address
 from erpnext.erpnext_integrations.utils import create_mode_of_payment
 from erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_connector import MpesaConnector
 from erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_custom_fields import create_custom_pos_fields
@@ -27,7 +26,7 @@
 	def on_update(self):
 		create_custom_pos_fields()
 		create_payment_gateway('Mpesa-' + self.payment_gateway_name, settings='Mpesa Settings', controller=self.payment_gateway_name)
-		create_mode_of_payment('Mpesa-' + self.payment_gateway_name)
+		create_mode_of_payment('Mpesa-' + self.payment_gateway_name, payment_type="Phone")
 		call_hook_method('payment_gateway_enabled', gateway='Mpesa-' + self.payment_gateway_name, payment_channel="Phone")
 
 	def request_for_payment(self, **kwargs):
@@ -44,6 +43,8 @@
 		self.handle_api_response("ConversationID", payload, response)
 
 	def handle_api_response(self, global_id, request_dict, response):
+		"""Response received from API calls returns a global identifier for each transaction,
+			this code is returned during the callback"""
 		# check error response
 		if getattr(response, "requestId"):
 			req_name = getattr(response, "requestId")
@@ -59,6 +60,7 @@
 			frappe.throw(_(getattr(response, "errorMessage")), title=_("Transaction Error"))
 
 def generate_stk_push(**kwargs):
+	"""Generate stk push by making a API call to the stk push API."""
 	args = frappe._dict(kwargs)
 	try:
 		callback_url = get_request_site_address(True) + "/api/method/erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings.verify_transaction"
@@ -70,6 +72,8 @@
 			app_key=mpesa_settings.consumer_key,
 			app_secret=mpesa_settings.get_password("consumer_secret"))
 
+		mobile_number = sanitize_mobile_number(args.sender)
+
 		response = connector.stk_push(business_shortcode=mpesa_settings.till_number,
 			passcode=mpesa_settings.get_password("online_passkey"), amount=args.grand_total,
 			callback_url=callback_url, reference_code=args.payment_request_name,
@@ -81,10 +85,15 @@
 		frappe.log_error(title=_("Mpesa Express Transaction Error"))
 		frappe.throw(_("Issue detected with Mpesa configuration, check the error logs for more details"), title=_("Mpesa Express Error"))
 
+def sanitize_mobile_number(number):
+	"""Add country code and strip leading zeroes from the phone number."""
+	return "254" + str(number).lstrip("0")
+
 @frappe.whitelist(allow_guest=True)
 def verify_transaction(**kwargs):
-	""" Verify the transaction result received via callback """
+	""" Verify the transaction result received via callback from stk """
 	transaction_response = frappe._dict(kwargs["Body"]["stkCallback"])
+	frappe.logger().debug(transaction_response)
 
 	checkout_id = getattr(transaction_response, "CheckoutRequestID", "")
 	request = frappe.get_doc("Integration Request", checkout_id)
@@ -93,9 +102,13 @@
 	if transaction_response['ResultCode'] == 0:
 		if transaction_data.reference_doctype and transaction_data.reference_docname:
 			try:
-				frappe.get_doc(transaction_data.reference_doctype,
+				doc = frappe.get_doc(transaction_data.reference_doctype,
 					transaction_data.reference_docname).run_method("on_payment_authorized", 'Completed')
-				request.process_response('error', transaction_response)
+
+				item_response = transaction_response["CallbackMetadata"]["Item"]
+				mpesa_receipt = fetch_param_value(item_response, "MpesaReceiptNumber", "Name")
+				frappe.db.set_value("POS Invoice", doc.reference_docname, "mpesa_receipt_number", mpesa_receipt)
+				request.process_response('output', transaction_response)
 			except Exception:
 				request.process_response('error', transaction_response)
 				frappe.log_error(frappe.get_traceback())
@@ -107,7 +120,7 @@
 		docname=transaction_data.reference_docname, user=request.owner, message=transaction_response)
 
 def get_account_balance(request_payload):
-	""" Call account balance API to send the request to the Mpesa Servers """
+	"""Call account balance API to send the request to the Mpesa Servers."""
 	try:
 		mpesa_settings = frappe.get_doc("Mpesa Settings", request_payload.get("reference_docname"))
 		env = "production" if not mpesa_settings.sandbox else "sandbox"
@@ -115,8 +128,7 @@
 			app_key=mpesa_settings.consumer_key,
 			app_secret=mpesa_settings.get_password("consumer_secret"))
 
-		# callback_url = get_request_site_address(True) + "/api/method/erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings.process_balance_info"
-		callback_url = "https://b014ca8e7957.ngrok.io/api/method/erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings.process_balance_info"
+		callback_url = get_request_site_address(True) + "/api/method/erpnext.erpnext_integrations.doctype.mpesa_settings.mpesa_settings.process_balance_info"
 
 		response = connector.get_balance(mpesa_settings.initiator_name, mpesa_settings.security_credential, mpesa_settings.till_number, 4, mpesa_settings.name, callback_url, callback_url)
 		return response
@@ -126,7 +138,8 @@
 
 @frappe.whitelist(allow_guest=True)
 def process_balance_info(**kwargs):
-
+	"""Process and store account balance information received via callback
+		from the account balance API call."""
 	account_balance_response = frappe._dict(kwargs["Result"])
 
 	conversation_id = getattr(account_balance_response, "ConversationID", "")
@@ -141,10 +154,9 @@
 	if account_balance_response["ResultCode"] == 0:
 		try:
 			result_params = account_balance_response["ResultParameters"]["ResultParameter"]
-			for param in result_params:
-				if param["Key"] == "AccountBalance":
-					balance_info = param["Value"]
-					balance_info = convert_to_json(balance_info)
+
+			balance_info = fetch_param_value(result_params, "AccountBalance", "Key")
+			balance_info = convert_to_json(balance_info)
 
 			ref_doc = frappe.get_doc(transaction_data.reference_doctype, transaction_data.reference_docname)
 			ref_doc.db_set("account_balance", balance_info)
@@ -157,13 +169,28 @@
 		request.process_response('error', account_balance_response)
 
 def convert_to_json(balance_info):
+	"""
+	Convert string to json
+
+	e.g: '''Working Account|KES|481000.00|481000.00|0.00|0.00'''
+	=> {'Working Account': {'current_balance': '481000.00',
+		'available_balance': '481000.00',
+		'reserved_balance': '0.00',
+		'uncleared_balance': '0.00'}
+	"""
 	balance_dict = frappe._dict()
 	for account_info in balance_info.split("&"):
 		account_info = account_info.split('|')
 		balance_dict[account_info[0]] = dict(
-			current_balance=account_info[2],
-			available_balance=account_info[3],
-			reserved_balance=account_info[4],
-			uncleared_balance=account_info[5]
+			current_balance=fmt_money(account_info[2], currency="KES"),
+			available_balance=fmt_money(account_info[3], currency="KES"),
+			reserved_balance=fmt_money(account_info[4], currency="KES"),
+			uncleared_balance=fmt_money(account_info[5], currency="KES")
 		)
-	return dumps(balance_dict)
\ No newline at end of file
+	return dumps(balance_dict)
+
+def fetch_param_value(response, key, key_field):
+	"""Fetch the specified key from list of dictionary. Key is identified via the key field"""
+	for param in response:
+		if param[key_field] == key:
+			return param["Value"]
\ No newline at end of file