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')
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)
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")
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')
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
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()
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')
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)
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)
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)
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)
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')
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)
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)
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)
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')
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())
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())
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())
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))
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()
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")
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()
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')
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))
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])
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()
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))
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))
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)
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))
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)
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)
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')
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)
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")
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")
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")
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', )
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))
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()
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
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)
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)
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))
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))
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))
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')
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)
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)