Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion payu/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
#

__author__ = 'Presslabs'
__version__ = '1.0.0-rc2'
__version__ = '1.0.0'
3 changes: 2 additions & 1 deletion payu/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
'https://secure.payu.ro/order/alu/v3')
PAYU_IDN_URL = getattr(settings, 'PAYU_IDN_URL',
'https://secure.payu.ro/order/idn.php')

PAYU_IOS_URL = getattr(settings, 'PAYU_IOS_URL',
'https://secure.payu.ro/order/ios.php')
PAYU_TOKENS_URL = getattr(settings, 'PAYU_TOKENS_URL',
'https://secure.payu.ro/order/tokens/')

Expand Down
4 changes: 1 addition & 3 deletions payu/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
import time
import hmac
import hashlib
from datetime import datetime
from collections import OrderedDict
from datetime import datetime

import pytz
import requests
Expand Down
45 changes: 45 additions & 0 deletions payu/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import hmac
from collections import OrderedDict
from xml.etree import ElementTree

import requests

from payu.conf import PAYU_IOS_URL, PAYU_MERCHANT, PAYU_MERCHANT_KEY


def _signature(payload, merchant_key):
confirmation_hash = "".join(["%s%s" % (len(str(payload[field])),
payload[field])
for field in payload])
return hmac.new(merchant_key, confirmation_hash).hexdigest()


def _build_payload(merchant, merchant_key, ref_no_ext):
payload = OrderedDict([
('MERCHANT', merchant),
('REFNOEXT', ref_no_ext)
])
payload["HASH"] = _signature(payload, merchant_key)

return payload


def get_instant_order_status(ipn):
"""
:param ipn: A PayUIPN object containing the REFNOEXT attribute.
:return: A dict containing details about the status of the order.
"""
payload = _build_payload(PAYU_MERCHANT, PAYU_MERCHANT_KEY, ipn.REFNOEXT)

response = requests.post(PAYU_IOS_URL, data=payload)

try:
parsed_response = ElementTree.fromstring(response.content)
except ElementTree.ParseError:
return {'error': "Couldn't parse the XML result from PayU.",
'result': response.content}

if parsed_response.tag == 'Error':
return {'error': parsed_response.text.strip()}

return {child.tag: (child.text or '').strip() for child in parsed_response}
80 changes: 80 additions & 0 deletions tests/test_ios.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from collections import OrderedDict

import pytest
from mock import MagicMock, patch
from django_dynamic_fixture import G

from payu.models import PayUIPN
from payu.utils import _signature, _build_payload, get_instant_order_status


def test_ios_signature():
# this data is taken from the PayU documentation

payload = OrderedDict([
('MERCHANT', 'PAYUDEMO'),
('REFNOEXT', 'EPAY10425')
])

signature = _signature(payload, merchant_key='1231234567890123')

assert signature == '6cb19f366fd9709b078b593b1736a4ea'


def test_ios_payload():
# this data is taken from the PayU documentation

payload = _build_payload(merchant='PAYUDEMO',
ref_no_ext='EPAY10425',
merchant_key='1231234567890123')

assert payload == OrderedDict([
('MERCHANT', 'PAYUDEMO'),
('REFNOEXT', 'EPAY10425'),
('HASH', '6cb19f366fd9709b078b593b1736a4ea')
])


@pytest.mark.django_db
@patch('payu.utils.requests')
def test_ios_get_instant_order_status(mocked_requests):
response_content = """<?xml version="1.0"?>
<Order>
<ORDER_DATE>2017-07-12 12:16:20</ORDER_DATE>
<REFNO>45459240</REFNO>
<REFNOEXT>b3045b90-d11f-4c78-9f6b-79f54317df36</REFNOEXT>
<ORDER_STATUS>TEST</ORDER_STATUS>
<PAYMETHOD>Visa/MasterCard/Eurocard</PAYMETHOD>
<HASH>adc135ddd73f0f4fe3eb9d26b84c662d</HASH>
</Order>"""

mocked_requests.post.return_value = MagicMock(status_code=200, content=response_content)

ipn = G(PayUIPN)

status = get_instant_order_status(ipn)

assert status == {'HASH': 'adc135ddd73f0f4fe3eb9d26b84c662d',
'PAYMETHOD': 'Visa/MasterCard/Eurocard',
'REFNO': '45459240',
'ORDER_STATUS': 'TEST',
'ORDER_DATE': '2017-07-12 12:16:20',
'REFNOEXT': 'b3045b90-d11f-4c78-9f6b-79f54317df36'}


@pytest.mark.django_db
@pytest.mark.parametrize('payu_response, expected_result', [
('<?xml version="1.0"?><Error>Limit calls for IOS exceeded!</Error>',
{'error': 'Limit calls for IOS exceeded!'}),
('Access not permitted', {'error': "Couldn't parse the XML result from PayU.",
'result': 'Access not permitted'}),
])
@patch('payu.utils.requests')
def test_ios_get_instant_order_status_error(mocked_requests, payu_response, expected_result):
mocked_requests.post.return_value = MagicMock(status_code=200, content=payu_response)

ipn = G(PayUIPN)

result = get_instant_order_status(ipn)

assert result == expected_result