From b66006d10a04c75d4a850537205fc8af6f88181e Mon Sep 17 00:00:00 2001 From: Abel Puentes Luberta Date: Wed, 30 Aug 2017 18:39:17 +0200 Subject: [PATCH 1/9] Fixed for python3 --- sofort/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sofort/client.py b/sofort/client.py index 4b24c85..86e888c 100644 --- a/sofort/client.py +++ b/sofort/client.py @@ -7,7 +7,7 @@ from sofort.internals import Config, as_list from sofort import model -from _version import __version__ +from sofort._version import __version__ API_URL = 'https://api.sofort.com/api/xml' TRANSACTION_ID = '-TRANSACTION-' From a8b368e62a8041647102a957aa6b550416b00ded Mon Sep 17 00:00:00 2001 From: Abel Puentes Luberta Date: Wed, 30 Aug 2017 18:39:34 +0200 Subject: [PATCH 2/9] Fixed for python3 --- sofort/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sofort/model.py b/sofort/model.py index d538d85..bf31f8c 100644 --- a/sofort/model.py +++ b/sofort/model.py @@ -14,7 +14,7 @@ def response(xmlstr): result = xmltodict.parse(xmlstr) # only one root element is allowed in XML - for root, value in result.iteritems(): + for root, value in result.items(): if value is None: return None factory = factories[root] From 341365f3877fe06949cabc6d934afdbe72cd9975 Mon Sep 17 00:00:00 2001 From: Abel Puentes Luberta Date: Wed, 30 Aug 2017 18:50:15 +0200 Subject: [PATCH 3/9] Fixed for python3 --- sofort/xml.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sofort/xml.py b/sofort/xml.py index ff76d70..a78c859 100644 --- a/sofort/xml.py +++ b/sofort/xml.py @@ -59,7 +59,7 @@ def multipay(config): def transaction_request_by_params(params): root = etree.Element('transaction_request') root.set('version', '2') - for name, value in params.iteritems(): + for name, value in params.items(): if name == 'transaction': for transaction_id in value: etree.SubElement(root, 'transaction').text = transaction_id From cb6214061f872a4ed970d9c32436fb52aebaadd8 Mon Sep 17 00:00:00 2001 From: Abel Puentes Luberta Date: Thu, 31 Aug 2017 11:46:54 +0200 Subject: [PATCH 4/9] Added function to create refunds xml --- sofort/xml.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sofort/xml.py b/sofort/xml.py index a78c859..6820af4 100644 --- a/sofort/xml.py +++ b/sofort/xml.py @@ -68,6 +68,27 @@ def transaction_request_by_params(params): return etree.tostring(root) +def refunds_by_params(params): + root = etree.Element('refunds') + root.set('version', '3') + + sender = params.pop('sender') + sender_xml = etree.SubElement(root, 'sender') + for name, value in sender.items(): + etree.SubElement(sender_xml, name).text = __serialize(value) + + refunds = params.pop('refunds') + for refund in refunds: + refund_xml = etree.SubElement(root, 'refund') + for name, value in refund.items(): + etree.SubElement(refund_xml, name).text = __serialize(value) + + for name, value in params.items(): + etree.SubElement(root, name).text = __serialize(value) + + return etree.tostring(root) + + def __compact_notification_addresses(addresses_): """ Input:: From ce0d4b163816bca4d420fa0ef41450522b5fe987 Mon Sep 17 00:00:00 2001 From: Abel Puentes Luberta Date: Thu, 31 Aug 2017 11:48:05 +0200 Subject: [PATCH 5/9] Added refunds function to client --- sofort/client.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sofort/client.py b/sofort/client.py index 86e888c..25e85c5 100644 --- a/sofort/client.py +++ b/sofort/client.py @@ -71,6 +71,13 @@ def payment(self, amount, **kwargs): return self._request(sofort.xml.multipay(params), params) + def refunds(self, sender, refunds): + request_body = sofort.xml.refunds_by_params({ + 'sender': sender, + 'refunds': refunds + }) + return self._request(request_body) + def details(self, transaction_ids): request_body = sofort.xml.transaction_request_by_params({ 'transaction': as_list(transaction_ids) From 77938c2f1dcd088c80739ffa07081be25f9e3645 Mon Sep 17 00:00:00 2001 From: Abel Puentes Luberta Date: Thu, 31 Aug 2017 12:11:04 +0200 Subject: [PATCH 6/9] Added RefundsModel --- sofort/model.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/sofort/model.py b/sofort/model.py index bf31f8c..3d2d19a 100644 --- a/sofort/model.py +++ b/sofort/model.py @@ -116,7 +116,26 @@ class TransactionDetailsModel(Model): costs = ModelType(CostsModel) su = ModelType(SuModel) status_history_items = SofortListType('status_history_item', - ModelType(StatusHistoryItemModel)) + ModelType(StatusHistoryItemModel))) + + +class RefundModel(Model): + recipient = ModelType(BankAccountModel) + transaction = StringType() + amount = DecimalType() + comment = StringType() + reason_1 = StringType() + reason_2 = StringType() + time = Iso8601DateTimeType() + partial_refund_id = StringType() + status = StringType() + errors = SofortListType('error', ModelType(ErrorModel)) + +class RefundsModel(Model): + sender = ModelType(BankAccountModel) + title = StringType() + pain = StringType() + refund = ModelType(RefundModel) def transaction_list(transactions): @@ -139,4 +158,5 @@ def error_handler(data): 'errors': error_handler, 'transactions': transaction_list, 'new_transaction': NewTransactionModel, + 'refunds': RefundsModel, } From 98dde6855c191853dfd988ef987ecf51655693d8 Mon Sep 17 00:00:00 2001 From: Abel Puentes Luberta Date: Thu, 31 Aug 2017 13:35:08 +0200 Subject: [PATCH 7/9] Fixed syntax error --- sofort/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sofort/model.py b/sofort/model.py index 3d2d19a..19848e8 100644 --- a/sofort/model.py +++ b/sofort/model.py @@ -116,7 +116,7 @@ class TransactionDetailsModel(Model): costs = ModelType(CostsModel) su = ModelType(SuModel) status_history_items = SofortListType('status_history_item', - ModelType(StatusHistoryItemModel))) + ModelType(StatusHistoryItemModel)) class RefundModel(Model): From dc0da1b2c0f743801d6e139427cb4ed044efb04d Mon Sep 17 00:00:00 2001 From: Abel Puentes Luberta Date: Thu, 31 Aug 2017 16:39:05 +0200 Subject: [PATCH 8/9] Added strict option --- sofort/model.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sofort/model.py b/sofort/model.py index 19848e8..b98efbe 100644 --- a/sofort/model.py +++ b/sofort/model.py @@ -18,7 +18,7 @@ def response(xmlstr): if value is None: return None factory = factories[root] - return factory(value) + return factory(value, strict=False) class ForcedListType(ListType): @@ -138,14 +138,14 @@ class RefundsModel(Model): refund = ModelType(RefundModel) -def transaction_list(transactions): - return [TransactionDetailsModel(transact) +def transaction_list(transactions, strict=False): + return [TransactionDetailsModel(transact, strict=strict) for transact in as_list(transactions['transaction_details'])] -def error_handler(data): - root = RootErrorsModel(data) +def error_handler(data, strict=False): + root = RootErrorsModel(data, strict) errors = [RequestError(**error_item) for error_item in root.error] if root.su: errors.extend([RequestError(**error_item) From 7a20a269424058b56d2e8c1a333defc2f0415eec Mon Sep 17 00:00:00 2001 From: Abel Puentes Luberta Date: Thu, 31 Aug 2017 19:26:12 +0200 Subject: [PATCH 9/9] Added test to improve coverage --- sofort/model.py | 2 +- tests/test_sofort.py | 46 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_xml.py | 39 +++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/sofort/model.py b/sofort/model.py index b98efbe..ace2745 100644 --- a/sofort/model.py +++ b/sofort/model.py @@ -145,7 +145,7 @@ def transaction_list(transactions, strict=False): def error_handler(data, strict=False): - root = RootErrorsModel(data, strict) + root = RootErrorsModel(data, strict=strict) errors = [RequestError(**error_item) for error_item in root.error] if root.su: errors.extend([RequestError(**error_item) diff --git a/tests/test_sofort.py b/tests/test_sofort.py index 82d559b..2cc5a46 100644 --- a/tests/test_sofort.py +++ b/tests/test_sofort.py @@ -63,6 +63,23 @@ def test_pay(self): self.assertIsInstance(tran.payment_url, basestring) self.assertEqual('https://www.sofort.com/payment/go/136b2012718da0160fac20c2ec2f51100c90406e', tran.payment_url) + def test_refunds(self): + self.client._request_xml = MagicMock(return_value=REFUNDS_RESPONSE) + refunds_response = self.client.refunds(sender={ + 'holder': 'Max Samplemerchant', + 'iban': 'DE71700111109999999999', + 'bic': 'DEKTDE7GXXX' + }, refunds=[{ + 'transaction': '00000-00000-00000000-0000', + 'amount': '1.11', + 'comment': 'Order cancelled by user.', + 'reason_1': 'OrderID 123456', + 'reason_2': 'Refund' + }]) + self.assertEqual('Max Samplemerchant', refunds_response.sender.holder) + self.assertEqual('00000-00000-00000000-0000', refunds_response.refund.transaction) + self.assertEqual('accepted', refunds_response.refund.status) + def test_details_multiple_transaction_ids(self): self.client._request_xml = MagicMock(return_value=TRANSACTION_LIST_BY_IDS_RESPONSE) tran_id = [ @@ -479,6 +496,35 @@ def test_empty_response(self): """ +REFUNDS_RESPONSE = u""" + + + Max Samplemerchant + Demo Bank + DE71700111109999999999 + DEKTDE7GXXX + + Test Refund December 5, 2013 + [Base-64 encoded content for PAIN-file] + + + Max Mustermann + Demo Bank + DE06000000000023456789 + SFRTDE20XXX + + 00000-00000-00000000-0000 + 1.11 + Order cancelled by user. + OrderID 123456 + Refund + + fb1244caad + accepted + + +""" + NEST_ERRORS = """ diff --git a/tests/test_xml.py b/tests/test_xml.py index 586a806..44b1a8b 100644 --- a/tests/test_xml.py +++ b/tests/test_xml.py @@ -62,6 +62,28 @@ def test_transactions_request_by_params(self): sofort.xml.transaction_request_by_params(params) ) + def test_refunds_by_params(self): + from collections import OrderedDict + params = { + 'sender': { + 'holder': 'Max Samplemerchant', + 'iban': 'DE71700111109999999999', + 'bic': 'DEKTDE7GXXX' + }, + 'refunds': [{ + 'transaction': '00000-00000-00000000-0000', + 'amount': '1.11', + 'comment': 'Order cancelled by user.', + 'reason_1': 'OrderID 123456', + 'reason_2': 'Refund' + }] + } + + self.assertXmlEqual( + REFUNDS_SAMPLE, + sofort.xml.refunds_by_params(params) + ) + MULTIPAY_SAMPLE = """ @@ -96,3 +118,20 @@ def test_transactions_request_by_params(self): 10 """ + +REFUNDS_SAMPLE = """ + + + DE71700111109999999999 + Max Samplemerchant + DEKTDE7GXXX + + + Order cancelled by user. + OrderID 123456 + 1.11 + 00000-00000-00000000-0000 + Refund + + +"""