From d0bfe3d0f310d09423747c02b89d178eedafa763 Mon Sep 17 00:00:00 2001 From: Benjamin Eckel Date: Fri, 17 Nov 2017 13:40:04 -0600 Subject: [PATCH 1/3] Bump API version 2.9 --- recurly/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recurly/__init__.py b/recurly/__init__.py index d01aecc3..6d316620 100644 --- a/recurly/__init__.py +++ b/recurly/__init__.py @@ -41,7 +41,7 @@ API_KEY = None """The API key to use when authenticating API requests.""" -API_VERSION = '2.8' +API_VERSION = '2.9' """The API version to use when making API requests.""" CA_CERTS_FILE = None From 47c081b64ac361e8553f90cc9a8118d574d3a575 Mon Sep 17 00:00:00 2001 From: Benjamin Eckel Date: Fri, 17 Nov 2017 13:52:34 -0600 Subject: [PATCH 2/3] Implement 2.9 Changes 1. Adding the authorize endpoint for Purchases --- recurly/__init__.py | 15 +++ tests/fixtures/purchase/authorized.xml | 152 +++++++++++++++++++++++++ tests/test_resources.py | 5 + 3 files changed, 172 insertions(+) create mode 100644 tests/fixtures/purchase/authorized.xml diff --git a/recurly/__init__.py b/recurly/__init__.py index 6d316620..e16ebe97 100644 --- a/recurly/__init__.py +++ b/recurly/__init__.py @@ -365,6 +365,7 @@ class BillingInfo(Resource): 'account_number', 'currency', 'updated_at', + 'external_hpp_type', ) sensitive_attributes = ('number', 'verification_value', 'account_number') xml_attribute_attributes = ('type',) @@ -853,6 +854,20 @@ def preview(self): url = urljoin(recurly.base_uri(), self.collection_path + '/preview') return self.__invoice(url) + def authorize(self): + """ + Will generate an authorized invoice for the purchase. Runs validations + but does not run any transactions. This endpoint will create a + pending purchase that can be activated at a later time once payment + has been completed on an external source (e.g. Adyen's Hosted + Payment Pages). + + Returns: + Invoice: The authorized invoice + """ + url = recurly.base_uri() + self.collection_path + '/authorize' + return self.__invoice(url) + def __invoice(self, url): # We must null out currency in subscriptions and adjustments # TODO we should deprecate and remove default currency support diff --git a/tests/fixtures/purchase/authorized.xml b/tests/fixtures/purchase/authorized.xml new file mode 100644 index 00000000..86391a74 --- /dev/null +++ b/tests/fixtures/purchase/authorized.xml @@ -0,0 +1,152 @@ +POST https://api.recurly.com/v2/purchases/authorize HTTP/1.1 +X-Api-Version: {api-version} +Accept: application/xml +Authorization: Basic YXBpa2V5Og== +User-Agent: {user-agent} +Content-Type: application/xml; charset=utf-8 + + + + + testmock + benjamin.dumonde@example.com + + Verena + Example + 4111-1111-1111-1111 + 123 + 2020 + 11 + 123 Main St + New Orleans + LA + 70114 + US + USD + adyen + + + + + Item 1 + 1 + 1000 + + + Item 2 + 2 + 2000 + + + USD + + + gold + + + + +HTTP/1.1 200 OK +Content-Type: application/xml; charset=utf-8 +Location: https://api.recurly.com/v2/invoices/1021 + + + + +
+ 123 Main St + + New Orleans + LA + 70114 + US + +
+ 40625fd700c1c2d90060744092b4a1b1 + open + + + + + 6000 + 0 + 6000 + 6000 + USD + + + 2017-10-06T21:25:55Z + + + + + 0 + automatic + + + + 40625fd6fc46181e2f15044db38c1c82 + pending + gold + + gold + plan + 1000 + 1 + 0 + 0 + 1000 + USD + false + 2017-10-06T21:25:55Z + 2017-11-06T21:25:55Z + + + evenly + + + + 40625fd6e6a5817b59a2174b72a92fea + pending + Item 1 + + + debit + 1000 + 1 + 0 + 0 + 1000 + USD + false + 2017-10-06T21:25:55Z + + + + + + + + 40625fd6e9096c9922b52646e29f6d5e + pending + Item 2 + + + debit + 2000 + 2 + 0 + 0 + 4000 + USD + false + 2017-10-06T21:25:55Z + + + + + + + + +
diff --git a/tests/test_resources.py b/tests/test_resources.py index 42a5df19..e654c975 100644 --- a/tests/test_resources.py +++ b/tests/test_resources.py @@ -88,6 +88,11 @@ def test_purchase(self): with self.mock_request('purchase/previewed.xml'): preview_invoice = purchase.preview() self.assertIsInstance(preview_invoice, Invoice) + with self.mock_request('purchase/authorized.xml'): + purchase.account.email = 'benjamin.dumonde@example.com' + purchase.account.billing_info.external_hpp_type = 'adyen' + authorized_invoice = purchase.authorize() + self.assertIsInstance(authorized_invoice, Invoice) def test_account(self): account_code = 'test%s' % self.test_id From 5b16f4d8e7ae5133f0dc92e9a76b3886870672b3 Mon Sep 17 00:00:00 2001 From: Benjamin Eckel Date: Fri, 17 Nov 2017 14:12:52 -0600 Subject: [PATCH 3/3] Fix urlencoding for bool params --- recurly/resource.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/recurly/resource.py b/recurly/resource.py index b0d214ae..23e404db 100644 --- a/recurly/resource.py +++ b/recurly/resource.py @@ -14,6 +14,13 @@ from six.moves import http_client from six.moves.urllib.parse import urlencode, urlsplit, quote +def urlencode_params(args): + # Need to make bools lowercase + for k, v in six.iteritems(args): + if isinstance(v, bool): + args[k] = str(v).lower() + return urlencode(args) + class Money(object): """An amount of money in one or more currencies.""" @@ -514,7 +521,7 @@ def update_from_element(self, elem): def _make_actionator(self, url, method, extra_handler=None): def actionator(*args, **kwargs): if kwargs: - full_url = '%s?%s' % (url, urlencode(kwargs)) + full_url = '%s?%s' % (url, urlencode_params(kwargs)) else: full_url = url @@ -574,7 +581,7 @@ def __getattr__(self, name): def make_relatitator(url): def relatitator(**kwargs): if kwargs: - full_url = '%s?%s' % (url, urlencode(kwargs)) + full_url = '%s?%s' % (url, urlencode_params(kwargs)) else: full_url = url @@ -608,7 +615,7 @@ def all(cls, **kwargs): """ url = recurly.base_uri() + cls.collection_path if kwargs: - url = '%s?%s' % (url, urlencode(kwargs)) + url = '%s?%s' % (url, urlencode_params(kwargs)) return Page.page_for_url(url) @classmethod @@ -618,7 +625,7 @@ def count(cls, **kwargs): """ url = recurly.base_uri() + cls.collection_path if kwargs: - url = '%s?%s' % (url, urlencode(kwargs)) + url = '%s?%s' % (url, urlencode_params(kwargs)) return Page.count_for_url(url) def save(self):