def post(self, request): basket_id = request.data['basket_id'] payment_processor = request.data['payment_processor'] # Get the basket, and make sure it belongs to the current user. try: basket = request.user.baskets.get(id=basket_id) except ObjectDoesNotExist: return HttpResponseBadRequest('Basket [{}] not found.'.format(basket_id)) # Freeze the basket so that it cannot be modified basket.strategy = request.strategy Applicator().apply(basket, request.user, request) basket.freeze() # Return the payment info try: payment_processor = get_processor_class_by_name(payment_processor)() except ProcessorNotFoundError: logger.exception('Failed to get payment processor [%s].', payment_processor) return HttpResponseBadRequest( 'Payment processor [{}] not found.'.format(payment_processor) ) parameters = payment_processor.get_transaction_parameters(basket, request=request) payment_page_url = parameters.pop('payment_page_url') data = { 'payment_form_data': parameters, 'payment_page_url': payment_page_url, 'payment_processor': payment_processor.NAME, } serializer = CheckoutSerializer(data) return Response(serializer.data)
def _issue_credit(self): """Issue a credit/refund to the purchaser via the payment processor used for the original order.""" try: # NOTE: Update this if we ever support multiple payment sources for a single order. source = self.order.sources.first() processor = get_processor_class_by_name(source.source_type.name)( self.order.site) amount = self.total_credit_excl_tax refund_reference_number = processor.issue_credit( self.order.number, self.order.basket, source.reference, amount, self.currency) source.refund(amount, reference=refund_reference_number) event_type, __ = PaymentEventType.objects.get_or_create( name=PaymentEventTypeName.REFUNDED) PaymentEvent.objects.create(event_type=event_type, order=self.order, amount=amount, reference=refund_reference_number, processor_name=processor.NAME) audit_log('credit_issued', amount=amount, currency=self.currency, processor_name=processor.NAME, refund_id=self.id, user_id=self.user.id) except AttributeError: # Order has no sources, resulting in an exception when trying to access `source_type`. # This occurs when attempting to refund free orders. logger.info("No payments to credit for Refund [%d]", self.id)
def _issue_credit(self): """Issue a credit to the purchaser via the payment processor used for the original order.""" try: # NOTE: Update this if we ever support multiple payment sources for a single order. source = self.order.sources.first() processor = get_processor_class_by_name(source.source_type.name)(self.order.site) amount = self.total_credit_excl_tax refund_reference_number = processor.issue_credit(self.order, source.reference, amount, self.currency) source.refund(amount, reference=refund_reference_number) event_type, __ = PaymentEventType.objects.get_or_create(name=PaymentEventTypeName.REFUNDED) PaymentEvent.objects.create( event_type=event_type, order=self.order, amount=amount, reference=refund_reference_number, processor_name=processor.NAME ) audit_log( 'credit_issued', amount=amount, currency=self.currency, processor_name=processor.NAME, refund_id=self.id, user_id=self.user.id ) except AttributeError: # Order has no sources, resulting in an exception when trying to access `source_type`. # This occurs when attempting to refund free orders. logger.info("No payments to credit for Refund [%d]", self.id)
def _get_payment_processor(site, name): key = ( site, name, ) payment_processor = _payment_processors.get(key) if not payment_processor: payment_processor = get_processor_class_by_name(name)(site) _payment_processors[key] = payment_processor return payment_processor
def _clean_payment_processors(self): """ Validates payment_processors field value Raises: ValidationError: If `payment_processors` field contains invalid/unknown payment_processor names """ value = self.payment_processors.strip() if not value: raise ValidationError( 'Invalid payment processors field: must not consist only of whitespace characters' ) processor_names = value.split(',') for name in processor_names: try: get_processor_class_by_name(name.strip()) except ProcessorNotFoundError as exc: log.exception( "Exception validating site configuration for site `%s` - payment processor %s could not be found", self.site.id, name) raise ValidationError(str(exc))
def _clean_payment_processors(self): """ Validates payment_processors field value Raises: ValidationError: If `payment_processors` field contains invalid/unknown payment_processor names """ value = self.payment_processors.strip() if not value: raise ValidationError('Invalid payment processors field: must not consist only of whitespace characters') processor_names = value.split(',') for name in processor_names: try: get_processor_class_by_name(name.strip()) except ProcessorNotFoundError as exc: log.exception( "Exception validating site configuration for site `%s` - payment processor %s could not be found", self.site.id, name ) raise ValidationError(exc.message)
def assert_successful_basket_creation(self, skus=None, checkout=None, payment_processor_name=None, requires_payment=False): """Verify that basket creation succeeded.""" # Ideally, we'd use Oscar's ShippingEventTypeFactory here, but it's not exposed/public. ShippingEventType.objects.get_or_create(name=SHIPPING_EVENT_NAME) with patch('ecommerce.extensions.analytics.utils.audit_log' ) as mock_audit_log: response = self.create_basket( skus=skus, checkout=checkout, payment_processor_name=payment_processor_name) self.assertEqual(response.status_code, 200) basket = Basket.objects.get() basket.strategy = Selector().strategy(user=self.user) self.assertEqual(response.data['id'], basket.id) if checkout: self.assertTrue( mock_audit_log.called_with('basket_frozen', amount=basket.total_excl_tax, basket_id=basket.id, currency=basket.currency, user_id=basket.owner.id)) if requires_payment: self.assertIsNone(response.data['order']) self.assertIsNotNone(response.data['payment_data'] ['payment_processor_name']) self.assertIsNotNone( response.data['payment_data']['payment_form_data']) if payment_processor_name is None: processor_class = get_default_processor_class() else: processor_class = get_processor_class_by_name( payment_processor_name) assert (response.data['payment_data']['payment_page_url'] == processor_class( Site.objects.all()[0]).client_side_payment_url) else: self.assertEqual(response.data['order']['number'], Order.objects.get().number) self.assertIsNone(response.data['payment_data']) else: self.assertIsNone(response.data['order']) self.assertIsNone(response.data['payment_data'])
def post(self, request): basket_id = request.data['basket_id'] payment_processor_name = request.data['payment_processor'] logger.info('Checkout view called for basket [%s].', basket_id) request._request.POST = request._request.POST.copy() # pylint: disable=protected-access request._request.POST['discount_jwt'] = request.data.get( 'discount_jwt') # pylint: disable=protected-access # Get the basket, and make sure it belongs to the current user. try: basket = request.user.baskets.get(id=basket_id) except ObjectDoesNotExist: return HttpResponseBadRequest( 'Basket [{}] not found.'.format(basket_id)) # Freeze the basket so that it cannot be modified basket.strategy = request.strategy Applicator().apply(basket, request.user, request) basket.freeze() # Return the payment info try: payment_processor = get_processor_class_by_name( payment_processor_name)(request.site) except ProcessorNotFoundError: logger.exception( 'Failed to get payment processor [%s]. basket id: [%s]. price: [%s]', payment_processor_name, basket_id, basket.total_excl_tax) return HttpResponseBadRequest( 'Payment processor [{}] not found.'.format( payment_processor_name)) parameters = payment_processor.get_transaction_parameters( basket, request=request) payment_page_url = parameters.pop('payment_page_url') data = { 'payment_form_data': parameters, 'payment_page_url': payment_page_url, 'payment_processor': payment_processor.NAME, } serializer = CheckoutSerializer(data) return Response(serializer.data)
def _issue_credit(self): """Issue a credit to the purchaser via the payment processor used for the original order.""" try: # TODO Update this if we ever support multiple payment sources for a single order. source = self.order.sources.first() processor = get_processor_class_by_name(source.source_type.name)() processor.issue_credit(source, self.total_credit_excl_tax, self.currency) audit_log('credit_issued', amount=self.total_credit_excl_tax, currency=self.currency, processor_name=processor.NAME, refund_id=self.id, user_id=self.user.id) except AttributeError: # Order has no sources, resulting in an exception when trying to access `source_type`. # This occurs when attempting to refund free orders. logger.info("No payments to credit for Refund [%d]", self.id)
def _issue_credit(self): """Issue a credit to the purchaser via the payment processor used for the original order.""" try: # TODO Update this if we ever support multiple payment sources for a single order. source = self.order.sources.first() processor = get_processor_class_by_name(source.source_type.name)() processor.issue_credit(source, self.total_credit_excl_tax, self.currency) audit_log( 'credit_issued', amount=self.total_credit_excl_tax, currency=self.currency, processor_name=processor.NAME, refund_id=self.id, user_id=self.user.id ) except AttributeError: # Order has no sources, resulting in an exception when trying to access `source_type`. # This occurs when attempting to refund free orders. logger.info("No payments to credit for Refund [%d]", self.id)
def post(self, request): basket_id = request.data['basket_id'] payment_processor = request.data['payment_processor'] # Get the basket, and make sure it belongs to the current user. try: basket = request.user.baskets.get(id=basket_id) except ObjectDoesNotExist: return HttpResponseBadRequest( 'Basket [{}] not found.'.format(basket_id)) # Freeze the basket so that it cannot be modified basket.strategy = request.strategy Applicator().apply(basket, request.user, request) basket.freeze() # Return the payment info try: payment_processor = get_processor_class_by_name( payment_processor)() except ProcessorNotFoundError: logger.exception('Failed to get payment processor [%s].', payment_processor) return HttpResponseBadRequest( 'Payment processor [{}] not found.'.format(payment_processor)) parameters = payment_processor.get_transaction_parameters( basket, request=request) payment_page_url = parameters.pop('payment_page_url') data = { 'payment_form_data': parameters, 'payment_page_url': payment_page_url, 'payment_processor': payment_processor.NAME, } serializer = CheckoutSerializer(data) return Response(serializer.data)
def create(self, request, *args, **kwargs): """Add products to the authenticated user's basket. Expects an array of product objects, 'products', each containing a SKU, in the request body. The SKUs are used to populate the user's basket with the corresponding products. The caller indicates whether checkout should occur by providing a Boolean value in the request body, 'checkout'. If checkout operations are requested and the contents of the user's basket are free, an order is placed immediately. If checkout operations are requested but the contents of the user's basket are not free, pre-payment operations are performed instead of placing an order. The caller indicates which payment processor to use by providing a string in the request body, 'payment_processor_name'. Protected by JWT authentication. Consuming services (e.g., the LMS) must authenticate themselves by passing a JWT in the Authorization HTTP header, prepended with the string 'JWT '. The JWT payload should contain user details. At a minimum, these details must include a username; providing an email is recommended. Arguments: request (HttpRequest): With parameters 'products', 'checkout', and 'payment_processor_name' in the body. Returns: 200 if a basket was created successfully; the basket ID is included in the response body along with either an order number corresponding to the placed order (None if one wasn't placed) or payment information (None if payment isn't required). 400 if the client provided invalid data or attempted to add an unavailable product to their basket, with reason for the failure in JSON format. 401 if an unauthenticated request is denied permission to access the endpoint. 429 if the client has made requests at a rate exceeding that allowed by the configured rate limit. 500 if an error occurs when attempting to initiate checkout. Examples: Create a basket for the user with username 'Saul' as follows. Successful fulfillment requires that a user with username 'Saul' exists on the LMS, and that EDX_API_KEY be configured within both the LMS and the ecommerce service. >>> url = 'http://*****:*****@bettercallsaul.com'}, 'insecure-secret-key') >>> headers = { 'content-type': 'application/json', 'Authorization': 'JWT ' + token } If checkout is not desired: >>> data = {'products': [{'sku': 'SOME-SEAT'}, {'sku': 'SOME-OTHER-SEAT'}], 'checkout': False} >>> response = requests.post(url, data=json.dumps(data), headers=headers) >>> response.json() { 'id': 7, 'order': None, 'payment_data': None } If the product with SKU 'FREE-SEAT' is free and checkout is desired: >>> data = {'products': [{'sku': 'FREE-SEAT'}], 'checkout': True, 'payment_processor_name': 'paypal'} >>> response = requests.post(url, data=json.dumps(data), headers=headers) >>> response.json() { 'id': 7, 'order': {'number': 'OSCR-100007'}, 'payment_data': None } If the product with SKU 'PAID-SEAT' is not free and checkout is desired: >>> data = {'products': [{'sku': 'PAID-SEAT'}], 'checkout': True, 'payment_processor_name': 'paypal'} >>> response = requests.post(url, data=json.dumps(data), headers=headers) >>> response.json() { 'id': 7, 'order': None, 'payment_data': { 'payment_processor_name': 'paypal', 'payment_form_data': {...}, 'payment_page_url': 'https://www.someexternallyhostedpaymentpage.com' } } """ # Explicitly delimit operations which will be rolled back if an exception occurs. # atomic() context managers restore atomicity at points where we are modifying data # (baskets, then orders) to ensure that we don't leave the system in a dirty state # in the event of an error. with transaction.atomic(): basket = Basket.create_basket(request.site, request.user) basket_id = basket.id attribute_cookie_data(basket, request) requested_products = request.data.get('products') if requested_products: is_multi_product_basket = True if len( requested_products) > 1 else False for requested_product in requested_products: # Ensure the requested products exist sku = requested_product.get('sku') if sku: try: product = data_api.get_product(sku) except api_exceptions.ProductNotFoundError as error: return self._report_bad_request( error.message, api_exceptions.PRODUCT_NOT_FOUND_USER_MESSAGE) else: return self._report_bad_request( api_exceptions.SKU_NOT_FOUND_DEVELOPER_MESSAGE, api_exceptions.SKU_NOT_FOUND_USER_MESSAGE) # Ensure the requested products are available for purchase before adding them to the basket availability = basket.strategy.fetch_for_product( product).availability if not availability.is_available_to_buy: return self._report_bad_request( api_exceptions. PRODUCT_UNAVAILABLE_DEVELOPER_MESSAGE.format( sku=sku, availability=availability.message), api_exceptions.PRODUCT_UNAVAILABLE_USER_MESSAGE) basket.add_product(product) logger.info('Added product with SKU [%s] to basket [%d]', sku, basket_id) # Call signal handler to notify listeners that something has been added to the basket basket_addition = get_class('basket.signals', 'basket_addition') basket_addition.send( sender=basket_addition, product=product, user=request.user, request=request, basket=basket, is_multi_product_basket=is_multi_product_basket) else: # If no products were included in the request, we cannot checkout. return self._report_bad_request( api_exceptions.PRODUCT_OBJECTS_MISSING_DEVELOPER_MESSAGE, api_exceptions.PRODUCT_OBJECTS_MISSING_USER_MESSAGE) if request.data.get('checkout') is True: # Begin the checkout process, if requested, with the requested payment processor. payment_processor_name = request.data.get('payment_processor_name') if payment_processor_name: try: payment_processor = get_processor_class_by_name( payment_processor_name) except payment_exceptions.ProcessorNotFoundError as error: return self._report_bad_request( error.message, payment_exceptions.PROCESSOR_NOT_FOUND_USER_MESSAGE) else: payment_processor = get_default_processor_class() try: response_data = self._checkout(basket, payment_processor(request.site), request) except Exception as ex: # pylint: disable=broad-except basket.delete() logger.exception( 'Failed to initiate checkout for Basket [%d]. The basket has been deleted.', basket_id) return Response({'developer_message': ex.message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) else: # Return a serialized basket, if checkout was not requested. response_data = self._generate_basic_response(basket) return Response(response_data, status=status.HTTP_200_OK)
def create(self, request, *args, **kwargs): """Add products to the authenticated user's basket. Expects an array of product objects, 'products', each containing a SKU, in the request body. The SKUs are used to populate the user's basket with the corresponding products. The caller indicates whether checkout should occur by providing a Boolean value in the request body, 'checkout'. If checkout operations are requested and the contents of the user's basket are free, an order is placed immediately. If checkout operations are requested but the contents of the user's basket are not free, pre-payment operations are performed instead of placing an order. The caller indicates which payment processor to use by providing a string in the request body, 'payment_processor_name'. Protected by JWT authentication. Consuming services (e.g., the LMS) must authenticate themselves by passing a JWT in the Authorization HTTP header, prepended with the string 'JWT '. The JWT payload should contain user details. At a minimum, these details must include a username; providing an email is recommended. Arguments: request (HttpRequest): With parameters 'products', 'checkout', and 'payment_processor_name' in the body. Returns: 200 if a basket was created successfully; the basket ID is included in the response body along with either an order number corresponding to the placed order (None if one wasn't placed) or payment information (None if payment isn't required). 400 if the client provided invalid data or attempted to add an unavailable product to their basket, with reason for the failure in JSON format. 401 if an unauthenticated request is denied permission to access the endpoint. 429 if the client has made requests at a rate exceeding that allowed by the configured rate limit. 500 if an error occurs when attempting to initiate checkout. Examples: Create a basket for the user with username 'Saul' as follows. Successful fulfillment requires that a user with username 'Saul' exists on the LMS, and that EDX_API_KEY be configured within both the LMS and the ecommerce service. >>> url = 'http://*****:*****@bettercallsaul.com'}, 'insecure-secret-key') >>> headers = { 'content-type': 'application/json', 'Authorization': 'JWT ' + token } If checkout is not desired: >>> data = {'products': [{'sku': 'SOME-SEAT'}, {'sku': 'SOME-OTHER-SEAT'}], 'checkout': False} >>> response = requests.post(url, data=json.dumps(data), headers=headers) >>> response.json() { 'id': 7, 'order': None, 'payment_data': None } If the product with SKU 'FREE-SEAT' is free and checkout is desired: >>> data = {'products': [{'sku': 'FREE-SEAT'}], 'checkout': True, 'payment_processor_name': 'paypal'} >>> response = requests.post(url, data=json.dumps(data), headers=headers) >>> response.json() { 'id': 7, 'order': {'number': 'OSCR-100007'}, 'payment_data': None } If the product with SKU 'PAID-SEAT' is not free and checkout is desired: >>> data = {'products': [{'sku': 'PAID-SEAT'}], 'checkout': True, 'payment_processor_name': 'paypal'} >>> response = requests.post(url, data=json.dumps(data), headers=headers) >>> response.json() { 'id': 7, 'order': None, 'payment_data': { 'payment_processor_name': 'paypal', 'payment_form_data': {...}, 'payment_page_url': 'https://www.someexternallyhostedpaymentpage.com' } } """ # Explicitly delimit operations which will be rolled back if an exception occurs. # atomic() context managers restore atomicity at points where we are modifying data # (baskets, then orders) to ensure that we don't leave the system in a dirty state # in the event of an error. with transaction.atomic(): basket = Basket.create_basket(request.site, request.user) basket_id = basket.id requested_products = request.data.get(AC.KEYS.PRODUCTS) if requested_products: for requested_product in requested_products: # Ensure the requested products exist sku = requested_product.get(AC.KEYS.SKU) if sku: try: product = data_api.get_product(sku) except api_exceptions.ProductNotFoundError as error: return self._report_bad_request( error.message, api_exceptions.PRODUCT_NOT_FOUND_USER_MESSAGE ) else: return self._report_bad_request( api_exceptions.SKU_NOT_FOUND_DEVELOPER_MESSAGE, api_exceptions.SKU_NOT_FOUND_USER_MESSAGE ) # Ensure the requested products are available for purchase before adding them to the basket availability = basket.strategy.fetch_for_product(product).availability if not availability.is_available_to_buy: return self._report_bad_request( api_exceptions.PRODUCT_UNAVAILABLE_DEVELOPER_MESSAGE.format( sku=sku, availability=availability.message ), api_exceptions.PRODUCT_UNAVAILABLE_USER_MESSAGE ) basket.add_product(product) logger.info('Added product with SKU [%s] to basket [%d]', sku, basket_id) else: # If no products were included in the request, we cannot checkout. return self._report_bad_request( api_exceptions.PRODUCT_OBJECTS_MISSING_DEVELOPER_MESSAGE, api_exceptions.PRODUCT_OBJECTS_MISSING_USER_MESSAGE ) if request.data.get(AC.KEYS.CHECKOUT) is True: # Begin the checkout process, if requested, with the requested payment processor. payment_processor_name = request.data.get(AC.KEYS.PAYMENT_PROCESSOR_NAME) if payment_processor_name: try: payment_processor = get_processor_class_by_name(payment_processor_name) except payment_exceptions.ProcessorNotFoundError as error: return self._report_bad_request( error.message, payment_exceptions.PROCESSOR_NOT_FOUND_USER_MESSAGE ) else: payment_processor = get_default_processor_class() try: response_data = self._checkout(basket, payment_processor()) except Exception as ex: # pylint: disable=broad-except basket.delete() logger.exception('Failed to initiate checkout for Basket [%d]. The basket has been deleted.', basket_id) return Response({'developer_message': ex.message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) else: # Return a serialized basket, if checkout was not requested. response_data = self._generate_basic_response(basket) return Response(response_data, status=status.HTTP_200_OK)
def create(self, request, *args, **kwargs): """Add products to the authenticated user's basket. Expects a list of product objects, 'products', each containing a SKU, in the request body. The SKUs are used to populate the user's basket with the corresponding products. The caller indicates whether checkout should occur by providing a Boolean value in the request body, 'checkout'. If checkout operations are requested and the contents of the user's basket are free, an order is placed immediately. If checkout operations are requested but the contents of the user's basket are not free, pre-payment operations are performed instead of placing an order. The caller indicates which payment processor to use by providing a string in the request body, 'payment_processor_name'. Protected by JWT authentication. Consuming services (e.g., the LMS) must authenticate themselves by passing a JWT in the Authorization HTTP header, prepended with the string 'JWT '. The JWT payload should contain user details. At a minimum, these details must include a username; providing an email is recommended. Arguments: request (HttpRequest): With parameters 'products', 'checkout', and 'payment_processor_name' in the body. Returns: HTTP_200_OK if a basket was created successfully; the basket ID is included in the response body along with either an order number corresponding to the placed order (None if one wasn't placed) or payment information (None if payment isn't required). HTTP_400_BAD_REQUEST if the client provided invalid data or attempted to add an unavailable product to their basket, with reason for the failure in JSON format. HTTP_401_UNAUTHORIZED if an unauthenticated request is denied permission to access the endpoint. HTTP_429_TOO_MANY_REQUESTS if the client has made requests at a rate exceeding that allowed by the configured rate limit. Examples: Create a basket for the user with username 'Saul' as follows. Successful fulfillment requires that a user with username 'Saul' exists on the LMS, and that EDX_API_KEY be configured within both the LMS and the ecommerce service. >>> url = 'http://*****:*****@bettercallsaul.com'}, 'insecure-secret-key') >>> headers = { 'content-type': 'application/json', 'Authorization': 'JWT ' + token } If checkout is not desired: >>> data = {'products': [{'sku': 'SOME-SEAT'}, {'sku': 'SOME-OTHER-SEAT'}], 'checkout': False} >>> response = requests.post(url, data=json.dumps(data), headers=headers) >>> json.loads(response.content) { u'id': 7, u'order': None, u'payment_data': None } If the product with SKU 'FREE-SEAT' is free and checkout is desired: >>> data = {'products': [{'sku': 'FREE-SEAT'}], 'checkout': True, 'payment_processor_name': 'paypal'} >>> response = requests.post(url, data=json.dumps(data), headers=headers) >>> json.loads(response.content) { u'id': 7, u'order': {u'number': u'OSCR-100007'}, u'payment_data': None } If the product with SKU 'PAID-SEAT' is not free and checkout is desired: >>> data = {'products': [{'sku': 'PAID-SEAT'}], 'checkout': True, 'payment_processor_name': 'paypal'} >>> response = requests.post(url, data=json.dumps(data), headers=headers) >>> json.loads(response.content) { u'id': 7, u'order': None, u'payment_data': { u'payment_processor_name': u'paypal', u'payment_form_data': {...}, u'payment_page_url': u'https://www.someexternallyhostedpaymentpage.com' } } """ basket = data.get_basket(request.user) requested_products = request.data.get(AC.KEYS.PRODUCTS) if requested_products: for requested_product in requested_products: sku = requested_product.get(AC.KEYS.SKU) if sku: try: product = data.get_product(sku) except api_exceptions.ProductNotFoundError as error: return self._report_bad_request(error.message, api_exceptions.PRODUCT_NOT_FOUND_USER_MESSAGE) else: return self._report_bad_request( api_exceptions.SKU_NOT_FOUND_DEVELOPER_MESSAGE, api_exceptions.SKU_NOT_FOUND_USER_MESSAGE ) availability = basket.strategy.fetch_for_product(product).availability if not availability.is_available_to_buy: return self._report_bad_request( api_exceptions.PRODUCT_UNAVAILABLE_DEVELOPER_MESSAGE.format( sku=sku, availability=availability.message ), api_exceptions.PRODUCT_UNAVAILABLE_USER_MESSAGE ) basket.add_product(product) logger.info( u"Added product with SKU [%s] to basket [%d]", sku, basket.id, ) else: return self._report_bad_request( api_exceptions.PRODUCT_OBJECTS_MISSING_DEVELOPER_MESSAGE, api_exceptions.PRODUCT_OBJECTS_MISSING_USER_MESSAGE ) if request.data.get(AC.KEYS.CHECKOUT) is True: payment_processor_name = request.data.get(AC.KEYS.PAYMENT_PROCESSOR_NAME) if payment_processor_name: try: payment_processor = get_processor_class_by_name(payment_processor_name) except payment_exceptions.ProcessorNotFoundError as error: return self._report_bad_request( error.message, payment_exceptions.PROCESSOR_NOT_FOUND_USER_MESSAGE ) else: payment_processor = get_default_processor_class() response_data = self._checkout(basket, payment_processor=payment_processor()) else: response_data = self._generate_basic_response(basket) return Response(response_data, status=status.HTTP_200_OK)
def test_get_processor_class_by_name(self, processor): """ Verify the function returns the appropriate processor class or raises an exception, if not found. """ self.assertIs(helpers.get_processor_class_by_name(processor.NAME), processor)
def test_get_processor_class_by_name(self, processor): """ Verify the function returns the appropriate processor class or raises an exception, if not found. """ self.assertIs(helpers.get_processor_class_by_name(processor.NAME), processor)