PK J)J9 plugs_payments/views.py"""
Payments Views
"""
from rest_framework.decorators import api_view, permission_classes
from rest_framework.response import Response
from rest_framework import permissions
from plugs_payments import models
@api_view(['GET'])
@permission_classes([permissions.AllowAny])
def confirmation(request):
"""
Callback to be used by the payments platform
"""
reason = models.IfThenPayment.objects.confirmation(request.GET)
return Response(data=reason)
PK J)Jɕ" " plugs_payments/fields.py"""
Plugs Payments Fields
"""
from django.db import models
from plugs_core.validators import NumberOfDigitsValidator
class ReferenceField(models.PositiveIntegerField):
"""
Custom model field that represent a payment reference
"""
default_validators = [NumberOfDigitsValidator(9)]
description = "A payment reference"
class EntityField(models.PositiveIntegerField):
"""
Custom model field that represents an entity
"""
default_validators = [NumberOfDigitsValidator(5)]
description = "A payment entity"
PK J)J4 plugs_payments/signals.py"""
Plugs Payments Signals
"""
from django.dispatch import Signal
# sent when a validated ifthen payment received
valid_ifthen_payment_received = Signal()
# sent when an invalid payment received
# could be an error with a reference, value or entity
# cannot be the anti phishing key
invalid_ifthen_payment_received = Signal()
# sent when a request to the confirmation callback
# was made with an incorrect or missing anti phisphing key
suspicious_ifthen_payment_received = Signal()
PK )J?l l plugs_payments/managers.py"""
Solo Payments Manager
"""
import logging
from datetime import datetime
from django.db import models
from django.http import Http404
from rest_framework.exceptions import ValidationError
from plugs_payments.settings import plugs_payments_settings as settings
from plugs_payments import signals
LOGGER = logging.getLogger(__name__)
class IfThenPaymentManager(models.Manager):
"""
IfThenPay Payment custom manager
"""
def confirmation(self, data):
"""
Payment confirmation callback
http://www.yoursite.com/callback.php?chave=[CHAVE_ANTI_PHISHING]&entidad
e=[ENTIDADE]&referencia=[REFERENCIA]&valor=[VALOR]&datahorapag=[DATA_HOR
A_PAGAMENTO]&terminal=[TERMINAL]
"""
self.data = data
self._verify_phishing_key()
mbpayment = self._get_mbpayment()
mbpayment.mark_as_paid(**data)
return {"message": "Payment Confirmation Received"}
def _verify_phishing_key(self):
"""
Uses key provided in the request to
authenticate the payment platform
"""
if settings['ANTI_PHISHING_KEY'] != self.data.get('chave'):
message = 'Anti Phishing Key is Missing or Incorrect.'
signals.suspicious_ifthen_payment_received.send(sender=self, data=self.data)
raise ValidationError(message)
def _get_mbpayment(self):
"""
Get object or 404
"""
entity = self.data.get('entidade')
reference = self.data.get('referencia')
value = self.data.get('valor')
try:
return self.model.objects.get(
is_paid=False,
entity=entity,
reference=reference,
value=value)
except self.model.DoesNotExist:
signals.invalid_ifthen_payment_received.send(sender=self, data=self.data)
raise Http404
PK K*JL* * plugs_payments/models.py"""
MB Payment
"""
import logging
from random import randint
from datetime import datetime
from django.utils.translation import ugettext_lazy as _
from django.db import models
from plugs_core import mixins
from plugs_payments.settings import plugs_payments_settings as settings
from plugs_payments.managers import IfThenPaymentManager
from plugs_payments.signals import valid_ifthen_payment_received
from plugs_payments import fields
LOGGER = logging.getLogger(__name__)
class IfThenPayment(mixins.Timestampable, models.Model):
"""
IfThenPay Payment model
"""
is_paid = models.BooleanField(default=False)
entity = fields.EntityField()
reference = fields.ReferenceField()
value = models.DecimalField(null=False, max_digits=20, decimal_places=2)
payment_date = models.DateTimeField(null=True, blank=True)
terminal = models.CharField(null=True, blank=True, max_length=40)
objects = IfThenPaymentManager()
def mark_as_paid(self, **data):
"""
Mark payment as paid
"""
self.is_paid = True
# why is this a list?
self.terminal = data.get('terminal')[0]
self.payment_date = self._format_payment_date(data.get('datahorapag')[0])
self.save()
valid_ifthen_payment_received.send(sender=self)
def generate_payment_details(self):
self.bookkeeper = self._generate_bookkeeper()
self.entity = settings['ENTITY']
self.reference = self._generate_reference()
def save(self, *args, **kwargs):
"""
Overrides model save method
"""
if not self.pk:
counter = 0
while(True):
# entity, reference and value must be unique together
# if a record exists with the same triad, loop and
# generate new payment details
# incremente and log counter, in the future
# can be used to set the max number of retries before
# giving up
try:
self.generate_payment_details()
data = {
'entity': self.entity,
'reference': self.reference,
'value': self.value
}
self.__class__.objects.get(**data)
counter += 1
except self.DoesNotExist:
# if the triad does not exist in the database
# proceed
break
message = 'Retrying payments details generation {0}'
LOGGER.warning(message.format(counter))
super(IfThenPayment, self).save(*args, **kwargs)
def __str__(self):
return str(self.reference)
def _format_payment_date(self, payment_date):
try:
# deal with payment_date
from_format = "%d-%m-%Y %H:%M:%S"
to_format = "%Y-%m-%d %H:%M:%S"
payment_date = datetime.strptime(payment_date, from_format).strftime(to_format)
except (TypeError, ValueError):
message = 'Payment date invalid. Date {0}'
LOGGER.warning(message.format(payment_date))
return payment_date
def _check_digits(self, integrity):
"""
Integrity string is used to calculate
the check digits using the provided algorithm
Multiply each digit of the integrity string
with a corresponding value in the lookup list
Sum all the multiplications and subtract
the modulus of summatory with 97 to the number 98
"""
multipliers = [51, 73, 17, 89, 38, 62, 45, 53, 15, 50, 5, 49, 34, 81, 76, 27, 90, 9, 30, 3]
# verify input len equal to multipliers
if len(integrity) != len(multipliers):
raise Http404
summatory = 0
for index, digit in enumerate(integrity):
summatory += int(digit) * multipliers[index]
result = 98 - (summatory % 97)
return '{0:02d}'.format(result)
def _format_value(self):
"""
The value must use 8 digits, we need to
multiply the decimal by 100 to shift the
decimal places, convert to int and then
padd the string with zeros if needed
"""
try:
inted_value = int(self.value * 100)
except TypeError:
raise
return '{0:08d}'.format(inted_value)
def _generate_bookkeeper(self):
"""
Generate a random int to
be used as a unique id when
generating references
"""
return randint(0, 9999)
def _generate_integrity_string(self):
"""
String that is gonna be used
to compute check digits
"""
return '{0}{1}{2}{3}'.format(
settings['ENTITY'],
settings['SUBENTITY'],
'{0:04d}'.format(self.bookkeeper),
self._format_value()
)
def _generate_reference(self):
"""
Generate a reference using the ifthenpay
payment platform algorithm
"""
integrity = self._generate_integrity_string()
check_digits = self._check_digits(integrity)
subentity = str(settings['SUBENTITY'])
return '{0}{1}{2}'.format(subentity, '{0:04d}'.format(self.bookkeeper), check_digits)
# pylint: disable=R0903
class Meta:
"""
Providing verbose names is recommended if
we want to use i18n in admin site
"""
unique_together = ('entity', 'reference', 'value')
verbose_name = _('payment')
verbose_name_plural = _('payments')
PK -tI
^z z plugs_payments/apps.py# -*- coding: utf-8
from django.apps import AppConfig
class PlugsPaymentsConfig(AppConfig):
name = 'plugs_payments'
PK L*J` plugs_payments/__init__.py__version__ = '0.1.1'
PK J)JC plugs_payments/settings.py"""
Plugs Payments Settings
"""
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
MANDATORY_SETTINGS = ['ANTI_PHISHING_KEY', 'ENTITY', 'SUBENTITY']
PROJECT_SETTINGS = getattr(settings, 'PLUGS_PAYMENTS', {})
for setting in MANDATORY_SETTINGS:
try:
PROJECT_SETTINGS[setting]
except KeyError:
raise ImproperlyConfigured('Missing setting: PLUGS_PAYMENTS[\'{0}\']'.format(setting))
plugs_payments_settings = PROJECT_SETTINGS
PK -tI , plugs_payments/static/css/plugs_payments.cssPK -tI * plugs_payments/static/js/plugs_payments.jsPK J)J9,7 7 4 plugs_payments/migrations/0002_auto_20170109_1847.py# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2017-01-09 18:47
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('plugs_payments', '0001_initial'),
]
operations = [
migrations.RenameModel(
old_name='MBPayment',
new_name='IfThenPayment',
),
migrations.AlterModelOptions(
name='ifthenpayment',
options={'verbose_name': 'payment', 'verbose_name_plural': 'payments'},
),
]
PK J)J4 4 plugs_payments/migrations/0004_auto_20170109_2011.py# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2017-01-09 20:11
from __future__ import unicode_literals
from django.db import migrations
import plugs_payments.fields
class Migration(migrations.Migration):
dependencies = [
('plugs_payments', '0003_auto_20170109_1851'),
]
operations = [
migrations.AlterField(
model_name='ifthenpayment',
name='entity',
field=plugs_payments.fields.EntityField(),
),
migrations.AlterField(
model_name='ifthenpayment',
name='reference',
field=plugs_payments.fields.ReferenceField(),
),
]
PK J)J'^j 4 plugs_payments/migrations/0003_auto_20170109_1851.py# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2017-01-09 18:51
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('plugs_payments', '0002_auto_20170109_1847'),
]
operations = [
migrations.AlterUniqueTogether(
name='ifthenpayment',
unique_together=set([('entity', 'reference', 'value')]),
),
]
PK J)J % plugs_payments/migrations/__init__.pyPK J)Jsl l ) plugs_payments/migrations/0001_initial.py# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2017-01-09 15:03
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='MBPayment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('is_paid', models.BooleanField(default=False)),
('entity', models.PositiveIntegerField()),
('reference', models.PositiveIntegerField()),
('value', models.DecimalField(decimal_places=2, max_digits=5)),
('payment_date', models.DateTimeField(blank=True, null=True)),
('terminal', models.CharField(blank=True, max_length=40, null=True)),
],
options={
'abstract': False,
},
),
]
PK -tI 1 plugs_payments/templates/plugs_payments/base.html
{% comment %}
As the developer of this package, don't place anything here if you can help it
since this allows developers to have interoperability between your template
structure and their own.
Example: Developer melding the 2SoD pattern to fit inside with another pattern::
{% extends "base.html" %}
{% load static %}
{% block extra_js %}
{% block javascript %}
{% endblock javascript %}
{% endblock extra_js %}
{% endcomment %}
PK rL*JQ$ . plugs_payments-0.1.1.dist-info/DESCRIPTION.rst=============================
Plugs Payments
=============================
.. image:: https://badge.fury.io/py/plugs-payments.png
:target: https://badge.fury.io/py/plugs-payments
.. image:: https://travis-ci.org/ricardolobo/plugs-payments.png?branch=master
:target: https://travis-ci.org/ricardolobo/plugs-payments
Your project description goes here
Documentation
-------------
The full documentation is at https://plugs-payments.readthedocs.io.
Quickstart
----------
Install Plugs Payments::
pip install plugs-payments
Add it to your `INSTALLED_APPS`:
.. code-block:: python
INSTALLED_APPS = (
...
'plugs_payments.apps.PlugsPaymentsConfig',
...
)
Add Plugs Payments's URL patterns:
.. code-block:: python
from plugs_payments import urls as plugs_payments_urls
urlpatterns = [
...
url(r'^', include(plugs_payments_urls)),
...
]
Features
--------
* TODO
Running Tests
-------------
Does the code actually work?
::
source /bin/activate
(myenv) $ pip install tox
(myenv) $ tox
Credits
-------
Tools used in rendering this package:
* Cookiecutter_
* `cookiecutter-djangopackage`_
.. _Cookiecutter: https://github.com/audreyr/cookiecutter
.. _`cookiecutter-djangopackage`: https://github.com/pydanny/cookiecutter-djangopackage
History
-------
0.1.0 (2016-12-29)
++++++++++++++++++
* First release on PyPI.
PK rL*JW\" , plugs_payments-0.1.1.dist-info/metadata.json{"classifiers": ["Development Status :: 3 - Alpha", "Framework :: Django", "Framework :: Django :: 1.9", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Natural Language :: English", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5"], "extensions": {"python.details": {"contacts": [{"email": "ricardolobo@soloweb.pt", "name": "Ricardo Lobo", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/ricardolobo/plugs-payments"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "keywords": ["plugs-payments"], "license": "MIT", "metadata_version": "2.0", "name": "plugs-payments", "run_requires": [{"requires": ["plugs-core (>=0.1.6)"]}], "summary": "Your project description goes here", "version": "0.1.1"}PK rL*JN , plugs_payments-0.1.1.dist-info/top_level.txtplugs_payments
PK rL*Jndn n $ plugs_payments-0.1.1.dist-info/WHEELWheel-Version: 1.0
Generator: bdist_wheel (0.26.0)
Root-Is-Purelib: true
Tag: py2-none-any
Tag: py3-none-any
PK rL*JO ' plugs_payments-0.1.1.dist-info/METADATAMetadata-Version: 2.0
Name: plugs-payments
Version: 0.1.1
Summary: Your project description goes here
Home-page: https://github.com/ricardolobo/plugs-payments
Author: Ricardo Lobo
Author-email: ricardolobo@soloweb.pt
License: MIT
Keywords: plugs-payments
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Django
Classifier: Framework :: Django :: 1.9
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Natural Language :: English
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Requires-Dist: plugs-core (>=0.1.6)
=============================
Plugs Payments
=============================
.. image:: https://badge.fury.io/py/plugs-payments.png
:target: https://badge.fury.io/py/plugs-payments
.. image:: https://travis-ci.org/ricardolobo/plugs-payments.png?branch=master
:target: https://travis-ci.org/ricardolobo/plugs-payments
Your project description goes here
Documentation
-------------
The full documentation is at https://plugs-payments.readthedocs.io.
Quickstart
----------
Install Plugs Payments::
pip install plugs-payments
Add it to your `INSTALLED_APPS`:
.. code-block:: python
INSTALLED_APPS = (
...
'plugs_payments.apps.PlugsPaymentsConfig',
...
)
Add Plugs Payments's URL patterns:
.. code-block:: python
from plugs_payments import urls as plugs_payments_urls
urlpatterns = [
...
url(r'^', include(plugs_payments_urls)),
...
]
Features
--------
* TODO
Running Tests
-------------
Does the code actually work?
::
source /bin/activate
(myenv) $ pip install tox
(myenv) $ tox
Credits
-------
Tools used in rendering this package:
* Cookiecutter_
* `cookiecutter-djangopackage`_
.. _Cookiecutter: https://github.com/audreyr/cookiecutter
.. _`cookiecutter-djangopackage`: https://github.com/pydanny/cookiecutter-djangopackage
History
-------
0.1.0 (2016-12-29)
++++++++++++++++++
* First release on PyPI.
PK rL*Jp % plugs_payments-0.1.1.dist-info/RECORDplugs_payments/__init__.py,sha256=ls1camlIoMxEZz9gSkZ1OJo-MXqHWwKPtdPbZJmwp7E,22
plugs_payments/apps.py,sha256=YZwHnXKbfKWgLMX0wQK1IMgIrguc5W6uWn4tR2-ZlOw,122
plugs_payments/fields.py,sha256=5hUu57If7N__9qqJy3bh2FmrXlH--dHUS5iJgle36PA,546
plugs_payments/managers.py,sha256=HMikD7lFOe9zkOKOWgOX1FEPVUrEJeVdp08EXYIYRIU,1900
plugs_payments/models.py,sha256=4Mvew9Co5UTrWS4HJ2uS8wfsNCYj9h1uBs7eDT4kiNQ,5674
plugs_payments/settings.py,sha256=0_P9wf6LjCnKQ7SJgXzQQFWpJ6ySR40nrGeHrbDdn2M,488
plugs_payments/signals.py,sha256=3aQ2iR8vcpmCkI7ZrNsnuX-x1wrS2XK0FS2w2OnDybo,487
plugs_payments/views.py,sha256=-oNBUzyhKCm8-cswt2PQ1p_Pp8nCojBQyfVs4r4jfY4,471
plugs_payments/migrations/0001_initial.py,sha256=j48NpMVbnmee7Piim0Is5e-8V601wFp3ecAOR4R6ICw,1132
plugs_payments/migrations/0002_auto_20170109_1847.py,sha256=SnF2ks99JQsUreGGOZk6gfQ5BSmZuGECmjJmt4LWuHQ,567
plugs_payments/migrations/0003_auto_20170109_1851.py,sha256=aoGaVg_UWlJsK3gvNpbls0S6O4XrPFX0pA0u-Tykf6Y,450
plugs_payments/migrations/0004_auto_20170109_2011.py,sha256=o5MLqmhAxP3IE05re-WVfWafvZqnQKKoa5WusO-Tihw,659
plugs_payments/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
plugs_payments/static/css/plugs_payments.css,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
plugs_payments/static/js/plugs_payments.js,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
plugs_payments/templates/plugs_payments/base.html,sha256=zzaUPjEgvwxg-Gn2rZ6gIMWphucmEGvAU_skUXJIcDA,661
plugs_payments-0.1.1.dist-info/DESCRIPTION.rst,sha256=oF5MQc53tmbllut0p0yIrZOcZc47FMeJFPADjzhnZoM,1445
plugs_payments-0.1.1.dist-info/METADATA,sha256=qlXsNF4u7EpC3rTPU5mWUlE2Z1Bc1vnZCWf4gnCRkKQ,2251
plugs_payments-0.1.1.dist-info/RECORD,,
plugs_payments-0.1.1.dist-info/WHEEL,sha256=GrqQvamwgBV4nLoJe0vhYRSWzWsx7xjlt74FT0SWYfE,110
plugs_payments-0.1.1.dist-info/metadata.json,sha256=MBawx2fuINqxDfd0jjo30dJYsoE6HzdF5TfwfIQreOg,953
plugs_payments-0.1.1.dist-info/top_level.txt,sha256=o2rWSDj7IMCEDZCAVcrRypZZekyeN0Q5HsFnszLJdcA,15
PK J)J9 plugs_payments/views.pyPK J)Jɕ" " plugs_payments/fields.pyPK J)J4 d plugs_payments/signals.pyPK )J?l l plugs_payments/managers.pyPK K*JL* * & plugs_payments/models.pyPK -tI
^z z $ plugs_payments/apps.pyPK L*J` 4% plugs_payments/__init__.pyPK J)JC % plugs_payments/settings.pyPK -tI , ' plugs_payments/static/css/plugs_payments.cssPK -tI * ' plugs_payments/static/js/plugs_payments.jsPK J)J9,7 7 4 4( plugs_payments/migrations/0002_auto_20170109_1847.pyPK J)J4 4 * plugs_payments/migrations/0004_auto_20170109_2011.pyPK J)J'^j 4 - plugs_payments/migrations/0003_auto_20170109_1851.pyPK J)J % / plugs_payments/migrations/__init__.pyPK J)Jsl l ) / plugs_payments/migrations/0001_initial.pyPK -tI 1 4 plugs_payments/templates/plugs_payments/base.htmlPK rL*JQ$ . 7 plugs_payments-0.1.1.dist-info/DESCRIPTION.rstPK rL*JW\" , = plugs_payments-0.1.1.dist-info/metadata.jsonPK rL*JN , A plugs_payments-0.1.1.dist-info/top_level.txtPK rL*Jndn n $ A plugs_payments-0.1.1.dist-info/WHEELPK rL*JO ' B plugs_payments-0.1.1.dist-info/METADATAPK rL*Jp % K plugs_payments-0.1.1.dist-info/RECORDPK S