def test_parse_consent_params(self): """ Verify that "parse_consent_params" util works as expected. """ mock_request = RequestFactory().get( '/any?consent_url_param_string=left_sidebar_text_override%3D') parsed = parse_consent_params(mock_request) self.assertDictEqual(parsed, {'left_sidebar_text_override': ''}) mock_request2 = RequestFactory().get('/any') parsed = parse_consent_params(mock_request2) assert parsed is None
def _redirect_for_enterprise_data_sharing_consent(self, basket): """ Redirect to LMS to get data sharing consent from learner. """ # check if basket contains only a single product of type seat if basket.lines.count() == 1 and basket.lines.first( ).product.is_seat_product: enterprise_custmer_uuid = get_enterprise_customer_from_enterprise_offer( basket) product = basket.lines.first().product course = product.course if enterprise_custmer_uuid is not None and enterprise_customer_user_needs_consent( self.request.site, enterprise_custmer_uuid, course.id, self.request.user.username, ): redirect_url = construct_enterprise_course_consent_url( self.request, product.course.id, enterprise_custmer_uuid, consent_url_param_dict=parse_consent_params(self.request)) raise RedirectException( response=HttpResponseRedirect(redirect_url))
def get(self, request): # pylint: disable=too-many-statements """ Looks up the passed code and adds the matching product to a basket, then applies the voucher and if the basket total is FREE places the order and enrolls the user in the course. """ template_name = 'coupons/_offer_error.html' code = request.GET.get('code') sku = request.GET.get('sku') failure_url = request.GET.get('failure_url') site_configuration = request.site.siteconfiguration if not code: return render(request, template_name, {'error': _('Code not provided.')}) if not sku: return render(request, template_name, {'error': _('SKU not provided.')}) try: voucher = Voucher.objects.get(code=code) except Voucher.DoesNotExist: msg = 'No voucher found with code {code}'.format(code=code) return render(request, template_name, {'error': _(msg)}) try: product = StockRecord.objects.get(partner_sku=sku).product except StockRecord.DoesNotExist: return render(request, template_name, {'error': _('The product does not exist.')}) valid_voucher, msg, hide_error_message = voucher_is_valid( voucher, [product], request) if not valid_voucher: logger.warning( '[Code Redemption Failure] The voucher is not valid for this product. ' 'User: %s, Product: %s, Code: %s, Message: %s', request.user.username, product.id, voucher.code, msg) return render(request, template_name, { 'error': msg, 'hide_error_message': hide_error_message }) offer = voucher.best_offer if not offer.is_email_valid(request.user.email): logger.warning( '[Code Redemption Failure] Unable to apply offer because the user\'s email ' 'does not meet the domain requirements. ' 'User: %s, Offer: %s, Code: %s', request.user.username, offer.id, voucher.code) return render( request, template_name, {'error': _('You are not eligible to use this coupon.')}) email_confirmation_response = get_redirect_to_email_confirmation_if_required( request, offer, product) if email_confirmation_response: return email_confirmation_response try: enterprise_customer = get_enterprise_customer_from_voucher( request.site, voucher) except EnterpriseDoesNotExist as e: # If an EnterpriseException is caught while pulling the EnterpriseCustomer, that means there's no # corresponding EnterpriseCustomer in the Enterprise service (which should never happen). logger.exception(str(e)) return render( request, template_name, { 'error': _('Couldn\'t find a matching Enterprise Customer for this coupon.' ) }) if enterprise_customer and product.is_course_entitlement_product: return render( request, template_name, { 'error': _('This coupon is not valid for purchasing a program. Try using this on an individual ' 'course in the program. If you need assistance, contact edX support.' ) }) if enterprise_customer is not None and enterprise_customer_user_needs_consent( request.site, enterprise_customer['id'], product.course.id, request.user.username, ): consent_token = get_enterprise_customer_data_sharing_consent_token( request.user.access_token, product.course.id, enterprise_customer['id']) received_consent_token = request.GET.get('consent_token') if received_consent_token: # If the consent token is set, then the user is returning from the consent view. Render out an error # if the computed token doesn't match the one received from the redirect URL. if received_consent_token != consent_token: logger.warning( '[Code Redemption Failure] Unable to complete code redemption because of ' 'invalid consent. User: %s, Offer: %s, Code: %s', request.user.username, offer.id, voucher.code) return render(request, template_name, { 'error': _('Invalid data sharing consent token provided.') }) else: # The user hasn't been redirected to the interstitial consent view to collect consent, so # redirect them now. redirect_url = get_enterprise_course_consent_url( request.site, code, sku, consent_token, product.course.id, enterprise_customer['id'], failure_url=failure_url, consent_url_param_dict=parse_consent_params(request), ) return HttpResponseRedirect(redirect_url) try: basket = prepare_basket(request, [product], voucher) except AlreadyPlacedOrderException: msg = _('You have already purchased {course} seat.').format( course=product.course.name) return render(request, template_name, {'error': msg}) if basket.total_excl_tax == 0: try: order = self.place_free_order(basket) if enterprise_customer: return HttpResponseRedirect( get_lms_course_about_url(product.course.id)) return HttpResponseRedirect( get_receipt_page_url( site_configuration, order.number, disable_back_button=True, ), ) except: # pylint: disable=bare-except logger.exception( 'Failed to create a free order for basket [%d]', basket.id) return absolute_redirect(self.request, 'checkout:error') if enterprise_customer: if is_voucher_applied(basket, voucher): message = _( 'A discount has been applied, courtesy of {enterprise_customer_name}.' ).format( enterprise_customer_name=enterprise_customer.get('name')) messages.info(self.request, message) else: # Display a generic message to the user if a condition-specific # message has not already been added by an unsatified Condition class. if not messages.get_messages(self.request): messages.warning( self.request, _('This coupon code is not valid for this course. Try a different course.' )) self.request.basket.vouchers.remove(voucher) # The coupon_redeem_redirect query param is used to communicate to the Payment MFE that it may redirect # and should not display the payment form before making that determination. # TODO: It would be cleaner if the user could be redirected to their final destination up front. redirect_url = get_payment_microfrontend_or_basket_url( self.request) + "?coupon_redeem_redirect=1" return HttpResponseRedirect(redirect_url)