Esempio n. 1
0
    def post(
        self, request, *args, **kwargs
    ):  # pylint: disable=too-many-locals,unused-argument
        """
        Create a new unfulfilled Order from the user's basket
        and return information used to submit to CyberSource.
        """
        validated_basket = validate_basket_for_checkout(request.user)
        affiliate_id = get_affiliate_id_from_request(request)
        order = create_unfulfilled_order(validated_basket, affiliate_id=affiliate_id)
        base_url = request.build_absolute_uri("/")
        text_id = validated_basket.product_version.product.content_object.text_id
        receipt_url = make_receipt_url(base_url=base_url, readable_id=text_id)
        user_ip, _ = get_client_ip(request)

        if order.total_price_paid == 0:
            # If price is $0, don't bother going to CyberSource, just mark as fulfilled
            order.status = Order.FULFILLED
            order.save()
            sync_hubspot_deal(order)

            complete_order(order)
            order.save_and_log(request.user)

            product = validated_basket.product_version.product

            # $0 orders do not go to CyberSource so we need to build a payload
            # for GTM in order to track these purchases as well. Actual tracking
            # call is sent from the frontend.
            payload = {
                "transaction_id": "T-{}".format(order.id),
                "transaction_total": 0.00,
                "product_type": product.type_string,
                "courseware_id": text_id,
                "reference_number": "REF-{}".format(order.id),
            }

            # This redirects the user to our order success page
            url = receipt_url
            if settings.ENABLE_ORDER_RECEIPTS:
                send_ecommerce_order_receipt(order)
            method = "GET"
        else:
            # This generates a signed payload which is submitted as an HTML form to CyberSource
            cancel_url = urljoin(base_url, "checkout/")
            payload = generate_cybersource_sa_payload(
                order=order,
                receipt_url=receipt_url,
                cancel_url=cancel_url,
                ip_address=user_ip,
            )
            url = settings.CYBERSOURCE_SECURE_ACCEPTANCE_URL
            method = "POST"

        return Response({"payload": payload, "url": url, "method": method})
Esempio n. 2
0
    def test_signed_payload(self):
        """
        A valid payload should be signed appropriately
        """
        course_run, user = create_purchasable_course_run()
        order = create_unfulfilled_order(course_run.edx_course_key, user)
        username = '******'
        transaction_uuid = 'hex'
        fake_user_ip = "194.100.0.1"

        now = now_in_utc()

        with patch('ecommerce.api.get_social_username', autospec=True, return_value=username):
            with patch('ecommerce.api.now_in_utc', autospec=True, return_value=now) as now_mock:
                with patch('ecommerce.api.uuid.uuid4', autospec=True, return_value=MagicMock(hex=transaction_uuid)):
                    payload = generate_cybersource_sa_payload(order, 'dashboard_url', fake_user_ip)
        signature = payload.pop('signature')
        assert generate_cybersource_sa_signature(payload) == signature
        signed_field_names = payload['signed_field_names'].split(',')
        assert signed_field_names == sorted(payload.keys())
        quoted_course_key = quote_plus(course_run.edx_course_key)

        assert payload == {
            'access_key': CYBERSOURCE_ACCESS_KEY,
            'amount': str(order.total_price_paid),
            'consumer_id': username,
            'currency': 'USD',
            'item_0_code': 'course',
            'item_0_name': '{}'.format(course_run.title),
            'item_0_quantity': 1,
            'item_0_sku': '{}'.format(course_run.edx_course_key),
            'item_0_tax_amount': '0',
            'item_0_unit_price': str(order.total_price_paid),
            'line_item_count': 1,
            'locale': 'en-us',
            'override_custom_cancel_page': 'dashboard_url?status=cancel&course_key={}'.format(quoted_course_key),
            'override_custom_receipt_page': "dashboard_url?status=receipt&course_key={}".format(quoted_course_key),
            'reference_number': make_reference_id(order),
            'profile_id': CYBERSOURCE_PROFILE_ID,
            'signed_date_time': now.strftime(ISO_8601_FORMAT),
            'signed_field_names': ','.join(signed_field_names),
            'transaction_type': 'sale',
            'transaction_uuid': transaction_uuid,
            'unsigned_field_names': '',
            'merchant_defined_data1': 'course',
            'merchant_defined_data2': '{}'.format(course_run.title),
            'merchant_defined_data3': '{}'.format(course_run.edx_course_key),
            "customer_ip_address": fake_user_ip if fake_user_ip else None,
        }
        assert now_mock.called
Esempio n. 3
0
    def test_signed_payload(self):
        """
        A valid payload should be signed appropriately
        """
        course_run, user = create_purchasable_course_run()
        order = create_unfulfilled_order(course_run.edx_course_key, user)
        username = '******'
        transaction_uuid = 'hex'

        now = now_in_utc()

        with patch('ecommerce.api.get_social_username', autospec=True, return_value=username):
            with patch('ecommerce.api.now_in_utc', autospec=True, return_value=now) as now_mock:
                with patch('ecommerce.api.uuid.uuid4', autospec=True, return_value=MagicMock(hex=transaction_uuid)):
                    payload = generate_cybersource_sa_payload(order, 'dashboard_url')
        signature = payload.pop('signature')
        assert generate_cybersource_sa_signature(payload) == signature
        signed_field_names = payload['signed_field_names'].split(',')
        assert signed_field_names == sorted(payload.keys())
        quoted_course_key = quote_plus(course_run.edx_course_key)

        assert payload == {
            'access_key': CYBERSOURCE_ACCESS_KEY,
            'amount': str(order.total_price_paid),
            'consumer_id': username,
            'currency': 'USD',
            'item_0_code': 'course',
            'item_0_name': '{}'.format(course_run.title),
            'item_0_quantity': 1,
            'item_0_sku': '{}'.format(course_run.edx_course_key),
            'item_0_tax_amount': '0',
            'item_0_unit_price': str(order.total_price_paid),
            'line_item_count': 1,
            'locale': 'en-us',
            'override_custom_cancel_page': 'dashboard_url?status=cancel&course_key={}'.format(quoted_course_key),
            'override_custom_receipt_page': "dashboard_url?status=receipt&course_key={}".format(quoted_course_key),
            'reference_number': make_reference_id(order),
            'profile_id': CYBERSOURCE_PROFILE_ID,
            'signed_date_time': now.strftime(ISO_8601_FORMAT),
            'signed_field_names': ','.join(signed_field_names),
            'transaction_type': 'sale',
            'transaction_uuid': transaction_uuid,
            'unsigned_field_names': '',
            'merchant_defined_data1': 'course',
            'merchant_defined_data2': '{}'.format(course_run.title),
            'merchant_defined_data3': '{}'.format(course_run.edx_course_key),
        }
        assert now_mock.called
Esempio n. 4
0
    def post(self, request, *args, **kwargs):
        """
        Creates a new unfulfilled Order and returns information used to submit to CyberSource
        """
        try:
            course_id = request.data['course_id']
        except KeyError:
            raise ValidationError("Missing course_id")

        order = create_unfulfilled_order(course_id, request.user)
        payload = generate_cybersource_sa_payload(order)

        return Response({
            'payload': payload,
            'url': settings.CYBERSOURCE_SECURE_ACCEPTANCE_URL,
        })
Esempio n. 5
0
    def test_signed_payload(self):
        """
        A valid payload should be signed appropriately
        """
        course_run, user = create_purchasable_course_run()
        order = create_unfulfilled_order(course_run.edx_course_key, user)
        username = '******'
        transaction_uuid = 'hex'

        now = datetime.utcnow()

        with patch('ecommerce.api.get_social_username', autospec=True, return_value=username):
            with patch('ecommerce.api.datetime', autospec=True, utcnow=MagicMock(return_value=now)):
                with patch('ecommerce.api.uuid.uuid4', autospec=True, return_value=MagicMock(hex=transaction_uuid)):
                    payload = generate_cybersource_sa_payload(order)
        signature = payload.pop('signature')
        assert generate_cybersource_sa_signature(payload) == signature
        signed_field_names = payload['signed_field_names'].split(',')
        assert signed_field_names == sorted(payload.keys())

        assert payload == {
            'access_key': CYBERSOURCE_ACCESS_KEY,
            'amount': str(order.total_price_paid),
            'consumer_id': username,
            'currency': 'USD',
            'locale': 'en-us',
            'override_custom_cancel_page': 'https://micromasters.mit.edu?cancel',
            'override_custom_receipt_page': "https://micromasters.mit.edu?receipt",
            'reference_number': make_reference_id(order),
            'profile_id': CYBERSOURCE_PROFILE_ID,
            'signed_date_time': now.strftime(ISO_8601_FORMAT),
            'signed_field_names': ','.join(signed_field_names),
            'transaction_type': 'sale',
            'transaction_uuid': transaction_uuid,
            'unsigned_field_names': '',
        }
Esempio n. 6
0
    def post(self, request, *args, **kwargs):
        """
        If the course run is part of a financial aid program, create a new unfulfilled Order
        and return information used to submit to CyberSource.

        If the program does not have financial aid, this will return a URL to let the user
        pay for the course on edX.
        """
        user_ip, _ = get_client_ip(request)
        try:
            course_id = request.data['course_id']
        except KeyError:
            raise ValidationError("Missing course_id")

        course_run = get_object_or_404(
            CourseRun,
            course__program__live=True,
            edx_course_key=course_id,
        )
        if course_run.course.program.financial_aid_availability:
            order = create_unfulfilled_order(course_id, request.user)
            dashboard_url = request.build_absolute_uri('/dashboard/')
            if order.total_price_paid == 0:
                # If price is $0, don't bother going to CyberSource, just mark as fulfilled
                order.status = Order.FULFILLED
                order.save_and_log(request.user)
                try:
                    enroll_user_on_success(order)
                except:  # pylint: disable=bare-except
                    log.exception(
                        "Error occurred when enrolling user in one or more courses for order %s. "
                        "See other errors above for more info.", order)
                    try:
                        MailgunClient().send_individual_email(
                            "Error occurred when enrolling user during $0 checkout",
                            "Error occurred when enrolling user during $0 checkout for {order}. "
                            "Exception: {exception}".format(
                                order=order, exception=traceback.format_exc()),
                            settings.ECOMMERCE_EMAIL,
                        )
                    except:  # pylint: disable=bare-except
                        log.exception(
                            "Error occurred when sending the email to notify support "
                            "of user enrollment error during order %s $0 checkout",
                            order,
                        )

                # This redirects the user to our order success page
                payload = {}
                url = make_dashboard_receipt_url(dashboard_url, course_id,
                                                 'receipt')
                method = 'GET'
            else:
                # This generates a signed payload which is submitted as an HTML form to CyberSource
                payload = generate_cybersource_sa_payload(
                    order, dashboard_url, user_ip)
                url = settings.CYBERSOURCE_SECURE_ACCEPTANCE_URL
                method = 'POST'
        else:
            # This redirects the user to edX to purchase the course there
            payload = {}
            url = urljoin(settings.EDXORG_BASE_URL,
                          '/course_modes/choose/{}/'.format(course_id))
            method = 'GET'

        return Response({
            'payload': payload,
            'url': url,
            'method': method,
        })
Esempio n. 7
0
    def post(self, request, *args, **kwargs):
        """
        If the course run is part of a financial aid program, create a new unfulfilled Order
        and return information used to submit to CyberSource.

        If the program does not have financial aid, this will return a URL to let the user
        pay for the course on edX.
        """
        try:
            course_id = request.data['course_id']
        except KeyError:
            raise ValidationError("Missing course_id")

        course_run = get_object_or_404(
            CourseRun,
            course__program__live=True,
            edx_course_key=course_id,
        )
        if course_run.course.program.financial_aid_availability:
            order = create_unfulfilled_order(course_id, request.user)
            dashboard_url = request.build_absolute_uri('/dashboard/')
            if order.total_price_paid == 0:
                # If price is $0, don't bother going to CyberSource, just mark as fulfilled
                order.status = Order.FULFILLED
                order.save_and_log(request.user)
                try:
                    enroll_user_on_success(order)
                except:  # pylint: disable=bare-except
                    log.exception(
                        "Error occurred when enrolling user in one or more courses for order %s. "
                        "See other errors above for more info.",
                        order
                    )
                    try:
                        MailgunClient().send_individual_email(
                            "Error occurred when enrolling user during $0 checkout",
                            "Error occurred when enrolling user during $0 checkout for {order}. "
                            "Exception: {exception}".format(
                                order=order,
                                exception=traceback.format_exc()
                            ),
                            settings.ECOMMERCE_EMAIL,
                        )
                    except:  # pylint: disable=bare-except
                        log.exception(
                            "Error occurred when sending the email to notify support "
                            "of user enrollment error during order %s $0 checkout",
                            order,
                        )

                # This redirects the user to our order success page
                payload = {}
                url = make_dashboard_receipt_url(dashboard_url, course_id, 'receipt')
                method = 'GET'
            else:
                # This generates a signed payload which is submitted as an HTML form to CyberSource
                payload = generate_cybersource_sa_payload(order, dashboard_url)
                url = settings.CYBERSOURCE_SECURE_ACCEPTANCE_URL
                method = 'POST'
        else:
            # This redirects the user to edX to purchase the course there
            payload = {}
            url = urljoin(settings.EDXORG_BASE_URL, '/course_modes/choose/{}/'.format(course_id))
            method = 'GET'

        return Response({
            'payload': payload,
            'url': url,
            'method': method,
        })