├── wm_sample ├── __init__.py ├── templates │ ├── wm_sample │ │ ├── base.html │ │ ├── simple_payment_desc.txt │ │ ├── fail.html │ │ ├── success.html │ │ └── simple_payment.html │ └── base.html ├── management │ └── __init__.py ├── manage.py ├── helpers.py ├── urls.py ├── views.py └── settings.py ├── webmoney ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ └── clean_webmoney.py ├── signals.py ├── urls.py ├── __init__.py ├── admin.py ├── forms.py ├── views.py └── models.py ├── .gitignore ├── README.rst ├── setup.py └── LICENSE /wm_sample/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webmoney/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webmoney/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /wm_sample/templates/wm_sample/base.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | -------------------------------------------------------------------------------- /wm_sample/templates/wm_sample/simple_payment_desc.txt: -------------------------------------------------------------------------------- 1 | payment for account {{ request.user }}. 2 | -------------------------------------------------------------------------------- /webmoney/signals.py: -------------------------------------------------------------------------------- 1 | from django.dispatch import Signal 2 | 3 | webmoney_payment_accepted = Signal(providing_args=["payment"]) 4 | -------------------------------------------------------------------------------- /wm_sample/templates/wm_sample/fail.html: -------------------------------------------------------------------------------- 1 | {% extends "wm_sample/base.html" %} 2 | 3 | {% block content %} 4 | Payment failed. 5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.pyc 3 | semantic.cache 4 | .ropeproject/ 5 | /build/ 6 | /dist/ 7 | /django_payment_webmoney.egg-info/ 8 | /wm_sample/wm_sample.db 9 | -------------------------------------------------------------------------------- /wm_sample/templates/wm_sample/success.html: -------------------------------------------------------------------------------- 1 | {% extends "wm_sample/base.html" %} 2 | 3 | {% block content %} 4 | Your payment number {{ id }} is successfully procceded at {{ date|date:"Y.m.d H:i" }}. 5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /wm_sample/templates/wm_sample/simple_payment.html: -------------------------------------------------------------------------------- 1 | {% extends "wm_sample/base.html" %} 2 | 3 | {% block content %} 4 | 5 |
6 | {{ form }} 7 | 8 |
9 | 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /wm_sample/management/__init__.py: -------------------------------------------------------------------------------- 1 | from webmoney.signals import webmoney_payment_accepted 2 | 3 | 4 | def webmoney_payment_accepted_processor(sender, payment, **kwargs): 5 | print payment 6 | 7 | 8 | webmoney_payment_accepted.connect( 9 | webmoney_payment_accepted_processor, sender=None) 10 | -------------------------------------------------------------------------------- /wm_sample/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | 6 | if __name__ == "__main__": 7 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") 8 | 9 | from django.core.management import execute_from_command_line 10 | 11 | execute_from_command_line(sys.argv) 12 | -------------------------------------------------------------------------------- /webmoney/urls.py: -------------------------------------------------------------------------------- 1 | try: 2 | from django.conf.urls import patterns, url 3 | except ImportError: 4 | from django.conf.urls.defaults import patterns, url 5 | 6 | from webmoney.views import result 7 | 8 | urlpatterns = patterns( 9 | '', 10 | url(r'^result/$', result, name='webmoney-result'), 11 | ) 12 | -------------------------------------------------------------------------------- /webmoney/__init__.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | VERSION = (0, 3, 0, "final") 4 | PURSE_RE = re.compile(ur'^(?P[ZREUYBGDC])(?P\d{12})$') 5 | WMID_RE = re.compile(ur'^\d{12}$') 6 | 7 | 8 | def get_version(): 9 | if VERSION[3] != "final": 10 | return "%s.%s.%s%s" % (VERSION[0], VERSION[1], VERSION[2], VERSION[3]) 11 | else: 12 | return "%s.%s.%s" % (VERSION[0], VERSION[1], VERSION[2]) 13 | 14 | 15 | __version__ = get_version() 16 | -------------------------------------------------------------------------------- /webmoney/management/commands/clean_webmoney.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timedelta 2 | 3 | from django.core.management.base import NoArgsCommand 4 | 5 | 6 | class Command(NoArgsCommand): 7 | help = "Clean unpayed invoces older than one day." 8 | 9 | def handle_noargs(self, **options): 10 | from webmoney.models import Invoice 11 | Invoice.objects.filter( 12 | created_on__lt=datetime.utcnow()-timedelta(days=1), 13 | payment__isnull=True).delete() 14 | -------------------------------------------------------------------------------- /wm_sample/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% block title %}{% endblock %} 6 | {% block css %}{% endblock %} 7 | {% block js %}{% endblock %} 8 | {% block extra-head %}{% endblock %} 9 | 10 | 11 | {% block content %}{% endblock %} 12 | 13 | 14 | -------------------------------------------------------------------------------- /wm_sample/helpers.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render_to_response 2 | from django.template import RequestContext 3 | 4 | 5 | def render_to(template=None): 6 | def renderer(function): 7 | def wrapper(request, *args, **kwargs): 8 | output = function(request, *args, **kwargs) 9 | if not isinstance(output, dict): 10 | return output 11 | tmpl = output.pop('TEMPLATE', template) 12 | return render_to_response( 13 | tmpl, output, context_instance=RequestContext(request)) 14 | 15 | return wrapper 16 | 17 | return renderer 18 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Django-Payment-WebMoney 2 | ======================= 3 | 4 | WebMoney Merchant Interface support for Django. 5 | 6 | 7 | How to configure WM 8 | ------------------- 9 | 1. Change your attestation from none to personal or higher 10 | 2. Configure payment settings on this page https://merchant.webmoney.ru/conf/purse.asp 11 | 3. Choose your purse & click to configure link 12 | 4. Edit the following fields: Result URL, Success URL, Fail URL 13 | 5. Save & enjoy 14 | 15 | Fields urls example for wm-sample: 16 | ---------------------------------- 17 | * Result URL = http://127.0.0.1:8000/webmoney/result/ 18 | 19 | * Success URL = http://127.0.0.1:8000/success/ 20 | 21 | * Fail URL = http://127.0.0.1:8000/fail/ 22 | -------------------------------------------------------------------------------- /wm_sample/urls.py: -------------------------------------------------------------------------------- 1 | try: 2 | from django.conf.urls import patterns, url, include 3 | except ImportError: 4 | from django.conf.urls.defaults import patterns, url, include 5 | 6 | from django.contrib.staticfiles.urls import staticfiles_urlpatterns 7 | from django.contrib import admin 8 | 9 | from views import * 10 | 11 | 12 | admin.autodiscover() 13 | 14 | urlpatterns = patterns( 15 | '', 16 | (r'^admin/', include(admin.site.urls)), 17 | (r'^webmoney/', include('webmoney.urls')), 18 | ) 19 | 20 | urlpatterns += patterns( 21 | '', 22 | url(r'^success/$', success, name='wm_sample-success'), 23 | url(r'^fail/$', fail, name='wm_sample-fail'), 24 | url(r'^$', simple_payment, name='wm_sample-payment'), 25 | ) 26 | 27 | urlpatterns += staticfiles_urlpatterns() 28 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | 2 | from setuptools import setup 3 | 4 | setup( 5 | name='django-payment-webmoney', 6 | version=__import__('webmoney').__version__, 7 | description='WebMoney Merchant Interface support for Django.', 8 | author='Ivan Fedorov', 9 | author_email='oxyum@oxyum.ru', 10 | url='http://code.google.com/p/django-payment-webmoney/', 11 | packages=['webmoney',], 12 | classifiers=[ 13 | 'Development Status :: 3 - Alpha', 14 | 'Environment :: Web Environment', 15 | 'Intended Audience :: Developers', 16 | 'License :: OSI Approved :: MIT License', 17 | 'Operating System :: OS Independent', 18 | 'Programming Language :: Python', 19 | 'Framework :: Django', 20 | ], 21 | include_package_data=True, 22 | zip_safe=False, 23 | ) 24 | -------------------------------------------------------------------------------- /webmoney/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from webmoney.models import Invoice, Payment, Purse 4 | 5 | 6 | class InvoiceAdmin(admin.ModelAdmin): 7 | list_display = [ 8 | '__unicode__', 'payment_no', 'created_on', 'user', '_is_payed_admin'] 9 | list_filter = ['created_on', 'user'] 10 | date_hierarchy = 'created_on' 11 | ordering = ['-created_on', 'user'] 12 | search_fields = ['payment_no', 'created_on'] 13 | 14 | 15 | class PaymentAdmin(admin.ModelAdmin): 16 | date_hierarchy = 'created_on' 17 | list_display = ['id', 'sys_invs_no', 'sys_trans_no', 'amount', 'invoice'] 18 | list_filter = ['created_on'] 19 | ordering = ['-created_on'] 20 | search_fields = ['id'] 21 | 22 | 23 | class PurseAdmin(admin.ModelAdmin): 24 | list_display = ['__unicode__'] 25 | ordering = ['purse'] 26 | search_fields = ['purse'] 27 | 28 | 29 | admin.site.register(Invoice, InvoiceAdmin) 30 | admin.site.register(Payment, PaymentAdmin) 31 | admin.site.register(Purse, PurseAdmin) 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2010 Ivan Fedorov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /wm_sample/views.py: -------------------------------------------------------------------------------- 1 | from django.template import loader, RequestContext 2 | from helpers import render_to 3 | 4 | from webmoney.forms import * 5 | from webmoney.models import Invoice, Purse 6 | 7 | 8 | @render_to('wm_sample/simple_payment.html') 9 | def simple_payment(request): 10 | response = {} 11 | 12 | initial = { 13 | 'LMI_PAYEE_PURSE': Purse.objects.all()[0], 14 | 'LMI_PAYMENT_NO': Invoice.objects.create(user=request.user).payment_no, 15 | 'LMI_PAYMENT_DESC': loader.render_to_string( 16 | 'wm_sample/simple_payment_desc.txt', 17 | RequestContext(request)).strip()[:255], 18 | } 19 | 20 | response['form'] = PaymentRequestForm(initial=initial) 21 | 22 | return response 23 | 24 | 25 | @render_to('wm_sample/success.html') 26 | def success(request): 27 | response = {} 28 | 29 | if request.method == 'POST': 30 | form = SettledPaymentForm(request.POST) 31 | if form.is_valid(): 32 | response['id'] = form.cleaned_data['LMI_PAYMENT_NO'] 33 | response['sys_invs_no'] = form.cleaned_data['LMI_SYS_INVS_NO'] 34 | response['sys_trans_no'] = form.cleaned_data['LMI_SYS_TRANS_NO'] 35 | response['date'] = form.cleaned_data['LMI_SYS_TRANS_DATE'] 36 | 37 | return response 38 | 39 | 40 | @render_to('wm_sample/fail.html') 41 | def fail(request): 42 | response = {} 43 | if request.method == 'POST': 44 | form = UnSettledPaymentForm(request.POST) 45 | if form.is_valid(): 46 | response['id'] = form.cleaned_data['LMI_PAYMENT_NO'] 47 | response['sys_invs_no'] = form.cleaned_data['LMI_SYS_INVS_NO'] 48 | response['sys_trans_no'] = form.cleaned_data['LMI_SYS_TRANS_NO'] 49 | response['date'] = form.cleaned_data['LMI_SYS_TRANS_DATE'] 50 | 51 | return response 52 | -------------------------------------------------------------------------------- /webmoney/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.utils.translation import ugettext_lazy as _ 3 | 4 | from webmoney import PURSE_RE, WMID_RE 5 | 6 | 7 | class PaymentRequestForm(forms.Form): 8 | LMI_PAYMENT_AMOUNT = forms.DecimalField( 9 | max_digits=7, decimal_places=2, label=_(u'Amount')) 10 | LMI_PAYMENT_DESC = forms.CharField( 11 | label=_(u'Description'), widget=forms.HiddenInput()) 12 | LMI_PAYMENT_NO = forms.IntegerField( 13 | label=_(u'Payment Number'), widget=forms.HiddenInput()) 14 | LMI_PAYEE_PURSE = forms.RegexField( 15 | regex=PURSE_RE, widget=forms.HiddenInput()) 16 | LMI_SIM_MODE = forms.IntegerField( 17 | initial="0", widget=forms.HiddenInput()) 18 | 19 | 20 | class BasePaymentForm(forms.Form): 21 | LMI_PAYMENT_NO = forms.IntegerField(label=_(u'Payment Number')) 22 | 23 | 24 | class ExtraPaymentForm(BasePaymentForm): 25 | # Paymer 26 | LMI_PAYMER_NUMBER = forms.CharField(required=False) 27 | LMI_PAYMER_EMAIL = forms.EmailField(required=False) 28 | 29 | # Telepat 30 | LMI_TELEPAT_PHONENUMBER = forms.CharField(required=False) 31 | LMI_TELEPAT_ORDERID = forms.CharField(required=False) 32 | 33 | # Credit 34 | LMI_PAYMENT_CREDITDAYS = forms.IntegerField(min_value=0, required=False) 35 | 36 | 37 | class PrerequestForm(ExtraPaymentForm): 38 | LMI_PREREQUEST = forms.BooleanField( 39 | label=_('Prerequest flag'), required=False) 40 | LMI_PAYEE_PURSE = forms.RegexField(regex=PURSE_RE) 41 | 42 | LMI_PAYMENT_AMOUNT = forms.DecimalField( 43 | max_digits=7, decimal_places=2, label=_(u'Amount')) 44 | LMI_MODE = forms.IntegerField( 45 | label=_('Test mode'), min_value=0, max_value=1) 46 | 47 | LMI_PAYER_WM = forms.RegexField(regex=WMID_RE) 48 | LMI_PAYER_PURSE = forms.RegexField(regex=PURSE_RE) 49 | 50 | 51 | class PayedPaymentForm(BasePaymentForm): 52 | LMI_SYS_INVS_NO = forms.IntegerField() 53 | LMI_SYS_TRANS_NO = forms.IntegerField() 54 | LMI_SYS_TRANS_DATE = forms.DateTimeField(input_formats=['%Y%m%d %H:%M:%S']) 55 | 56 | 57 | class PaymentNotificationForm(PrerequestForm, PayedPaymentForm): 58 | LMI_HASH = forms.CharField() 59 | 60 | # Please do not USE IT!!! Security flaw! 61 | # LMI_SECRET_KEY 62 | 63 | 64 | class SettledPaymentForm(PayedPaymentForm, ExtraPaymentForm): 65 | pass 66 | 67 | 68 | class UnSettledPaymentForm(PayedPaymentForm, ExtraPaymentForm): 69 | pass 70 | -------------------------------------------------------------------------------- /webmoney/views.py: -------------------------------------------------------------------------------- 1 | try: 2 | from hashlib import md5 3 | except ImportError: 4 | from md5 import md5 5 | 6 | from django.core.exceptions import ObjectDoesNotExist 7 | from django.core.mail import mail_admins 8 | from django.http import ( 9 | HttpResponse, HttpResponseBadRequest, HttpResponseNotAllowed) 10 | 11 | try: 12 | from django.views.decorators.csrf import csrf_exempt 13 | except ImportError: 14 | from django.contrib.csrf.middleware import csrf_exempt 15 | 16 | from webmoney.forms import PrerequestForm, PaymentNotificationForm 17 | from webmoney.models import Invoice, Payment, Purse 18 | from webmoney.signals import webmoney_payment_accepted 19 | 20 | 21 | @csrf_exempt 22 | def result(request): 23 | if request.method != 'POST': 24 | return HttpResponseNotAllowed(permitted_methods=('POST',)) 25 | 26 | form = PrerequestForm(request.POST) 27 | if form.is_valid() and form.cleaned_data['LMI_PREREQUEST']: 28 | payment_no = int(form.cleaned_data['LMI_PAYMENT_NO']) 29 | try: 30 | Invoice.objects.get(payment_no=payment_no) 31 | except ObjectDoesNotExist: 32 | return HttpResponseBadRequest( 33 | "Invoice with number %s not found." % payment_no) 34 | return HttpResponse("YES") 35 | 36 | form = PaymentNotificationForm(request.POST) 37 | if form.is_valid(): 38 | purse = Purse.objects.get(purse=form.cleaned_data['LMI_PAYEE_PURSE']) 39 | 40 | key = "%s%s%s%s%s%s%s%s%s%s" % ( 41 | purse.purse, 42 | form.cleaned_data['LMI_PAYMENT_AMOUNT'], 43 | form.cleaned_data['LMI_PAYMENT_NO'], 44 | form.cleaned_data['LMI_MODE'], 45 | form.cleaned_data['LMI_SYS_INVS_NO'], 46 | form.cleaned_data['LMI_SYS_TRANS_NO'], 47 | form.cleaned_data['LMI_SYS_TRANS_DATE'].strftime( 48 | '%Y%m%d %H:%M:%S'), 49 | purse.secret_key, 50 | form.cleaned_data['LMI_PAYER_PURSE'], 51 | form.cleaned_data['LMI_PAYER_WM'] 52 | ) 53 | 54 | generated_hash = md5(key).hexdigest().upper() 55 | 56 | if generated_hash == form.cleaned_data['LMI_HASH']: 57 | payment = Payment( 58 | payee_purse=purse, 59 | amount=form.cleaned_data['LMI_PAYMENT_AMOUNT'], 60 | payment_no=form.cleaned_data['LMI_PAYMENT_NO'], 61 | mode=form.cleaned_data['LMI_MODE'], 62 | sys_invs_no=form.cleaned_data['LMI_SYS_INVS_NO'], 63 | sys_trans_no=form.cleaned_data['LMI_SYS_TRANS_NO'], 64 | sys_trans_date=form.cleaned_data['LMI_SYS_TRANS_DATE'], 65 | payer_purse=form.cleaned_data['LMI_PAYER_PURSE'], 66 | payer_wm=form.cleaned_data['LMI_PAYER_WM'], 67 | paymer_number=form.cleaned_data['LMI_PAYMER_NUMBER'], 68 | paymer_email=form.cleaned_data['LMI_PAYMER_EMAIL'], 69 | telepat_phonenumber=form.cleaned_data[ 70 | 'LMI_TELEPAT_PHONENUMBER'], 71 | telepat_orderid=form.cleaned_data['LMI_TELEPAT_ORDERID'], 72 | payment_creditdays=form.cleaned_data['LMI_PAYMENT_CREDITDAYS'] 73 | ) 74 | 75 | try: 76 | payment.invoice = Invoice.objects.get( 77 | payment_no=form.cleaned_data['LMI_PAYMENT_NO']) 78 | except ObjectDoesNotExist: 79 | mail_admins( 80 | 'Unprocessed payment without invoice!', 81 | 'Payment NO is %s.' % form.cleaned_data['LMI_PAYMENT_NO'], 82 | fail_silently=True) 83 | 84 | payment.save() 85 | 86 | webmoney_payment_accepted.send( 87 | sender=payment.__class__, payment=payment) 88 | return HttpResponse("OK") 89 | else: 90 | mail_admins( 91 | 'Unprocessed payment with incorrect hash!', 92 | 'Payment NO is %s.' % form.cleaned_data['LMI_PAYMENT_NO'], 93 | fail_silently=True) 94 | return HttpResponseBadRequest("Incorrect hash") 95 | return HttpResponseBadRequest("Unknown error!") 96 | -------------------------------------------------------------------------------- /wm_sample/settings.py: -------------------------------------------------------------------------------- 1 | # Django settings for wm_sample project. 2 | import os.path 3 | 4 | 5 | PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__)) 6 | 7 | DEBUG = True 8 | TEMPLATE_DEBUG = DEBUG 9 | 10 | ADMINS = ( 11 | # ('Your Name', 'your_email@domain.com'), 12 | ) 13 | 14 | MANAGERS = ADMINS 15 | 16 | DATABASES = { 17 | 'default': { 18 | 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. 19 | 'NAME': 'wm_sample.db', # Or path to database file if using sqlite3. 20 | 'USER': '', # Not used with sqlite3. 21 | 'PASSWORD': '', # Not used with sqlite3. 22 | 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. 23 | 'PORT': '', # Set to empty string for default. Not used with sqlite3. 24 | } 25 | } 26 | 27 | # Local time zone for this installation. Choices can be found here: 28 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 29 | # although not all choices may be available on all operating systems. 30 | # On Unix systems, a value of None will cause Django to use the same 31 | # timezone as the operating system. 32 | # If running in a Windows environment this must be set to the same as your 33 | # system time zone. 34 | TIME_ZONE = 'America/Chicago' 35 | 36 | # Language code for this installation. All choices can be found here: 37 | # http://www.i18nguy.com/unicode/language-identifiers.html 38 | LANGUAGE_CODE = 'en-us' 39 | 40 | SITE_ID = 1 41 | 42 | # If you set this to False, Django will make some optimizations so as not 43 | # to load the internationalization machinery. 44 | USE_I18N = True 45 | 46 | # If you set this to False, Django will not format dates, numbers and 47 | # calendars according to the current locale 48 | USE_L10N = True 49 | 50 | # Absolute path to the directory that holds media. 51 | # Example: "/home/media/media.lawrence.com/" 52 | MEDIA_ROOT = '' 53 | 54 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 55 | # trailing slash if there is a path component (optional in other cases). 56 | # Examples: "http://media.lawrence.com", "http://example.com/media/" 57 | MEDIA_URL = '/media/' 58 | 59 | STATIC_URL = '/static/' 60 | 61 | # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a 62 | # trailing slash. 63 | # Examples: "http://foo.com/media/", "/media/". 64 | ADMIN_MEDIA_PREFIX = '/media/' 65 | 66 | # Make this unique, and don't share it with anybody. 67 | SECRET_KEY = 'secret' 68 | 69 | # List of callables that know how to import templates from various sources. 70 | TEMPLATE_LOADERS = ( 71 | 'django.template.loaders.filesystem.Loader', 72 | 'django.template.loaders.app_directories.Loader', 73 | ) 74 | 75 | TEMPLATE_CONTEXT_PROCESSORS = ( 76 | 'django.contrib.auth.context_processors.auth', 77 | 'django.core.context_processors.debug', 78 | 'django.core.context_processors.i18n', 79 | 'django.core.context_processors.csrf', 80 | 'django.core.context_processors.request', 81 | 'django.core.context_processors.media', 82 | 'django.core.context_processors.static', 83 | ) 84 | 85 | MIDDLEWARE_CLASSES = ( 86 | 'django.middleware.common.CommonMiddleware', 87 | 'django.contrib.sessions.middleware.SessionMiddleware', 88 | 'django.middleware.csrf.CsrfViewMiddleware', 89 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 90 | 'django.contrib.messages.middleware.MessageMiddleware', 91 | ) 92 | 93 | ROOT_URLCONF = 'urls' 94 | 95 | TEMPLATE_DIRS = ( 96 | os.path.join(PROJECT_ROOT, 'templates') 97 | ) 98 | 99 | INSTALLED_APPS = ( 100 | 'django.contrib.auth', 101 | 'django.contrib.contenttypes', 102 | 'django.contrib.sessions', 103 | 'django.contrib.sites', 104 | 'django.contrib.messages', 105 | 'django.contrib.admin', 106 | 107 | 'webmoney', 108 | ) 109 | 110 | STATICFILES_FINDERS = ( 111 | 'django.contrib.staticfiles.finders.FileSystemFinder', 112 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 113 | ) 114 | -------------------------------------------------------------------------------- /webmoney/models.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timedelta 2 | from time import sleep 3 | 4 | from django.core.exceptions import ObjectDoesNotExist 5 | from django.db import IntegrityError, models, transaction 6 | from django.conf import settings 7 | 8 | 9 | AUTH_USER_MODEL = getattr(settings, 'AUTH_USER_MODEL', 'auth.User') 10 | 11 | PAYMENT_MODE_CHOICES = ( 12 | (0, 'REAL'), 13 | (1, 'TEST'), 14 | ) 15 | 16 | 17 | class Purse(models.Model): 18 | purse = models.CharField(max_length=13, unique=True) 19 | secret_key = models.CharField(max_length=50) 20 | 21 | def __unicode__(self): 22 | return '%s' % (self.purse, ) 23 | 24 | 25 | class Invoice(models.Model): 26 | user = models.ForeignKey(AUTH_USER_MODEL) 27 | created_on = models.DateTimeField(unique=True, editable=False) 28 | payment_no = models.PositiveIntegerField(unique=True, editable=False) 29 | 30 | def _is_payed_admin(self): 31 | try: 32 | self.payment 33 | return True 34 | except ObjectDoesNotExist: 35 | return False 36 | 37 | _is_payed_admin.boolean = True 38 | _is_payed_admin.short_description = 'is payed' 39 | _is_payed_admin.admin_order_field = 'payment' 40 | 41 | is_payed = property(_is_payed_admin) 42 | 43 | @transaction.commit_manually 44 | def save(self, force_insert=False, force_update=False, using=None): 45 | sid = transaction.savepoint() 46 | if self.pk is None: 47 | i = 1 48 | while self.pk is None: 49 | 50 | if i > 10: 51 | sleep(0.001) 52 | 53 | if i > 20: 54 | # Protection from infinite loop 55 | raise IntegrityError( 56 | 'Too many iterations while generating ' 57 | 'unique Invoice number.' 58 | ) 59 | 60 | try: 61 | self.created_on = datetime.utcnow() 62 | self.created_on = self.created_on - timedelta( 63 | microseconds=self.created_on.microsecond % 100) 64 | 65 | self.payment_no = ( 66 | self.created_on.hour * 3600 + 67 | self.created_on.minute * 60 + 68 | self.created_on.second) * 10000 + ( 69 | self.created_on.microsecond // 100) 70 | super(Invoice, self).save(force_insert, force_update) 71 | 72 | except IntegrityError: 73 | transaction.savepoint_rollback(sid) 74 | 75 | i += 1 76 | else: 77 | super(Invoice, self).save(force_insert, force_update) 78 | 79 | transaction.savepoint_commit(sid) 80 | transaction.commit() 81 | 82 | def __unicode__(self): 83 | return '%s/%s (for: %s)' % ( 84 | self.payment_no, self.created_on.date(), self.user, ) 85 | 86 | 87 | class Payment(models.Model): 88 | created_on = models.DateTimeField(auto_now_add=True, editable=False) 89 | 90 | invoice = models.OneToOneField( 91 | Invoice, blank=True, null=True, related_name='payment') 92 | payee_purse = models.ForeignKey(Purse, related_name='payments') 93 | amount = models.DecimalField(decimal_places=2, max_digits=9) 94 | payment_no = models.PositiveIntegerField(unique=True) 95 | mode = models.PositiveSmallIntegerField(choices=PAYMENT_MODE_CHOICES) 96 | 97 | sys_invs_no = models.PositiveIntegerField() 98 | sys_trans_no = models.PositiveIntegerField() 99 | sys_trans_date = models.DateTimeField() 100 | 101 | payer_purse = models.CharField(max_length=13) 102 | payer_wm = models.CharField(max_length=12) 103 | 104 | paymer_number = models.CharField(max_length=30, blank=True) 105 | paymer_email = models.EmailField(blank=True) 106 | 107 | telepat_phonenumber = models.CharField(max_length=30, blank=True) 108 | telepat_orderid = models.CharField(max_length=30, blank=True) 109 | 110 | payment_creditdays = models.PositiveIntegerField(blank=True, null=True) 111 | 112 | def __unicode__(self): 113 | return "%s - %s WM%s" % ( 114 | self.payment_no, self.amount, self.payee_purse.purse[0]) 115 | --------------------------------------------------------------------------------