diff --git a/lib/solitude/api.py b/lib/solitude/api.py index 755b53162..eb2306bf3 100644 --- a/lib/solitude/api.py +++ b/lib/solitude/api.py @@ -237,13 +237,14 @@ def configure_product_for_billing(self, transaction_uuid, 'seller_uuid={uuid} external_id={ext}' .format(provider=provider, uuid=seller_uuid, ext=product_id)) + + product = None try: product = self.slumber.generic.product.get_object_or_404( external_id=product_id, seller=seller_id, ) log.info('found product {pr}'.format(pr=product)) - print self.provider.products provider_product = self.provider.products.get_object_or_404( seller_uuid=seller_uuid, external_id=product_id) @@ -251,7 +252,8 @@ def configure_product_for_billing(self, transaction_uuid, except ObjectDoesNotExist: product, provider_product = self.create_product( product_id, product_name, - seller, provider=provider) + seller, provider=provider, + generic_product=product) # TODO: Make these real values. See bug 941952. carrier = 'USA_TMOBILE' @@ -292,59 +294,45 @@ def configure_product_for_billing(self, transaction_uuid, return provider_trans['token'], seller_id - def create_product(self, external_id, product_name, seller, - provider=None): + def create_product(self, external_id, product_name, generic_seller, + provider=None, generic_product=None): """ - Creates a product and a payment provider ID on the fly in solitude. + Creates a generic product and provider product on the fly. + + This is for scenarios like adhoc in-app payments where the + system might be selling a product for the first time. """ provider = self.set_provider(provider) - log.info(('creating product with name: %s, external_id: %s , ' - 'seller: %s') % (product_name, external_id, seller)) - if not seller['bango']: - raise ValueError('No bango account set up for %s' % - seller['resource_pk']) - - product = self.slumber.generic.product.post({ + log.info(('creating product with name: {name}, ' + 'external_id: {ext_id}, seller: {seller}') + .format(name=product_name, ext_id=external_id, + seller=generic_seller)) + + if not generic_product: + generic_product = self.slumber.generic.product.post({ + 'external_id': external_id, + 'seller': generic_seller['resource_uri'], + 'public_id': str(uuid.uuid4()), + 'access': solitude_const.ACCESS_PURCHASE, + }) + log.info('created generic product {pr}' + .format(pr=generic_product)) + + # If there is no provider seller it means the billing account has + # not yet been set up in Devhub. + provider_seller = self.provider.sellers.get_object_or_404( + seller_uuid=generic_seller['uuid']) + + provider_product = self.provider.products.post({ 'external_id': external_id, - 'seller': seller['bango']['seller'], - 'public_id': str(uuid.uuid4()), - 'access': solitude_const.ACCESS_PURCHASE, - }) - bango = self.slumber.bango.product.post({ - 'seller_bango': seller['bango']['resource_uri'], - 'seller_product': product['resource_uri'], + 'seller_id': provider_seller['resource_pk'], 'name': product_name, - 'categoryId': 1, - 'packageId': seller['bango']['package_id'], - 'secret': 'n' # This is likely going to be removed. - }) - self.slumber.bango.premium.post({ - 'bango': bango['bango_id'], - 'seller_product_bango': bango['resource_uri'], - # TODO(Kumar): why do we still need this? - # The array of all possible prices/currencies is - # set in the configure billing call. - # Marketplace also sets dummy prices here. - 'price': '0.99', - 'currencyIso': 'USD', - }) - - self.slumber.bango.rating.post({ - 'bango': bango['bango_id'], - 'rating': 'UNIVERSAL', - 'ratingScheme': 'GLOBAL', - 'seller_product_bango': bango['resource_uri'] - }) - # Bug 836865. - self.slumber.bango.rating.post({ - 'bango': bango['bango_id'], - 'rating': 'GENERAL', - 'ratingScheme': 'USA', - 'seller_product_bango': bango['resource_uri'] }) + log.info('created provider product {pr}' + .format(pr=provider_product)) - return bango + return generic_product, provider_product def get_transaction(self, uuid): transaction = self.slumber.generic.transaction.get_object(uuid=uuid) diff --git a/lib/solitude/tests.py b/lib/solitude/tests.py index 251f38a85..051c5a577 100644 --- a/lib/solitude/tests.py +++ b/lib/solitude/tests.py @@ -399,13 +399,16 @@ def set_mocks(self, returns={}, conf = returns.get(k, {}) method = conf.get('method', 'get_object_or_404') - getattr(api, method).return_value = conf.get('return', { - 'resource_pk': 1, - 'resource_uri': '/something/1', - }) + api = getattr(api, method) + if conf.get('side_effect'): + api.side_effect = conf['side_effect'] + else: + api.return_value = conf.get('return', { + 'resource_pk': 1, + 'resource_uri': '/something/1', + }) def test_with_existing_prod(self): - seller_id = 99 self.set_mocks({ 'generic.seller': { @@ -436,6 +439,52 @@ def test_with_existing_prod(self): eq_(kw['external_id'], product_uuid) eq_(kw['seller_uuid'], seller_uuid) + def test_with_new_prod(self): + new_product_id = 66 + provider_sel_id = 99 + product_uuid = 'app-xyz' + seller_uuid = 'seller-xyz' + + self.set_mocks({ + 'generic.seller': { + 'return': { + 'resource_pk': 1, + 'resource_uri': '/seller/1', + 'uuid': seller_uuid, + } + }, + 'provider.reference.transactions': { + 'method': 'post', + 'return': { + 'token': 'zippy-trans-token', + } + }, + 'provider.reference.products': { + 'side_effect': ObjectDoesNotExist, + }, + 'provider.reference.sellers': { + 'return': { + 'resource_pk': provider_sel_id, + } + }, + }) + + self.slumber.provider.reference.products.post.return_value = { + 'resource_pk': new_product_id, + } + + result = self.configure(seller_uuid=seller_uuid, + product_uuid=product_uuid) + + eq_(result[0], 'zippy-trans-token') + + kw = self.slumber.provider.reference.products.post.call_args[0][0] + eq_(kw['external_id'], product_uuid) + eq_(kw['seller_id'], provider_sel_id) + + kw = self.slumber.provider.reference.transactions.post.call_args[0][0] + eq_(kw['product_id'], new_product_id) + @mock.patch('lib.solitude.api.client.slumber') class TransactionTest(TestCase):