Skip to content

Commit

Permalink
Add support for Customer Balance Transaction resource and APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
remi-stripe committed May 4, 2019
1 parent 25b21ce commit 1586af2
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 6 deletions.
3 changes: 3 additions & 0 deletions stripe/api_resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
from stripe.api_resources.coupon import Coupon
from stripe.api_resources.credit_note import CreditNote
from stripe.api_resources.customer import Customer
from stripe.api_resources.customer_balance_transaction import (
CustomerBalanceTransaction,
)
from stripe.api_resources.dispute import Dispute
from stripe.api_resources.ephemeral_key import EphemeralKey
from stripe.api_resources.event import Event
Expand Down
16 changes: 10 additions & 6 deletions stripe/api_resources/abstract/nested_resource_class_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@
from stripe.six.moves.urllib.parse import quote_plus


def nested_resource_class_methods(resource, path=None, operations=None):
def nested_resource_class_methods(
resource, path=None, operations=None, resource_method_name=None
):
if path is None:
path = "%ss" % resource
if operations is None:
raise ValueError("operations list required")
if resource_method_name is None:
resource_method_name = resource

def wrapper(cls):
def nested_resource_url(cls, id, nested_id=None):
Expand Down Expand Up @@ -57,7 +61,7 @@ def create_nested_resource(cls, id, **params):
"post", url, **params
)

create_method = "create_%s" % resource
create_method = "create_%s" % resource_method_name
setattr(
cls, create_method, classmethod(create_nested_resource)
)
Expand All @@ -70,7 +74,7 @@ def retrieve_nested_resource(cls, id, nested_id, **params):
"get", url, **params
)

retrieve_method = "retrieve_%s" % resource
retrieve_method = "retrieve_%s" % resource_method_name
setattr(
cls, retrieve_method, classmethod(retrieve_nested_resource)
)
Expand All @@ -83,7 +87,7 @@ def modify_nested_resource(cls, id, nested_id, **params):
"post", url, **params
)

modify_method = "modify_%s" % resource
modify_method = "modify_%s" % resource_method_name
setattr(
cls, modify_method, classmethod(modify_nested_resource)
)
Expand All @@ -96,7 +100,7 @@ def delete_nested_resource(cls, id, nested_id, **params):
"delete", url, **params
)

delete_method = "delete_%s" % resource
delete_method = "delete_%s" % resource_method_name
setattr(
cls, delete_method, classmethod(delete_nested_resource)
)
Expand All @@ -109,7 +113,7 @@ def list_nested_resources(cls, id, **params):
"get", url, **params
)

list_method = "list_%ss" % resource
list_method = "list_%ss" % resource_method_name
setattr(cls, list_method, classmethod(list_nested_resources))

else:
Expand Down
5 changes: 5 additions & 0 deletions stripe/api_resources/customer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
@nested_resource_class_methods(
"tax_id", operations=["create", "retrieve", "delete", "list"]
)
@nested_resource_class_methods(
"customer_balance_transaction",
operations=["create", "retrieve", "delete", "list"],
resource_method_name="transaction",
)
class Customer(
CreateableAPIResource,
UpdateableAPIResource,
Expand Down
28 changes: 28 additions & 0 deletions stripe/api_resources/customer_balance_transaction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
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 CustomerBalanceTransaction(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/customer_balance_transactions/%s" % (
base,
cust_extn,
extn,
)

@classmethod
def retrieve(cls, id, api_key=None, **params):
raise NotImplementedError(
"Can't retrieve a customer balance transaction without a customer ID. Use customer.retrieve_transaction('transaction_id')"
)
1 change: 1 addition & 0 deletions stripe/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ def load_object_classes():
api_resources.Coupon.OBJECT_NAME: api_resources.Coupon,
api_resources.CreditNote.OBJECT_NAME: api_resources.CreditNote,
api_resources.Customer.OBJECT_NAME: api_resources.Customer,
api_resources.Customer.OBJECT_NAME: api_resources.Customer,
api_resources.Dispute.OBJECT_NAME: api_resources.Dispute,
api_resources.EphemeralKey.OBJECT_NAME: api_resources.EphemeralKey,
api_resources.Event.OBJECT_NAME: api_resources.Event,
Expand Down
32 changes: 32 additions & 0 deletions tests/api_resources/test_customer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
TEST_SUB_ID = "sub_123"
TEST_SOURCE_ID = "ba_123"
TEST_TAX_ID_ID = "txi_123"
TEST_TRANSACTION_ID = "txi_123"


class TestCustomer(object):
Expand Down Expand Up @@ -141,3 +142,34 @@ def test_is_listable(self, request_mock):
"get", "/v1/customers/%s/tax_ids" % TEST_RESOURCE_ID
)
assert isinstance(resources.data, list)


class TestCustomerTransactions(object):
def test_is_creatable(self, request_mock):
stripe.Customer.create_transaction(
TEST_RESOURCE_ID, amount=1234, currency="usd"
)
request_mock.assert_requested(
"post",
"/v1/customers/%s/customer_balance_transactions"
% TEST_RESOURCE_ID,
)

def test_is_retrievable(self, request_mock):
stripe.Customer.retrieve_transaction(
TEST_RESOURCE_ID, TEST_TRANSACTION_ID
)
request_mock.assert_requested(
"get",
"/v1/customers/%s/customer_balance_transactions/%s"
% (TEST_RESOURCE_ID, TEST_TRANSACTION_ID),
)

def test_is_listable(self, request_mock):
resources = stripe.Customer.list_transactions(TEST_RESOURCE_ID)
request_mock.assert_requested(
"get",
"/v1/customers/%s/customer_balance_transactions"
% TEST_RESOURCE_ID,
)
assert isinstance(resources.data, list)
32 changes: 32 additions & 0 deletions tests/api_resources/test_customer_balance_transaction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from __future__ import absolute_import, division, print_function

import pytest

import stripe


TEST_RESOURCE_ID = "cbtxn_123"


class TestCustomerBalanceTransaction(object):
def construct_resource(self):
tax_id_dict = {
"id": TEST_RESOURCE_ID,
"object": "customer_balance_transaction",
"customer": "cus_123",
}
return stripe.CustomerBalanceTransaction.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/customer_balance_transactions/%s"
% TEST_RESOURCE_ID
)

def test_is_not_retrievable(self, request_mock):
with pytest.raises(NotImplementedError):
stripe.CustomerBalanceTransaction.retrieve(TEST_RESOURCE_ID)

0 comments on commit 1586af2

Please sign in to comment.