diff --git a/lib/solitude/api.py b/lib/solitude/api.py index 40ccdd7a0..6ec062f28 100644 --- a/lib/solitude/api.py +++ b/lib/solitude/api.py @@ -174,7 +174,7 @@ def configure_product_for_billing(self, transaction_uuid, redirect_url_onsuccess, redirect_url_onerror, prices, icon_url, - user_uuid): + user_uuid, application_size): """ Get the billing configuration ID for a Bango transaction. """ @@ -192,8 +192,8 @@ def configure_product_for_billing(self, transaction_uuid, seller_product__seller=seller_id, seller_product__external_id=product_id)['resource_uri'] except ObjectDoesNotExist: - bango_product_uri = self.create_product(product_id, - product_name, seller) + bango_product_uri = self.create_product(product_id, product_name, + seller) log.info('transaction %s: bango product: %s' % (transaction_uuid, bango_product_uri)) @@ -206,7 +206,8 @@ def configure_product_for_billing(self, transaction_uuid, 'redirect_url_onsuccess': redirect_url_onsuccess, 'redirect_url_onerror': redirect_url_onerror, 'icon_url': icon_url, - 'user_uuid': user_uuid + 'user_uuid': user_uuid, + 'application_size': application_size }) bill_id = res['billingConfigurationId'] log.info('transaction %s: billing config ID: %s; ' diff --git a/lib/solitude/tests.py b/lib/solitude/tests.py index 36da03f74..88acce9fa 100644 --- a/lib/solitude/tests.py +++ b/lib/solitude/tests.py @@ -206,7 +206,7 @@ def test_create_bango(self, slumber): def test_no_seller(self, slumber): slumber.generic.seller.get_object.side_effect = ObjectDoesNotExist with self.assertRaises(SellerNotConfigured): - client.configure_product_for_billing(*range(0, 9)) + client.configure_product_for_billing(*range(0, 10)) @mock.patch('lib.solitude.api.client.slumber') def test_no_bango(self, slumber): @@ -214,7 +214,7 @@ def test_no_bango(self, slumber): slumber.bango.billing.post.return_value = { 'billingConfigurationId': 'bar'} slumber.bango.product.get_object.side_effect = ObjectDoesNotExist - eq_(client.configure_product_for_billing(*range(0, 9)), ('bar', 'foo')) + eq_(client.configure_product_for_billing(*range(0, 10)), ('bar', 'foo')) @mock.patch('lib.solitude.api.client.slumber') def test_has_bango(self, slumber): @@ -222,7 +222,7 @@ def test_has_bango(self, slumber): slumber.bango.billing.post.return_value = { 'billingConfigurationId': 'bar'} slumber.bango.product.get_object.return_value = {'resource_uri': 'foo'} - eq_(client.configure_product_for_billing(*range(0, 9)), ('bar', 'foo')) + eq_(client.configure_product_for_billing(*range(0, 10)), ('bar', 'foo')) @mock.patch('lib.solitude.api.client.slumber') diff --git a/webpay/pay/tasks.py b/webpay/pay/tasks.py index 46f85f201..9610ff8e2 100644 --- a/webpay/pay/tasks.py +++ b/webpay/pay/tasks.py @@ -89,7 +89,7 @@ def get_seller_uuid(issuer_key, product_data): # actual Solitude/Bango seller_uuid to associate the # product to the right account. try: - seller_uuid = urlparse.parse_qs(product_data)['seller_uuid'][0] + seller_uuid = product_data['seller_uuid'][0] except KeyError: raise ValueError('Marketplace %r did not put a seller_uuid ' 'in productData: %r' @@ -124,9 +124,14 @@ def start_pay(transaction_uuid, notes, user_uuid, **kw): ready to be fulfilled by Bango. """ pay = notes['pay_request'] + product_data = urlparse.parse_qs(pay['request'].get('productData', '')) try: - seller_uuid = get_seller_uuid(notes['issuer_key'], - pay['request'].get('productData', '')) + seller_uuid = get_seller_uuid(notes['issuer_key'], product_data) + try: + application_size = int(product_data['application_size'][0]) + except (KeyError, ValueError): + application_size = None + # Ask the marketplace for a valid price point. prices = mkt_client.get_price(pay['request']['pricePoint']) log.debug('pricePoint=%s prices=%s' % (pay['request']['pricePoint'], @@ -148,7 +153,8 @@ def start_pay(transaction_uuid, notes, user_uuid, **kw): absolutify(reverse('bango.error')), prices['prices'], icon_url, - user_uuid + user_uuid, + application_size, ) trans_pk = client.slumber.generic.transaction.get_object( uuid=transaction_uuid)['resource_pk'] diff --git a/webpay/pay/tests/test_tasks.py b/webpay/pay/tests/test_tasks.py index 1d479da45..16229c656 100644 --- a/webpay/pay/tests/test_tasks.py +++ b/webpay/pay/tests/test_tasks.py @@ -1,6 +1,7 @@ import calendar import time import urllib2 +from urllib import urlencode from django import test from django.conf import settings @@ -405,10 +406,10 @@ def start(self, marketplace, solitude): prices.get_object.return_value = self.prices marketplace.webpay.prices.return_value = prices solitude.get_transaction.return_value = { - 'status': constants.STATUS_CANCELLED, - 'notes': self.notes, - 'type': constants.TYPE_PAYMENT, - 'uuid': self.transaction_uuid + 'status': constants.STATUS_CANCELLED, + 'notes': self.notes, + 'type': constants.TYPE_PAYMENT, + 'uuid': self.transaction_uuid } tasks.start_pay(self.transaction_uuid, self.notes, self.user_uuid) @@ -505,7 +506,7 @@ def test_marketplace_seller_switch(self, marketplace, solitude): # Simulate how the Marketplace would add # a custom seller_uuid to the product data in the JWT. app_seller_uuid = 'some-seller-uuid' - data = 'seller_uuid=%s' % app_seller_uuid + data = urlencode({'seller_uuid': app_seller_uuid}) self.notes['issuer_key'] = 'marketplace-domain' self.notes['pay_request']['request']['productData'] = data self.start() @@ -514,6 +515,45 @@ def test_marketplace_seller_switch(self, marketplace, solitude): solitude.generic.seller.get_object.assert_called_with( uuid=app_seller_uuid) + @mock.patch.object(settings, 'KEY', 'marketplace-domain') + @mock.patch('lib.solitude.api.client.slumber') + def test_marketplace_application_size(self, solitude): + # Simulate how the Marketplace would add + # a custom seller_uuid and application_size + # to the product data in the JWT. + app_seller_uuid = 'some-seller-uuid' + application_size = 10 + data = urlencode({ + 'seller_uuid': app_seller_uuid, + 'application_size': application_size}) + self.notes['issuer_key'] = 'marketplace-domain' + self.notes['pay_request']['request']['productData'] = data + solitude.bango.product.get_object.side_effect = ObjectDoesNotExist + self.start() + + # Check that the application size is the one submitted in productData. + eq_(solitude.bango.billing.post.call_args[0][0]['application_size'], + application_size) + + + @mock.patch.object(settings, 'KEY', 'marketplace-domain') + @mock.patch('lib.solitude.api.client.slumber') + def test_marketplace_wrong_application_size(self, solitude): + app_seller_uuid = 'some-seller-uuid' + application_size = 'foo' + data = urlencode({ + 'seller_uuid': app_seller_uuid, + 'application_size': application_size}) + self.notes['issuer_key'] = 'marketplace-domain' + self.notes['pay_request']['request']['productData'] = data + solitude.bango.product.get_object.side_effect = ObjectDoesNotExist + self.start() + + # Check that the application size fallbacks to None if invalid. + eq_(solitude.bango.billing.post.call_args[0][0]['application_size'], + None) + + @raises(ValueError) @mock.patch.object(settings, 'KEY', 'marketplace-domain') @mock.patch('lib.solitude.api.client.api') @@ -533,10 +573,10 @@ def start(self, marketplace, solitude): prices.get_object.return_value = self.prices marketplace.webpay.prices.return_value = prices solitude.get_transaction.return_value = { - 'status': constants.STATUS_CANCELLED, - 'notes': self.notes, - 'type': constants.TYPE_PAYMENT, - 'uuid': self.transaction_uuid + 'status': constants.STATUS_CANCELLED, + 'notes': self.notes, + 'type': constants.TYPE_PAYMENT, + 'uuid': self.transaction_uuid } request = RequestFactory().get('/') request.session = {}