diff --git a/.travis.yml b/.travis.yml
index 0e63505..df230e1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,6 +4,8 @@ language: python
python:
- "2.7"
+ - "3.3"
+ - "3.4"
# command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors
install: pip install -r requirements-test.txt
diff --git a/HISTORY.rst b/HISTORY.rst
index 9494aac..e2b286e 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -2,6 +2,12 @@
History
-------
+0.3.5 (2015-11-11)
+++++++++++++++++++
+- Support Django 1.8
+- Support Py33 and Py34
+- Return proper IPN response
+
0.3.4 (2015-08-12)
++++++++++++++++++
- Restructure flow to better support IPN processing
diff --git a/django_pesapal/__init__.py b/django_pesapal/__init__.py
index bfeb9e7..40ed83d 100644
--- a/django_pesapal/__init__.py
+++ b/django_pesapal/__init__.py
@@ -1 +1 @@
-__version__ = '0.3.4'
+__version__ = '0.3.5'
diff --git a/django_pesapal/templates/django_pesapal/post_payment.html b/django_pesapal/templates/django_pesapal/post_payment.html
index 0de639d..47efd1c 100644
--- a/django_pesapal/templates/django_pesapal/post_payment.html
+++ b/django_pesapal/templates/django_pesapal/post_payment.html
@@ -18,6 +18,7 @@
{% trans "Redirect Page" %}
{{ message }}
{% trans "Finish Ordering" %}
+ {% trans "Check Status" %}
diff --git a/django_pesapal/tests.py b/django_pesapal/tests.py
new file mode 100644
index 0000000..97a580c
--- /dev/null
+++ b/django_pesapal/tests.py
@@ -0,0 +1,7 @@
+
+from django.test import TestCase
+
+
+class UserModelTestCase(TestCase):
+
+ pass
diff --git a/django_pesapal/urls.py b/django_pesapal/urls.py
index 7d8a6c7..d5a2fd5 100644
--- a/django_pesapal/urls.py
+++ b/django_pesapal/urls.py
@@ -1,6 +1,10 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import
+
from django.conf.urls import patterns, url
-import views
+from . import views
urlpatterns = patterns(
'',
diff --git a/django_pesapal/views.py b/django_pesapal/views.py
index 73dba0a..267c975 100644
--- a/django_pesapal/views.py
+++ b/django_pesapal/views.py
@@ -1,8 +1,15 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import
+
+import logging
+import oauth2 as oauth
+import requests
from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse
from django.db.models.loading import get_model
-from django.http import HttpResponse
+from django.http import HttpResponse, QueryDict
from django.shortcuts import get_object_or_404
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
@@ -10,12 +17,8 @@
from xml.etree import cElementTree as ET
-import conf as settings
+from . import conf as settings
-import logging
-import oauth2 as oauth
-import requests
-import urllib
DEFAULT_TYPE = "MERCHANT"
Transaction = get_model(settings.PESAPAL_TRANSACTION_MODEL)
@@ -148,12 +151,14 @@ def get_payment_status(self, **kwargs):
class PaymentResponseMixin(object):
def build_url_params(self):
- url_params = '?' + urllib.urlencode(
+ url_params = QueryDict(mutable=True)
+ url_params.update(
{
'pesapal_merchant_reference': self.transaction.merchant_reference,
'pesapal_transaction_tracking_id': self.transaction.pesapal_transaction
}
)
+ url_params = '?' + url_params.urlencode()
return url_params
def get_payment_status_url(self):
@@ -196,6 +201,7 @@ def get_context_data(self, **kwargs):
ctx = super(TransactionCompletedView, self).get_context_data(**kwargs)
ctx['transaction_completed_url'] = self.get_order_completion_url()
+ ctx['transaction_status_url'] = self.get_payment_status_url()
ctx['payment_status'] = self.transaction.payment_status
if self.transaction.payment_status == Transaction.PENDING:
@@ -265,18 +271,30 @@ class TransactionStatusView(UpdatePaymentStatusMixin, RedirectView):
def get_redirect_url(self, *args, **kwargs):
params = self.get_params()
-
self.process_payment_status()
# redirect back to Transaction completed view
url = reverse('transaction_completed')
- url += '?' + urllib.urlencode(params)
+
+ query_dict = QueryDict(mutable=True)
+ query_dict.update(params)
+ url += '?' + query_dict.urlencode()
return url
class IPNCallbackView(UpdatePaymentStatusMixin, PaymentResponseMixin, View):
+ def build_ipn_response(self):
+ params = self.get_params()
+ params['pesapal_notification_type'] = self.request.GET.get('pesapal_notification_type')
+
+ query_dict = QueryDict(mutable=True)
+ query_dict.update(params)
+ response = query_dict.urlencode()
+ return HttpResponse(response)
+
def get(self, request, *args, **kwargs):
self.process_payment_status()
- return HttpResponse('OK')
+ response = self.build_ipn_response()
+ return response
diff --git a/docs/usage.rst b/docs/usage.rst
index 3e879f5..9aa1711 100644
--- a/docs/usage.rst
+++ b/docs/usage.rst
@@ -2,40 +2,55 @@
Usage
======
-Take a look at the sandbox app for a quick overview on how to use `django-pesapal`.
+Install django-pesapal::
-To use django-pesapal in a project::
+ pip install django-pesapal
+
+Then use it in a project::
+
+ import django_pesapal
+
+#. Add `django_pesapal` to your `INSTALLED_APPS` setting like this::
+
+ INSTALLED_APPS = (
+ ...
+ 'django_pesapal',
+ )
+
+#. Include the `django_pesapal` URLconf in your project urls.py like this::
+
+ url(r'^payments/', include('django_pesapal.urls')),
+
+#. You can set your own return url by adding this to `settings.py`::
+
+ PESAPAL_TRANSACTION_DEFAULT_REDIRECT_URL = 'app_name:url_name' # this needs to be a reversible
+
+#. Run `python manage.py migrate` to create the models.
+
+#. Create a method that receives payment details and returns the pesapal iframe url::
- from django.views.generic import TemplateView
from django_pesapal.views import PaymentRequestMixin
- from django_pesapal.models import Transaction
- class PaymentView(TemplateView, PaymentRequestMixin):
- """
- This view returns secure payment form from pesapal
- """
- template_name = 'testapp/payment.html'
+ class PaymentView(PaymentRequestMixin):
- def get_context_data(self, **kwargs):
- ctx = super(PaymentView, self).get_context_data(**kwargs)
+ def get_pesapal_payment_iframe(self):
+ '''
+ Authenticates with pesapal to get the payment iframe src
+ '''
order_info = {
'first_name': 'Some',
'last_name': 'User',
'amount': 100,
'description': 'Payment for X',
- 'reference': 2,
- 'email': 'pesapal@example.com'
+ 'reference': 2, # some object id
+ 'email': 'user@example.com',
}
- # get_payment_url returns the URL of the secure payment form from pesapal
- # You can use this URL in an iframe
- ctx['pesapal_url'] = self.get_payment_url(**order_info)
- return ctx
-
+ iframe_src_url = self.get_payment_url(**order_info)
+ return iframe_src_url
-Once processing is complete the user will be redirected to the intermediate processing where
-they can update check the status of the payment
+#. Once payment has been processed, you will be redirected to an intermediate screen where the user can finish ordering. Clicking the "Finish Ordering" button will check the payment status to ensure that the payment was successful and then redirects the user to `PESAPAL_TRANSACTION_DEFAULT_REDIRECT_URL`.
**NOTE:** You can override the intermediate (`post_payment.html`) processing template if you
need to have a customized look.
diff --git a/requirements.txt b/requirements.txt
index d1599b2..813ba08 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,7 @@
-Django>=1.7,<=1.7.8
-wheel==0.23.0
-oauth2==1.5.211
-requests==2.1
+Django>=1.7
+wheel==0.26.0
+oauth2==1.9.0.post1
+requests[security]>=2.8.1
# Custom requirements
git+https://github.com/dcramer/django-uuidfield.git@9bd27e9#egg=django-uuidfield
diff --git a/runtests.py b/runtests.py
index ad7416c..0bf8930 100644
--- a/runtests.py
+++ b/runtests.py
@@ -42,10 +42,10 @@
def run_tests(*test_args):
if not test_args:
- test_args = ['tests']
+ test_args = ['django_pesapal.tests']
# Run tests
- test_runner = NoseTestSuiteRunner(verbosity=1)
+ test_runner = NoseTestSuiteRunner()
failures = test_runner.run_tests(test_args)
diff --git a/sandbox/sandbox/settings.py b/sandbox/sandbox/settings.py
index c251004..5ff5130 100644
--- a/sandbox/sandbox/settings.py
+++ b/sandbox/sandbox/settings.py
@@ -119,6 +119,10 @@
PESAPAL_OAUTH_CALLBACK_URL ='transaction_completed'
PESAPAL_OAUTH_SIGNATURE_METHOD ='SignatureMethod_HMAC_SHA1'
PESAPAL_TRANSACTION_DEFAULT_REDIRECT_URL = 'payment'
+PESAPAL_DEMO = True
+PESAPAL_OAUTH_CALLBACK_URL = 'transaction_completed'
+PESAPAL_OAUTH_SIGNATURE_METHOD = 'SignatureMethod_HMAC_SHA1'
+PESAPAL_TRANSACTION_DEFAULT_REDIRECT_URL = 'transaction_completed'
PESAPAL_TRANSACTION_FAILED_REDIRECT_URL = ''
PESAPAL_ITEM_DESCRIPTION = False
PESAPAL_TRANSACTION_MODEL = 'testapp.Transaction'
@@ -128,4 +132,4 @@
try:
from local_config import *
except ImportError:
- pass
\ No newline at end of file
+ pass
diff --git a/setup.py b/setup.py
index 1621941..93edeff 100644
--- a/setup.py
+++ b/setup.py
@@ -34,10 +34,10 @@
packages=find_packages(exclude=['sandbox*']),
package_data={'django_pesapal': ['templates/django_pesapal/*.html']},
install_requires=[
- 'Django>=1.7,<=1.7.8',
- 'oauth2==1.5.211',
+ 'Django>=1.7',
+ 'oauth2==1.9.0.post1',
'django-uuidfield<=0.6.0',
- 'requests==2.1',
+ 'requests[security]==2.8.1',
],
dependency_links=[
"https://github.com/dcramer/django-uuidfield/archive/9bd27e9.zip#egg=django-uuidfield-0.6.0",
@@ -55,7 +55,9 @@
'Natural Language :: English',
'Programming Language :: Python',
'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3.3',
+ 'Programming Language :: Python :: 3.4',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
],
-)
\ No newline at end of file
+)
diff --git a/tox.ini b/tox.ini
index f9d0793..fa9abfa 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = py27, py33
+envlist = py27, py33, py34
[testenv]
setenv =