Search code examples
pythonbraintree

Braintree subscription search


I am working on a Braintree subscription search in order to retrieve customer IDs and subscription prices tied to these IDs. In my code I am following the suggestions from this post.

Here is an excerpt of my code:

gateway = braintree.BraintreeGateway(
    braintree.Configuration(
        environment=braintree.Environment.Production,
        merchant_id= 'our_merchant_id',
        public_key='our_public_key',
        private_key='our_private_key'
    )
)
subscriptions = gateway.subscription.search(
    braintree.SubscriptionSearch.status.in_list(
        braintree.Subscription.Status.Active,
        braintree.Subscription.Status.PastDue,
        braintree.Subscription.Status.Pending
    )
)

result = {}

for subscription in subscriptions.items:
    payment_method = gateway.payment_method.find(subscription.payment_method_token)
    result[payment_method.customer_id] = subscription.price

"""do something with result """

This approach works fine in the BT sandbox and on small queries around 100 records. However, whenever I try to query for more than about 120 subscriptions the BT server responds consistently with a 504 error. I would like to query for around 5000 subscriptions at a time in production. Any suggestions?

Traceback:

Traceback (most recent call last):
File "BT_subscrAmount.py", line 22, in <module>
for subscription in subscriptions.items:
File "/home/karen/miniconda3/lib/python3.6/site- 
packages/braintree/resource_collection.py", line 38, in items
for item in self.__method(self.__query, batch):
File "/home/karen/miniconda3/lib/python3.6/site- 
packages/braintree/subscription_gateway.py", line 79, in __fetch
response = self.config.http().post(self.config.base_merchant_path() + 
"/subscriptions/advanced_search", {"search": criteria})
File "/home/karen/miniconda3/lib/python3.6/site- 
packages/braintree/util/http.py", line 56, in post
return self.__http_do("POST", path, Http.ContentType.Xml, params)
File "/home/karen/miniconda3/lib/python3.6/site- 
packages/braintree/util/http.py", line 86, in __http_do
Http.raise_exception_from_status(status)
File "/home/karen/miniconda3/lib/python3.6/site- 
packages/braintree/util/http.py", line 49, in raise_exception_from_status
raise UnexpectedError("Unexpected HTTP_RESPONSE " + str(status))
braintree.exceptions.unexpected_error.UnexpectedError: Unexpected 
HTTP_RESPONSE 504

Solution

  • With the help from an amazing BT support engineer, we were finally able to figure out a solution to our problem.

    The explanation:

    When BT returns search results, they gather all associated data and return a large (serialized) response object to the client. In our case some of the subscriptions had a large number of associated transactions, which makes the process of fetching, serializing and returning them to the client slow. When making a request, the BT gateway returns pages in groups of 50 with a default timeout of 60 seconds. Any group with several large response objects will likely time out and produce a 504 error.

    The solution:

    To avoid this delay, instead of using the items method, we can use the id method from the ResourceCollections class and return just the ids of the objects. With the individual subscription id, we can fetch the individual subscription and only the attributes we need, like so:

    subscriptions = gateway.subscription.search(
        braintree.SubscriptionSearch.status.in_list(
        braintree.Subscription.Status.Active
        )
    )
    
    subscription_ids = subscriptions.ids
    
    def find_customer_id(id):
        subscription = gateway.subscription.find(id)
        payment_method = gateway.payment_method.find(subscription.payment_method_token)
        return (payment_method.customer_id, subscription.price) 
    

    Hope this will help someone else!