예제 #1
0
    def test_start_purchase(self):
        # Start the purchase, which will mark the cart as "paying"
        cart = Order.get_cart_for_user(user=self.user)
        CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor', currency='usd')
        cart.start_purchase()
        self.assertEqual(cart.status, 'paying')
        for item in cart.orderitem_set.all():
            self.assertEqual(item.status, 'paying')

        # Starting the purchase should be idempotent
        cart.start_purchase()
        self.assertEqual(cart.status, 'paying')
        for item in cart.orderitem_set.all():
            self.assertEqual(item.status, 'paying')

        # If we retrieve the cart for the user, we should get a different order
        next_cart = Order.get_cart_for_user(user=self.user)
        self.assertNotEqual(cart, next_cart)
        self.assertEqual(next_cart.status, 'cart')

        # Complete the first purchase
        cart.purchase()
        self.assertEqual(cart.status, 'purchased')
        for item in cart.orderitem_set.all():
            self.assertEqual(item.status, 'purchased')

        # Starting the purchase again should be a no-op
        cart.start_purchase()
        self.assertEqual(cart.status, 'purchased')
        for item in cart.orderitem_set.all():
            self.assertEqual(item.status, 'purchased')
예제 #2
0
 def test_purchase_item_email_boto_failure(self, error_logger):
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor')
     with patch.object(EmailMessage, 'send') as mock_send:
         mock_send.side_effect = BotoServerError("status", "reason")
         cart.purchase()
         self.assertTrue(error_logger.called)
예제 #3
0
    def test_refund_cert_callback_before_expiration(self):
        # If the expiration date has not yet passed on a verified mode, the user can be refunded
        many_days = datetime.timedelta(days=60)

        course = CourseFactory.create(org="refund_before_expiration", number="test", display_name="one")
        course_key = course.id
        course_mode = CourseMode(
            course_id=course_key,
            mode_slug="verified",
            mode_display_name="verified cert",
            min_price=self.cost,
            expiration_datetime=(datetime.datetime.now(pytz.utc) + many_days),
        )
        course_mode.save()

        CourseEnrollment.enroll(self.user, course_key, "verified")
        cart = Order.get_cart_for_user(user=self.user)
        CertificateItem.add_to_order(cart, course_key, self.cost, "verified")
        cart.purchase()

        CourseEnrollment.unenroll(self.user, course_key)
        target_certs = CertificateItem.objects.filter(
            course_id=course_key, user_id=self.user, status="refunded", mode="verified"
        )
        self.assertTrue(target_certs[0])
        self.assertTrue(target_certs[0].refund_requested_time)
        self.assertEquals(target_certs[0].order.status, "refunded")
예제 #4
0
    def test_confirmation_email_error(self):
        CourseMode.objects.create(
            course_id=self.course_key,
            mode_slug="verified",
            mode_display_name="Verified",
            min_price=self.cost
        )

        cart = Order.get_cart_for_user(self.user)
        CertificateItem.add_to_order(cart, self.course_key, self.cost, 'verified')

        # Simulate an error when sending the confirmation
        # email.  This should NOT raise an exception.
        # If it does, then the implicit view-level
        # transaction could cause a roll-back, effectively
        # reversing order fulfillment.
        with patch.object(mail.message.EmailMessage, 'send') as mock_send:
            mock_send.side_effect = Exception("Kaboom!")
            cart.purchase()

        # Verify that the purchase completed successfully
        self.assertEqual(cart.status, 'purchased')

        # Verify that the user is enrolled as "verified"
        mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course_key)
        self.assertTrue(is_active)
        self.assertEqual(mode, 'verified')
예제 #5
0
def checkout_with_shoppingcart(request, user, course_key, course_mode, amount):
    """ Create an order and trigger checkout using shoppingcart."""
    cart = Order.get_cart_for_user(user)
    cart.clear()
    enrollment_mode = course_mode.slug
    CertificateItem.add_to_order(cart, course_key, amount, enrollment_mode)

    # Change the order's status so that we don't accidentally modify it later.
    # We need to do this to ensure that the parameters we send to the payment system
    # match what we store in the database.
    # (Ordinarily we would do this client-side when the user submits the form, but since
    # the JavaScript on this page does that immediately, we make the change here instead.
    # This avoids a second AJAX call and some additional complication of the JavaScript.)
    # If a user later re-enters the verification / payment flow, she will create a new order.
    cart.start_purchase()

    callback_url = request.build_absolute_uri(
        reverse("shoppingcart.views.postpay_callback")
    )

    payment_data = {
        'payment_processor_name': settings.CC_PROCESSOR_NAME,
        'payment_page_url': get_purchase_endpoint(),
        'payment_form_data': get_signed_purchase_params(
            cart,
            callback_url=callback_url,
            extra_data=[unicode(course_key), course_mode.slug]
        ),
    }
    return payment_data
예제 #6
0
    def test_refund_cert_callback_before_expiration(self):
        # If the expiration date has not yet passed on a verified mode, the user can be refunded
        many_days = datetime.timedelta(days=60)

        course = CourseFactory.create()
        self.course_key = course.id
        course_mode = CourseMode(course_id=self.course_key,
                                 mode_slug="verified",
                                 mode_display_name="verified cert",
                                 min_price=self.cost,
                                 expiration_datetime=(datetime.datetime.now(pytz.utc) + many_days))
        course_mode.save()

        # need to prevent analytics errors from appearing in stderr
        with patch('sys.stderr', sys.stdout.write):
            CourseEnrollment.enroll(self.user, self.course_key, 'verified')
            cart = Order.get_cart_for_user(user=self.user)
            CertificateItem.add_to_order(cart, self.course_key, self.cost, 'verified')
            cart.purchase()
            CourseEnrollment.unenroll(self.user, self.course_key)

        target_certs = CertificateItem.objects.filter(course_id=self.course_key, user_id=self.user, status='refunded', mode='verified')
        self.assertTrue(target_certs[0])
        self.assertTrue(target_certs[0].refund_requested_time)
        self.assertEquals(target_certs[0].order.status, 'refunded')
        self._assert_refund_tracked()
예제 #7
0
    def test_single_item_template(self):
        cart = Order.get_cart_for_user(user=self.user)
        cert_item = CertificateItem.add_to_order(cart, self.course_key, self.cost, 'verified')
        self.assertEquals(cert_item.single_item_receipt_template, 'shoppingcart/receipt.html')

        cert_item = CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor')
        self.assertEquals(cert_item.single_item_receipt_template, 'shoppingcart/receipt.html')
예제 #8
0
 def test_cart_clear(self):
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified')
     CertificateItem.add_to_order(cart, 'test/course1', self.cost, 'verified')
     self.assertEquals(cart.orderitem_set.count(), 2)
     cart.clear()
     self.assertEquals(cart.orderitem_set.count(), 0)
예제 #9
0
 def test_purchase_twice(self):
     cart = Order.get_cart_for_user(self.user)
     CertificateItem.add_to_order(cart, self.course_id, self.cost, 'honor')
     # purchase the cart more than once
     cart.purchase()
     cart.purchase()
     self.assertEquals(len(mail.outbox), 1)
예제 #10
0
    def test_refund_cert_callback_before_expiration_email(self):
        """ Test that refund emails are being sent correctly. """
        course = CourseFactory.create()
        course_key = course.id
        many_days = datetime.timedelta(days=60)

        course_mode = CourseMode(course_id=course_key,
                                 mode_slug="verified",
                                 mode_display_name="verified cert",
                                 min_price=self.cost,
                                 expiration_datetime=datetime.datetime.now(pytz.utc) + many_days)
        course_mode.save()

        CourseEnrollment.enroll(self.user, course_key, 'verified')
        cart = Order.get_cart_for_user(user=self.user)
        CertificateItem.add_to_order(cart, course_key, self.cost, 'verified')
        cart.purchase()

        mail.outbox = []
        with patch('shoppingcart.models.log.error') as mock_error_logger:
            CourseEnrollment.unenroll(self.user, course_key)
            self.assertFalse(mock_error_logger.called)
            self.assertEquals(len(mail.outbox), 1)
            self.assertEquals('[Refund] User-Requested Refund', mail.outbox[0].subject)
            self.assertEquals(settings.PAYMENT_SUPPORT_EMAIL, mail.outbox[0].from_email)
            self.assertIn('has requested a refund on Order', mail.outbox[0].body)
예제 #11
0
 def test_get_cart_for_user(self):
     # create a cart
     cart = Order.get_cart_for_user(user=self.user)
     # add something to it
     CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified')
     # should return the same cart
     cart2 = Order.get_cart_for_user(user=self.user)
     self.assertEquals(cart2.orderitem_set.count(), 1)
예제 #12
0
    def test_retire_order_cart(self):
        """Test that an order in cart can successfully be retired"""
        cart = Order.get_cart_for_user(user=self.user)
        CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor', currency='usd')

        cart.retire()
        self.assertEqual(cart.status, 'defunct-cart')
        self.assertEqual(cart.orderitem_set.get().status, 'defunct-cart')
예제 #13
0
    def test_retire_order_cart(self):
        """Test that an order in cart can successfully be retired"""
        cart = Order.get_cart_for_user(user=self.user)
        CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor', currency='usd')

        cart.retire()
        self.assertEqual(cart.status, 'defunct-cart')
        self.assertEqual(cart.orderitem_set.get().status, 'defunct-cart')
예제 #14
0
 def test_get_cart_for_user(self):
     # create a cart
     cart = Order.get_cart_for_user(user=self.user)
     # add something to it
     CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor')
     # should return the same cart
     cart2 = Order.get_cart_for_user(user=self.user)
     self.assertEquals(cart2.orderitem_set.count(), 1)
예제 #15
0
 def test_clear_cart(self):
     self.login_user()
     PaidCourseRegistration.add_to_order(self.cart, self.course_key)
     CertificateItem.add_to_order(self.cart, self.verified_course_key, self.cost, 'honor')
     self.assertEquals(self.cart.orderitem_set.count(), 2)
     resp = self.client.post(reverse('shoppingcart.views.clear_cart', args=[]))
     self.assertEqual(resp.status_code, 200)
     self.assertEquals(self.cart.orderitem_set.count(), 0)
예제 #16
0
 def test_clear_cart(self):
     self.login_user()
     PaidCourseRegistration.add_to_order(self.cart, self.course_key)
     CertificateItem.add_to_order(self.cart, self.verified_course_key, self.cost, 'honor')
     self.assertEquals(self.cart.orderitem_set.count(), 2)
     resp = self.client.post(reverse('shoppingcart.views.clear_cart', args=[]))
     self.assertEqual(resp.status_code, 200)
     self.assertEquals(self.cart.orderitem_set.count(), 0)
예제 #17
0
 def test_cart_clear(self):
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_id, self.cost, 'honor')
     CertificateItem.add_to_order(cart, 'org/test/Test_Course_1', self.cost,
                                  'honor')
     self.assertEquals(cart.orderitem_set.count(), 2)
     cart.clear()
     self.assertEquals(cart.orderitem_set.count(), 0)
예제 #18
0
 def test_existing_enrollment(self):
     CourseEnrollment.enroll(self.user, self.course_id)
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified')
     # verify that we are still enrolled
     self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course_id))
     cart.purchase()
     enrollment = CourseEnrollment.objects.get(user=self.user, course_id=self.course_id)
     self.assertEquals(enrollment.mode, u'verified')
예제 #19
0
 def test_existing_enrollment(self):
     CourseEnrollment.enroll(self.user, self.course_id)
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified')
     # verify that we are still enrolled
     self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course_id))
     cart.purchase()
     enrollment = CourseEnrollment.objects.get(user=self.user, course_id=self.course_id)
     self.assertEquals(enrollment.mode, u'verified')
예제 #20
0
 def test_cart_clear(self):
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_id, self.cost, 'honor')
     CertificateItem.add_to_order(cart, 'org/test/Test_Course_1', self.cost, 'honor')
     self.assertEquals(cart.orderitem_set.count(), 2)
     self.assertTrue(cart.has_items())
     cart.clear()
     self.assertEquals(cart.orderitem_set.count(), 0)
     self.assertFalse(cart.has_items())
예제 #21
0
 def test_cart_clear(self):
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor')
     CertificateItem.add_to_order(cart, SlashSeparatedCourseKey('org', 'test', 'Test_Course_1'), self.cost, 'honor')
     self.assertEquals(cart.orderitem_set.count(), 2)
     self.assertTrue(cart.has_items())
     cart.clear()
     self.assertEquals(cart.orderitem_set.count(), 0)
     self.assertFalse(cart.has_items())
예제 #22
0
 def test_cart_clear(self):
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor')
     CertificateItem.add_to_order(cart, SlashSeparatedCourseKey('org', 'test', 'Test_Course_1'), self.cost, 'honor')
     self.assertEquals(cart.orderitem_set.count(), 2)
     self.assertTrue(cart.has_items())
     cart.clear()
     self.assertEquals(cart.orderitem_set.count(), 0)
     self.assertFalse(cart.has_items())
예제 #23
0
 def test_cart_clear(self):
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor')
     CertificateItem.add_to_order(cart, self.other_course_keys[0], self.cost, 'honor')
     self.assertEquals(cart.orderitem_set.count(), 2)
     self.assertTrue(cart.has_items())
     cart.clear()
     self.assertEquals(cart.orderitem_set.count(), 0)
     self.assertFalse(cart.has_items())
예제 #24
0
 def test_user_cart_has_both_items(self):
     """
     This test exists b/c having both CertificateItem and PaidCourseRegistration in an order used to break
     PaidCourseRegistration.contained_in_order
     """
     cart = Order.get_cart_for_user(self.user)
     CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor')
     PaidCourseRegistration.add_to_order(self.cart, self.course_key)
     self.assertTrue(PaidCourseRegistration.contained_in_order(cart, self.course_key))
예제 #25
0
 def test_user_cart_has_both_items(self):
     """
     This test exists b/c having both CertificateItem and PaidCourseRegistration in an order used to break
     PaidCourseRegistration.contained_in_order
     """
     cart = Order.get_cart_for_user(self.user)
     CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor')
     PaidCourseRegistration.add_to_order(self.cart, self.course_key)
     self.assertTrue(PaidCourseRegistration.contained_in_order(cart, self.course_key))
예제 #26
0
 def _enroll(self, purchase=True):
     # pylint: disable=missing-docstring
     CourseEnrollment.enroll(self.student, self.course_id, self.course_mode.mode_slug)
     if purchase:
         self.order = Order.get_cart_for_user(self.student)
         CertificateItem.add_to_order(self.order, self.course_id, 1, self.course_mode.mode_slug)
         self.order.purchase()
     self.course_mode.expiration_datetime = datetime.datetime(1983, 4, 6, tzinfo=pytz.UTC)
     self.course_mode.save()
예제 #27
0
def create_order(request):
    """
    Submit PhotoVerification and create a new Order for this verified cert
    """
    if not SoftwareSecurePhotoVerification.user_has_valid_or_pending(request.user):
        attempt = SoftwareSecurePhotoVerification(user=request.user)
        b64_face_image = request.POST['face_image'].split(",")[1]
        b64_photo_id_image = request.POST['photo_id_image'].split(",")[1]

        attempt.upload_face_image(b64_face_image.decode('base64'))
        attempt.upload_photo_id_image(b64_photo_id_image.decode('base64'))
        attempt.mark_ready()

        attempt.save()

    course_id = request.POST['course_id']
    course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    donation_for_course = request.session.get('donation_for_course', {})
    current_donation = donation_for_course.get(course_id, decimal.Decimal(0))
    contribution = request.POST.get("contribution", donation_for_course.get(course_id, 0))
    try:
        amount = decimal.Decimal(contribution).quantize(decimal.Decimal('.01'), rounding=decimal.ROUND_DOWN)
    except decimal.InvalidOperation:
        return HttpResponseBadRequest(_("Selected price is not valid number."))

    if amount != current_donation:
        donation_for_course[course_id] = amount
        request.session['donation_for_course'] = donation_for_course

    # prefer professional mode over verified_mode
    current_mode = CourseMode.verified_mode_for_course(course_id)

    if current_mode.slug == 'professional':
        amount = current_mode.min_price

    # make sure this course has a verified mode
    if not current_mode:
        return HttpResponseBadRequest(_("This course doesn't support verified certificates"))

    if amount < current_mode.min_price:
        return HttpResponseBadRequest(_("No selected price or selected price is below minimum."))

    # I know, we should check this is valid. All kinds of stuff missing here
    cart = Order.get_cart_for_user(request.user)
    cart.clear()
    enrollment_mode = current_mode.slug
    CertificateItem.add_to_order(cart, course_id, amount, enrollment_mode)

    callback_url = request.build_absolute_uri(
        reverse("shoppingcart.views.postpay_callback")
    )
    params = get_signed_purchase_params(
        cart, callback_url=callback_url
    )

    return HttpResponse(json.dumps(params), content_type="text/json")
예제 #28
0
 def _create_and_purchase_verified(self, student, course_id):
     """ Creates a verified mode for the course and purchases it for the student. """
     course_mode = CourseMode(
         course_id=course_id, mode_slug="verified", mode_display_name="verified cert", min_price=50
     )
     course_mode.save()
     # When there is no expiration date on a verified mode, the user can always get a refund
     cart = Order.get_cart_for_user(user=student)
     CertificateItem.add_to_order(cart, course_id, 50, "verified")
     cart.purchase()
예제 #29
0
    def test_single_item_template(self):
        cart = Order.get_cart_for_user(user=self.user)
        cert_item = CertificateItem.add_to_order(cart, self.course_key, self.cost, 'verified')

        self.assertEquals(cert_item.single_item_receipt_template,
                          'shoppingcart/verified_cert_receipt.html')

        cert_item = CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor')
        self.assertEquals(cert_item.single_item_receipt_template,
                          'shoppingcart/receipt.html')
예제 #30
0
 def test_total_cost(self):
     cart = Order.get_cart_for_user(user=self.user)
     # add items to the order
     course_costs = [('test/course1', 30), ('test/course2', 40),
                     ('test/course3', 10), ('test/course4', 20)]
     for course, cost in course_costs:
         CertificateItem.add_to_order(cart, course, cost, 'verified')
     self.assertEquals(cart.orderitem_set.count(), len(course_costs))
     self.assertEquals(cart.total_cost,
                       sum(cost for _course, cost in course_costs))
예제 #31
0
    def test_refund_cert_callback_no_expiration(self):
        # When there is no expiration date on a verified mode, the user can always get a refund
        CourseEnrollment.enroll(self.user, self.course_id, 'verified')
        cart = Order.get_cart_for_user(user=self.user)
        CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified')
        cart.purchase()

        CourseEnrollment.unenroll(self.user, self.course_id)
        target_certs = CertificateItem.objects.filter(course_id=self.course_id, user_id=self.user, status='refunded', mode='verified')
        self.assertTrue(target_certs[0])
예제 #32
0
 def test_purchase_item_failure(self):
     # once again, we're testing against the specific implementation of
     # CertificateItem
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified')
     with patch('shoppingcart.models.CertificateItem.save', side_effect=DatabaseError):
         with self.assertRaises(DatabaseError):
             cart.purchase()
             # verify that we rolled back the entire transaction
             self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course_id))
 def _create_and_purchase_verified(self, student, course_id):
     """ Creates a verified mode for the course and purchases it for the student. """
     course_mode = CourseMode(course_id=course_id,
                              mode_slug="verified",
                              mode_display_name="verified cert",
                              min_price=50)
     course_mode.save()
     # When there is no expiration date on a verified mode, the user can always get a refund
     cart = Order.get_cart_for_user(user=student)
     CertificateItem.add_to_order(cart, course_id, 50, 'verified')
     cart.purchase()
예제 #34
0
 def test_total_cost(self):
     cart = Order.get_cart_for_user(user=self.user)
     # add items to the order
     course_costs = [('test/course1', 30),
                     ('test/course2', 40),
                     ('test/course3', 10),
                     ('test/course4', 20)]
     for course, cost in course_costs:
         CertificateItem.add_to_order(cart, course, cost, 'verified')
     self.assertEquals(cart.orderitem_set.count(), len(course_costs))
     self.assertEquals(cart.total_cost, sum(cost for _course, cost in course_costs))
예제 #35
0
 def test_total_cost(self):
     cart = Order.get_cart_for_user(user=self.user)
     # add items to the order
     course_costs = [('org/test/Test_Course_1', 30),
                     ('org/test/Test_Course_2', 40),
                     ('org/test/Test_Course_3', 10),
                     ('org/test/Test_Course_4', 20)]
     for course, cost in course_costs:
         CertificateItem.add_to_order(cart, SlashSeparatedCourseKey.from_deprecated_string(course), cost, 'honor')
     self.assertEquals(cart.orderitem_set.count(), len(course_costs))
     self.assertEquals(cart.total_cost, sum(cost for _course, cost in course_costs))
예제 #36
0
 def test_purchase(self):
     # This test is for testing the subclassing functionality of OrderItem, but in
     # order to do this, we end up testing the specific functionality of
     # CertificateItem, which is not quite good unit test form. Sorry.
     cart = Order.get_cart_for_user(user=self.user)
     self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course_id))
     CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified')
     # course enrollment object should be created but still inactive
     self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course_id))
     cart.purchase()
     self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course_id))
예제 #37
0
 def test_add_item_to_cart_currency_match(self):
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor', currency='eur')
     # verify that a new item has been added
     self.assertEquals(cart.orderitem_set.count(), 1)
     # verify that the cart's currency was updated
     self.assertEquals(cart.currency, 'eur')
     with self.assertRaises(InvalidCartItem):
         CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor', currency='usd')
     # assert that this item did not get added to the cart
     self.assertEquals(cart.orderitem_set.count(), 1)
예제 #38
0
 def test_total_cost(self):
     cart = Order.get_cart_for_user(user=self.user)
     # add items to the order
     course_costs = [('org/test/Test_Course_1', 30),
                     ('org/test/Test_Course_2', 40),
                     ('org/test/Test_Course_3', 10),
                     ('org/test/Test_Course_4', 20)]
     for course, cost in course_costs:
         CertificateItem.add_to_order(cart, SlashSeparatedCourseKey.from_deprecated_string(course), cost, 'honor')
     self.assertEquals(cart.orderitem_set.count(), len(course_costs))
     self.assertEquals(cart.total_cost, sum(cost for _course, cost in course_costs))
예제 #39
0
 def test_total_cost(self):
     cart = Order.get_cart_for_user(user=self.user)
     # add items to the order
     course_costs = [(self.other_course_keys[0], 30),
                     (self.other_course_keys[1], 40),
                     (self.other_course_keys[2], 10),
                     (self.other_course_keys[3], 20)]
     for course, cost in course_costs:
         CertificateItem.add_to_order(cart, course, cost, 'honor')
     self.assertEquals(cart.orderitem_set.count(), len(course_costs))
     self.assertEquals(cart.total_cost, sum(cost for _course, cost in course_costs))
예제 #40
0
 def test_total_cost(self):
     cart = Order.get_cart_for_user(user=self.user)
     # add items to the order
     course_costs = [(self.other_course_keys[0], 30),
                     (self.other_course_keys[1], 40),
                     (self.other_course_keys[2], 10),
                     (self.other_course_keys[3], 20)]
     for course, cost in course_costs:
         CertificateItem.add_to_order(cart, course, cost, 'honor')
     self.assertEquals(cart.orderitem_set.count(), len(course_costs))
     self.assertEquals(cart.total_cost, sum(cost for _course, cost in course_costs))
예제 #41
0
 def test_add_item_to_cart_currency_match(self):
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified', currency='eur')
     # verify that a new item has been added
     self.assertEquals(cart.orderitem_set.count(), 1)
     # verify that the cart's currency was updated
     self.assertEquals(cart.currency, 'eur')
     with self.assertRaises(InvalidCartItem):
         CertificateItem.add_to_order(cart, self.course_id, self.cost, 'verified', currency='usd')
     # assert that this item did not get added to the cart
     self.assertEquals(cart.orderitem_set.count(), 1)
예제 #42
0
 def test_purchase_item_failure(self):
     # once again, we're testing against the specific implementation of
     # CertificateItem
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_key, self.cost, "honor")
     with patch("shoppingcart.models.CertificateItem.save", side_effect=DatabaseError):
         with self.assertRaises(DatabaseError):
             cart.purchase()
             # verify that we rolled back the entire transaction
             self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course_key))
             # verify that e-mail wasn't sent
             self.assertEquals(len(mail.outbox), 0)
예제 #43
0
    def test_refund_cert_callback_no_expiration(self):
        # When there is no expiration date on a verified mode, the user can always get a refund
        CourseEnrollment.enroll(self.user, self.course_key, 'verified')
        cart = Order.get_cart_for_user(user=self.user)
        CertificateItem.add_to_order(cart, self.course_key, self.cost, 'verified')
        cart.purchase()

        CourseEnrollment.unenroll(self.user, self.course_key)
        target_certs = CertificateItem.objects.filter(course_id=self.course_key, user_id=self.user, status='refunded', mode='verified')
        self.assertTrue(target_certs[0])
        self.assertTrue(target_certs[0].refund_requested_time)
        self.assertEquals(target_certs[0].order.status, 'refunded')
예제 #44
0
 def test_purchase_item_failure(self):
     # once again, we're testing against the specific implementation of
     # CertificateItem
     cart = Order.get_cart_for_user(user=self.user)
     CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor')
     with patch('shoppingcart.models.CertificateItem.save', side_effect=DatabaseError):
         with self.assertRaises(DatabaseError):
             cart.purchase()
             # verify that we rolled back the entire transaction
             self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course_key))
             # verify that e-mail wasn't sent
             self.assertEquals(len(mail.outbox), 0)
예제 #45
0
def create_order(request):
    """
    Submit PhotoVerification and create a new Order for this verified cert
    """
    if not SoftwareSecurePhotoVerification.user_has_valid_or_pending(
            request.user):
        attempt = SoftwareSecurePhotoVerification(user=request.user)
        b64_face_image = request.POST['face_image'].split(",")[1]
        b64_photo_id_image = request.POST['photo_id_image'].split(",")[1]

        attempt.upload_face_image(b64_face_image.decode('base64'))
        attempt.upload_photo_id_image(b64_photo_id_image.decode('base64'))
        attempt.mark_ready()

        attempt.save()

    course_id = request.POST['course_id']
    course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
    donation_for_course = request.session.get('donation_for_course', {})
    current_donation = donation_for_course.get(course_id, decimal.Decimal(0))
    contribution = request.POST.get("contribution",
                                    donation_for_course.get(course_id, 0))
    try:
        amount = decimal.Decimal(contribution).quantize(
            decimal.Decimal('.01'), rounding=decimal.ROUND_DOWN)
    except decimal.InvalidOperation:
        return HttpResponseBadRequest(_("Selected price is not valid number."))

    if amount != current_donation:
        donation_for_course[course_id] = amount
        request.session['donation_for_course'] = donation_for_course

    verified_mode = CourseMode.modes_for_course_dict(course_id).get(
        'verified', None)

    # make sure this course has a verified mode
    if not verified_mode:
        return HttpResponseBadRequest(
            _("This course doesn't support verified certificates"))

    if amount < verified_mode.min_price:
        return HttpResponseBadRequest(
            _("No selected price or selected price is below minimum."))

    # I know, we should check this is valid. All kinds of stuff missing here
    cart = Order.get_cart_for_user(request.user)
    cart.clear()
    CertificateItem.add_to_order(cart, course_id, amount, 'verified')

    params = get_signed_purchase_params(cart)

    return HttpResponse(json.dumps(params), content_type="text/json")
예제 #46
0
    def test_refund_cert_callback_no_expiration(self):
        # When there is no expiration date on a verified mode, the user can always get a refund
        CourseEnrollment.enroll(self.user, self.course_key, "verified")
        cart = Order.get_cart_for_user(user=self.user)
        CertificateItem.add_to_order(cart, self.course_key, self.cost, "verified")
        cart.purchase()

        CourseEnrollment.unenroll(self.user, self.course_key)
        target_certs = CertificateItem.objects.filter(
            course_id=self.course_key, user_id=self.user, status="refunded", mode="verified"
        )
        self.assertTrue(target_certs[0])
        self.assertTrue(target_certs[0].refund_requested_time)
        self.assertEquals(target_certs[0].order.status, "refunded")
예제 #47
0
def create_order(request):
    course_id = request.POST['course_id']
    course_id = CourseKey.from_string(course_id)
    contribution = request.POST.get("contribution")
    try:
        amount = decimal.Decimal(contribution).quantize(decimal.Decimal('.01'), rounding=decimal.ROUND_DOWN)
    except decimal.InvalidOperation:
        return HttpResponseBadRequest(_("Selected price is not valid number."))

    request.session['donation_for_course'] = amount

    # prefer professional mode over verified_mode
    current_mode = CourseMode.verified_mode_for_course(course_id)

    # make sure this course has a verified mode
    if not current_mode:
        log.warn(u"Verification requested for course {course_id} without a verified mode.".format(course_id=course_id))
        return HttpResponseBadRequest(_("This course doesn't support verified certificates"))

    if amount < current_mode.min_price:
        return HttpResponseBadRequest(_("No selected price or selected price is below minimum."))

    # I know, we should check this is valid. All kinds of stuff missing here
    cart = Order.get_cart_for_user(request.user)
    cart.clear()
    enrollment_mode = current_mode.slug
    CertificateItem.add_to_order(cart, course_id, amount, enrollment_mode)

    # Change the order's status so that we don't accidentally modify it later.
    # We need to do this to ensure that the parameters we send to the payment system
    # match what we store in the database.
    # (Ordinarily we would do this client-side when the user submits the form, but since
    # the JavaScript on this page does that immediately, we make the change here instead.
    # This avoids a second AJAX call and some additional complication of the JavaScript.)
    # If a user later re-enters the verification / payment flow, she will create a new order.
    cart.start_purchase()

    callback_url = request.build_absolute_uri(
        reverse("shoppingcart.views.postpay_callback")
    )

    params = get_signed_purchase_params(
        cart,
        callback_url=callback_url,
        extra_data=[unicode(course_id), current_mode.slug]
    )

    params['success'] = True
    return HttpResponse(json.dumps(params), content_type="text/json")
예제 #48
0
 def purchase_with_data(self, cart):
     """ purchase a cart with billing information """
     CertificateItem.add_to_order(cart, self.course_key, self.cost, 'honor')
     cart.purchase(
         first='John',
         last='Smith',
         street1='11 Cambridge Center',
         street2='Suite 101',
         city='Cambridge',
         state='MA',
         postalcode='02412',
         country='US',
         ccnum='1111',
         cardtype='001',
     )
예제 #49
0
 def test_purchase(self):
     # This test is for testing the subclassing functionality of OrderItem, but in
     # order to do this, we end up testing the specific functionality of
     # CertificateItem, which is not quite good unit test form. Sorry.
     cart = Order.get_cart_for_user(user=self.user)
     self.assertFalse(
         CourseEnrollment.is_enrolled(self.user, self.course_id))
     CertificateItem.add_to_order(cart, self.course_id, self.cost,
                                  'verified')
     # course enrollment object should be created but still inactive
     self.assertFalse(
         CourseEnrollment.is_enrolled(self.user, self.course_id))
     cart.purchase()
     self.assertTrue(CourseEnrollment.is_enrolled(self.user,
                                                  self.course_id))
예제 #50
0
    def test_refund_cert_callback_no_expiration(self):
        # When there is no expiration date on a verified mode, the user can always get a refund

        # need to prevent analytics errors from appearing in stderr
        with patch('sys.stderr', sys.stdout.write):
            CourseEnrollment.enroll(self.user, self.course_key, 'verified')
            cart = Order.get_cart_for_user(user=self.user)
            CertificateItem.add_to_order(cart, self.course_key, self.cost, 'verified')
            cart.purchase()
            CourseEnrollment.unenroll(self.user, self.course_key)

        target_certs = CertificateItem.objects.filter(course_id=self.course_key, user_id=self.user, status='refunded', mode='verified')
        self.assertTrue(target_certs[0])
        self.assertTrue(target_certs[0].refund_requested_time)
        self.assertEquals(target_certs[0].order.status, 'refunded')
        self._assert_refund_tracked()
예제 #51
0
 def _create_cart(self):
     """Creates a cart and adds a CertificateItem to it"""
     cart = Order.get_cart_for_user(UserFactory.create())
     item = CertificateItem.add_to_order(
         cart, self.course_key, 10, 'honor', currency='usd'
     )
     return cart, item
예제 #52
0
 def setUp(self):
     self.user = UserFactory.create()
     self.course_id = "MITx/999/Robot_Super_Course"
     self.cost = 40
     self.course = CourseFactory.create(org='MITx',
                                        number='999',
                                        display_name=u'Robot Super Course')
     course_mode = CourseMode(course_id=self.course_id,
                              mode_slug="honor",
                              mode_display_name="honor cert",
                              min_price=self.cost)
     course_mode.save()
     course_mode2 = CourseMode(course_id=self.course_id,
                               mode_slug="verified",
                               mode_display_name="verified cert",
                               min_price=self.cost)
     course_mode2.save()
     self.annotation = PaidCourseRegistrationAnnotation(
         course_id=self.course_id, annotation=self.TEST_ANNOTATION)
     self.annotation.save()
     self.cart = Order.get_cart_for_user(self.user)
     self.reg = PaidCourseRegistration.add_to_order(self.cart,
                                                    self.course_id)
     self.cert_item = CertificateItem.add_to_order(self.cart,
                                                   self.course_id,
                                                   self.cost, 'verified')
     self.cart.purchase()
     self.now = datetime.datetime.now(pytz.UTC)
예제 #53
0
    def test_show_receipt_404s(self):
        PaidCourseRegistration.add_to_order(self.cart, self.course_key)
        CertificateItem.add_to_order(self.cart, self.verified_course_key, self.cost, 'honor')
        self.cart.purchase()

        user2 = UserFactory.create()
        cart2 = Order.get_cart_for_user(user2)
        PaidCourseRegistration.add_to_order(cart2, self.course_key)
        cart2.purchase()

        self.login_user()
        resp = self.client.get(reverse('shoppingcart.views.show_receipt', args=[cart2.id]))
        self.assertEqual(resp.status_code, 404)

        resp2 = self.client.get(reverse('shoppingcart.views.show_receipt', args=[1000]))
        self.assertEqual(resp2.status_code, 404)
예제 #54
0
    def test_remove_registration_code_redemption_on_clear_cart(self, info_log):

        reg_item = self.add_course_to_user_cart(self.course_key)
        CertificateItem.add_to_order(self.cart, self.verified_course_key, self.cost, 'honor')
        self.assertEquals(self.cart.orderitem_set.count(), 2)

        self.add_reg_code(self.course_key)
        resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.reg_code})
        self.assertEqual(resp.status_code, 200)

        resp = self.client.post(reverse('shoppingcart.views.clear_cart', args=[]))
        self.assertEqual(resp.status_code, 200)
        self.assertEquals(self.cart.orderitem_set.count(), 0)

        info_log.assert_called_with(
            'Registration code redemption entry removed for user {0} for order {1}'.format(self.user, reg_item.id))
예제 #55
0
    def test_reg_code_free_discount_with_multiple_courses_in_cart(self, info_log):

        reg_item = self.add_course_to_user_cart(self.course_key)
        self.add_reg_code(self.course_key)
        cert_item = CertificateItem.add_to_order(self.cart, self.verified_course_key, self.cost, 'honor')
        self.assertEquals(self.cart.orderitem_set.count(), 2)

        resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.reg_code})
        self.assertEqual(resp.status_code, 200)

        # unit_cost should be 0 for that particular course for which registration code is registered
        items = self.cart.orderitem_set.all().select_subclasses()
        for item in items:
            if item.id == reg_item.id:
                self.assertEquals(item.unit_cost, 0)
            elif item.id == cert_item.id:
                self.assertEquals(item.list_price, None)

        # Delete the discounted item, corresponding reg code redemption should be removed for that particular item
        resp = self.client.post(reverse('shoppingcart.views.remove_item', args=[]),
                                {'id': reg_item.id})

        self.assertEqual(resp.status_code, 200)
        self.assertEquals(self.cart.orderitem_set.count(), 1)
        info_log.assert_called_with(
            'Registration code "{0}" redemption entry removed for user "{1}" for order item "{2}"'.format(self.reg_code, self.user, reg_item.id))
예제 #56
0
    def test_remove_item(self, exception_log):
        self.login_user()
        reg_item = PaidCourseRegistration.add_to_order(self.cart,
                                                       self.course_id)
        cert_item = CertificateItem.add_to_order(self.cart,
                                                 self.verified_course_id,
                                                 self.cost, 'honor')
        self.assertEquals(self.cart.orderitem_set.count(), 2)
        resp = self.client.post(
            reverse('shoppingcart.views.remove_item', args=[]),
            {'id': reg_item.id})
        self.assertEqual(resp.status_code, 200)
        self.assertEquals(self.cart.orderitem_set.count(), 1)
        self.assertNotIn(reg_item,
                         self.cart.orderitem_set.all().select_subclasses())

        self.cart.purchase()
        resp2 = self.client.post(
            reverse('shoppingcart.views.remove_item', args=[]),
            {'id': cert_item.id})
        self.assertEqual(resp2.status_code, 200)
        exception_log.assert_called_with(
            'Cannot remove cart OrderItem id={0}. DoesNotExist or item is already purchased'
            .format(cert_item.id))

        resp3 = self.client.post(
            reverse('shoppingcart.views.remove_item', args=[]), {'id': -1})
        self.assertEqual(resp3.status_code, 200)
        exception_log.assert_called_with(
            'Cannot remove cart OrderItem id={0}. DoesNotExist or item is already purchased'
            .format(-1))
예제 #57
0
    def test_no_refund_on_cert_callback(self):
        # If we explicitly skip refunds, the unenroll action should not modify the purchase.
        CourseEnrollment.enroll(self.user, self.course_key, 'verified')
        cart = Order.get_cart_for_user(user=self.user)
        CertificateItem.add_to_order(cart, self.course_key, self.cost,
                                     'verified')
        cart.purchase()

        CourseEnrollment.unenroll(self.user, self.course_key, skip_refund=True)
        target_certs = CertificateItem.objects.filter(
            course_id=self.course_key,
            user_id=self.user,
            status='purchased',
            mode='verified')
        self.assertTrue(target_certs[0])
        self.assertFalse(target_certs[0].refund_requested_time)
        self.assertEqual(target_certs[0].order.status, 'purchased')
예제 #58
0
    def test_payment_separate_from_verification_email(self):
        cart = Order.get_cart_for_user(user=self.user)
        item = CertificateItem.add_to_order(cart, self.course_key, self.cost,
                                            'honor')
        cart.purchase()

        self.assertEquals(len(mail.outbox), 1)
        # Verify that the verification reminder appears in the sent email.
        self.assertIn(item.additional_instruction_text, mail.outbox[0].body)
예제 #59
0
 def test_show_receipt_success_custom_receipt_page(self):
     cert_item = CertificateItem.add_to_order(self.cart, self.course_key, self.cost, 'honor')
     self.cart.purchase()
     self.login_user()
     receipt_url = reverse('shoppingcart.views.show_receipt', args=[self.cart.id])
     resp = self.client.get(receipt_url)
     self.assertEqual(resp.status_code, 200)
     ((template, _context), _tmp) = render_mock.call_args
     self.assertEqual(template, cert_item.single_item_receipt_template)