Ejemplo n.º 1
0
def checkout_with_ecommerce_service(user, course_key, course_mode, processor):     # pylint: disable=invalid-name
    """ Create a new basket and trigger immediate checkout, using the E-Commerce API. """
    course_id = unicode(course_key)
    try:
        api = ecommerce_api_client(user)
        # Make an API call to create the order and retrieve the results
        result = api.baskets.post({
            'products': [{'sku': course_mode.sku}],
            'checkout': True,
            'payment_processor_name': processor
        })

        # Pass the payment parameters directly from the API response.
        return result.get('payment_data')
    except SlumberBaseException:
        params = {'username': user.username, 'mode': course_mode.slug, 'course_id': course_id}
        log.exception('Failed to create order for %(username)s %(mode)s mode of %(course_id)s', params)
        raise
    finally:
        audit_log(
            'checkout_requested',
            course_id=course_id,
            mode=course_mode.slug,
            processor_name=processor,
            user_id=user.id
        )
Ejemplo n.º 2
0
    def test_log_message(self, mock_log):
        """Verify that log messages are constructed correctly."""
        audit_log('foo', qux='quux', bar='baz')

        # Verify that the logged message contains comma-separated
        # key-value pairs ordered alphabetically by key.
        message = 'foo: bar="baz", qux="quux"'
        self.assertTrue(mock_log.info.called_with(message))
Ejemplo n.º 3
0
    def post(self, request, *args, **kwargs):  # pylint: disable=unused-argument
        """
        Attempt to create the basket and enroll the user.
        """
        user = request.user
        valid, course_key, error = self._is_data_valid(request)
        if not valid:
            return DetailResponse(error, status=HTTP_406_NOT_ACCEPTABLE)

        embargo_response = embargo_api.get_embargo_response(request, course_key, user)

        if embargo_response:
            return embargo_response

        # Don't do anything if an enrollment already exists
        course_id = unicode(course_key)
        enrollment = CourseEnrollment.get_enrollment(user, course_key)
        if enrollment and enrollment.is_active:
            msg = Messages.ENROLLMENT_EXISTS.format(course_id=course_id, username=user.username)
            return DetailResponse(msg, status=HTTP_409_CONFLICT)

        # If there is no honor course mode, this most likely a Prof-Ed course. Return an error so that the JS
        # redirects to track selection.
        honor_mode = CourseMode.mode_for_course(course_key, CourseMode.HONOR)

        if not honor_mode:
            msg = Messages.NO_HONOR_MODE.format(course_id=course_id)
            return DetailResponse(msg, status=HTTP_406_NOT_ACCEPTABLE)
        elif not honor_mode.sku:
            # If there are no course modes with SKUs, enroll the user without contacting the external API.
            msg = Messages.NO_SKU_ENROLLED.format(enrollment_mode=CourseMode.HONOR, course_id=course_id,
                                                  username=user.username)
            log.info(msg)
            self._enroll(course_key, user)
            self._handle_marketing_opt_in(request, course_key, user)
            return DetailResponse(msg)

        # Setup the API

        try:
            api = ecommerce_api_client(user)
        except ValueError:
            self._enroll(course_key, user)
            msg = Messages.NO_ECOM_API.format(username=user.username, course_id=unicode(course_key))
            log.debug(msg)
            return DetailResponse(msg)

        response = None

        # Make the API call
        try:
            response_data = api.baskets.post({
                'products': [{'sku': honor_mode.sku}],
                'checkout': True,
            })

            payment_data = response_data["payment_data"]
            if payment_data:
                # Pass data to the client to begin the payment flow.
                response = JsonResponse(payment_data)
            elif response_data['order']:
                # The order was completed immediately because there is no charge.
                msg = Messages.ORDER_COMPLETED.format(order_number=response_data['order']['number'])
                log.debug(msg)
                response = DetailResponse(msg)
            else:
                msg = u'Unexpected response from basket endpoint.'
                log.error(
                    msg + u' Could not enroll user %(username)s in course %(course_id)s.',
                    {'username': user.id, 'course_id': course_id},
                )
                raise InvalidResponseError(msg)
        except (exceptions.SlumberBaseException, exceptions.Timeout) as ex:
            log.exception(ex.message)
            return InternalRequestErrorResponse(ex.message)
        finally:
            audit_log(
                'checkout_requested',
                course_id=course_id,
                mode=honor_mode.slug,
                processor_name=None,
                user_id=user.id
            )

        self._handle_marketing_opt_in(request, course_key, user)
        return response
Ejemplo n.º 4
0
    def post(self, request, *args, **kwargs):
        """
        Attempt to create the basket and enroll the user.
        """
        user = request.user
        valid, course_key, error = self._is_data_valid(request)
        if not valid:
            return DetailResponse(error, status=HTTP_406_NOT_ACCEPTABLE)

        embargo_response = embargo_api.get_embargo_response(request, course_key, user)

        if embargo_response:
            return embargo_response

        # Don't do anything if an enrollment already exists
        course_id = unicode(course_key)
        enrollment = CourseEnrollment.get_enrollment(user, course_key)
        if enrollment and enrollment.is_active:
            msg = Messages.ENROLLMENT_EXISTS.format(course_id=course_id, username=user.username)
            return DetailResponse(msg, status=HTTP_409_CONFLICT)

        # Check to see if enrollment for this course is closed.
        course = courses.get_course(course_key)
        if CourseEnrollment.is_enrollment_closed(user, course):
            msg = Messages.ENROLLMENT_CLOSED.format(course_id=course_id)
            log.info(u'Unable to enroll user %s in closed course %s.', user.id, course_id)
            return DetailResponse(msg, status=HTTP_406_NOT_ACCEPTABLE)

        # If there is no audit or honor course mode, this most likely
        # a Prof-Ed course. Return an error so that the JS redirects
        # to track selection.
        honor_mode = CourseMode.mode_for_course(course_key, CourseMode.HONOR)
        audit_mode = CourseMode.mode_for_course(course_key, CourseMode.AUDIT)

        # Accept either honor or audit as an enrollment mode to
        # maintain backwards compatibility with existing courses
        default_enrollment_mode = audit_mode or honor_mode

        if not default_enrollment_mode:
            msg = Messages.NO_DEFAULT_ENROLLMENT_MODE.format(course_id=course_id)
            return DetailResponse(msg, status=HTTP_406_NOT_ACCEPTABLE)
        elif default_enrollment_mode and not default_enrollment_mode.sku:
            # If there are no course modes with SKUs, enroll the user without contacting the external API.
            msg = Messages.NO_SKU_ENROLLED.format(
                enrollment_mode=default_enrollment_mode.slug,
                course_id=course_id,
                username=user.username
            )
            log.info(msg)
            self._enroll(course_key, user, default_enrollment_mode.slug)
            self._handle_marketing_opt_in(request, course_key, user)
            return DetailResponse(msg)

        # Setup the API

        try:
            api_session = requests.Session()
            api = ecommerce_api_client(user, session=api_session)
        except ValueError:
            self._enroll(course_key, user)
            msg = Messages.NO_ECOM_API.format(username=user.username, course_id=unicode(course_key))
            log.debug(msg)
            return DetailResponse(msg)

        response = None

        # Make the API call
        try:
            # Pass along Sailthru campaign id
            campaign_cookie = request.COOKIES.get(SAILTHRU_CAMPAIGN_COOKIE)
            if campaign_cookie:
                cookie = {SAILTHRU_CAMPAIGN_COOKIE: campaign_cookie}
                if api_session.cookies:
                    requests.utils.add_dict_to_cookiejar(api_session.cookies, cookie)
                else:
                    api_session.cookies = requests.utils.cookiejar_from_dict(cookie)

            response_data = api.baskets.post({
                'products': [{'sku': default_enrollment_mode.sku}],
                'checkout': True,
            })

            payment_data = response_data["payment_data"]
            if payment_data:
                # Pass data to the client to begin the payment flow.
                response = JsonResponse(payment_data)
            elif response_data['order']:
                # The order was completed immediately because there is no charge.
                msg = Messages.ORDER_COMPLETED.format(order_number=response_data['order']['number'])
                log.debug(msg)
                response = DetailResponse(msg)
            else:
                msg = u'Unexpected response from basket endpoint.'
                log.error(
                    msg + u' Could not enroll user %(username)s in course %(course_id)s.',
                    {'username': user.id, 'course_id': course_id},
                )
                raise InvalidResponseError(msg)
        except (exceptions.SlumberBaseException, exceptions.Timeout) as ex:
            log.exception(ex.message)
            return InternalRequestErrorResponse(ex.message)
        finally:
            audit_log(
                'checkout_requested',
                course_id=course_id,
                mode=default_enrollment_mode.slug,
                processor_name=None,
                user_id=user.id
            )

        self._handle_marketing_opt_in(request, course_key, user)
        return response
Ejemplo n.º 5
0
    def post(self, request, *args, **kwargs):  # pylint: disable=unused-argument
        """
        Attempt to create the basket and enroll the user.
        """
        user = request.user
        valid, course_key, error = self._is_data_valid(request)
        if not valid:
            return DetailResponse(error, status=HTTP_406_NOT_ACCEPTABLE)

        embargo_response = embargo_api.get_embargo_response(request, course_key, user)

        if embargo_response:
            return embargo_response

        # Don't do anything if an enrollment already exists
        course_id = unicode(course_key)
        enrollment = CourseEnrollment.get_enrollment(user, course_key)
        if enrollment and enrollment.is_active:
            msg = Messages.ENROLLMENT_EXISTS.format(course_id=course_id, username=user.username)
            return DetailResponse(msg, status=HTTP_409_CONFLICT)

        # If there is no honor course mode, this most likely a Prof-Ed course. Return an error so that the JS
        # redirects to track selection.
        honor_mode = CourseMode.mode_for_course(course_key, CourseMode.HONOR)

        if not honor_mode:
            msg = Messages.NO_HONOR_MODE.format(course_id=course_id)
            return DetailResponse(msg, status=HTTP_406_NOT_ACCEPTABLE)
        elif not honor_mode.sku:
            # If there are no course modes with SKUs, enroll the user without contacting the external API.
            msg = Messages.NO_SKU_ENROLLED.format(enrollment_mode=CourseMode.HONOR, course_id=course_id,
                                                  username=user.username)
            log.debug(msg)
            self._enroll(course_key, user)
            return DetailResponse(msg)

        # Setup the API

        try:
            api = ecommerce_api_client(user)
        except ValueError:
            self._enroll(course_key, user)
            msg = Messages.NO_ECOM_API.format(username=user.username, course_id=unicode(course_key))
            log.debug(msg)
            return DetailResponse(msg)

        # Make the API call
        try:
            response_data = api.baskets.post({
                'products': [{'sku': honor_mode.sku}],
                'checkout': True,
            })

            payment_data = response_data["payment_data"]
            if payment_data:
                # Pass data to the client to begin the payment flow.
                return JsonResponse(payment_data)
            elif response_data['order']:
                # The order was completed immediately because there is no charge.
                msg = Messages.ORDER_COMPLETED.format(order_number=response_data['order']['number'])
                log.debug(msg)
                return DetailResponse(msg)
            else:
                msg = u'Unexpected response from basket endpoint.'
                log.error(
                    msg + u' Could not enroll user %(username)s in course %(course_id)s.',
                    {'username': user.id, 'course_id': course_id},
                )
                raise InvalidResponseError(msg)
        except (exceptions.SlumberBaseException, exceptions.Timeout) as ex:
            log.exception(ex.message)
            return InternalRequestErrorResponse(ex.message)
        finally:
            audit_log(
                'checkout_requested',
                course_id=course_id,
                mode=honor_mode.slug,
                processor_name=None,
                user_id=user.id
            )
Ejemplo n.º 6
0
    def post(self, request, *args, **kwargs):
        """
        Attempt to create the basket and enroll the user.
        """
        user = request.user
        valid, course_key, error = self._is_data_valid(request)
        if not valid:
            return DetailResponse(error, status=HTTP_406_NOT_ACCEPTABLE)

        embargo_response = embargo_api.get_embargo_response(
            request, course_key, user)

        if embargo_response:
            return embargo_response

        # Don't do anything if an enrollment already exists
        course_id = unicode(course_key)
        enrollment = CourseEnrollment.get_enrollment(user, course_key)
        if enrollment and enrollment.is_active:
            msg = Messages.ENROLLMENT_EXISTS.format(course_id=course_id,
                                                    username=user.username)
            return DetailResponse(msg, status=HTTP_409_CONFLICT)

        # Check to see if enrollment for this course is closed.
        course = courses.get_course(course_key)
        if CourseEnrollment.is_enrollment_closed(user, course):
            msg = Messages.ENROLLMENT_CLOSED.format(course_id=course_id)
            log.info(u'Unable to enroll user %s in closed course %s.', user.id,
                     course_id)
            return DetailResponse(msg, status=HTTP_406_NOT_ACCEPTABLE)

        # If there is no audit or honor course mode, this most likely
        # a Prof-Ed course. Return an error so that the JS redirects
        # to track selection.
        honor_mode = CourseMode.mode_for_course(course_key, CourseMode.HONOR)
        audit_mode = CourseMode.mode_for_course(course_key, CourseMode.AUDIT)

        # Accept either honor or audit as an enrollment mode to
        # maintain backwards compatibility with existing courses
        default_enrollment_mode = audit_mode or honor_mode

        if not default_enrollment_mode:
            msg = Messages.NO_DEFAULT_ENROLLMENT_MODE.format(
                course_id=course_id)
            return DetailResponse(msg, status=HTTP_406_NOT_ACCEPTABLE)
        elif default_enrollment_mode and not default_enrollment_mode.sku:
            # If there are no course modes with SKUs, enroll the user without contacting the external API.
            msg = Messages.NO_SKU_ENROLLED.format(
                enrollment_mode=default_enrollment_mode.slug,
                course_id=course_id,
                username=user.username)
            log.info(msg)
            self._enroll(course_key, user, default_enrollment_mode.slug)
            self._handle_marketing_opt_in(request, course_key, user)
            return DetailResponse(msg)

        # Setup the API

        try:
            api_session = requests.Session()
            api = ecommerce_api_client(user, session=api_session)
        except ValueError:
            self._enroll(course_key, user)
            msg = Messages.NO_ECOM_API.format(username=user.username,
                                              course_id=unicode(course_key))
            log.debug(msg)
            return DetailResponse(msg)

        response = None

        # Make the API call
        try:
            # Pass along Sailthru campaign id
            campaign_cookie = request.COOKIES.get(SAILTHRU_CAMPAIGN_COOKIE)
            if campaign_cookie:
                cookie = {SAILTHRU_CAMPAIGN_COOKIE: campaign_cookie}
                if api_session.cookies:
                    requests.utils.add_dict_to_cookiejar(
                        api_session.cookies, cookie)
                else:
                    api_session.cookies = requests.utils.cookiejar_from_dict(
                        cookie)

            response_data = api.baskets.post({
                'products': [{
                    'sku': default_enrollment_mode.sku
                }],
                'checkout':
                True,
            })

            payment_data = response_data["payment_data"]
            if payment_data:
                # Pass data to the client to begin the payment flow.
                response = JsonResponse(payment_data)
            elif response_data['order']:
                # The order was completed immediately because there is no charge.
                msg = Messages.ORDER_COMPLETED.format(
                    order_number=response_data['order']['number'])
                log.debug(msg)
                response = DetailResponse(msg)
            else:
                msg = u'Unexpected response from basket endpoint.'
                log.error(
                    msg +
                    u' Could not enroll user %(username)s in course %(course_id)s.',
                    {
                        'username': user.id,
                        'course_id': course_id
                    },
                )
                raise InvalidResponseError(msg)
        except (exceptions.SlumberBaseException, exceptions.Timeout) as ex:
            log.exception(ex.message)
            return InternalRequestErrorResponse(ex.message)
        finally:
            audit_log('checkout_requested',
                      course_id=course_id,
                      mode=default_enrollment_mode.slug,
                      processor_name=None,
                      user_id=user.id)

        self._handle_marketing_opt_in(request, course_key, user)
        return response