def test_initial_verification_for_user(self): """Test that method 'get_initial_verification' of model 'SoftwareSecurePhotoVerification' always returns the initial verification with field 'photo_id_key' set against a user. """ user = UserFactory.create() # No initial verification for the user result = SoftwareSecurePhotoVerification.get_initial_verification(user=user) self.assertIs(result, None) # Make an initial verification with 'photo_id_key' attempt = SoftwareSecurePhotoVerification(user=user, photo_id_key="dummy_photo_id_key") attempt.status = 'approved' attempt.save() # Check that method 'get_initial_verification' returns the correct # initial verification attempt first_result = SoftwareSecurePhotoVerification.get_initial_verification(user=user) self.assertIsNotNone(first_result) # Now create a second verification without 'photo_id_key' attempt = SoftwareSecurePhotoVerification(user=user) attempt.status = 'submitted' attempt.save() # Test method 'get_initial_verification' still returns the correct # initial verification attempt which have 'photo_id_key' set second_result = SoftwareSecurePhotoVerification.get_initial_verification(user=user) self.assertIsNotNone(second_result) self.assertEqual(second_result, first_result)
def test_user_status(self): # test for correct status when no error returned user = UserFactory.create() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('none', '')) # test for when one has been created attempt = SoftwareSecurePhotoVerification(user=user) attempt.status = 'approved' attempt.save() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('approved', '')) # create another one for the same user, make sure the right one is # returned attempt2 = SoftwareSecurePhotoVerification(user=user) attempt2.status = 'denied' attempt2.error_msg = '[{"photoIdReasons": ["Not provided"]}]' attempt2.save() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('approved', '')) # now delete the first one and verify that the denial is being handled # properly attempt.delete() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('must_reverify', "No photo ID was provided."))
def post(self, request): """ submits the reverification to SoftwareSecure """ try: 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() # save this attempt attempt.save() # then submit it across attempt.submit() return HttpResponseRedirect(reverse('verify_student_reverification_confirmation')) except Exception: log.exception( "Could not submit verification attempt for user {}".format(request.user.id) ) context = { "user_full_name": request.user.profile.name, "error": True, } return render_to_response("verify_student/photo_reverification.html", context)
def post(self, request): """ submits the reverification to SoftwareSecure """ try: 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() # save this attempt attempt.save() # then submit it across attempt.submit() return HttpResponseRedirect( reverse('verify_student_reverification_confirmation')) except Exception: log.exception( "Could not submit verification attempt for user {}".format( request.user.id)) context = { "user_full_name": request.user.profile.name, "error": True, } return render_to_response( "verify_student/photo_reverification.html", context)
def test_initial_verification_for_user(self): """Test that method 'get_initial_verification' of model 'SoftwareSecurePhotoVerification' always returns the initial verification with field 'photo_id_key' set against a user. """ user = UserFactory.create() # No initial verification for the user result = SoftwareSecurePhotoVerification.get_initial_verification( user=user) self.assertIs(result, None) # Make an initial verification with 'photo_id_key' attempt = SoftwareSecurePhotoVerification( user=user, photo_id_key="dummy_photo_id_key") attempt.status = 'approved' attempt.save() # Check that method 'get_initial_verification' returns the correct # initial verification attempt first_result = SoftwareSecurePhotoVerification.get_initial_verification( user=user) self.assertIsNotNone(first_result) # Now create a second verification without 'photo_id_key' attempt = SoftwareSecurePhotoVerification(user=user) attempt.status = 'submitted' attempt.save() # Test method 'get_initial_verification' still returns the correct # initial verification attempt which have 'photo_id_key' set second_result = SoftwareSecurePhotoVerification.get_initial_verification( user=user) self.assertIsNotNone(second_result) self.assertEqual(second_result, first_result)
def post(self, request, course_id): """ submits the reverification to SoftwareSecure """ try: now = datetime.datetime.now(UTC) course_id = CourseKey.from_string(course_id) window = MidcourseReverificationWindow.get_window(course_id, now) if window is None: raise WindowExpiredException attempt = SoftwareSecurePhotoVerification(user=request.user, window=window) b64_face_image = request.POST["face_image"].split(",")[1] attempt.upload_face_image(b64_face_image.decode("base64")) attempt.fetch_photo_id_image() attempt.mark_ready() attempt.save() attempt.submit() course_enrollment = CourseEnrollment.get_or_create_enrollment(request.user, course_id) course_enrollment.update_enrollment(mode="verified") course_enrollment.emit_event(EVENT_NAME_USER_SUBMITTED_MIDCOURSE_REVERIFY) return HttpResponseRedirect(reverse("verify_student_midcourse_reverification_confirmation")) except WindowExpiredException: log.exception( "User {} attempted to re-verify, but the window expired before the attempt".format(request.user.id) ) return HttpResponseRedirect(reverse("verify_student_reverification_window_expired")) except Exception: log.exception("Could not submit verification attempt for user {}".format(request.user.id)) context = {"user_full_name": request.user.profile.name, "error": True} return render_to_response("verify_student/midcourse_photo_reverification.html", context)
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 test_original_verification(self): orig_attempt = SoftwareSecurePhotoVerification(user=self.user) orig_attempt.save() window = MidcourseReverificationWindowFactory( course_id=self.course.id, start_date=datetime.now(pytz.UTC) - timedelta(days=15), end_date=datetime.now(pytz.UTC) - timedelta(days=13), ) midcourse_attempt = SoftwareSecurePhotoVerification(user=self.user, window=window) self.assertEquals(midcourse_attempt.original_verification(user=self.user), orig_attempt)
def test_user_status(self): # test for correct status when no error returned user = UserFactory.create() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('none', '')) # test for when one has been created attempt = SoftwareSecurePhotoVerification(user=user) attempt.status = 'approved' attempt.save() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('approved', '')) # create another one for the same user, make sure the right one is # returned attempt2 = SoftwareSecurePhotoVerification(user=user) attempt2.status = 'denied' attempt2.error_msg = '[{"photoIdReasons": ["Not provided"]}]' attempt2.save() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('approved', '')) # now delete the first one and verify that the denial is being handled # properly attempt.delete() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ('must_reverify', "No photo ID was provided.")) # test for correct status for reverifications window = MidcourseReverificationWindowFactory() reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window) self.assertEquals(reverify_status, ('must_reverify', '')) reverify_attempt = SoftwareSecurePhotoVerification(user=user, window=window) reverify_attempt.status = 'approved' reverify_attempt.save() reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window) self.assertEquals(reverify_status, ('approved', '')) reverify_attempt.status = 'denied' reverify_attempt.save() reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window) self.assertEquals(reverify_status, ('denied', '')) reverify_attempt.status = 'approved' # pylint: disable=protected-access reverify_attempt.created_at = SoftwareSecurePhotoVerification._earliest_allowed_date() + timedelta(days=-1) reverify_attempt.save() reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window) message = 'Your {platform_name} verification has expired.'.format(platform_name=settings.PLATFORM_NAME) self.assertEquals(reverify_status, ('expired', message))
def test_fetch_photo_id_image(self): user = UserFactory.create() orig_attempt = SoftwareSecurePhotoVerification(user=user) orig_attempt.save() old_key = orig_attempt.photo_id_key new_attempt = SoftwareSecurePhotoVerification(user=user) new_attempt.save() new_attempt.fetch_photo_id_image() assert_equals(new_attempt.photo_id_key, old_key)
def test_display(self): user = UserFactory.create() window = MidcourseReverificationWindowFactory() attempt = SoftwareSecurePhotoVerification(user=user, window=window, status="denied") attempt.save() # We expect the verification to be displayed by default self.assertEquals(SoftwareSecurePhotoVerification.display_status(user, window), True) # Turn it off SoftwareSecurePhotoVerification.display_off(user.id) self.assertEquals(SoftwareSecurePhotoVerification.display_status(user, window), False)
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_user_is_reverified_for_all(self): # if there are no windows for a course, this should return True self.assertTrue( SoftwareSecurePhotoVerification.user_is_reverified_for_all( self.course.id, self.user)) # first, make three windows window1 = MidcourseReverificationWindowFactory( course_id=self.course.id, start_date=datetime.now(pytz.UTC) - timedelta(days=15), end_date=datetime.now(pytz.UTC) - timedelta(days=13), ) window2 = MidcourseReverificationWindowFactory( course_id=self.course.id, start_date=datetime.now(pytz.UTC) - timedelta(days=10), end_date=datetime.now(pytz.UTC) - timedelta(days=8), ) window3 = MidcourseReverificationWindowFactory( course_id=self.course.id, start_date=datetime.now(pytz.UTC) - timedelta(days=5), end_date=datetime.now(pytz.UTC) - timedelta(days=3), ) # make two SSPMidcourseReverifications for those windows attempt1 = SoftwareSecurePhotoVerification(status="approved", user=self.user, window=window1) attempt1.save() attempt2 = SoftwareSecurePhotoVerification(status="approved", user=self.user, window=window2) attempt2.save() # should return False because only 2 of 3 windows have verifications self.assertFalse( SoftwareSecurePhotoVerification.user_is_reverified_for_all( self.course.id, self.user)) attempt3 = SoftwareSecurePhotoVerification(status="must_retry", user=self.user, window=window3) attempt3.save() # should return False because the last verification exists BUT is not approved self.assertFalse( SoftwareSecurePhotoVerification.user_is_reverified_for_all( self.course.id, self.user)) attempt3.status = "approved" attempt3.save() # should now return True because all windows have approved verifications self.assertTrue( SoftwareSecurePhotoVerification.user_is_reverified_for_all( self.course.id, self.user))
def test_user_has_valid_or_pending(self): window = MidcourseReverificationWindowFactory( course_id=self.course.id, start_date=datetime.now(pytz.UTC) - timedelta(days=15), end_date=datetime.now(pytz.UTC) - timedelta(days=13), ) attempt = SoftwareSecurePhotoVerification(status="must_retry", user=self.user, window=window) attempt.save() assert_false(SoftwareSecurePhotoVerification.user_has_valid_or_pending(user=self.user, window=window)) attempt.status = "approved" attempt.save() assert_true(SoftwareSecurePhotoVerification.user_has_valid_or_pending(user=self.user, window=window))
def test_fetch_photo_id_image(self): user = UserFactory.create() orig_attempt = SoftwareSecurePhotoVerification(user=user, window=None) orig_attempt.save() old_key = orig_attempt.photo_id_key window = MidcourseReverificationWindowFactory( course_id=SlashSeparatedCourseKey("pony", "rainbow", "dash"), start_date=datetime.now(pytz.utc) - timedelta(days=5), end_date=datetime.now(pytz.utc) + timedelta(days=5)) new_attempt = SoftwareSecurePhotoVerification(user=user, window=window) new_attempt.save() new_attempt.fetch_photo_id_image() assert_equals(new_attempt.photo_id_key, old_key)
def test_active_for_user(self): """ Make sure we can retrive a user's active (in progress) verification attempt. """ user = UserFactory.create() # This user has no active at the moment... assert_is_none(SoftwareSecurePhotoVerification.active_for_user(user)) # Create an attempt and mark it ready... attempt = SoftwareSecurePhotoVerification(user=user) attempt.mark_ready() assert_equals(attempt, SoftwareSecurePhotoVerification.active_for_user(user)) # A new user won't see this... user2 = UserFactory.create() user2.save() assert_is_none(SoftwareSecurePhotoVerification.active_for_user(user2)) # If it's got a different status, it doesn't count for status in ["submitted", "must_retry", "approved", "denied"]: attempt.status = status attempt.save() assert_is_none( SoftwareSecurePhotoVerification.active_for_user(user)) # But if we create yet another one and mark it ready, it passes again. attempt_2 = SoftwareSecurePhotoVerification(user=user) attempt_2.mark_ready() assert_equals(attempt_2, SoftwareSecurePhotoVerification.active_for_user(user)) # And if we add yet another one with a later created time, we get that # one instead. We always want the most recent attempt marked ready() attempt_3 = SoftwareSecurePhotoVerification( user=user, created_at=attempt_2.created_at + timedelta(days=1)) attempt_3.save() # We haven't marked attempt_3 ready yet, so attempt_2 still wins assert_equals(attempt_2, SoftwareSecurePhotoVerification.active_for_user(user)) # Now we mark attempt_3 ready and expect it to come back attempt_3.mark_ready() assert_equals(attempt_3, SoftwareSecurePhotoVerification.active_for_user(user))
def test_fetch_photo_id_image(self): user = UserFactory.create() orig_attempt = SoftwareSecurePhotoVerification(user=user, window=None) orig_attempt.save() old_key = orig_attempt.photo_id_key window = MidcourseReverificationWindowFactory( course_id=SlashSeparatedCourseKey("pony", "rainbow", "dash"), start_date=datetime.now(pytz.utc) - timedelta(days=5), end_date=datetime.now(pytz.utc) + timedelta(days=5) ) new_attempt = SoftwareSecurePhotoVerification(user=user, window=window) new_attempt.save() new_attempt.fetch_photo_id_image() assert_equals(new_attempt.photo_id_key, old_key)
def post(self, request, course_id): """ submits the reverification to SoftwareSecure """ try: now = datetime.datetime.now(UTC) course_id = SlashSeparatedCourseKey.from_deprecated_string( course_id) window = MidcourseReverificationWindow.get_window(course_id, now) if window is None: raise WindowExpiredException attempt = SoftwareSecurePhotoVerification(user=request.user, window=window) b64_face_image = request.POST['face_image'].split(",")[1] attempt.upload_face_image(b64_face_image.decode('base64')) attempt.fetch_photo_id_image() attempt.mark_ready() attempt.save() attempt.submit() course_enrollment = CourseEnrollment.get_or_create_enrollment( request.user, course_id) course_enrollment.update_enrollment(mode="verified") course_enrollment.emit_event( EVENT_NAME_USER_SUBMITTED_MIDCOURSE_REVERIFY) return HttpResponseRedirect( reverse( 'verify_student_midcourse_reverification_confirmation')) except WindowExpiredException: log.exception( "User {} attempted to re-verify, but the window expired before the attempt" .format(request.user.id)) return HttpResponseRedirect( reverse('verify_student_reverification_window_expired')) except Exception: log.exception( "Could not submit verification attempt for user {}".format( request.user.id)) context = { "user_full_name": request.user.profile.name, "error": True, } return render_to_response( "verify_student/midcourse_photo_reverification.html", context)
def test_user_is_reverified_for_all(self): # if there are no windows for a course, this should return True self.assertTrue(SoftwareSecurePhotoVerification.user_is_reverified_for_all(self.course.id, self.user)) # first, make three windows window1 = MidcourseReverificationWindowFactory( course_id=self.course.id, start_date=datetime.now(pytz.UTC) - timedelta(days=15), end_date=datetime.now(pytz.UTC) - timedelta(days=13), ) window2 = MidcourseReverificationWindowFactory( course_id=self.course.id, start_date=datetime.now(pytz.UTC) - timedelta(days=10), end_date=datetime.now(pytz.UTC) - timedelta(days=8), ) window3 = MidcourseReverificationWindowFactory( course_id=self.course.id, start_date=datetime.now(pytz.UTC) - timedelta(days=5), end_date=datetime.now(pytz.UTC) - timedelta(days=3), ) # make two SSPMidcourseReverifications for those windows attempt1 = SoftwareSecurePhotoVerification( status="approved", user=self.user, window=window1 ) attempt1.save() attempt2 = SoftwareSecurePhotoVerification( status="approved", user=self.user, window=window2 ) attempt2.save() # should return False because only 2 of 3 windows have verifications self.assertFalse(SoftwareSecurePhotoVerification.user_is_reverified_for_all(self.course.id, self.user)) attempt3 = SoftwareSecurePhotoVerification( status="must_retry", user=self.user, window=window3 ) attempt3.save() # should return False because the last verification exists BUT is not approved self.assertFalse(SoftwareSecurePhotoVerification.user_is_reverified_for_all(self.course.id, self.user)) attempt3.status = "approved" attempt3.save() # should now return True because all windows have approved verifications self.assertTrue(SoftwareSecurePhotoVerification.user_is_reverified_for_all(self.course.id, self.user))
def test_active_for_user(self): """ Make sure we can retrive a user's active (in progress) verification attempt. """ user = UserFactory.create() # This user has no active at the moment... assert_is_none(SoftwareSecurePhotoVerification.active_for_user(user)) # Create an attempt and mark it ready... attempt = SoftwareSecurePhotoVerification(user=user) attempt.mark_ready() assert_equals(attempt, SoftwareSecurePhotoVerification.active_for_user(user)) # A new user won't see this... user2 = UserFactory.create() user2.save() assert_is_none(SoftwareSecurePhotoVerification.active_for_user(user2)) # If it's got a different status, it doesn't count for status in ["submitted", "must_retry", "approved", "denied"]: attempt.status = status attempt.save() assert_is_none(SoftwareSecurePhotoVerification.active_for_user(user)) # But if we create yet another one and mark it ready, it passes again. attempt_2 = SoftwareSecurePhotoVerification(user=user) attempt_2.mark_ready() assert_equals(attempt_2, SoftwareSecurePhotoVerification.active_for_user(user)) # And if we add yet another one with a later created time, we get that # one instead. We always want the most recent attempt marked ready() attempt_3 = SoftwareSecurePhotoVerification( user=user, created_at=attempt_2.created_at + timedelta(days=1) ) attempt_3.save() # We haven't marked attempt_3 ready yet, so attempt_2 still wins assert_equals(attempt_2, SoftwareSecurePhotoVerification.active_for_user(user)) # Now we mark attempt_3 ready and expect it to come back attempt_3.mark_ready() assert_equals(attempt_3, SoftwareSecurePhotoVerification.active_for_user(user))
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'] 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_user_has_valid_or_pending(self): """ Determine whether we have to prompt this user to verify, or if they've already at least initiated a verification submission. """ user = UserFactory.create() attempt = SoftwareSecurePhotoVerification(user=user) # If it's any of these statuses, they don't have anything outstanding for status in ["created", "ready", "denied"]: attempt.status = status attempt.save() assert_false(SoftwareSecurePhotoVerification.user_has_valid_or_pending(user), status) # Any of these, and we are. Note the benefit of the doubt we're giving # -- must_retry, and submitted both count until we hear otherwise for status in ["submitted", "must_retry", "approved"]: attempt.status = status attempt.save() assert_true(SoftwareSecurePhotoVerification.user_has_valid_or_pending(user), status)
def test_user_status(self): # test for correct status when no error returned user = UserFactory.create() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ("none", "")) # test for when one has been created attempt = SoftwareSecurePhotoVerification(user=user) attempt.status = "approved" attempt.save() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ("approved", "")) # create another one for the same user, make sure the right one is # returned attempt2 = SoftwareSecurePhotoVerification(user=user) attempt2.status = "denied" attempt2.error_msg = '[{"photoIdReasons": ["Not provided"]}]' attempt2.save() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ("approved", "")) # now delete the first one and verify that the denial is being handled # properly attempt.delete() status = SoftwareSecurePhotoVerification.user_status(user) self.assertEquals(status, ("must_reverify", "No photo ID was provided.")) # test for correct status for reverifications window = MidcourseReverificationWindowFactory() reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window) self.assertEquals(reverify_status, ("must_reverify", "")) reverify_attempt = SoftwareSecurePhotoVerification(user=user, window=window) reverify_attempt.status = "approved" reverify_attempt.save() reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window) self.assertEquals(reverify_status, ("approved", "")) reverify_attempt.status = "denied" reverify_attempt.save() reverify_status = SoftwareSecurePhotoVerification.user_status(user=user, window=window) self.assertEquals(reverify_status, ("denied", ""))
def test_user_is_verified(self): """ Test to make sure we correctly answer whether a user has been verified. """ user = UserFactory.create() attempt = SoftwareSecurePhotoVerification(user=user) attempt.save() # If it's any of these, they're not verified... for status in ["created", "ready", "denied", "submitted", "must_retry"]: attempt.status = status attempt.save() assert_false(SoftwareSecurePhotoVerification.user_is_verified(user), status) attempt.status = "approved" attempt.save() assert_true(SoftwareSecurePhotoVerification.user_is_verified(user), attempt.status)
class TestPhotoVerificationResultsCallback(ModuleStoreTestCase): """ Tests for the results_callback view. """ def setUp(self): self.course = CourseFactory.create(org='Robot', number='999', display_name='Test Course') self.course_id = self.course.id self.user = UserFactory.create() self.attempt = SoftwareSecurePhotoVerification( status="submitted", user=self.user ) self.attempt.save() self.receipt_id = self.attempt.receipt_id self.client = Client() def mocked_has_valid_signature(method, headers_dict, body_dict, access_key, secret_key): return True def test_invalid_json(self): """ Test for invalid json being posted by software secure. """ data = {"Testing invalid"} response = self.client.post( reverse('verify_student_results_callback'), data=data, content_type='application/json', HTTP_AUTHORIZATION='test BBBBBBBBBBBBBBBBBBBB: testing', HTTP_DATE='testdate' ) self.assertIn('Invalid JSON', response.content) self.assertEqual(response.status_code, 400) def test_invalid_dict(self): """ Test for invalid dictionary being posted by software secure. """ data = '"\\"Test\\tTesting"' response = self.client.post( reverse('verify_student_results_callback'), data=data, content_type='application/json', HTTP_AUTHORIZATION='test BBBBBBBBBBBBBBBBBBBB:testing', HTTP_DATE='testdate' ) self.assertIn('JSON should be dict', response.content) self.assertEqual(response.status_code, 400) @mock.patch('verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature)) def test_invalid_access_key(self): """ Test for invalid access key. """ data = { "EdX-ID": self.receipt_id, "Result": "Testing", "Reason": "Testing", "MessageType": "Testing" } json_data = json.dumps(data) response = self.client.post( reverse('verify_student_results_callback'), data=json_data, content_type='application/json', HTTP_AUTHORIZATION='test testing:testing', HTTP_DATE='testdate' ) self.assertIn('Access key invalid', response.content) self.assertEqual(response.status_code, 400) @mock.patch('verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature)) def test_wrong_edx_id(self): """ Test for wrong id of Software secure verification attempt. """ data = { "EdX-ID": "Invalid-Id", "Result": "Testing", "Reason": "Testing", "MessageType": "Testing" } json_data = json.dumps(data) response = self.client.post( reverse('verify_student_results_callback'), data=json_data, content_type='application/json', HTTP_AUTHORIZATION='test BBBBBBBBBBBBBBBBBBBB:testing', HTTP_DATE='testdate' ) self.assertIn('edX ID Invalid-Id not found', response.content) self.assertEqual(response.status_code, 400) @mock.patch('verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature)) def test_pass_result(self): """ Test for verification passed. """ data = { "EdX-ID": self.receipt_id, "Result": "PASS", "Reason": "", "MessageType": "You have been verified." } json_data = json.dumps(data) response = self.client.post( reverse('verify_student_results_callback'), data=json_data, content_type='application/json', HTTP_AUTHORIZATION='test BBBBBBBBBBBBBBBBBBBB:testing', HTTP_DATE='testdate' ) attempt = SoftwareSecurePhotoVerification.objects.get(receipt_id=self.receipt_id) self.assertEqual(attempt.status, u'approved') self.assertEquals(response.content, 'OK!') @mock.patch('verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature)) def test_fail_result(self): """ Test for failed verification. """ data = { "EdX-ID": self.receipt_id, "Result": 'FAIL', "Reason": 'Invalid photo', "MessageType": 'Your photo doesn\'t meet standards.' } json_data = json.dumps(data) response = self.client.post( reverse('verify_student_results_callback'), data=json_data, content_type='application/json', HTTP_AUTHORIZATION='test BBBBBBBBBBBBBBBBBBBB:testing', HTTP_DATE='testdate' ) attempt = SoftwareSecurePhotoVerification.objects.get(receipt_id=self.receipt_id) self.assertEqual(attempt.status, u'denied') self.assertEqual(attempt.error_code, u'Your photo doesn\'t meet standards.') self.assertEqual(attempt.error_msg, u'"Invalid photo"') self.assertEquals(response.content, 'OK!') @mock.patch('verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature)) def test_system_fail_result(self): """ Test for software secure result system failure. """ data = {"EdX-ID": self.receipt_id, "Result": 'SYSTEM FAIL', "Reason": 'Memory overflow', "MessageType": 'You must retry the verification.'} json_data = json.dumps(data) response = self.client.post( reverse('verify_student_results_callback'), data=json_data, content_type='application/json', HTTP_AUTHORIZATION='test BBBBBBBBBBBBBBBBBBBB:testing', HTTP_DATE='testdate' ) attempt = SoftwareSecurePhotoVerification.objects.get(receipt_id=self.receipt_id) self.assertEqual(attempt.status, u'must_retry') self.assertEqual(attempt.error_code, u'You must retry the verification.') self.assertEqual(attempt.error_msg, u'"Memory overflow"') self.assertEquals(response.content, 'OK!') @mock.patch('verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature)) def test_unknown_result(self): """ test for unknown software secure result """ data = { "EdX-ID": self.receipt_id, "Result": 'Unknown', "Reason": 'Unknown reason', "MessageType": 'Unknown message' } json_data = json.dumps(data) response = self.client.post( reverse('verify_student_results_callback'), data=json_data, content_type='application/json', HTTP_AUTHORIZATION='test BBBBBBBBBBBBBBBBBBBB:testing', HTTP_DATE='testdate' ) self.assertIn('Result Unknown not understood', response.content) @mock.patch('verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature)) def test_reverification(self): """ Test software secure result for reverification window. """ data = { "EdX-ID": self.receipt_id, "Result": "PASS", "Reason": "", "MessageType": "You have been verified." } window = MidcourseReverificationWindowFactory(course_id=self.course_id) self.attempt.window = window self.attempt.save() json_data = json.dumps(data) self.assertEqual(CourseEnrollment.objects.filter(course_id=self.course_id).count(), 0) response = self.client.post( reverse('verify_student_results_callback'), data=json_data, content_type='application/json', HTTP_AUTHORIZATION='test BBBBBBBBBBBBBBBBBBBB:testing', HTTP_DATE='testdate' ) self.assertEquals(response.content, 'OK!') self.assertIsNotNone(CourseEnrollment.objects.get(course_id=self.course_id))
def create_order(request): """ Submit PhotoVerification and create a new Order for this verified cert """ # Only submit photos if photo data is provided by the client. # TODO (ECOM-188): Once the A/B test of decoupling verified / payment # completes, we may be able to remove photo submission from this step # entirely. submit_photo = ( 'face_image' in request.POST and 'photo_id_image' in request.POST ) if ( submit_photo and not SoftwareSecurePhotoVerification.user_has_valid_or_pending(request.user) ): attempt = SoftwareSecurePhotoVerification(user=request.user) try: b64_face_image = request.POST['face_image'].split(",")[1] b64_photo_id_image = request.POST['photo_id_image'].split(",")[1] except IndexError: log.error(u"Invalid image data during photo verification.") context = { 'success': False, } return JsonResponse(context) 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 = CourseKey.from_string(course_id) donation_for_course = request.session.get('donation_for_course', {}) contribution = request.POST.get("contribution", donation_for_course.get(unicode(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.")) current_mode = None paid_modes = CourseMode.paid_modes_for_course(course_id) # Check if there are more than 1 paid(mode with min_price>0 e.g verified/professional/no-id-professional) modes # for course exist then choose the first one if paid_modes: if len(paid_modes) > 1: log.warn(u"Multiple paid course modes found for course '%s' for create order request", course_id) current_mode = paid_modes[0] # Make sure this course has a paid mode if not current_mode: log.warn(u"Create order requested for course '%s' without a paid mode.", course_id) return HttpResponseBadRequest(_("This course doesn't support paid certificates")) if CourseMode.is_professional_mode(current_mode): amount = current_mode.min_price if amount < current_mode.min_price: return HttpResponseBadRequest(_("No selected price or selected price is below minimum.")) if current_mode.sku: return create_order_with_ecommerce_service(request.user, course_id, current_mode) # 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 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) try: b64_face_image = request.POST['face_image'].split(",")[1] b64_photo_id_image = request.POST['photo_id_image'].split(",")[1] except IndexError: context = { 'success': False, } return JsonResponse(context) 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 = CourseKey.from_string(course_id) donation_for_course = request.session.get('donation_for_course', {}) current_donation = donation_for_course.get(unicode(course_id), decimal.Decimal(0)) contribution = request.POST.get("contribution", donation_for_course.get(unicode(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[unicode(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) # make sure this course has a verified mode if not current_mode: return HttpResponseBadRequest(_("This course doesn't support verified certificates")) if current_mode.slug == 'professional': amount = current_mode.min_price 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")
class TestPhotoVerificationResultsCallback(ModuleStoreTestCase): """ Tests for the results_callback view. """ def setUp(self): self.course = CourseFactory.create(org='Robot', number='999', display_name='Test Course') self.course_id = self.course.id self.user = UserFactory.create() self.attempt = SoftwareSecurePhotoVerification(status="submitted", user=self.user) self.attempt.save() self.receipt_id = self.attempt.receipt_id self.client = Client() def mocked_has_valid_signature(method, headers_dict, body_dict, access_key, secret_key): return True def test_invalid_json(self): """ Test for invalid json being posted by software secure. """ data = {"Testing invalid"} response = self.client.post( reverse('verify_student_results_callback'), data=data, content_type='application/json', HTTP_AUTHORIZATION='test BBBBBBBBBBBBBBBBBBBB: testing', HTTP_DATE='testdate') self.assertIn('Invalid JSON', response.content) self.assertEqual(response.status_code, 400) def test_invalid_dict(self): """ Test for invalid dictionary being posted by software secure. """ data = '"\\"Test\\tTesting"' response = self.client.post( reverse('verify_student_results_callback'), data=data, content_type='application/json', HTTP_AUTHORIZATION='test BBBBBBBBBBBBBBBBBBBB:testing', HTTP_DATE='testdate') self.assertIn('JSON should be dict', response.content) self.assertEqual(response.status_code, 400) @mock.patch('verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature)) def test_invalid_access_key(self): """ Test for invalid access key. """ data = { "EdX-ID": self.receipt_id, "Result": "Testing", "Reason": "Testing", "MessageType": "Testing" } json_data = json.dumps(data) response = self.client.post(reverse('verify_student_results_callback'), data=json_data, content_type='application/json', HTTP_AUTHORIZATION='test testing:testing', HTTP_DATE='testdate') self.assertIn('Access key invalid', response.content) self.assertEqual(response.status_code, 400) @mock.patch('verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature)) def test_wrong_edx_id(self): """ Test for wrong id of Software secure verification attempt. """ data = { "EdX-ID": "Invalid-Id", "Result": "Testing", "Reason": "Testing", "MessageType": "Testing" } json_data = json.dumps(data) response = self.client.post( reverse('verify_student_results_callback'), data=json_data, content_type='application/json', HTTP_AUTHORIZATION='test BBBBBBBBBBBBBBBBBBBB:testing', HTTP_DATE='testdate') self.assertIn('edX ID Invalid-Id not found', response.content) self.assertEqual(response.status_code, 400) @mock.patch('verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature)) def test_pass_result(self): """ Test for verification passed. """ data = { "EdX-ID": self.receipt_id, "Result": "PASS", "Reason": "", "MessageType": "You have been verified." } json_data = json.dumps(data) response = self.client.post( reverse('verify_student_results_callback'), data=json_data, content_type='application/json', HTTP_AUTHORIZATION='test BBBBBBBBBBBBBBBBBBBB:testing', HTTP_DATE='testdate') attempt = SoftwareSecurePhotoVerification.objects.get( receipt_id=self.receipt_id) self.assertEqual(attempt.status, u'approved') self.assertEquals(response.content, 'OK!') @mock.patch('verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature)) def test_fail_result(self): """ Test for failed verification. """ data = { "EdX-ID": self.receipt_id, "Result": 'FAIL', "Reason": 'Invalid photo', "MessageType": 'Your photo doesn\'t meet standards.' } json_data = json.dumps(data) response = self.client.post( reverse('verify_student_results_callback'), data=json_data, content_type='application/json', HTTP_AUTHORIZATION='test BBBBBBBBBBBBBBBBBBBB:testing', HTTP_DATE='testdate') attempt = SoftwareSecurePhotoVerification.objects.get( receipt_id=self.receipt_id) self.assertEqual(attempt.status, u'denied') self.assertEqual(attempt.error_code, u'Your photo doesn\'t meet standards.') self.assertEqual(attempt.error_msg, u'"Invalid photo"') self.assertEquals(response.content, 'OK!') @mock.patch('verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature)) def test_system_fail_result(self): """ Test for software secure result system failure. """ data = { "EdX-ID": self.receipt_id, "Result": 'SYSTEM FAIL', "Reason": 'Memory overflow', "MessageType": 'You must retry the verification.' } json_data = json.dumps(data) response = self.client.post( reverse('verify_student_results_callback'), data=json_data, content_type='application/json', HTTP_AUTHORIZATION='test BBBBBBBBBBBBBBBBBBBB:testing', HTTP_DATE='testdate') attempt = SoftwareSecurePhotoVerification.objects.get( receipt_id=self.receipt_id) self.assertEqual(attempt.status, u'must_retry') self.assertEqual(attempt.error_code, u'You must retry the verification.') self.assertEqual(attempt.error_msg, u'"Memory overflow"') self.assertEquals(response.content, 'OK!') @mock.patch('verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature)) def test_unknown_result(self): """ test for unknown software secure result """ data = { "EdX-ID": self.receipt_id, "Result": 'Unknown', "Reason": 'Unknown reason', "MessageType": 'Unknown message' } json_data = json.dumps(data) response = self.client.post( reverse('verify_student_results_callback'), data=json_data, content_type='application/json', HTTP_AUTHORIZATION='test BBBBBBBBBBBBBBBBBBBB:testing', HTTP_DATE='testdate') self.assertIn('Result Unknown not understood', response.content) @mock.patch('verify_student.ssencrypt.has_valid_signature', mock.Mock(side_effect=mocked_has_valid_signature)) def test_reverification(self): """ Test software secure result for reverification window. """ data = { "EdX-ID": self.receipt_id, "Result": "PASS", "Reason": "", "MessageType": "You have been verified." } window = MidcourseReverificationWindowFactory(course_id=self.course_id) self.attempt.window = window self.attempt.save() json_data = json.dumps(data) self.assertEqual( CourseEnrollment.objects.filter(course_id=self.course_id).count(), 0) response = self.client.post( reverse('verify_student_results_callback'), data=json_data, content_type='application/json', HTTP_AUTHORIZATION='test BBBBBBBBBBBBBBBBBBBB:testing', HTTP_DATE='testdate') self.assertEquals(response.content, 'OK!') self.assertIsNotNone( CourseEnrollment.objects.get(course_id=self.course_id))
def create_order(request): """ Submit PhotoVerification and create a new Order for this verified cert """ # Only submit photos if photo data is provided by the client. # TODO (ECOM-188): Once the A/B test of decoupling verified / payment # completes, we may be able to remove photo submission from this step # entirely. submit_photo = "face_image" in request.POST and "photo_id_image" in request.POST if submit_photo and not SoftwareSecurePhotoVerification.user_has_valid_or_pending(request.user): attempt = SoftwareSecurePhotoVerification(user=request.user) try: b64_face_image = request.POST["face_image"].split(",")[1] b64_photo_id_image = request.POST["photo_id_image"].split(",")[1] except IndexError: log.error(u"Invalid image data during photo verification.") context = {"success": False} return JsonResponse(context) 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 = CourseKey.from_string(course_id) donation_for_course = request.session.get("donation_for_course", {}) current_donation = donation_for_course.get(unicode(course_id), decimal.Decimal(0)) contribution = request.POST.get("contribution", donation_for_course.get(unicode(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[unicode(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) # 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 current_mode.slug == "professional": amount = current_mode.min_price 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, currency=current_mode.currency) # 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") processor = settings.CC_PROCESSOR.get(settings.CC_PROCESSOR_NAME, {}) callback_data = { "description": "Верифікований сертифікат", "order_id": cart.id, "server_url": processor.get("SERVER_URL", ""), "currency": processor.get("CURRENCY", ""), "result_url": processor.get("RESULT_URL", ""), "public_key": processor.get("PUBLIC_KEY", ""), "language": processor.get("LANGUAGE", "en"), "pay_way": processor.get("PAY_WAY", ""), "amount": current_mode.min_price, "sandbox": processor.get("SANDBOX", 0), "version": processor.get("VERSION", 3), "type": "buy", } log.warn(json.dumps(callback_data)) # callback_data = { # "description": "Верифікований сертифікат", # "order_id": cart.id, # "server_url": "http://courses.prometheus.org.ua/shoppingcart/postpay_callback/", # "currency": "UAH", # "result_url": "http://courses.prometheus.org.ua/shoppingcart/postpay_callback/", # "public_key": "", # "language": "en", # "pay_way": "card,liqpay,privat24", # "amount": "5", # "sandbox": "1", # "version": 3, # "type": "buy" # } callback_data = base64.b64encode(json.dumps(callback_data)) private_key = processor.get("PRIVATE_KEY", "") callback_signature = base64.b64encode(sha.new(private_key + callback_data + private_key).digest()) params["data"] = callback_data params["signature"] = callback_signature return HttpResponse(json.dumps(params), content_type="text/json")
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) try: b64_face_image = request.POST['face_image'].split(",")[1] b64_photo_id_image = request.POST['photo_id_image'].split(",")[1] except IndexError: context = { 'success': False, } return JsonResponse(context) 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 = CourseKey.from_string(course_id) donation_for_course = request.session.get('donation_for_course', {}) current_donation = donation_for_course.get(unicode(course_id), decimal.Decimal(0)) contribution = request.POST.get( "contribution", donation_for_course.get(unicode(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[unicode(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) # make sure this course has a verified mode if not current_mode: return HttpResponseBadRequest( _("This course doesn't support verified certificates")) if current_mode.slug == 'professional': amount = current_mode.min_price 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)]) params['success'] = True return HttpResponse(json.dumps(params), content_type="text/json")