Skip to content

Instantly share code, notes, and snippets.

@Znack
Created June 4, 2015 12:15
Show Gist options
  • Save Znack/6edc51d79b67d3c8bea7 to your computer and use it in GitHub Desktop.
Save Znack/6edc51d79b67d3c8bea7 to your computer and use it in GitHub Desktop.
# -*- coding: utf-8 -*-
from hashlib import md5
from urllib import urlencode
from django.conf import settings
_ADDRESS = 'https://auth.robokassa.ru/Merchant/Index.aspx?'
_EXTRA_PARAMS_PREFIX = 'shp_'
_COST = settings.CARDBOARD_COST
_LOGIN = settings.ROBOKASSA['LOGIN']
_PASSWORD1 = settings.ROBOKASSA['PASSWORD1']
_PASSWORD2 = settings.ROBOKASSA['PASSWORD2']
class RobokassaBaseException(Exception):
def __init__(self, message):
self.message = message
def __str__(self):
return '%s: %s.' % (self.__class__.__name__, self.message)
class InvalidRequestError(RobokassaBaseException):
pass
class InvalidSignError(RobokassaBaseException):
pass
class IncorrectAmountError(RobokassaBaseException):
pass
class InitializationHandler():
def __init__(self, amount, **kwargs):
self._amount = amount
self._extra_params = kwargs
def get_url(self):
params = self._get_params()
return _ADDRESS + params
def _get_params(self):
params = {
'OutSum': unicode(self._amount),
}
sign_extra_parts = self._set_extra_params(params)
self._set_auth_params(params, sign_extra_parts)
for k, v in params.iteritems():
params[k] = v.encode('utf-8')
return urlencode(params)
def _set_extra_params(self, params):
for key in sorted(self._extra_params.keys()):
formatted_key = _EXTRA_PARAMS_PREFIX + key
value = self._extra_params[key]
params[formatted_key] = value
yield '='.join([formatted_key, value])
def _set_auth_params(self, params, sign_extra_parts):
params.update({
'MrchLogin': _LOGIN,
'SignatureValue': self._get_sign(sign_extra_parts)
})
def _get_sign(self, extra_parts):
sign_src = ':'.join([_LOGIN, str(self._amount), ':' + _PASSWORD1] + [
part for part in extra_parts])
return md5(sign_src.encode('utf-8')).hexdigest()
class ResultHandler():
def __init__(self, data):
try:
self._sign = data['SignatureValue']
self._amount = data['OutSum']
self.transaction_id = data['InvId']
self.form_data = {}
sign_extra_parts = self._get_extra_params(data)
except KeyError as e:
raise InvalidRequestError('key %s not found' % e.args[0])
self._check_sign(sign_extra_parts)
self._check_amount()
def _get_extra_params(self, data):
for key in sorted(data.keys()):
value = data[key]
formatted_key = key.replace(_EXTRA_PARAMS_PREFIX, '')
if formatted_key == key:
continue
form_value = value.encode('cp1251').decode('utf-8')
self.form_data[formatted_key] = form_value
yield '='.join([key, value])
def _check_sign(self, sign_extra_parts):
if self._sign.lower() != self._get_sign(sign_extra_parts).lower():
raise InvalidSignError('sign %s is not valid' % self._sign)
def _get_sign(self, extra_parts):
sign_src = ':'.join(
[str(self._amount), str(self.transaction_id), _PASSWORD2] + [
part for part in extra_parts])
return md5(sign_src.encode('utf-8')).hexdigest()
def _check_amount(self):
expected_amount = _COST * int(self.form_data['quantity'])
if long(float(self._amount)) != long(expected_amount):
raise IncorrectAmountError(
'received %s, expected %s' % (self._amount, _COST))
from django.http.response import HttpResponseNotAllowed, HttpResponse, \
HttpResponseBadRequest, HttpResponseForbidden
from django.shortcuts import render, redirect
from django.conf import settings
from django.views.decorators.csrf import csrf_exempt
from project.core.forms import BuyRequestForm
from project.core.payments.robokassa import InitializationHandler, \
ResultHandler, InvalidRequestError, InvalidSignError, IncorrectAmountError
from project.core.models import BuyRequest
# Create your views here.
def get_main_page(request):
form = BuyRequestForm()
return render(request, 'index.html', {'form': form}, )
def sent_buy_request(request):
if request.method == 'POST':
form = BuyRequestForm(request.POST)
if form.is_valid():
if form.cleaned_data['delivery'] == '1':
quantity = form.cleaned_data['quantity']
extra_fields = {
'name': request.POST['name'],
'phone': request.POST['phone'],
'email': request.POST['email'],
'city': request.POST['city'],
'address': request.POST['address'],
'quantity': str(quantity),
'delivery': request.POST['delivery'],
}
final_amount = settings.CARDBOARD_COST * quantity
handler = InitializationHandler(
final_amount, **extra_fields)
return redirect(handler.get_url())
else:
form.save()
return render(request, 'index.html', {'form': BuyRequestForm(), 'success': True})
else:
form = BuyRequestForm()
return render(request, 'index.html', {'form': form, 'success': False})
@csrf_exempt
def robokassa_result_handler(request):
if request.method != 'POST':
return HttpResponseNotAllowed(['POST'])
try:
handler = ResultHandler(request.POST.dict())
except (InvalidRequestError, InvalidSignError, IncorrectAmountError) as e:
if isinstance(e, InvalidRequestError):
return HttpResponseBadRequest()
elif isinstance(e, InvalidSignError):
return HttpResponseForbidden()
else:
return HttpResponse('Invalid amount.')
form = BuyRequestForm(handler.form_data)
form.save()
return HttpResponse('OK' + handler.transaction_id)
@csrf_exempt
def successful_order_handler(request):
if request.method != 'POST':
return HttpResponseNotAllowed(['POST'])
return render(
request, 'index.html', {'form': BuyRequestForm(), 'success': True})
@csrf_exempt
def failed_order_handler(request):
if request.method != 'POST':
return HttpResponseNotAllowed(['POST'])
return render(
request, 'index.html', {'form': BuyRequestForm(), 'success': False})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment