Merge branch 'develop' of https://github.com/frappe/erpnext into mpesa-integration
diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json
index 50fc3bb..51fc3f7 100644
--- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json
+++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "allow_import": 1,
  "allow_rename": 1,
  "autoname": "field:mode_of_payment",
@@ -28,7 +29,7 @@
    "fieldtype": "Select",
    "in_standard_filter": 1,
    "label": "Type",
-   "options": "Cash\nBank\nGeneral"
+   "options": "Cash\nBank\nGeneral\nPhone"
   },
   {
    "fieldname": "accounts",
@@ -45,7 +46,9 @@
  ],
  "icon": "fa fa-credit-card",
  "idx": 1,
- "modified": "2020-09-18 17:26:09.703215",
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2020-09-18 17:57:23.835236",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Mode of Payment",
diff --git a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.json b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.json
index 8dc2628..12e6f5e 100644
--- a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.json
+++ b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.json
@@ -1,313 +1,98 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0,
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2015-12-23 21:31:52.699821", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
+ "actions": [],
+ "creation": "2015-12-23 21:31:52.699821",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "field_order": [
+  "payment_gateway",
+  "payment_channel",
+  "is_default",
+  "column_break_4",
+  "payment_account",
+  "currency",
+  "payment_request_message",
+  "message",
+  "message_examples"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0,
-   "fieldname": "payment_gateway", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0,
+   "fieldname": "payment_gateway",
+   "fieldtype": "Link",
    "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Payment Gateway", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Payment Gateway", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0,
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0,
-   "unique": 0
-  }, 
+   "label": "Payment Gateway",
+   "options": "Payment Gateway",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0,
-   "fieldname": "is_default", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0,
-   "in_list_view": 0, 
-   "in_standard_filter": 0,
-   "label": "Is Default", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0,
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0,
-   "unique": 0
-  }, 
+   "default": "0",
+   "fieldname": "is_default",
+   "fieldtype": "Check",
+   "label": "Is Default"
+  },
   {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0,
-   "fieldname": "column_break_4", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0,
-   "in_list_view": 0, 
-   "in_standard_filter": 0,
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0,
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0,
-   "unique": 0
-  }, 
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0,
-   "fieldname": "payment_account", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0,
+   "fieldname": "payment_account",
+   "fieldtype": "Link",
    "in_list_view": 1,
-   "in_standard_filter": 0,
-   "label": "Payment Account", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Account", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0,
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0,
-   "unique": 0
-  }, 
+   "label": "Payment Account",
+   "options": "Account",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0,
    "fetch_from": "payment_account.account_currency",
    "fieldname": "currency",
-   "fieldtype": "Read Only", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0,
-   "in_list_view": 0, 
-   "in_standard_filter": 0,
-   "label": "Currency", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0,
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0,
-   "unique": 0
-  }, 
+   "fieldtype": "Read Only",
+   "label": "Currency"
+  },
   {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0,
-   "fieldname": "payment_request_message", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0,
-   "in_list_view": 0, 
-   "in_standard_filter": 0,
-   "label": "", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0,
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0,
-   "unique": 0
-  }, 
+   "depends_on": "eval: doc.payment_channel !== \"Phone\"",
+   "fieldname": "payment_request_message",
+   "fieldtype": "Section Break"
+  },
   {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0,
-   "default": "Please click on the link below to make your payment", 
-   "fieldname": "message", 
-   "fieldtype": "Small Text", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0,
-   "in_list_view": 0, 
-   "in_standard_filter": 0,
-   "label": "Default Payment Request Message", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0,
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0,
-   "unique": 0
-  }, 
+   "default": "Please click on the link below to make your payment",
+   "fieldname": "message",
+   "fieldtype": "Small Text",
+   "label": "Default Payment Request Message"
+  },
   {
-   "allow_bulk_edit": 0,
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0,
-   "fieldname": "message_examples", 
-   "fieldtype": "HTML", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0,
-   "in_list_view": 0, 
-   "in_standard_filter": 0,
-   "label": "Message Examples", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "<pre><h5>Message Example</h5>\n\n&lt;p&gt; Thank You for being a part of {{ doc.company }}! We hope you are enjoying the service.&lt;/p&gt;\n\n&lt;p&gt; Please find enclosed the E Bill statement. The outstanding amount is {{ doc.grand_total }}.&lt;/p&gt;\n\n&lt;p&gt; We don't want you to be spending time running around in order to pay for your Bill.<br>After all, life is beautiful and the time you have in hand should be spent to enjoy it!<br>So here are our little ways to help you get more time for life! &lt;/p&gt;\n\n&lt;a href=\"{{ payment_url }}\"&gt; click here to pay &lt;/a&gt;\n\n</pre>\n", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0,
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0,
-   "unique": 0
+   "fieldname": "message_examples",
+   "fieldtype": "HTML",
+   "label": "Message Examples",
+   "options": "<pre><h5>Message Example</h5>\n\n&lt;p&gt; Thank You for being a part of {{ doc.company }}! We hope you are enjoying the service.&lt;/p&gt;\n\n&lt;p&gt; Please find enclosed the E Bill statement. The outstanding amount is {{ doc.grand_total }}.&lt;/p&gt;\n\n&lt;p&gt; We don't want you to be spending time running around in order to pay for your Bill.<br>After all, life is beautiful and the time you have in hand should be spent to enjoy it!<br>So here are our little ways to help you get more time for life! &lt;/p&gt;\n\n&lt;a href=\"{{ payment_url }}\"&gt; click here to pay &lt;/a&gt;\n\n</pre>\n"
+  },
+  {
+   "default": "Email",
+   "fieldname": "payment_channel",
+   "fieldtype": "Select",
+   "label": "Payment Channel",
+   "options": "\nEmail\nPhone"
   }
- ], 
- "has_web_view": 0,
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2018-05-16 22:43:34.970491",
- "modified_by": "Administrator", 
- "module": "Accounts", 
- "name": "Payment Gateway Account", 
- "name_case": "", 
- "owner": "Administrator", 
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2020-09-20 13:30:27.722852",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payment Gateway Account",
+ "owner": "Administrator",
  "permissions": [
   {
-   "amend": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Accounts Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Accounts Manager",
+   "share": 1,
    "write": 1
   }
- ], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0,
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0,
- "track_seen": 0
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC"
 }
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.js b/erpnext/accounts/doctype/payment_request/payment_request.js
index e1e4314..901ef19 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.js
+++ b/erpnext/accounts/doctype/payment_request/payment_request.js
@@ -25,7 +25,7 @@
 })
 
 frappe.ui.form.on("Payment Request", "refresh", function(frm) {
-	if(frm.doc.payment_request_type == 'Inward' &&
+	if(frm.doc.payment_request_type == 'Inward' && frm.doc.payment_channel !== "Phone" &&
 		!in_list(["Initiated", "Paid"], frm.doc.status) && !frm.doc.__islocal && frm.doc.docstatus==1){
 		frm.add_custom_button(__('Resend Payment Email'), function(){
 			frappe.call({
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json
index 8eadfd0..2ee356a 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.json
+++ b/erpnext/accounts/doctype/payment_request/payment_request.json
@@ -48,6 +48,7 @@
   "section_break_7",
   "payment_gateway",
   "payment_account",
+  "payment_channel",
   "payment_order",
   "amended_from"
  ],
@@ -230,6 +231,7 @@
    "label": "Recipient Message And Payment Details"
   },
   {
+   "depends_on": "eval: doc.payment_channel != \"Phone\"",
    "fieldname": "print_format",
    "fieldtype": "Select",
    "label": "Print Format"
@@ -241,6 +243,7 @@
    "label": "To"
   },
   {
+   "depends_on": "eval: doc.payment_channel != \"Phone\"",
    "fieldname": "subject",
    "fieldtype": "Data",
    "in_global_search": 1,
@@ -277,16 +280,18 @@
    "read_only": 1
   },
   {
-   "depends_on": "eval: doc.payment_request_type == 'Inward'",
+   "depends_on": "eval: doc.payment_request_type == 'Inward' || doc.payment_channel != \"Phone\"",
    "fieldname": "section_break_10",
    "fieldtype": "Section Break"
   },
   {
+   "depends_on": "eval: doc.payment_channel != \"Phone\"",
    "fieldname": "message",
    "fieldtype": "Text",
    "label": "Message"
   },
   {
+   "depends_on": "eval: doc.payment_channel != \"Phone\"",
    "fieldname": "message_examples",
    "fieldtype": "HTML",
    "label": "Message Examples",
@@ -347,12 +352,21 @@
    "options": "Payment Request",
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "fetch_from": "payment_gateway_account.payment_channel",
+   "fieldname": "payment_channel",
+   "fieldtype": "Select",
+   "label": "Payment Channel",
+   "options": "\nEmail\nPhone",
+   "read_only": 1
   }
  ],
  "in_create": 1,
+ "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-07-17 14:06:42.185763",
+ "modified": "2020-09-18 12:24:14.178853",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Request",
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index e93ec95..51c090c 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -36,7 +36,7 @@
 			ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
 			if (hasattr(ref_doc, "order_type") \
 					and getattr(ref_doc, "order_type") != "Shopping Cart"):
-				ref_amount = get_amount(ref_doc)
+				ref_amount = get_amount(ref_doc, self.payment_account)
 
 				if existing_payment_request_amount + flt(self.grand_total)> ref_amount:
 					frappe.throw(_("Total Payment Request amount cannot be greater than {0} amount")
@@ -76,11 +76,25 @@
 			or self.flags.mute_email:
 			send_mail = False
 
-		if send_mail:
+		if send_mail and self.payment_channel != "Phone":
 			self.set_payment_request_url()
 			self.send_email()
 			self.make_communication_entry()
 
+		elif self.payment_channel == "Phone":
+			controller = get_payment_gateway_controller(self.payment_gateway)
+			payment_record = dict(
+				reference_doctype="Payment Request",
+				reference_docname=self.name,
+				payment_reference=self.reference_name,
+				grand_total=self.grand_total,
+				sender=self.email_to,
+				currency=self.currency,
+				payment_gateway=self.payment_gateway
+			)
+			controller.validate_transaction_currency(self.currency)
+			controller.request_for_payment(**payment_record)
+
 	def on_cancel(self):
 		self.check_if_payment_entry_exists()
 		self.set_as_cancelled()
@@ -105,13 +119,14 @@
 			return False
 
 	def set_payment_request_url(self):
-		if self.payment_account:
+		if self.payment_account and self.payment_channel != "Phone":
 			self.payment_url = self.get_payment_url()
 
 		if self.payment_url:
 			self.db_set('payment_url', self.payment_url)
 
-		if self.payment_url or not self.payment_gateway_account:
+		if self.payment_url or not self.payment_gateway_account \
+			or (self.payment_gateway_account and self.payment_channel == "Phone"):
 			self.db_set('status', 'Initiated')
 
 	def get_payment_url(self):
@@ -140,10 +155,14 @@
 		})
 
 	def set_as_paid(self):
-		payment_entry = self.create_payment_entry()
-		self.make_invoice()
+		if self.payment_channel == "Phone":
+			self.db_set("status", "Paid")
 
-		return payment_entry
+		else:
+			payment_entry = self.create_payment_entry()
+			self.make_invoice()
+
+			return payment_entry
 
 	def create_payment_entry(self, submit=True):
 		"""create entry"""
@@ -151,7 +170,7 @@
 
 		ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
 
-		if self.reference_doctype == "Sales Invoice":
+		if self.reference_doctype in ["Sales Invoice", "POS Invoice"]:
 			party_account = ref_doc.debit_to
 		elif self.reference_doctype == "Purchase Invoice":
 			party_account = ref_doc.credit_to
@@ -166,8 +185,8 @@
 		else:
 			party_amount = self.grand_total
 
-		payment_entry = get_payment_entry(self.reference_doctype, self.reference_name,
-			party_amount=party_amount, bank_account=self.payment_account, bank_amount=bank_amount)
+		payment_entry = get_payment_entry(self.reference_doctype, self.reference_name, party_amount=party_amount,
+			bank_account=self.payment_account, bank_amount=bank_amount)
 
 		payment_entry.update({
 			"reference_no": self.name,
@@ -255,7 +274,7 @@
 
 			# if shopping cart enabled and in session
 			if (shopping_cart_settings.enabled and hasattr(frappe.local, "session")
-				and frappe.local.session.user != "Guest"):
+				and frappe.local.session.user != "Guest") and self.payment_channel != "Phone":
 
 				success_url = shopping_cart_settings.payment_success_url
 				if success_url:
@@ -280,7 +299,9 @@
 	args = frappe._dict(args)
 
 	ref_doc = frappe.get_doc(args.dt, args.dn)
-	grand_total = get_amount(ref_doc)
+	gateway_account = get_gateway_details(args) or frappe._dict()
+
+	grand_total = get_amount(ref_doc, gateway_account.get("payment_account"))
 	if args.loyalty_points and args.dt == "Sales Order":
 		from erpnext.accounts.doctype.loyalty_program.loyalty_program import validate_loyalty_points
 		loyalty_amount = validate_loyalty_points(ref_doc, int(args.loyalty_points))
@@ -288,8 +309,6 @@
 		frappe.db.set_value("Sales Order", args.dn, "loyalty_amount", loyalty_amount, update_modified=False)
 		grand_total = grand_total - loyalty_amount
 
-	gateway_account = get_gateway_details(args) or frappe._dict()
-
 	bank_account = (get_party_bank_account(args.get('party_type'), args.get('party'))
 		if args.get('party_type') else '')
 
@@ -314,9 +333,11 @@
 			"payment_gateway_account": gateway_account.get("name"),
 			"payment_gateway": gateway_account.get("payment_gateway"),
 			"payment_account": gateway_account.get("payment_account"),
+			"payment_channel": gateway_account.get("payment_channel"),
 			"payment_request_type": args.get("payment_request_type"),
 			"currency": ref_doc.currency,
 			"grand_total": grand_total,
+			"mode_of_payment": args.mode_of_payment,
 			"email_to": args.recipient_id or ref_doc.owner,
 			"subject": _("Payment Request for {0}").format(args.dn),
 			"message": gateway_account.get("message") or get_dummy_message(ref_doc),
@@ -344,7 +365,7 @@
 
 	return pr.as_dict()
 
-def get_amount(ref_doc):
+def get_amount(ref_doc, payment_account=None):
 	"""get amount based on doctype"""
 	dt = ref_doc.doctype
 	if dt in ["Sales Order", "Purchase Order"]:
@@ -356,6 +377,12 @@
 		else:
 			grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate
 
+	elif dt == "POS Invoice":
+		for pay in ref_doc.payments:
+			if pay.type == "Phone" and pay.account == payment_account:
+				grand_total = pay.amount
+				break
+
 	elif dt == "Fees":
 		grand_total = ref_doc.outstanding_amount
 
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
index 3be4304..c43cb79 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js
@@ -201,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/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index 7229aff..5b0822e 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -15,6 +15,7 @@
 
 from erpnext.accounts.doctype.sales_invoice.sales_invoice import SalesInvoice, get_bank_cash_account, update_multi_mode_option
 from erpnext.stock.doctype.serial_no.serial_no import get_pos_reserved_serial_nos
+from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
 
 from six import iteritems
 
@@ -57,6 +58,7 @@
 			against_psi_doc.make_loyalty_point_entry()
 		if self.redeem_loyalty_points and self.loyalty_points:
 			self.apply_loyalty_points()
+		self.check_phone_payments()
 		self.set_status(update=True)
 
 	def on_cancel(self):
@@ -69,6 +71,18 @@
 			against_psi_doc.delete_loyalty_point_entry()
 			against_psi_doc.make_loyalty_point_entry()
 
+	def check_phone_payments(self):
+		for pay in self.payments:
+			if pay.type == "Phone" and pay.amount >= 0:
+				paid_amt = frappe.db.get_value("Payment Request",
+					filters=dict(
+						reference_doctype="POS Invoice", reference_name=self.name,
+						mode_of_payment=pay.mode_of_payment, status="Paid"),
+					fieldname="grand_total")
+
+				if pay.amount != paid_amt:
+					return frappe.throw(_("Payment related to {0} is not completed").format(pay.mode_of_payment))
+
 	def validate_stock_availablility(self):
 		allow_negative_stock = frappe.db.get_value('Stock Settings', None, 'allow_negative_stock')
 
@@ -312,6 +326,32 @@
 			if not pay.account:
 				pay.account = get_bank_cash_account(pay.mode_of_payment, self.company).get("account")
 
+	def create_payment_request(self):
+		for pay in self.payments:
+			if pay.type == "Phone":
+				if pay.amount <= 0:
+					frappe.throw(_("Payment amount cannot be less than or equal to 0"))
+
+				if not self.contact_mobile:
+					frappe.throw(_("Please enter the phone number first"))
+
+				payment_gateway = frappe.db.get_value("Payment Gateway Account", {
+					"payment_account": pay.account,
+				})
+				record = {
+					"payment_gateway": payment_gateway,
+					"dt": "POS Invoice",
+					"dn": self.name,
+					"payment_request_type": "Inward",
+					"party_type": "Customer",
+					"party": self.customer,
+					"mode_of_payment": pay.mode_of_payment,
+					"recipient_id": self.contact_mobile,
+					"submit_doc": True
+				}
+
+				return make_payment_request(**record)
+
 @frappe.whitelist()
 def get_stock_availability(item_code, warehouse):
 	latest_sle = frappe.db.sql("""select qty_after_transaction
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 008f6e8..53677cd 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -796,7 +796,7 @@
 
 	return acc
 
-def create_payment_gateway_account(gateway):
+def create_payment_gateway_account(gateway, payment_channel="Email"):
 	from erpnext.setup.setup_wizard.operations.company_setup import create_bank_account
 
 	company = frappe.db.get_value("Global Defaults", None, "default_company")
@@ -831,7 +831,8 @@
 			"is_default": 1,
 			"payment_gateway": gateway,
 			"payment_account": bank_account.name,
-			"currency": bank_account.account_currency
+			"currency": bank_account.account_currency,
+			"payment_channel": payment_channel
 		}).insert(ignore_permissions=True)
 
 	except frappe.DuplicateEntryError:
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/__init__.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/__init__.py
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/account_balance.html b/erpnext/erpnext_integrations/doctype/mpesa_settings/account_balance.html
new file mode 100644
index 0000000..2c4d4bb
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/account_balance.html
@@ -0,0 +1,28 @@
+
+{% if not jQuery.isEmptyObject(data) %}
+<h5 style="margin-top: 20px;"> {{ __("Balance Details") }} </h5>
+<table class="table table-bordered small">
+	<thead>
+		<tr>
+			<th style="width: 20%">{{ __("Account Type") }}</th>
+			<th style="width: 20%" class="text-right">{{ __("Current Balance") }}</th>
+			<th style="width: 20%" class="text-right">{{ __("Available Balance") }}</th>
+			<th style="width: 20%" class="text-right">{{ __("Reserved Balance") }}</th>
+			<th style="width: 20%" class="text-right">{{ __("Uncleared Balance") }}</th>
+		</tr>
+	</thead>
+	<tbody>
+		{% for(const [key, value] of Object.entries(data)) { %}
+			<tr>
+				<td> {%= key %} </td>
+				<td class="text-right"> {%= value["current_balance"] %} </td>
+				<td class="text-right"> {%= value["available_balance"] %} </td>
+				<td class="text-right"> {%= value["reserved_balance"] %} </td>
+				<td class="text-right"> {%= value["uncleared_balance"] %} </td>
+			</tr>
+		{% } %}
+	</tbody>
+</table>
+{% else %}
+<p style="margin-top: 30px;"> Account Balance Information Not Available. </p>
+{% endif %}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
new file mode 100644
index 0000000..d33b0a7
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py
@@ -0,0 +1,118 @@
+import base64
+import requests
+from requests.auth import HTTPBasicAuth
+import datetime
+
+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
+		if env == "sandbox":
+			self.base_url = sandbox_url
+		else:
+			self.base_url = live_url
+		self.authenticate()
+
+	def authenticate(self):
+		"""
+		This method is used to fetch the access token required by Mpesa.
+
+		Returns:
+			access_token (str): This token is to be used with the Bearer header for further API calls to Mpesa.
+		"""
+		authenticate_uri = "/oauth/v1/generate?grant_type=client_credentials"
+		authenticate_url = "{0}{1}".format(self.base_url, authenticate_uri)
+		r = requests.get(
+			authenticate_url,
+			auth=HTTPBasicAuth(self.app_key, self.app_secret)
+		)
+		self.authentication_token = r.json()['access_token']
+		return r.json()['access_token']
+
+	def get_balance(self, initiator=None, security_credential=None, party_a=None, identifier_type=None,
+					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.
+			command_id (str): AccountBalance.
+			party_a (int): Till number being queried.
+			identifier_type (int): Type of organization receiving the transaction. (MSISDN/Till Number/Organization short code)
+			remarks (str): Comments that are sent along with the transaction(maximum 100 characters).
+			queue_timeout_url (str): The url that handles information of timed out transactions.
+			result_url (str): The url that receives results from M-Pesa api call.
+
+		Returns:
+			OriginatorConverstionID (str): The unique request ID for tracking a transaction.
+			ConversationID (str): The unique request ID returned by mpesa for each request made
+			ResponseDescription (str): Response Description message
+		"""
+
+		payload = {
+			"Initiator": initiator,
+			"SecurityCredential": security_credential,
+			"CommandID": "AccountBalance",
+			"PartyA": party_a,
+			"IdentifierType": identifier_type,
+			"Remarks": remarks,
+			"QueueTimeOutURL": queue_timeout_url,
+			"ResultURL": result_url
+		}
+		headers = {'Authorization': 'Bearer {0}'.format(self.authentication_token), 'Content-Type': "application/json"}
+		saf_url = "{0}{1}".format(self.base_url, "/mpesa/accountbalance/v1/query")
+		r = requests.post(saf_url, headers=headers, json=payload)
+		return r.json()
+
+	def stk_push(self, business_shortcode=None, passcode=None, amount=None, callback_url=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(str): 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(int): The Mobile Number to receive the STK Pin Prompt.
+			description(str): This is any additional information/comment that can be sent along with the request from your system. MAX 13 characters
+
+		Success Response:
+			CustomerMessage(str): Messages that customers can understand.
+			CheckoutRequestID(str): This is a global unique identifier of the processed checkout transaction request.
+			ResponseDescription(str): Describes Success or failure
+			MerchantRequestID(str): This is a global unique Identifier for any submitted payment request.
+			ResponseCode(int): 0 means success all others are error codes. e.g.404.001.03
+
+		Error Reponse:
+			requestId(str): This is a unique requestID for the payment request
+			errorCode(str): This is a predefined code that indicates the reason for request failure.
+			errorMessage(str): This is a predefined code that indicates the reason for request failure.
+		"""
+
+		time = str(datetime.datetime.now()).split(".")[0].replace("-", "").replace(" ", "").replace(":", "")
+		password = "{0}{1}{2}".format(str(business_shortcode), str(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": callback_url,
+			"AccountReference": reference_code,
+			"TransactionDesc": description
+		}
+		headers = {'Authorization': 'Bearer {0}'.format(self.authentication_token), 'Content-Type': "application/json"}
+
+		saf_url = "{0}{1}".format(self.base_url, "/mpesa/stkpush/v1/processrequest")
+		r = requests.post(saf_url, headers=headers, json=payload)
+		return r.json()
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py
new file mode 100644
index 0000000..0499e88
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py
@@ -0,0 +1,53 @@
+import frappe
+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."""
+	pos_field = {
+		"POS Invoice": [
+			{
+				"fieldname": "request_for_payment",
+				"label": "Request for Payment",
+				"fieldtype": "Button",
+				"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"):
+		create_custom_fields(pos_field)
+
+	record_dict = [{
+			"doctype": "POS Field",
+			"fieldname": "contact_mobile",
+			"label": "Mobile No",
+			"fieldtype": "Data",
+			"options": "Phone",
+			"parenttype": "POS Settings",
+			"parent": "POS Settings",
+			"parentfield": "invoice_fields"
+		},
+		{
+			"doctype": "POS Field",
+			"fieldname": "request_for_payment",
+			"label": "Request for Payment",
+			"fieldtype": "Button",
+			"parenttype": "POS Settings",
+			"parent": "POS Settings",
+			"parentfield": "invoice_fields"
+		}
+	]
+	create_pos_settings(record_dict)
+
+def create_pos_settings(record_dict):
+	for record in record_dict:
+		if frappe.db.exists("POS Field", {"fieldname": record.get("fieldname")}):
+			continue
+		frappe.get_doc(record).insert()
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.js b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.js
new file mode 100644
index 0000000..c24a104
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.js
@@ -0,0 +1,33 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Mpesa Settings', {
+	onload_post_render: function(frm) {
+		frm.events.setup_account_balance_html(frm);
+	},
+
+	refresh: function(frm) {
+		frappe.realtime.on("refresh_mpesa_dashboard", function(){
+			frm.reload_doc();
+		});
+	},
+
+	get_account_balance: function(frm) {
+		if (!frm.initiator_name && !frm.security_credentials) return;
+		frappe.call({
+			method: "get_account_balance_info",
+			doc: frm.doc
+		});
+	},
+
+	setup_account_balance_html: function(frm) {
+		$("div").remove(".form-dashboard-section.custom");
+		frm.dashboard.add_section(
+			frappe.render_template('account_balance', {
+				data: JSON.parse(frm.doc.account_balance)
+			})
+		);
+		frm.dashboard.show();
+	}
+
+});
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json
new file mode 100644
index 0000000..fc7b310
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.json
@@ -0,0 +1,135 @@
+{
+ "actions": [],
+ "autoname": "field:payment_gateway_name",
+ "creation": "2020-09-10 13:21:27.398088",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "payment_gateway_name",
+  "consumer_key",
+  "consumer_secret",
+  "initiator_name",
+  "till_number",
+  "sandbox",
+  "column_break_4",
+  "online_passkey",
+  "security_credential",
+  "get_account_balance",
+  "account_balance"
+ ],
+ "fields": [
+  {
+   "fieldname": "payment_gateway_name",
+   "fieldtype": "Data",
+   "label": "Payment Gateway Name",
+   "reqd": 1,
+   "unique": 1
+  },
+  {
+   "fieldname": "consumer_key",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Consumer Key",
+   "reqd": 1
+  },
+  {
+   "fieldname": "consumer_secret",
+   "fieldtype": "Password",
+   "in_list_view": 1,
+   "label": "Consumer Secret",
+   "reqd": 1
+  },
+  {
+   "fieldname": "till_number",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Till Number",
+   "reqd": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "sandbox",
+   "fieldtype": "Check",
+   "label": "Sandbox"
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "online_passkey",
+   "fieldtype": "Password",
+   "label": " Online PassKey",
+   "reqd": 1
+  },
+  {
+   "fieldname": "initiator_name",
+   "fieldtype": "Data",
+   "label": "Initiator Name"
+  },
+  {
+   "fieldname": "security_credential",
+   "fieldtype": "Small Text",
+   "label": "Security Credential"
+  },
+  {
+   "fieldname": "account_balance",
+   "fieldtype": "Long Text",
+   "hidden": 1,
+   "label": "Account Balance",
+   "read_only": 1
+  },
+  {
+   "fieldname": "get_account_balance",
+   "fieldtype": "Button",
+   "label": "Get Account Balance"
+  }
+ ],
+ "links": [],
+ "modified": "2020-09-25 20:21:38.215494",
+ "modified_by": "Administrator",
+ "module": "ERPNext Integrations",
+ "name": "Mpesa Settings",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Accounts Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Accounts User",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
new file mode 100644
index 0000000..1d318bb
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py
@@ -0,0 +1,196 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies and contributors
+# For license information, please see license.txt
+
+
+from __future__ import unicode_literals
+from json import loads, dumps
+
+import frappe
+from frappe.model.document import Document
+from frappe import _
+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 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
+
+class MpesaSettings(Document):
+	supported_currencies = ["KES"]
+
+	def validate_transaction_currency(self, currency):
+		if currency not in self.supported_currencies:
+			frappe.throw(_("Please select another payment method. Mpesa does not support transactions in currency '{0}'").format(currency))
+
+	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, payment_type="Phone")
+		call_hook_method('payment_gateway_enabled', gateway='Mpesa-' + self.payment_gateway_name, payment_channel="Phone")
+
+	def request_for_payment(self, **kwargs):
+		response = frappe._dict(generate_stk_push(**kwargs))
+		self.handle_api_response("CheckoutRequestID", kwargs, response)
+
+	def get_account_balance_info(self):
+		payload = dict(
+			reference_doctype="Mpesa Settings",
+			reference_docname=self.name,
+			doc_details=vars(self)
+		)
+		response = frappe._dict(get_account_balance(payload))
+		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")
+			error = response
+		else:
+			# global checkout id used as request name
+			req_name = getattr(response, global_id)
+			error = None
+
+		create_request_log(request_dict, "Host", "Mpesa", req_name, error)
+
+		if error:
+			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"
+
+		mpesa_settings = frappe.get_doc("Mpesa Settings", args.payment_gateway[6:])
+		env = "production" if not mpesa_settings.sandbox else "sandbox"
+
+		connector = MpesaConnector(env=env,
+			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=mpesa_settings.till_number,
+			phone_number=mobile_number, description="POS Payment")
+
+		return response
+
+	except Exception:
+		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 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)
+	transaction_data = frappe._dict(loads(request.data))
+
+	if transaction_response['ResultCode'] == 0:
+		if request.reference_doctype and request.reference_docname:
+			try:
+				doc = frappe.get_doc(request.reference_doctype,
+					request.reference_docname)
+				doc.run_method("on_payment_authorized", 'Completed')
+
+				item_response = transaction_response["CallbackMetadata"]["Item"]
+				mpesa_receipt = fetch_param_value(item_response, "MpesaReceiptNumber", "Name")
+				frappe.db.set_value("POS Invoice", doc.reference_name, "mpesa_receipt_number", mpesa_receipt)
+				request.handle_success(transaction_response)
+			except Exception:
+				request.handle_failure(transaction_response)
+				frappe.log_error(frappe.get_traceback())
+
+	else:
+		request.handle_failure(transaction_response)
+
+	frappe.publish_realtime('process_phone_payment', doctype="POS Invoice",
+		docname=transaction_data.payment_reference, user=request.owner, message=transaction_response)
+
+def get_account_balance(request_payload):
+	"""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"
+		connector = MpesaConnector(env=env,
+			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"
+
+		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
+	except Exception:
+		frappe.log_error(title=_("Account Balance Processing Error"))
+		frappe.throw(title=_("Error"), message=_("Please check your configuration and try again"))
+
+@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", "")
+	request = frappe.get_doc("Integration Request", conversation_id)
+
+	if request.status == "Completed":
+		return
+
+	transaction_data = frappe._dict(loads(request.data))
+	frappe.logger().debug(account_balance_response)
+
+	if account_balance_response["ResultCode"] == 0:
+		try:
+			result_params = account_balance_response["ResultParameters"]["ResultParameter"]
+
+			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)
+
+			request.handle_success(account_balance_response)
+			frappe.publish_realtime("refresh_mpesa_dashboard")
+		except Exception:
+			request.handle_failure(account_balance_response)
+			frappe.log_error(title=_("Mpesa Account Balance Processing Error"), message=account_balance_response)
+	else:
+		request.handle_failure(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=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)
+
+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
diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
new file mode 100644
index 0000000..4aa970e
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestMpesaSettings(unittest.TestCase):
+	pass
diff --git a/erpnext/erpnext_integrations/utils.py b/erpnext/erpnext_integrations/utils.py
index 84f7f5a..e278fd7 100644
--- a/erpnext/erpnext_integrations/utils.py
+++ b/erpnext/erpnext_integrations/utils.py
@@ -3,6 +3,7 @@
 from frappe import _
 import base64, hashlib, hmac
 from six.moves.urllib.parse import urlparse
+from erpnext import get_default_company
 
 def validate_webhooks_request(doctype,  hmac_key, secret_key='secret'):
 	def innerfn(fn):
@@ -41,3 +42,22 @@
 	server_url = '{uri.scheme}://{uri.netloc}/api/method/{endpoint}'.format(uri=urlparse(url), endpoint=endpoint)
 
 	return server_url
+
+def create_mode_of_payment(gateway, payment_type="General"):
+	payment_gateway_account = frappe.db.get_value("Payment Gateway Account", {
+			"payment_gateway": gateway
+		}, ['payment_account'])
+
+	if not frappe.db.exists("Mode of Payment", gateway) and payment_gateway_account:
+		mode_of_payment = frappe.get_doc({
+			"doctype": "Mode of Payment",
+			"mode_of_payment": gateway,
+			"enabled": 1,
+			"type": payment_type,
+			"accounts": [{
+				"doctype": "Mode of Payment Account",
+				"company": get_default_company(),
+				"default_account": payment_gateway_account
+			}]
+		})
+		mode_of_payment.insert(ignore_permissions=True)
\ No newline at end of file
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 77310cd..5b45c22 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -729,4 +729,5 @@
 erpnext.patches.v13_0.rename_issue_doctype_fields
 erpnext.patches.v13_0.change_default_pos_print_format
 erpnext.patches.v13_0.set_youtube_video_id
-erpnext.patches.v13_0.print_uom_after_quantity_patch
\ No newline at end of file
+erpnext.patches.v13_0.print_uom_after_quantity_patch
+erpnext.patches.v13_0.set_payment_channel_in_payment_gateway_account
diff --git a/erpnext/patches/v13_0/set_payment_channel_in_payment_gateway_account.py b/erpnext/patches/v13_0/set_payment_channel_in_payment_gateway_account.py
new file mode 100644
index 0000000..edca238
--- /dev/null
+++ b/erpnext/patches/v13_0/set_payment_channel_in_payment_gateway_account.py
@@ -0,0 +1,17 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	"""Set the payment gateway account as Email for all the existing payment channel."""
+	doc_meta = frappe.get_meta("Payment Gateway Account")
+	if doc_meta.get_field("payment_channel"):
+		return
+
+	frappe.reload_doc("Accounts", "doctype", "Payment Gateway Account")
+	set_payment_channel_as_email()
+
+def set_payment_channel_as_email():
+	frappe.db.sql("""
+		UPDATE `tabPayment Gateway Account`
+		SET `payment_channel` = "Email"
+	""")
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js
index 7f0cabe..ec886d7 100644
--- a/erpnext/selling/page/point_of_sale/pos_payment.js
+++ b/erpnext/selling/page/point_of_sale/pos_payment.js
@@ -9,8 +9,8 @@
 	}
 
 	init_component() {
-        this.prepare_dom();
-        this.initialize_numpad();
+		this.prepare_dom();
+		this.initialize_numpad();
 		this.bind_events();
 		this.attach_shortcuts();
 		
@@ -18,32 +18,32 @@
 
 	prepare_dom() {
 		this.wrapper.append(
-            `<section class="col-span-6 flex shadow rounded payment-section bg-white mx-h-70 h-100 d-none">
+			`<section class="col-span-6 flex shadow rounded payment-section bg-white mx-h-70 h-100 d-none">
 				<div class="flex flex-col p-16 pt-8 pb-8 w-full">
 					<div class="text-grey mb-6 payment-section no-select pointer">
 						PAYMENT METHOD<span class="octicon octicon-chevron-down collapse-indicator"></span>
 					</div>
 					<div class="payment-modes flex flex-wrap"></div>
 					<div class="invoice-details-section"></div>
-                    <div class="flex mt-auto justify-center w-full">
-                        <div class="flex flex-col justify-center flex-1 ml-4">
-                            <div class="flex w-full">
-                                <div class="totals-remarks items-end justify-end flex flex-1">
-                                    <div class="remarks text-md-0 text-grey mr-auto"></div>
-                                    <div class="totals flex justify-end pt-4"></div>
-                                </div>
-                                <div class="number-pad w-40 mb-4 ml-8 d-none"></div>
-                            </div>
-                            <div class="flex items-center justify-center mt-4 submit-order h-16 w-full rounded bg-primary text-md text-white no-select pointer text-bold">
-                                Complete Order
+					<div class="flex mt-auto justify-center w-full">
+							<div class="flex flex-col justify-center flex-1 ml-4">
+							<div class="flex w-full">
+								<div class="totals-remarks items-end justify-end flex flex-1">
+									<div class="remarks text-md-0 text-grey mr-auto"></div>
+									<div class="totals flex justify-end pt-4"></div>
+								</div>
+								<div class="number-pad w-40 mb-4 ml-8 d-none"></div>
+							</div>
+							<div class="flex items-center justify-center mt-4 submit-order h-16 w-full rounded bg-primary text-md text-white no-select pointer text-bold">
+								Complete Order
 							</div>
 							<div class="order-time flex items-center justify-end mt-2 pt-2 pb-2 w-full text-md-0 text-grey no-select pointer d-none"></div>
-                        </div>
-                    </div>
-                </div>
-            </section>`
-        )
-        this.$component = this.wrapper.find('.payment-section');
+						</div>
+					</div>
+				</div>
+			</section>`
+		)
+		this.$component = this.wrapper.find('.payment-section');
 		this.$payment_modes = this.$component.find('.payment-modes');
 		this.$totals_remarks = this.$component.find('.totals-remarks');
 		this.$totals = this.$component.find('.totals');
@@ -174,6 +174,24 @@
 			}
 		})
 
+		frappe.realtime.on("process_phone_payment", function(data) {
+			frappe.dom.unfreeze();
+			cur_frm.reload_doc();
+			let message = data["ResultDesc"];
+			let title = __("Payment Failed");
+
+			if (data["ResultCode"] == 0) {
+				title = __("Payment Received");
+				$('.btn.btn-xs.btn-default[data-fieldname=request_for_payment]').html(`Payment Received`)
+				me.events.submit_invoice();
+			}
+
+			frappe.msgprint({
+				"message": message,
+				"title": title
+			});
+		});
+
 		this.$payment_modes.on('click', '.shortcut', function(e) {
 			const value = $(this).attr('data-value');
 			me.selected_mode.set_value(value);
@@ -509,5 +527,5 @@
 
 	toggle_component(show) {
 		show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none');
-    }
+	}
  }
\ No newline at end of file
diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js
index 21fa4c3..20c6342 100644
--- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js
+++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js
@@ -7,6 +7,10 @@
 			frm.fields_dict.quotation_series.df.options = frm.doc.__onload.quotation_series;
 			frm.refresh_field("quotation_series");
 		}
+
+		frm.set_query('payment_gateway_account', function() {
+			return { 'filters': { 'payment_channel': "Email" } };
+		});
 	},
 	enabled: function(frm) {
 		if (frm.doc.enabled === 1) {