-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b8dced5
Showing
22 changed files
with
478 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
# Byte-compiled / optimized / DLL files | ||
__pycache__/ | ||
*.py[cod] | ||
*$py.class | ||
|
||
# C extensions | ||
*.so | ||
|
||
# Distribution / packaging | ||
.Python | ||
env/ | ||
build/ | ||
develop-eggs/ | ||
dist/ | ||
downloads/ | ||
eggs/ | ||
.eggs/ | ||
lib/ | ||
lib64/ | ||
parts/ | ||
sdist/ | ||
var/ | ||
*.egg-info/ | ||
.installed.cfg | ||
*.egg | ||
.ropeproject/ | ||
|
||
# PyInstaller | ||
# Usually these files are written by a python script from a template | ||
# before PyInstaller builds the exe, so as to inject date/other infos into it. | ||
*.manifest | ||
*.spec | ||
|
||
# Installer logs | ||
pip-log.txt | ||
pip-delete-this-directory.txt | ||
|
||
# Unit test / coverage reports | ||
htmlcov/ | ||
.tox/ | ||
.coverage | ||
.coverage.* | ||
.cache | ||
nosetests.xml | ||
coverage.xml | ||
*,cover | ||
.hypothesis/ | ||
|
||
# Translations | ||
*.mo | ||
|
||
# Django stuff: | ||
*.log | ||
|
||
# Sphinx documentation | ||
docs/_build/ | ||
|
||
# PyBuilder | ||
target/ | ||
|
||
#Ipython Notebook | ||
.ipynb_checkpoints |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
Copyright Current year Emmanuel Nyachoke | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
recursive-include pretix_mpesa/static * | ||
recursive-include pretix_mpesa/templates * | ||
recursive-include pretix_mpesa/locale * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
all: localecompile | ||
LNGS:=`find pretix_mpesa/locale/ -mindepth 1 -maxdepth 1 -type d -printf "-l %f "` | ||
|
||
localecompile: | ||
django-admin compilemessages | ||
|
||
localegen: | ||
django-admin makemessages --keep-pot -i build -i dist -i "*egg*" $(LNGS) | ||
|
||
.PHONY: all localecompile localegen |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
Mpesa mobile payments | ||
========================== | ||
|
||
This is a plugin for `pretix`_. | ||
|
||
Development setup | ||
----------------- | ||
|
||
1. Make sure that you have a working `pretix development setup`_. | ||
|
||
2. Clone this repository, eg to ``local/pretix-mpesa``. | ||
|
||
3. Activate the virtual environment you use for pretix development. | ||
|
||
4. Execute ``python setup.py develop`` within this directory to register this application with pretix's plugin registry. | ||
|
||
5. Execute ``make`` within this directory to compile translations. | ||
|
||
6. Restart your local pretix server. You can now use the plugin from this repository for your events by enabling it in | ||
the 'plugins' tab in the settings. | ||
|
||
|
||
License | ||
------- | ||
|
||
Copyright Current year Emmanuel Nyachoke | ||
|
||
Released under the terms of the Apache License 2.0 | ||
|
||
|
||
.. _pretix: https://github.com/pretix/pretix | ||
.. _pretix development setup: https://docs.pretix.eu/en/latest/development/setup.html |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
from django.apps import AppConfig | ||
from django.utils.translation import ugettext_lazy | ||
|
||
|
||
class PluginApp(AppConfig): | ||
name = 'pretix_mpesa' | ||
verbose_name = 'Mpesa mobile payments' | ||
|
||
class PretixPluginMeta: | ||
name = ugettext_lazy('Mpesa mobile payments') | ||
author = 'Emmanuel Nyachoke' | ||
description = ugettext_lazy('Adds safaricom mpesa payments to pretix') | ||
visible = True | ||
version = '1.0.0' | ||
|
||
def ready(self): | ||
from . import signals # NOQA | ||
|
||
|
||
default_app_config = 'pretix_mpesa.PluginApp' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
msgid "" | ||
msgstr "" | ||
"Project-Id-Version: \n" | ||
"Report-Msgid-Bugs-To: \n" | ||
"POT-Creation-Date: 2017-03-07 19:01+0100\n" | ||
"PO-Revision-Date: \n" | ||
"Last-Translator: Emmanuel Nyachoke\n" | ||
"Language-Team: \n" | ||
"Language: de\n" | ||
"MIME-Version: 1.0\n" | ||
"Content-Type: text/plain; charset=UTF-8\n" | ||
"Content-Transfer-Encoding: 8bit\n" |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
msgid "" | ||
msgstr "" | ||
"Project-Id-Version: \n" | ||
"Report-Msgid-Bugs-To: \n" | ||
"POT-Creation-Date: 2017-03-07 19:01+0100\n" | ||
"PO-Revision-Date: \n" | ||
"Last-Translator: Emmanuel Nyachoke\n" | ||
"Language-Team: \n" | ||
"Language: de\n" | ||
"MIME-Version: 1.0\n" | ||
"Content-Type: text/plain; charset=UTF-8\n" | ||
"Content-Transfer-Encoding: 8bit\n" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
import json | ||
import logging | ||
import urllib.parse | ||
from collections import OrderedDict | ||
import phonenumbers | ||
|
||
from pympesa import Pympesa | ||
from django import forms | ||
from django.contrib import messages | ||
from django.core import signing | ||
from django.template.loader import get_template | ||
from django.utils.translation import ugettext as __, ugettext_lazy as _ | ||
from django.utils.functional import cached_property | ||
|
||
from pretix.base.decimal import round_decimal | ||
from pretix.base.models import Order, Quota, RequiredAction | ||
from pretix.base.payment import BasePaymentProvider, PaymentException | ||
from pretix.base.services.mail import SendMailException | ||
from pretix.base.services.orders import mark_order_paid, mark_order_refunded | ||
from pretix.helpers.urls import build_absolute_uri as build_global_uri | ||
from pretix.multidomain.urlreverse import build_absolute_uri | ||
from pretix.plugins.paypal.models import ReferencedPayPalObject | ||
from pretix.presale.views.cart import ( | ||
cart_session, create_empty_cart_id, get_or_create_cart_id, | ||
) | ||
from .tasks import send_stk | ||
|
||
logger = logging.getLogger('pretix.plugins.mpesa') | ||
|
||
|
||
class Mpesa(BasePaymentProvider): | ||
identifier = 'mpesa' | ||
verbose_name = _('Mpesa') | ||
payment_form_fields = OrderedDict([ | ||
]) | ||
|
||
@cached_property | ||
def cart_session(self): | ||
return cart_session(self.request) | ||
|
||
@property | ||
def settings_form_fields(self): | ||
d = OrderedDict( | ||
[ | ||
('endpoint', | ||
forms.ChoiceField( | ||
label=_('Endpoint'), | ||
initial='sandbox', | ||
choices=( | ||
('production', 'Live'), | ||
('sandbox', 'Sandbox'), | ||
), | ||
)), | ||
('safaricom_consumer_key', | ||
forms.CharField( | ||
label=_('Safaricom Consumer Key'), | ||
required=True, | ||
help_text=_('<a target="_blank" rel="noopener" href="{docs_url}">{text}</a>').format( | ||
text=_('Go to the safaricom developer portal to obtain developer keys a get guidance on going live'), | ||
docs_url='https://developer.safaricom.co.ke' | ||
) | ||
)), | ||
('safaricom_consumer_secret', | ||
forms.CharField( | ||
label=_('Safaricom Consumer Secret'), | ||
required=True, | ||
)), | ||
('mpesa_shortcode', | ||
forms.CharField( | ||
label=_('Lipa na Mpesa Online shortcode'), | ||
required=True, | ||
help_text=_('Apply for this from safaricom') | ||
)), | ||
('encryption_password', | ||
forms.CharField( | ||
label=_('Encription Password'), | ||
required=True, | ||
help_text=_('The password for encrypting the request') | ||
)), | ||
('mpesa_phone_number_field_required', | ||
forms.BooleanField( | ||
label=_('Will the mpesa phone number be required to place an order'), | ||
help_text=_("If this is not checked, entering a mpesa phone number is optional and the mpesa payment my not work."), | ||
required=False, | ||
)), | ||
|
||
] + list(super().settings_form_fields.items()) | ||
) | ||
return d | ||
|
||
def checkout_confirm_render(self, request) -> str: | ||
""" | ||
Returns the HTML that should be displayed when the user selected this provider | ||
on the 'confirm order' page. | ||
""" | ||
template = get_template('pretix_mpesa/checkout_payment_confirm.html') | ||
ctx = {'request': request, 'event': self.event, 'settings': self.settings} | ||
return template.render(ctx) | ||
|
||
def order_pending_render(self, request, order) -> str: | ||
template = get_template('pretix_mpesa/pending.html') | ||
ctx = {'request': request, 'event': self.event, 'settings': self.settings, 'order': order} | ||
return template.render(ctx) | ||
|
||
def payment_form_render(self, request) -> str: | ||
template = get_template('pretix_mpesa/checkout_payment_form.html') | ||
ctx = {'request': request, 'event': self.event, 'settings': self.settings} | ||
return template.render(ctx) | ||
|
||
def checkout_prepare(self, request, cart): | ||
self.request = request | ||
mpesa_phone_number = self.cart_session.get('contact_form_data', {}).get('mpesa_phone_number', '') | ||
try: | ||
parsed_num = phonenumbers.parse(mpesa_phone_number, 'KE') | ||
except phonenumbers.NumberParseException: | ||
messages.error(request, _('Please check to confirm that you entered the mpesa phone number and that it was a valid phone number')) | ||
return False | ||
else: | ||
if phonenumbers.is_valid_number(parsed_num): | ||
return True | ||
else: | ||
messages.error(request, _('The Mpesa number is not a valid phone number')) | ||
return False | ||
|
||
def payment_is_valid_session(self, request): | ||
return True | ||
|
||
def order_can_retry(self, order): | ||
return self._is_still_available(order=order) | ||
|
||
def payment_perform(self, request, order) -> str: | ||
""" | ||
Will be called if the user submitted his order successfully to initiate the | ||
payment process. | ||
It should return a custom redirct URL, if you need special behavior, or None to | ||
continue with default behavior. | ||
On errors, it should use Django's message framework to display an error message | ||
to the user (or the normal form validation error messages). | ||
:param order: The order object | ||
""" | ||
kwargs = {} | ||
if request.resolver_match and 'cart_namespace' in request.resolver_match.kwargs: | ||
kwargs['cart_namespace'] = request.resolver_match.kwargs['cart_namespace'] | ||
mode = self.settings.get('endpoint') | ||
consumer_key = self.settings.get('safaricom_consumer_key') | ||
consumer_secret = self.settings.get('safaricom_consumer_secret') | ||
business_short_code = self.settings.get('mpesa_shortcode') | ||
password = self.settings.get('encryption_password') | ||
callback_url = ''.join(build_absolute_uri(request.event, 'plugins:pretix_mpesa:callback', kwargs=kwargs)), | ||
send_stk.apply_async(kwargs={'consumer_key': consumer_key, 'consumer_secret': consumer_secret, | ||
'business_short_code': business_short_code, | ||
'password': password, 'amount': 10, 'phone': '254700247286', 'order_number': 'Test Order', | ||
'callback_url': callback_url, 'mode': mode}) | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Register your receivers here | ||
from django.dispatch import receiver | ||
from i18nfield.strings import LazyI18nString | ||
|
||
from pretix.base.signals import register_payment_providers | ||
from pretix.presale.signals import contact_form_fields | ||
from django.utils.translation import ugettext_lazy as _ | ||
from django import forms | ||
|
||
|
||
|
||
@receiver(register_payment_providers, dispatch_uid="payment_mpesa") | ||
def register_payment_provider(sender, **kwargs): | ||
from .payment import Mpesa | ||
return Mpesa | ||
|
||
@receiver(contact_form_fields, dispatch_uid="pretix_mpesa_phone_question") | ||
def add_telephone_question(sender, **kwargs): | ||
return {'mpesa_phone_number': forms.CharField( | ||
label=_('Mpesa Phone number'), | ||
required=sender.settings.telephone_field_required, | ||
help_text='The payment request will be made to this number by the mpesa payment provider', | ||
widget=forms.TextInput(attrs={'placeholder': _('Mpesa Phone number')}), | ||
)} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
|
||
from pretix.base.services.async import ProfiledTask | ||
import pympesa | ||
from pretix.celery_app import app | ||
import logging | ||
logger = logging.getLogger('pretix.plugins.mpesa') | ||
import time | ||
|
||
@app.task(base=ProfiledTask) | ||
def send_stk(consumer_key, consumer_secret, business_short_code, password, amount, phone, order_number,callback_url,mode) -> None: | ||
response = pympesa.oauth_generate_token(consumer_key, consumer_secret,'client_credentials',mode).json() | ||
access_token = response.get("access_token") | ||
from pympesa import Pympesa | ||
mpesa_client = Pympesa(access_token,mode) | ||
response = mpesa_client.lipa_na_mpesa_online_payment( | ||
BusinessShortCode=business_short_code, | ||
Password=password, | ||
Timestamp=time.strftime('%Y%m%d%H%M%S'), | ||
TransactionType="CustomerPayBillOnline", | ||
Amount="100", | ||
PartyA="254708374149", | ||
PartyB=business_short_code, | ||
PhoneNumber=phone, | ||
CallBackURL='https://nyachoke.localtunnel.me/bigevents/2019/mpesa/callback', | ||
AccountReference=order_number, | ||
TransactionDesc=order_number | ||
) | ||
logger.debug(response.json()) | ||
|
||
@app.task(base=ProfiledTask) | ||
def simulate_C2B(consumer_key,consumer_secret): | ||
response = pympesa.oauth_generate_token(consumer_key, consumer_secret).json() | ||
access_token = response.get("access_token") | ||
from pympesa import Pympesa | ||
mpesa_client = Pympesa(access_token) | ||
mpesa_client.c2b_simulate_transaction() |
Empty file.
Oops, something went wrong.