Skip to content

Commit

Permalink
Merge branch 'feature/python3' into develop (Resolves #2)
Browse files Browse the repository at this point in the history
  • Loading branch information
dessibelle committed Feb 17, 2015
2 parents 85831c1 + 0f3a534 commit de6883b
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 64 deletions.
6 changes: 3 additions & 3 deletions .travis.yml
Expand Up @@ -7,9 +7,9 @@ script:
env:
- PIP_DOWNLOAD_CACHE=pip_cache TOXENV=py26
- PIP_DOWNLOAD_CACHE=pip_cache TOXENV=py27
# - PIP_DOWNLOAD_CACHE=pip_cache TOXENV=py32
# - PIP_DOWNLOAD_CACHE=pip_cache TOXENV=py33
# - PIP_DOWNLOAD_CACHE=pip_cache TOXENV=py34
- PIP_DOWNLOAD_CACHE=pip_cache TOXENV=py32
- PIP_DOWNLOAD_CACHE=pip_cache TOXENV=py33
- PIP_DOWNLOAD_CACHE=pip_cache TOXENV=py34
- PIP_DOWNLOAD_CACHE=pip_cache TOXENV=coverage
- PIP_DOWNLOAD_CACHE=pip_cache TOXENV=flake8
after_success: coveralls
Expand Down
7 changes: 4 additions & 3 deletions payer_api/order.py
@@ -1,3 +1,4 @@
from __future__ import unicode_literals


class PayerProcessingControl(object):
Expand Down Expand Up @@ -44,7 +45,7 @@ class PayerOrderItem(DictObject):
def __init__(self, description, price_including_vat, vat_percentage,
*args, **kwargs):

self.description = unicode(description)
self.description = str(description)
self.price_including_vat = float(price_including_vat)
self.vat_percentage = float(vat_percentage)
self.quantity = int(kwargs.get('quantity', 1))
Expand All @@ -54,7 +55,7 @@ class PayerOrder(object):

def __init__(self, order_id, *args, **kwargs):

self.order_id = unicode(order_id)
self.order_id = str(order_id)
self.buyer_details = kwargs.get('buyer_details', PayerBuyerDetails())
self.description = kwargs.get('description',
'Order #%s' % self.order_id or '')
Expand All @@ -65,7 +66,7 @@ def add_order_item(self, order_item):
self.order_items.append(order_item)

def add_info_line(self, info_line):
self.info_lines.append(unicode(info_line))
self.info_lines.append(str(info_line))

def set_buyer_details(self, buyer_details):
self.buyer_details = buyer_details
20 changes: 13 additions & 7 deletions payer_api/postapi.py
Expand Up @@ -8,8 +8,11 @@
)
import base64
import hashlib
from xml import PayerXMLDocument
import urlparse
from .xml import PayerXMLDocument
try: # NOQA
from urllib.parse import urlparse, parse_qsl # NOQA
except: # NOQA
from urlparse import urlparse, parse_qsl # NOQA


class PayerPostAPI(object):
Expand Down Expand Up @@ -78,7 +81,10 @@ def get_checksum(self, data):
if not self.key_2:
raise PayerPostAPIError(PayerPostAPIError.ERROR_MISSING_KEY_2)

return hashlib.md5(self.key_1 + data + self.key_2).hexdigest()
data = self.key_1 + data + self.key_2
data = data.encode(self.encoding)

return hashlib.md5(data).hexdigest()

def _generate_xml(self):

Expand Down Expand Up @@ -119,10 +125,10 @@ def get_base64_data(self, xml_data=None, *args, **kwargs):
if not xml_data:
xml_data = self.get_xml_data(*args, **kwargs)

return base64.b64encode(xml_data)
return base64.b64encode(xml_data.encode(self.encoding))

def get_post_data(self):
base64_data = self.get_base64_data()
base64_data = self.get_base64_data().decode(self.encoding)

return {
'payer_agentid': self.get_agent_id(),
Expand Down Expand Up @@ -153,8 +159,8 @@ def validate_callback_url(self, url):
return True

try:
url_parts = urlparse.urlparse(url)
query = dict(urlparse.parse_qsl(url_parts.query,
url_parts = urlparse(url)
query = dict(parse_qsl(url_parts.query,
keep_blank_values=True))
supplied_checksum = query.pop('md5sum').lower()

Expand Down
32 changes: 18 additions & 14 deletions payer_api/tests/test_api.py
Expand Up @@ -78,14 +78,15 @@ def test_settings(self):

def test_checksums(self):

def check_checksums(xml_data, b64_data):
checksum = self.api.get_checksum(b64_data)
expected_checksum = hashlib.md5(
"6866ef97a972ba3a2c6ff8bb2812981054770162" +
base64.b64encode(xml_data) +
"1388ac756f07b0dda2961436ba8596c7b7995e94").hexdigest()
def check_checksums(xml, b64_data):
checksum = self.api.get_checksum(b64_data.decode('utf-8'))

self.assertEqual(checksum, expected_checksum)
data = "6866ef97a972ba3a2c6ff8bb2812981054770162" + \
base64.b64encode(xml.encode('utf-8')).decode('utf-8') + \
"1388ac756f07b0dda2961436ba8596c7b7995e94"

expected_checksum = hashlib.md5(data.encode('utf-8')).hexdigest()
self.assertEqual(expected_checksum, checksum)

xml_data = self.api.get_xml_data()
b64_data = self.api.get_base64_data()
Expand All @@ -96,10 +97,11 @@ def check_checksums(xml_data, b64_data):
checksum = self.api.get_checksum("Shrimp sandwich")
self.assertEqual("46b4c1ae6b8529a93d39b3f8b821ae9d", checksum)

checksum = self.api.get_checksum("Räksmörgås")
checksum = self.api.get_checksum('R\xe4ksm\xf6rg\xe5s')
self.assertEqual("89e526a8d65ddb803b952c93c6a6c10a", checksum)

checksum = self.api.get_checksum(base64.b64encode("Räksmörgås"))
b64_data = base64.b64encode('R\xe4ksm\xf6rg\xe5s'.encode("utf-8"))
checksum = self.api.get_checksum(b64_data.decode("utf-8"))
self.assertEqual("ed4e18a2088e023ba637dc47108b93bd", checksum)

def test_callback_validation(self):
Expand Down Expand Up @@ -129,17 +131,19 @@ def test_callback_validation(self):
# be erroneously encoded, e.g. enecoded @ characters in query string

def add_query_params(url, params):
qsl = ["%s=%s" % (k, v,) for k, v in params.iteritems()]
qsl = ["%s=%s" % (k, v,) for k, v in params.items()]
return "&".join([url] + qsl)

auth_url = add_query_params(auth_url, ad)
settle_url = add_query_params(settle_url, sd)

def get_md5_sum(url):
return hashlib.md5(
"6866ef97a972ba3a2c6ff8bb2812981054770162" +
url +
"1388ac756f07b0dda2961436ba8596c7b7995e94").hexdigest()

data = "6866ef97a972ba3a2c6ff8bb2812981054770162" + \
url + \
"1388ac756f07b0dda2961436ba8596c7b7995e94"

return hashlib.md5(data.encode('utf-8')).hexdigest()

auth_url_test = "%s&md5sum=%s" % (
auth_url, get_md5_sum(auth_url))
Expand Down
29 changes: 17 additions & 12 deletions payer_api/tests/test_xml.py
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
import unittest
import urlparse
from payer_api.tests import TestCase
from payer_api.xml import PayerXMLDocument
from payer_api.order import (
Expand All @@ -18,6 +18,10 @@
PAYMENT_METHOD_INVOICE,
)
from lxml import etree
try: # NOQA
from urllib.parse import urlparse, parse_qsl # NOQA
except: # NOQA
from urlparse import urlparse, parse_qsl # NOQA


class XMLTestCase(TestCase):
Expand Down Expand Up @@ -160,8 +164,8 @@ def test_urls(self):
'empty': '',
})

url_parts = list(urlparse.urlparse(url))
params = dict(urlparse.parse_qsl(url_parts[4], keep_blank_values=True))
url_parts = list(urlparse(url))
params = dict(parse_qsl(url_parts[4], keep_blank_values=True))

self.assertEqual(
set(params.keys()),
Expand All @@ -174,8 +178,8 @@ def test_urls(self):
'empty': '',
})

url_parts = list(urlparse.urlparse(url))
params = dict(urlparse.parse_qsl(url_parts[4], keep_blank_values=True))
url_parts = list(urlparse(url))
params = dict(parse_qsl(url_parts[4], keep_blank_values=True))

self.assertEqual(
set(params.keys()),
Expand All @@ -191,31 +195,32 @@ def test_urls(self):

def test_xml(self):
xml = self.xml_document.tostring()
self.assertEqual(xml, str(self.xml_document))
self.assertEqual(str(self.xml_document), xml)

try:
parser = etree.XMLParser(dtd_validation=False)
root = etree.fromstring(xml, parser)
root = etree.fromstring(xml.encode('utf-8'), parser)
if root is None:
raise Exception("No XML data")
except Exception as e:
raise e

def missing_dtd():
parser = etree.XMLParser(dtd_validation=True)
root = etree.fromstring(xml, parser)
root = etree.fromstring(xml.encode('utf-8'), parser)
assert root

def malformed_xml():
parser = etree.XMLParser(dtd_validation=False)
root = etree.fromstring(xml + "malformed xml", parser)
root = etree.fromstring(xml.encode('utf-8') +
b"malformed xml", parser)
assert root

self.assertRaises(etree.XMLSyntaxError, missing_dtd)
self.assertRaises(etree.XMLSyntaxError, malformed_xml)

xml = self.xml_document.tostring()
root = etree.fromstring(xml)
root = etree.fromstring(xml.encode('utf-8'))

currency = root.xpath("/payread_post_api_0_2/purchase/currency/text()")
self.assertEqual(currency[0], 'SEK')
Expand All @@ -225,7 +230,7 @@ def malformed_xml():

self.xml_document.message = "Foo bar"
xml = self.xml_document.tostring(rebuild_tree=True)
root = etree.fromstring(xml)
root = etree.fromstring(xml.encode('utf-8'))

message = root.xpath("/payread_post_api_0_2/purchase/message/text()")
self.assertEqual(message[0], "Foo bar")
Expand All @@ -239,7 +244,7 @@ def malformed_xml():

self.xml_document.payment_methods = []
xml = self.xml_document.tostring(rebuild_tree=True)
root = etree.fromstring(xml)
root = etree.fromstring(xml.encode('utf-8'))
payment_methods = root.xpath(
"/payread_post_api_0_2/database_overrides" +
"/accepted_payment_methods")
Expand Down
48 changes: 26 additions & 22 deletions payer_api/xml.py
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from . import (
PAYMENT_METHOD_CARD,
PAYMENT_METHOD_BANK,
Expand All @@ -7,9 +8,12 @@
DEBUG_MODE_SILENT,
)
from lxml import etree as ET
import urllib
import urlparse
import StringIO
from io import BytesIO
try: # NOQA
from urllib.parse import urlparse, parse_qsl, urlunparse, urlencode # NOQA
except: # NOQA
from urlparse import urlparse, parse_qsl, urlunparse # NOQA
from urllib import urlencode # NOQA


class PayerXMLDocument(object):
Expand Down Expand Up @@ -67,20 +71,20 @@ def _build_xml_tree(self):
ET.SubElement(seller_details, 'agent_id').text = self.agent_id

buyer_details = ET.SubElement(self.root, 'buyer_details')
for key, value in self.order.buyer_details.as_dict().iteritems():
for key, value in self.order.buyer_details.as_dict().items():
if value:
ET.SubElement(buyer_details, key).text = \
unicode(value) if value else ''
str(value) if value else ''

# purchase element
purchase = ET.SubElement(self.root, 'purchase')
ET.SubElement(purchase, 'currency').text = unicode(self.currency)
ET.SubElement(purchase, 'currency').text = str(self.currency)
ET.SubElement(purchase, 'description').text = \
unicode(self.order.description)
str(self.order.description)
ET.SubElement(purchase, 'reference_id').text = \
unicode(self.order.order_id)
str(self.order.order_id)
if self.message is not None:
ET.SubElement(purchase, 'message').text = unicode(self.message)
ET.SubElement(purchase, 'message').text = str(self.message)
ET.SubElement(purchase, 'hide_details').text = \
"true" if self.hide_details else "false"

Expand All @@ -93,21 +97,21 @@ def _build_xml_tree(self):
data = item.as_dict()

ET.SubElement(freeform_purchase, 'line_number').text = \
unicode(idx + 1)
str(idx + 1)
ET.SubElement(freeform_purchase, 'description').text = \
unicode(data.get('description', 'Product'))
str(data.get('description', 'Product'))
ET.SubElement(freeform_purchase, 'price_including_vat').text = \
unicode("%.2f" % float(data.get('price_including_vat', 0)))
str("%.2f" % float(data.get('price_including_vat', 0)))
ET.SubElement(freeform_purchase, 'vat_percentage').text = \
unicode("%.2f" % float(data.get('vat_percentage', 25)))
str("%.2f" % float(data.get('vat_percentage', 25)))
ET.SubElement(freeform_purchase, 'quantity').text = \
unicode(data.get('quantity', 1))
str(data.get('quantity', 1))

for idx, value in enumerate(self.order.info_lines):
info_line = ET.SubElement(purchase_list, 'info_line')

ET.SubElement(info_line, 'line_number').text = unicode(3000 + idx)
ET.SubElement(info_line, 'text').text = unicode(value[0:255])
ET.SubElement(info_line, 'line_number').text = str(3000 + idx)
ET.SubElement(info_line, 'text').text = str(value[0:255])

# processing_control element
processing_ctrl = ET.SubElement(self.root, 'processing_control')
Expand Down Expand Up @@ -141,11 +145,11 @@ def tostring(self, encoding="utf-8", pretty_print=False,
self._build_xml_tree()

tree = ET.ElementTree(self.root)
output = StringIO.StringIO()
output = BytesIO()
tree.write(output, pretty_print=pretty_print, xml_declaration=True,
encoding=encoding, method="xml")

retval = output.getvalue()
retval = output.getvalue().decode(encoding)
output.close()

return retval
Expand All @@ -155,13 +159,13 @@ def __str__(self):

@classmethod
def _add_params_to_url(cls, url, params={}):
url_parts = list(urlparse.urlparse(url))
query = dict(urlparse.parse_qsl(url_parts[4], keep_blank_values=True))
url_parts = list(urlparse(url))
query = dict(parse_qsl(url_parts[4], keep_blank_values=True))
query.update(params)

url_parts[4] = urllib.urlencode(query)
url_parts[4] = urlencode(query)

return urlparse.urlunparse(url_parts)
return urlunparse(url_parts)

def get_success_redirect_url(self):
return self.success_redirect_url
Expand Down
3 changes: 3 additions & 0 deletions setup.py
Expand Up @@ -10,6 +10,9 @@
'Topic :: Software Development',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
]

install_requires = [
Expand Down
6 changes: 3 additions & 3 deletions tox.ini
Expand Up @@ -3,9 +3,9 @@ args_are_paths = false
envlist =
py26,
py27,
; py32,
; py33,
; py34,
py32,
py33,
py34,
coverage,
flake8

Expand Down

0 comments on commit de6883b

Please sign in to comment.