PKCH#,Q,,payu/__init__.pydefault_app_config = 'payu.apps.PayuConfig' PKcEHlYkk payu/api.pyimport json import hashlib from django.views.decorators.http import require_http_methods from django.utils.html import escape from django.views.decorators.csrf import csrf_exempt from django.http import HttpResponse from .models import Payment @require_http_methods(['POST']) @csrf_exempt def notify(request): signature = escape(request.META.get('HTTP_OPENPAYU_SIGNATURE')).split(';') signature_data = {} for param in signature: try: param = param.split('=') signature_data[param[0]] = param[1] except IndexError: continue try: incoming_signature = signature_data['signature'] except KeyError: return HttpResponse(status=400) second_md5_key = Payment.get_payu_params()['second_md5_key'].encode('utf-8') expected_signature = hashlib.md5(request.body + second_md5_key).hexdigest() if incoming_signature != expected_signature: return HttpResponse(status=403) try: data = json.loads(request.body.decode('utf-8')) payu_order_id = escape(data['order']['orderId']) internal_id = escape(data['order']['extOrderId']) except: return HttpResponse(status=400) try: payment = Payment.objects.exclude(status='COMPLETED').get(id=internal_id, payu_order_id=payu_order_id) except (Payment.DoesNotExist, ValueError): return HttpResponse(status=200) status = escape(data['order']['status']) if status in ('PENDING', 'WAITING_FOR_CONFIRMATION', 'COMPLETED', 'CANCELED', 'REJECTED'): payment.status = status payment.save() return HttpResponse(status=200) PKAEHd payu/setup.pyfrom setuptools import setup, find_packages setup( name='django-payu-payments', version='0.1.0', description='PayU payments system intergration.', url='https://github.com/michalwerner/django-payu-payments', author='Michal Werner', author_email='werner@hurtowniapixeli.pl', license='MIT', classifiers=[ 'Development Status :: 3 - Alpha', 'Environment :: Web Environment', 'Framework :: Django', 'Intended Audience :: Developers', 'Topic :: Software Development :: Python Modules', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 3' ], packages=find_packages(), install_requires=['django', 'requests', 'django-ipware'], ) PKKEHU{{ payu/urls.pyfrom django.conf.urls import url, include urlpatterns = [ url(r'^api/', include('payu.urls_api', namespace='api')) ] PK#dEH}EI payu/models.pyimport uuid import json import requests from django.db import models from django.utils.translation import ugettext_lazy as _ from django.conf import settings from django.contrib.postgres.fields import JSONField from django.core.urlresolvers import reverse from django.utils.html import escape from django.core.exceptions import ImproperlyConfigured from ipware.ip import get_real_ip, get_ip ENDPOINT_URL = 'https://secure.payu.com/api/v2_1/orders' OAUTH_URL = 'https://secure.payu.com/pl/standard/user/oauth/authorize' TEST_POS_ID = 145227 TEST_MD5_KEY = '12f071174cb7eb79d4aac5bc2f07563f' TEST_SECOND_MD5_KEY = '13a980d4f851f3d9a1cfc792fb1f5e50' STATUS_CHOICES = ( ('NEW', _('New')), ('PENDING', _('Pending')), ('WAITING_FOR_CONFIRMATION', _('Waiting for confirmation')), ('COMPLETED', _('Completed')), ('CANCELED', _('Canceled')), ('REJECTED', _('Rejected')), ) class Payment(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) payu_order_id = models.CharField(_('PayU order ID'), max_length=255) pos_id = models.CharField(_('PayU POS ID'), max_length=255) customer_ip = models.CharField(_('customer IP'), max_length=255) created = models.DateTimeField(_('creation date'), auto_now_add=True, editable=True) status = models.CharField(_('status'), max_length=255, choices=STATUS_CHOICES, default='NEW') total = models.PositiveIntegerField(_('total')) description = models.TextField(_('description'), null=True, blank=True) products = JSONField(_('products'), default='', blank=True) notes = models.TextField(_('notes'), null=True, blank=True) class Meta: app_label = 'payu' verbose_name = _('payment') verbose_name_plural = _('payments') def __str__(self): return str(self.id) @staticmethod def get_payu_params(): try: payu_settings = settings.PAYU if payu_settings['test']: return { 'pos_id': TEST_POS_ID, 'md5_key': TEST_MD5_KEY, 'second_md5_key': TEST_SECOND_MD5_KEY, 'continue_path': payu_settings['continue_path'] } else: return { 'pos_id': payu_settings['pos_id'], 'md5_key': payu_settings['md5_key'], 'second_md5_key': payu_settings['second_md5_key'], 'continue_path': payu_settings['continue_path'] } except (AttributeError, KeyError): raise ImproperlyConfigured('PayU settings does not exist or not complete.') @classmethod def get_oauth_token(cls): params = cls.get_payu_params() oauth_request_data = { 'grant_type': 'client_credentials', 'client_id': params['pos_id'], 'client_secret': params['md5_key'] } try: oauth_request = requests.post(OAUTH_URL, data=oauth_request_data) response = oauth_request.json() return response['access_token'] except: return False @classmethod def create(cls, request, description, products, buyer, notes=None): params = cls.get_payu_params() try: processed_products = [{ 'name': p['name'], 'unitPrice': int(p['unitPrice']*100), 'quantity': p['quantity'] } for p in products] except (KeyError, ValueError): raise ValueError('Invalid list of products.') total = 0 for p in processed_products: total += p['unitPrice'] * p['quantity'] customer_ip = get_real_ip(request) or get_ip(request) payment = cls( pos_id=params['pos_id'], customer_ip=customer_ip, total=total, description=description, products=json.dumps(processed_products), notes=notes ) payment_request_data = { 'extOrderId': str(payment.id), 'customerIp': customer_ip, 'merchantPosId': params['pos_id'], 'description': description, 'currencyCode': 'PLN', 'totalAmount': total, 'products': processed_products, 'buyer': buyer, 'settings': {'invoiceDisabled': True}, 'notifyUrl': request.build_absolute_uri(reverse('payu:api:notify')), 'continueUrl': request.build_absolute_uri(params['continue_path']), } payment_request_headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer {}'.format(cls.get_oauth_token()) } try: payment_request = requests.post(ENDPOINT_URL, json=payment_request_data, headers=payment_request_headers, allow_redirects=False) response = payment_request.json() payu_order_id = escape(response['orderId']) redirect_url = response['redirectUri'] except: return False payment.payu_order_id = payu_order_id payment.save() return redirect_url PKPH|5Ő payu/admin.pyimport json from decimal import Decimal from django.contrib import admin from django.utils.translation import ugettext as _ from django.utils.html import format_html, mark_safe from django.contrib.humanize.templatetags.humanize import intcomma from django.conf import settings from .models import Payment @admin.register(Payment) class PaymentAdmin(admin.ModelAdmin): def has_add_permission(self, request): return False def get_status(self, obj): if obj.status == 'COMPLETED': color = '#009200' elif obj.status in ('CANCELED', 'REJECTED'): color = '#e60000' else: color = None if color: return format_html('{}', color, obj.get_status_display()) else: return obj.get_status_display() get_status.short_description = _('Status') def get_products(self, obj): products = json.loads(obj.products) try: if not products: return '' output = format_html('', _('Product'), _('Unit price'), _('Quantity'), _('Sum')) for p in products: unit_price = intcomma(round(Decimal(p['unitPrice'] / 100), 2)) product_sum = intcomma(round(Decimal(p['unitPrice'] / 100), 2) * p['quantity']) output += format_html('', p['name'], unit_price, p['quantity'], product_sum) output += '
{}{}{}{}
{}{} PLN{}{} PLN
' return mark_safe(output) except (KeyError, ValueError): return _('Invalid data.') get_products.short_description = _('Products') def get_total(self, obj): return '{} PLN'.format(intcomma(round(Decimal(obj.total / 100), 2))) get_total.short_description = _('Total') list_display = ('id', 'payu_order_id', 'pos_id', 'created', 'get_status', 'get_total') list_filter = ('status',) readonly_fields = ('id', 'payu_order_id', 'pos_id', 'customer_ip', 'created', 'get_status', 'get_total', 'description', 'get_products') fieldsets = [ (None, { 'fields': (('id', 'payu_order_id'), ('pos_id', 'customer_ip'), 'created', 'description', 'get_status', 'get_products', 'get_total', 'notes') }), ] if 'grappelli' in settings.INSTALLED_APPS: change_list_template = 'admin/change_list_filter_sidebar.html' change_list_filter_template = 'admin/filter_listing.html' PKEH[،payu/urls_api.pyfrom django.conf.urls import url # callable views from .api import notify urlpatterns = [ url(r'^notify/$', notify, name='notify') ] PK'CH}Ψ payu/apps.pyfrom django.apps import AppConfig from django.utils.translation import ugettext_lazy as _ class PayuConfig(AppConfig): name = 'payu' verbose_name = _('PayU') PK{CHpayu/migrations/__init__.pyPKPEHF9payu/migrations/0001_initial.py# -*- coding: utf-8 -*- # Generated by Django 1.9.2 on 2016-02-05 10:07 from __future__ import unicode_literals import django.contrib.postgres.fields.jsonb from django.db import migrations, models import uuid class Migration(migrations.Migration): initial = True dependencies = [ ] operations = [ migrations.CreateModel( name='Payment', fields=[ ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('payu_order_id', models.CharField(max_length=255, verbose_name='PayU order ID')), ('pos_id', models.CharField(max_length=255, verbose_name='PayU POS ID')), ('customer_ip', models.CharField(max_length=255, verbose_name='customer IP')), ('created', models.DateTimeField(auto_now_add=True, verbose_name='creation date')), ('status', models.CharField(choices=[('NEW', 'New'), ('PENDING', 'Pending'), ('WAITING_FOR_CONFIRMATION', 'Waiting for confirmation'), ('COMPLETED', 'Completed'), ('CANCELED', 'Canceled'), ('REJECTED', 'Rejected')], default='NEW', max_length=255, verbose_name='status')), ('total', models.PositiveIntegerField(verbose_name='total')), ('description', models.TextField(blank=True, null=True, verbose_name='description')), ('products', django.contrib.postgres.fields.jsonb.JSONField(blank=True, default='', verbose_name='products')), ('notes', models.TextField(blank=True, null=True, verbose_name='notes')), ], options={ 'verbose_name_plural': 'payments', 'verbose_name': 'payment', }, ), ] PKPH^- 4django_payu_payments-0.1.1.dist-info/DESCRIPTION.rstUNKNOWN PKPH$j3django_payu_payments-0.1.1.dist-info/SOURCES.txt.pyXXXXXXX XXXXXXXXXXX XXXXXXXXXX XXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXX XXXXXXXXXXX XXXXXXXXXXXX XXXXXXXXXXXXXX XXXXXXXXXXXXX XXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXPKPH2<django_payu_payments-0.1.1.dist-info/dependency_links.txt.py PKPHL 2django_payu_payments-0.1.1.dist-info/metadata.json{"classifiers": ["Development Status :: 3 - Alpha", "Intended Audience :: Developers", "Environment :: Web Environment", "Framework :: Django", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3"], "extensions": {"python.details": {"contacts": [{"email": "werner@hurtowniapixeli.pl", "name": "Michal Werner", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/michalwerner/django-payu-payments"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "license": "MIT", "metadata_version": "2.0", "name": "django-payu-payments", "run_requires": [{"requires": ["django", "django-ipware", "requests"]}], "summary": "PayU integration for Django.", "version": "0.1.1"}PKPHF@4django_payu_payments-0.1.1.dist-info/requires.txt.pyXXXXXX XXXXXXXX XXXXXXXXXXXXX PKPHưU2django_payu_payments-0.1.1.dist-info/top_level.txtpayu PKPH05django_payu_payments-0.1.1.dist-info/top_level.txt.pyXXXX PKPH}\\*django_payu_payments-0.1.1.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py3-none-any PKPHJ-django_payu_payments-0.1.1.dist-info/METADATAMetadata-Version: 2.0 Name: django-payu-payments Version: 0.1.1 Summary: PayU integration for Django. Home-page: https://github.com/michalwerner/django-payu-payments Author: Michal Werner Author-email: werner@hurtowniapixeli.pl License: MIT Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Developers Classifier: Environment :: Web Environment Classifier: Framework :: Django Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 3 Requires-Dist: django Requires-Dist: django-ipware Requires-Dist: requests UNKNOWN PKPH+django_payu_payments-0.1.1.dist-info/RECORDdjango_payu_payments-0.1.1.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10 django_payu_payments-0.1.1.dist-info/METADATA,sha256=QTiF1J8Ghj_iLLWgx4Z40eMt2ADPxqF6rW4xsVYrX-Q,654 django_payu_payments-0.1.1.dist-info/RECORD,, django_payu_payments-0.1.1.dist-info/SOURCES.txt.py,sha256=EHEsMdpxep-owoDUojYLwBxuQvYFOBYbHzPKZeRmkFE,470 django_payu_payments-0.1.1.dist-info/WHEEL,sha256=zX7PHtH_7K-lEzyK75et0UBa3Bj8egCBMXe1M4gc6SU,92 django_payu_payments-0.1.1.dist-info/dependency_links.txt.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 django_payu_payments-0.1.1.dist-info/metadata.json,sha256=A7ii44WxpxLUSxwnR3CkHnyLcolesEO33M1EejXEG4A,796 django_payu_payments-0.1.1.dist-info/requires.txt.py,sha256=xRnL_P258evCo1jppR52z_mfzU4S1RzHkooerWso_bA,30 django_payu_payments-0.1.1.dist-info/top_level.txt,sha256=GWMJjZJPd6Iy45RyBaQYXGNwq8qSvV-b7PdUYZT0bsg,5 django_payu_payments-0.1.1.dist-info/top_level.txt.py,sha256=EYYeqi5w6Kxz2dIM0XK2pTlssBFv7Vv-QyygdRRLLUg,5 payu/__init__.py,sha256=uNFS-xOS60sJpG2rsg1DxwiOJCOMG8L4G_ZxXVMH9Zo,44 payu/admin.py,sha256=TJBDqPEJOgpNjI9RZKC9pvfLEFzhUkrsRYQ4YMsKaDc,2704 payu/api.py,sha256=6I8n-DKRZaO7ihOIXZtyqV4DoF5vaKo4Ghjf0AfEY0M,1643 payu/apps.py,sha256=FZVYEmVerxwFv3wn_7IeVPMvWtJvtQ8E68gFmyFjBYY,168 payu/models.py,sha256=DJLEzV22AfG66M2tFYyragKitcbWoScniBGx7n2mXcE,5325 payu/setup.py,sha256=CR7NXbYdb-z_V4I6PIkc3U_RtnXm3U_gVDGlBsyH7Og,751 payu/urls.py,sha256=pkAXSFyOzDJCrI8F7DL6GaUKuXMfJxYcHox7pgKFk2o,123 payu/urls_api.py,sha256=uxbCScZu3yecf4yV0JiEHF6SJ6YTXsogd6jqOSgUoJY,140 payu/migrations/0001_initial.py,sha256=DrvsxDRMocpazfsEDmfNs-IY4GTqsZ5IViOPVoP4nFg,1745 payu/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 PKCH#,Q,,payu/__init__.pyPKcEHlYkk Zpayu/api.pyPKAEHd payu/setup.pyPKKEHU{{  payu/urls.pyPK#dEH}EI  payu/models.pyPKPH|5Ő payu/admin.pyPKEH[،a*payu/urls_api.pyPK'CH}Ψ +payu/apps.pyPK{CH+payu/migrations/__init__.pyPKPEHF9&,payu/migrations/0001_initial.pyPKPH^- 443django_payu_payments-0.1.1.dist-info/DESCRIPTION.rstPKPH$j33django_payu_payments-0.1.1.dist-info/SOURCES.txt.pyPKPH2<5django_payu_payments-0.1.1.dist-info/dependency_links.txt.pyPKPHL 26django_payu_payments-0.1.1.dist-info/metadata.jsonPKPHF@4~9django_payu_payments-0.1.1.dist-info/requires.txt.pyPKPHưU29django_payu_payments-0.1.1.dist-info/top_level.txtPKPH05C:django_payu_payments-0.1.1.dist-info/top_level.txt.pyPKPH}\\*:django_payu_payments-0.1.1.dist-info/WHEELPKPHJ-?;django_payu_payments-0.1.1.dist-info/METADATAPKPH+>django_payu_payments-0.1.1.dist-info/RECORDPK//E