diff --git a/.travis.yml b/.travis.yml index 492e41381..b4744a9c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,7 @@ cache: env: global: # If changing this number, please also change it in `tests/conftest.py`. - - STRIPE_MOCK_VERSION=0.52.0 + - STRIPE_MOCK_VERSION=0.54.0 before_install: # Unpack and start stripe-mock so that the test suite can talk to it diff --git a/stripe/api_resources/__init__.py b/stripe/api_resources/__init__.py index ce93cba37..ff94a411a 100644 --- a/stripe/api_resources/__init__.py +++ b/stripe/api_resources/__init__.py @@ -55,6 +55,7 @@ from stripe.api_resources.subscription_schedule_revision import ( SubscriptionScheduleRevision, ) +from stripe.api_resources.tax_id import TaxId from stripe.api_resources.three_d_secure import ThreeDSecure from stripe.api_resources.token import Token from stripe.api_resources.topup import Topup diff --git a/stripe/api_resources/customer.py b/stripe/api_resources/customer.py index 68cae6211..b1c97fa0f 100644 --- a/stripe/api_resources/customer.py +++ b/stripe/api_resources/customer.py @@ -13,6 +13,9 @@ @nested_resource_class_methods( "source", operations=["create", "retrieve", "update", "delete", "list"] ) +@nested_resource_class_methods( + "tax_id", operations=["create", "retrieve", "delete", "list"] +) class Customer( CreateableAPIResource, UpdateableAPIResource, diff --git a/stripe/api_resources/tax_id.py b/stripe/api_resources/tax_id.py new file mode 100644 index 000000000..7c79442c1 --- /dev/null +++ b/stripe/api_resources/tax_id.py @@ -0,0 +1,24 @@ +from __future__ import absolute_import, division, print_function + +from stripe import util +from stripe.api_resources.customer import Customer +from stripe.api_resources.abstract import APIResource +from stripe.six.moves.urllib.parse import quote_plus + + +class TaxId(APIResource): + OBJECT_NAME = "tax_id" + + def instance_url(self): + token = util.utf8(self.id) + customer = util.utf8(self.customer) + base = Customer.class_url() + cust_extn = quote_plus(customer) + extn = quote_plus(token) + return "%s/%s/tax_ids/%s" % (base, cust_extn, extn) + + @classmethod + def retrieve(cls, id, api_key=None, **params): + raise NotImplementedError( + "Can't retrieve a tax id without a customer ID. Use customer.retrieve_tax_id('tax_id')" + ) diff --git a/stripe/util.py b/stripe/util.py index 983a7f9a8..63f74ee58 100644 --- a/stripe/util.py +++ b/stripe/util.py @@ -204,6 +204,7 @@ def load_object_classes(): api_resources.SubscriptionItem.OBJECT_NAME: api_resources.SubscriptionItem, api_resources.SubscriptionSchedule.OBJECT_NAME: api_resources.SubscriptionSchedule, api_resources.SubscriptionScheduleRevision.OBJECT_NAME: api_resources.SubscriptionScheduleRevision, + api_resources.TaxId.OBJECT_NAME: api_resources.TaxId, api_resources.ThreeDSecure.OBJECT_NAME: api_resources.ThreeDSecure, api_resources.Token.OBJECT_NAME: api_resources.Token, api_resources.Topup.OBJECT_NAME: api_resources.Topup, diff --git a/tests/api_resources/test_customer.py b/tests/api_resources/test_customer.py index e89b89f35..0450845f1 100644 --- a/tests/api_resources/test_customer.py +++ b/tests/api_resources/test_customer.py @@ -6,6 +6,7 @@ TEST_RESOURCE_ID = "cus_123" TEST_SUB_ID = "sub_123" TEST_SOURCE_ID = "ba_123" +TEST_TAX_ID_ID = "txi_123" class TestCustomer(object): @@ -109,3 +110,34 @@ def test_is_listable(self, request_mock): "get", "/v1/customers/%s/sources" % TEST_RESOURCE_ID ) assert isinstance(resources.data, list) + + +class TestCustomerTaxIds(object): + def test_is_creatable(self, request_mock): + stripe.Customer.create_tax_id( + TEST_RESOURCE_ID, type="eu_vat", value="11111" + ) + request_mock.assert_requested( + "post", "/v1/customers/%s/tax_ids" % TEST_RESOURCE_ID + ) + + def test_is_retrievable(self, request_mock): + stripe.Customer.retrieve_tax_id(TEST_RESOURCE_ID, TEST_TAX_ID_ID) + request_mock.assert_requested( + "get", + "/v1/customers/%s/tax_ids/%s" % (TEST_RESOURCE_ID, TEST_TAX_ID_ID), + ) + + def test_is_deletable(self, request_mock): + stripe.Customer.delete_tax_id(TEST_RESOURCE_ID, TEST_TAX_ID_ID) + request_mock.assert_requested( + "delete", + "/v1/customers/%s/tax_ids/%s" % (TEST_RESOURCE_ID, TEST_TAX_ID_ID), + ) + + def test_is_listable(self, request_mock): + resources = stripe.Customer.list_tax_ids(TEST_RESOURCE_ID) + request_mock.assert_requested( + "get", "/v1/customers/%s/tax_ids" % TEST_RESOURCE_ID + ) + assert isinstance(resources.data, list) diff --git a/tests/api_resources/test_tax_id.py b/tests/api_resources/test_tax_id.py new file mode 100644 index 000000000..424cc1911 --- /dev/null +++ b/tests/api_resources/test_tax_id.py @@ -0,0 +1,29 @@ +from __future__ import absolute_import, division, print_function + +import pytest + +import stripe + + +TEST_RESOURCE_ID = "txi_123" + + +class TestTaxId(object): + def construct_resource(self): + tax_id_dict = { + "id": TEST_RESOURCE_ID, + "object": "tax_id", + "customer": "cus_123", + } + return stripe.TaxId.construct_from(tax_id_dict, stripe.api_key) + + def test_has_instance_url(self, request_mock): + resource = self.construct_resource() + assert ( + resource.instance_url() + == "/v1/customers/cus_123/tax_ids/%s" % TEST_RESOURCE_ID + ) + + def test_is_not_retrievable(self, request_mock): + with pytest.raises(NotImplementedError): + stripe.TaxId.retrieve(TEST_RESOURCE_ID) diff --git a/tests/conftest.py b/tests/conftest.py index 8707aa5ab..728cb1800 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -16,7 +16,7 @@ # When changing this number, don't forget to change it in `.travis.yml` too. -MOCK_MINIMUM_VERSION = "0.52.0" +MOCK_MINIMUM_VERSION = "0.54.0" # Starts stripe-mock if an OpenAPI spec override is found in `openapi/`, and # otherwise fall back to `STRIPE_MOCK_PORT` or 12111.