def accounting(self, request, tl, one, two, module, extra, prog): '''Lists accounting for a student. Defaults to the current user, but can take the user ID in the extra argument instead.''' if extra: user = ESPUser.objects.get(id=extra) else: user = request.user iac = IndividualAccountingController(prog, user) classified_transfers = [{ 'transfer': t, 'type': iac.classify_transfer(t) } for t in iac.get_transfers().select_related('line_item')] context = { 'target_user': user, 'transfers': classified_transfers, 'identifier': iac.get_identifier(), 'grant': iac.latest_finaid_grant(), } if iac.transfers_to_program_exist(): context['transfers_exist'] = True context['requested'] = iac.amount_requested(ensure_required=False) context['finaid'] = iac.amount_finaid() context['siblingdiscount'] = iac.amount_siblingdiscount() context['paid'] = iac.amount_paid() context['due'] = iac.amount_due() return render_to_response(self.baseDir() + 'accounting.html', request, context)
def payonline(self, request, tl, one, two, module, extra, prog): user = ESPUser(request.user) iac = IndividualAccountingController(self.program, request.user) context = {} context['module'] = self context['one'] = one context['two'] = two context['tl'] = tl context['user'] = user context['invoice_id'] = iac.get_id() context['identifier'] = iac.get_identifier() payment_type = iac.default_payments_lineitemtype() sibling_type = iac.default_siblingdiscount_lineitemtype() grant_type = iac.default_finaid_lineitemtype() context['itemizedcosts'] = iac.get_transfers().exclude( line_item__in=[payment_type, sibling_type, grant_type]).order_by( '-line_item__required') context['itemizedcosttotal'] = iac.amount_due() context['subtotal'] = iac.amount_requested() context['financial_aid'] = iac.amount_finaid() context['sibling_discount'] = iac.amount_siblingdiscount() context['amount_paid'] = iac.amount_paid() if 'HTTP_HOST' in request.META: context['hostname'] = request.META['HTTP_HOST'] else: context['hostname'] = Site.objects.get_current().domain context['institution'] = settings.INSTITUTION_NAME context['storename'] = self.store_id context['support_email'] = settings.DEFAULT_EMAIL_ADDRESSES['support'] return render_to_response(self.baseDir() + 'cardpay.html', request, context)
def paynow_cybersource(self, request, tl, one, two, module, extra, prog): # Force users to pay for non-optional stuffs user = ESPUser(request.user) iac = IndividualAccountingController(self.program, request.user) context = {} context['module'] = self context['one'] = one context['two'] = two context['tl'] = tl context['user'] = user context['invoice_id'] = iac.get_id() context['identifier'] = iac.get_identifier() payment_type = iac.default_payments_lineitemtype() sibling_type = iac.default_siblingdiscount_lineitemtype() grant_type = iac.default_finaid_lineitemtype() context['itemizedcosts'] = iac.get_transfers().exclude( line_item__in=[payment_type, sibling_type, grant_type]).order_by( '-line_item__required') context['itemizedcosttotal'] = iac.amount_due() context['subtotal'] = iac.amount_requested() context['financial_aid'] = iac.amount_finaid() context['sibling_discount'] = iac.amount_siblingdiscount() context['amount_paid'] = iac.amount_paid() return render_to_response(self.baseDir() + 'cardpay.html', request, context)
def cybersource(self, request, tl, one, two, module, extra, prog): # Force users to pay for non-optional stuffs user = request.user iac = IndividualAccountingController(self.program, request.user) context = {} context['module'] = self context['one'] = one context['two'] = two context['tl'] = tl context['user'] = user context['contact_email'] = self.program.director_email context['invoice_id'] = iac.get_id() context['identifier'] = iac.get_identifier() payment_type = iac.default_payments_lineitemtype() sibling_type = iac.default_siblingdiscount_lineitemtype() grant_type = iac.default_finaid_lineitemtype() context['itemizedcosts'] = iac.get_transfers().exclude( line_item__in=[payment_type, sibling_type, grant_type]).order_by( '-line_item__required') context['itemizedcosttotal'] = iac.amount_due() context['subtotal'] = iac.amount_requested() context['financial_aid'] = iac.amount_finaid() context['sibling_discount'] = iac.amount_siblingdiscount() context['amount_paid'] = iac.amount_paid() context['result'] = request.GET.get("result") context['post_url'] = settings.CYBERSOURCE_CONFIG['post_url'] context['merchant_id'] = settings.CYBERSOURCE_CONFIG['merchant_id'] if (not context['post_url']) or (not context['merchant_id']): raise ESPError("The Cybersource module is not configured") return render_to_response(self.baseDir() + 'cardpay.html', request, context)
def cybersource(self, request, tl, one, two, module, extra, prog): # Force users to pay for non-optional stuffs user = request.user iac = IndividualAccountingController(self.program, request.user) context = {} context['module'] = self context['one'] = one context['two'] = two context['tl'] = tl context['user'] = user context['contact_email'] = self.program.director_email context['invoice_id'] = iac.get_id() context['identifier'] = iac.get_identifier() payment_type = iac.default_payments_lineitemtype() sibling_type = iac.default_siblingdiscount_lineitemtype() grant_type = iac.default_finaid_lineitemtype() context['itemizedcosts'] = iac.get_transfers().exclude(line_item__in=[payment_type, sibling_type, grant_type]).order_by('-line_item__required') context['itemizedcosttotal'] = iac.amount_due() context['subtotal'] = iac.amount_requested() context['financial_aid'] = iac.amount_finaid() context['sibling_discount'] = iac.amount_siblingdiscount() context['amount_paid'] = iac.amount_paid() context['result'] = request.GET.get("result") context['post_url'] = settings.CYBERSOURCE_CONFIG['post_url'] context['merchant_id'] = settings.CYBERSOURCE_CONFIG['merchant_id'] if (not context['post_url']) or (not context['merchant_id']): raise ESPError("The Cybersource module is not configured") return render_to_response(self.baseDir() + 'cardpay.html', request, context)
def payonline(self, request, tl, one, two, module, extra, prog): # Check that the user has completed all required modules so that they # are "finished" registering for the program. (In other words, they # should be registered for at least one class, and filled out other # required forms, before paying by credit card.) modules = prog.getModules(request.user, tl) completedAll = True for module in modules: if not module.isCompleted() and module.required: completedAll = False if not completedAll and not request.user.isAdmin(prog): raise ESPError("Please go back and ensure that you have completed all required steps of registration before paying by credit card.", log=False) # Check for setup of module. This is also required to initialize settings. self.check_setup() user = request.user iac = IndividualAccountingController(self.program, request.user) context = {} context['module'] = self context['program'] = prog context['user'] = user context['invoice_id'] = iac.get_id() context['identifier'] = iac.get_identifier() payment_type = iac.default_payments_lineitemtype() sibling_type = iac.default_siblingdiscount_lineitemtype() grant_type = iac.default_finaid_lineitemtype() offer_donation = self.settings['offer_donation'] donate_type = iac.get_lineitemtypes().get(text=self.settings['donation_text']) if offer_donation else None context['itemizedcosts'] = iac.get_transfers().exclude(line_item__in=filter(None, [payment_type, sibling_type, grant_type, donate_type])).order_by('-line_item__required') context['itemizedcosttotal'] = iac.amount_due() # This amount should be formatted as an integer in order to be # accepted by Stripe. context['totalcost_cents'] = int(context['itemizedcosttotal'] * 100) context['subtotal'] = iac.amount_requested() context['financial_aid'] = iac.amount_finaid() context['sibling_discount'] = iac.amount_siblingdiscount() context['amount_paid'] = iac.amount_paid() # Load donation amount separately, since the client-side code needs to know about it separately. donation_prefs = iac.get_preferences([donate_type,]) if offer_donation else None if donation_prefs: context['amount_donation'] = Decimal(donation_prefs[0][2]) context['has_donation'] = True else: context['amount_donation'] = Decimal('0.00') context['has_donation'] = False context['amount_without_donation'] = context['itemizedcosttotal'] - context['amount_donation'] if 'HTTP_HOST' in request.META: context['hostname'] = request.META['HTTP_HOST'] else: context['hostname'] = Site.objects.get_current().domain context['institution'] = settings.INSTITUTION_NAME context['support_email'] = settings.DEFAULT_EMAIL_ADDRESSES['support'] return render_to_response(self.baseDir() + 'cardpay.html', request, context)
def confirmreg_forreal(self, request, tl, one, two, module, extra, prog, new_reg): """ The page that is shown once the user saves their student reg, giving them the option of printing a confirmation """ self.request = request from esp.program.modules.module_ext import DBReceipt iac = IndividualAccountingController(prog, request.user) context = {} context['one'] = one context['two'] = two context['itemizedcosts'] = iac.get_transfers() user = request.user context['finaid'] = user.hasFinancialAid(prog) if user.appliedFinancialAid(prog): context['finaid_app'] = user.financialaidrequest_set.filter(program=prog).order_by('-id')[0] else: context['finaid_app'] = None context['balance'] = iac.amount_due() context['owe_money'] = ( context['balance'] != Decimal("0.0") ) if not prog.user_can_join(user): raise ESPError("This program has filled! It can't accept any more students. Please try again next session.", log=False) modules = prog.getModules(request.user, tl) completedAll = True for module in modules: if hasattr(module, 'onConfirm'): module.onConfirm(request) if not module.isCompleted() and module.required: completedAll = False context = module.prepare(context) if completedAll: if new_reg: rec = Record.objects.create(user=user, event="reg_confirmed", program=prog) else: raise ESPError("You must finish all the necessary steps first, then click on the Save button to finish registration.", log=False) cfe = ConfirmationEmailController() cfe.send_confirmation_email(request.user, self.program) try: receipt_text = DBReceipt.objects.get(program=self.program, action='confirm').receipt context["request"] = request context["program"] = prog return HttpResponse( Template(receipt_text).render( Context(context, autoescape=False) ) ) except DBReceipt.DoesNotExist: try: receipt = 'program/receipts/'+str(prog.id)+'_custom_receipt.html' return render_to_response(receipt, request, context) except: receipt = 'program/receipts/default.html' return render_to_response(receipt, request, context)
def updatePaid(self, paid=True): """ Create an invoice for the student and, if paid is True, create a receipt showing that they have paid all of the money they owe for the program. """ iac = IndividualAccountingController(self.program, self.student) if not iac.has_paid(): iac.ensure_required_transfers() if paid: iac.submit_payment(iac.amount_due())
def test_extracosts(self): """ Verify that the "Student Extra Costs" module behaves as specified. """ program_cost = 25.0 # Set up some extra costs options: Item1 (max-qty 1) for $10, Item2 (max-qty 10) for $5, and selectable food pac = ProgramAccountingController(self.program) pac.clear_all_data() pac.setup_accounts() pac.setup_lineitemtypes(program_cost, [('Item1', 10, 1), ('Item2', 5, 10)], [('Food', [('Small', 3), ('Large', 7)])]) # Choose a random student and check that the extracosts page loads student = random.choice(self.students) iac = IndividualAccountingController(self.program, student) self.assertTrue( self.client.login( username=student.username, password='******' ), "Couldn't log in as student %s" % student.username ) response = self.client.get('/learn/%s/extracosts' % self.program.url) self.assertEqual(response.status_code, 200) # Check that they are being charged the program admission fee self.assertEqual(iac.amount_due(), program_cost) # Check that selecting one of the "buy-one" extra items works lit = LineItemType.objects.get(program=self.program, text='Item1') response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'%d-cost' % lit.id: 'checked'}) self.assertEqual(response.status_code, 302) self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location']) self.assertEqual(iac.amount_due(), program_cost + 10) # Check that selecting one or more than one of the "buy many" extra items works lit = LineItemType.objects.get(program=self.program, text='Item2') response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'%d-cost' % lit.id: 'checked', '%d-count' % lit.id: '1'}) self.assertEqual(response.status_code, 302) self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location']) self.assertEqual(iac.amount_due(), program_cost + 5) response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'%d-cost' % lit.id: 'checked', '%d-count' % lit.id: '3'}) self.assertEqual(response.status_code, 302) self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location']) self.assertEqual(iac.amount_due(), program_cost + 15) # Check that selecting an option for a "multiple choice" extra item works lit = LineItemType.objects.get(program=self.program, text='Food') lio = filter(lambda x: x[2] == 'Large', lit.options)[0] response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {'multi%d-option' % lit.id: str(lio[0])}) self.assertEqual(response.status_code, 302) self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location']) self.assertEqual(iac.amount_due(), program_cost + 7) # Check that financial aid applies to the "full" cost including the extra items # (e.g. we are not forcing financial aid students to pay for food) request = FinancialAidRequest.objects.create(user=student, program=self.program) (fg, created) = FinancialAidGrant.objects.get_or_create(request=request, percent=100) self.assertEqual(iac.amount_due(), 0.0) fg.delete() # Check that removing items on the form removes their cost for the student response = self.client.post('/learn/%s/extracosts' % self.program.getUrlBase(), {}) self.assertEqual(response.status_code, 302) self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location']) self.assertEqual(iac.amount_due(), program_cost)
def payment_success(self, request, tl, one, two, module, extra, prog): """ Receive payment from First Data Global Gateway """ if request.method == 'GET' or request.POST.get('status', '') != 'APPROVED': return self.payment_failure(request, tl, one, two, module, extra, prog) # We should already know what user/program this is for, but it should also be stored. iac = IndividualAccountingController(self.program, request.user) post_locator = request.POST.get('ponumber', '') assert (post_locator == iac.get_id()) post_identifier = request.POST.get('invoice_number', '') # Optional: The line of code below would check consistency of the user's # invoice items against the ones associated with the payment. # assert(post_identifier == iac.get_identifier()) post_amount = Decimal(request.POST.get('total', '0.0')) # Warn for possible duplicate payments prev_payments = iac.get_transfers().filter( line_item=iac.default_payments_lineitemtype()) if prev_payments.count() > 0 and iac.amount_due() <= 0: from django.conf import settings recipient_list = [contact[1] for contact in settings.ADMINS] subject = 'Possible Duplicate Postback/Payment' refs = 'User: %s (%d); Program: %s (%d)' % (request.user.name( ), request.user.id, self.program.niceName(), self.program.id) refs += '\n\nPrevious payments\' Transfer IDs: ' + (u', '.join( [x.id for x in prev_payments])) # Send mail! send_mail('[ ESP CC ] ' + subject + ' by ' + invoice.user.first_name + ' ' + invoice.user.last_name, \ """%s Notification\n--------------------------------- \n\n%s\n\nUser: %s %s (%s)\n\nCardholder: %s\n\nRequest: %s\n\n""" % \ (subject, refs, request.user.first_name, request.user.last_name, request.user.id, request.POST.get('bname', '--'), request) , \ settings.SERVER_EMAIL, recipient_list, True) # Save the payment as a transfer in the database iac.submit_payment(post_amount) context = {} context['postdata'] = request.POST.copy() context['support_email'] = settings.DEFAULT_EMAIL_ADDRESSES['support'] context['prog'] = prog # Don't redirect to receipt just yet, in case they haven't finished all steps of registration # return HttpResponseRedirect("http://%s/learn/%s/%s/confirmreg" % (request.META['HTTP_HOST'], one, two)) return render_to_response(self.baseDir() + 'success.html', request, context)
def viewpay_cybersource(self, request, tl, one, two, module, extra, prog): pac = ProgramAccountingController(prog) student_list = list(pac.all_students()) payment_table = [] for student in student_list: iac = IndividualAccountingController(prog, student) payment_table.append((student, iac.get_transfers(), iac.amount_requested(), iac.amount_due())) context = {'program': prog, 'payment_table': payment_table} return render_to_response(self.baseDir() + 'viewpay_cybersource.html', request, context)
def set_donation_amount(self, request, tl, one, two, module, extra, prog): """ Set the student's desired donation amount. Creates a line item type for donations if it does not exist. """ amount_donation = Decimal(request.POST.get('amount', '0')) iac = IndividualAccountingController(prog, request.user) # Clear the Transfers by specifying quantity 0 iac.set_preference('Donation to Learning Unlimited', 0) if amount_donation != Decimal('0'): # Specify quantity 1 and the desired amount iac.set_preference('Donation to Learning Unlimited', 1, amount=amount_donation) data = {'amount_donation': amount_donation, 'amount_due': iac.amount_due()} return HttpResponse(json.dumps(data), content_type='application/json')
def paiditems(self, request, tl, one, two, module, extra, prog): # Get a user user, found = search_for_user(request) if not found: return user # Get the optional purchases for that user iac = IndividualAccountingController(prog, user) context = {} context['student'] = user context['requireditems'] = iac.get_transfers(required_only=True) context['reserveditems'] = iac.get_transfers(optional_only=True) context['amount_requested'] = iac.amount_requested() context['amount_finaid'] = iac.amount_finaid() context['amount_due'] = iac.amount_due() return render_to_response(self.baseDir()+'paiditems.html', request, context)
def set_donation_amount(self, request, tl, one, two, module, extra, prog): """ Set the student's desired donation amount. Creates a line item type for donations if it does not exist. """ amount_donation = Decimal(request.POST.get('amount', '0')) iac = IndividualAccountingController(prog, request.user) # Clear the Transfers by specifying quantity 0 iac.set_preference('Donation to Learning Unlimited', 0) if amount_donation != Decimal('0'): # Specify quantity 1 and the desired amount iac.set_preference('Donation to Learning Unlimited', 1, amount=amount_donation) data = { 'amount_donation': amount_donation, 'amount_due': iac.amount_due() } return HttpResponse(json.dumps(data), content_type='application/json')
def donation(self, request, tl, one, two, module, extra, prog): user = request.user iac = IndividualAccountingController(self.program, user) # It's unclear if we support changing line item preferences after # credit card payment has occured. For now, just do the same thing we # do in other accounting modules, and don't allow changes after payment # has occured. if iac.amount_due() <= 0: raise ESPError("You've already paid for this program. Please make any further changes on-site so that we can charge or refund you properly.", log=False) # Donations and non-donations go through different code paths. If a # user chooses to make a donation, set_donation_amount() is called via # an AJAX request. If a user chooses not to make a donation, their # browser makes a request to the studentreg main page. Therefore, it is # impossible set the donation_done Record after the user is actually # done with the module. So our only option is to mark them done when # they first visit the page. This should be fine, since donations are # always optional. If we really care about being correct, if we switch # this page to not use AJAX but instead use a normal form submission, # we can then switch to granting the Record after the user is done with # the page. Record.objects.get_or_create(user=user, program=self.program, event=self.event) context = {} context['module'] = self context['program'] = prog context['user'] = user # Load donation amount separately, since the client-side code needs to know about it separately. donation_prefs = iac.get_preferences([self.line_item_type(),]) if donation_prefs: context['amount_donation'] = Decimal(donation_prefs[0][2]) context['has_donation'] = True else: context['amount_donation'] = Decimal('0.00') context['has_donation'] = False context['institution'] = settings.INSTITUTION_NAME return render_to_response(self.baseDir() + 'donation.html', request, context)
def paiditems(self, request, tl, one, two, module, extra, prog): # Get a user filterObj, found = UserSearchController().create_filter( request, self.program) if not found: return filterObj user = filterObj.getList(ESPUser).distinct()[0] # Get the optional purchases for that user iac = IndividualAccountingController(prog, user) context = {} context['student'] = user context['requireditems'] = iac.get_transfers(required_only=True) context['reserveditems'] = iac.get_transfers(optional_only=True) context['amount_requested'] = iac.amount_requested() context['amount_finaid'] = iac.amount_finaid() context['amount_due'] = iac.amount_due() return render_to_response(self.baseDir() + 'paiditems.html', request, context)
def test_finaid(self): """ Verify that financial aid behaves as specified. """ program_cost = 25.0 # Set the cost of the program pac = ProgramAccountingController(self.program) pac.clear_all_data() pac.setup_accounts() pac.setup_lineitemtypes(program_cost) # Choose a random student and sign up for a class student = random.choice(self.students) iac = IndividualAccountingController(self.program, student) sec = random.choice(self.program.sections()) sec.preregister_student(student) # Check that the student owes the cost of the program self.assertEqual(iac.amount_due(), program_cost) # Apply for financial aid self.assertTrue( self.client.login( username=student.username, password='******' ), "Couldn't log in as student %s" % student.username ) response = self.client.get( '/learn/%s/finaid' % self.program.url, **{'wsgi.url_scheme': 'https'}) self.assertEqual(response.status_code, 200) form_settings = { 'reduced_lunch': '', 'household_income': '12345', 'extra_explaination': 'No', 'student_prepare': '', } response = self.client.post('/learn/%s/finaid' % self.program.getUrlBase(), form_settings) self.assertEqual(response.status_code, 302) self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location']) # Check that the student still owes the cost of the program self.assertEqual(iac.amount_due(), program_cost) # Have an admin approve the financial aid app and check a few different cases: request = FinancialAidRequest.objects.get(user=student, program=self.program) # - 100 percent (fg, created) = FinancialAidGrant.objects.get_or_create(request=request, percent=100) self.assertEqual(iac.amount_due(), 0.0) # - absolute discount amount fg.percent = None fg.amount_max_dec = Decimal('15.0') fg.save() self.assertEqual(iac.amount_due(), program_cost - 15.0) # - discount percentage fg.amount_max_dec = None fg.percent = 50 fg.save() self.assertEqual(iac.amount_due(), program_cost / 2) # Check that deleting the financial aid grant restores original program cost fg.delete() self.assertEqual(iac.amount_due(), program_cost) # Check that the 'free/reduced lunch' option on the finaid results in zero amount due form_settings = { 'reduced_lunch': 'checked', 'household_income': '12345', 'extra_explaination': 'No', 'student_prepare': '', } response = self.client.post('/learn/%s/finaid' % self.program.getUrlBase(), form_settings) self.assertEqual(response.status_code, 302) self.assertIn('/learn/%s/studentreg' % self.program.url, response['Location']) self.assertEqual(iac.amount_due(), 0)
def viewpay_cybersource(self, request, tl, one, two, module, extra, prog): pac = ProgramAccountingController(prog) student_list = list(pac.all_students()) payment_table = [] # Fetch detailed information for every student associated with the program for student in student_list: iac = IndividualAccountingController(prog, student) payment_table.append((student, iac.get_transfers(), iac.amount_requested(), iac.amount_due())) # Also fetch summary information about the payments (num_payments, total_payment) = pac.payments_summary() context = { 'program': prog, 'payment_table': payment_table, 'num_students': len(student_list), 'num_payments': num_payments, 'total_payment': total_payment, } return render_to_response(self.baseDir() + 'viewpay_cybersource.html', request, context)
def _payment_table_row_cached(prog, student): iac = IndividualAccountingController(prog, student) return (student, iac.get_transfers(), iac.amount_requested(), iac.amount_due())
def have_paid(self): iac = IndividualAccountingController(self.program, get_current_request().user) return (iac.amount_due() <= 0)
def charge_payment(self, request, tl, one, two, module, extra, prog): # Check for setup of module. This is also required to initialize settings. self.check_setup() context = {'postdata': request.POST.copy()} group_name = Tag.getTag('full_group_name') or '%s %s' % (settings.INSTITUTION_NAME, settings.ORGANIZATION_SHORT_NAME) iac = IndividualAccountingController(self.program, request.user) # Set Stripe key based on settings. Also require the API version # which our code is designed for. stripe.api_key = self.settings['secret_key'] # We are using the 2014-03-13 version of the Stripe API, which is # v1.12.2. stripe.api_version = '2014-03-13' if request.POST.get('ponumber', '') != iac.get_id(): # If we received a payment for the wrong PO: # This is not a Python exception, but an error nonetheless. context['error_type'] = 'inconsistent_po' context['error_info'] = {'request_po': request.POST.get('ponumber', ''), 'user_po': iac.get_id()} if 'error_type' not in context: # Check the amount in the POST against the amount in our records. # If they don't match, raise an error. amount_cents_post = Decimal(request.POST['totalcost_cents']) amount_cents_iac = Decimal(iac.amount_due()) * 100 if amount_cents_post != amount_cents_iac: context['error_type'] = 'inconsistent_amount' context['error_info'] = { 'amount_cents_post': amount_cents_post, 'amount_cents_iac': amount_cents_iac, } if 'error_type' not in context: try: with transaction.atomic(): # Save a record of the charge if we can uniquely identify the user/program. # If this causes an error, the user will get a 500 error # page, and the card will NOT be charged. # If an exception is later raised by # stripe.Charge.create(), then the transaction will be # rolled back. # Thus, we will never be in a state where the card has been # charged without a record being created on the site, nor # vice-versa. totalcost_dollars = Decimal(request.POST['totalcost_cents']) / 100 # Create a record of the transfer without the transaction ID. transfer = iac.submit_payment(totalcost_dollars, 'TBD') # Create the charge on Stripe's servers - this will charge # the user's card. charge = stripe.Charge.create( amount=amount_cents_post, currency="usd", card=request.POST['stripeToken'], description="Payment for %s %s - %s" % (group_name, prog.niceName(), request.user.name()), statement_descriptor=group_name[0:22], #stripe limits statement descriptors to 22 characters metadata={ 'ponumber': request.POST['ponumber'], }, ) # Now that the charge has been performed by Stripe, save its # transaction ID for our records. transfer.transaction_id = charge.id transfer.save() except stripe.error.CardError, e: context['error_type'] = 'declined' context['error_info'] = e.json_body['error'] except stripe.error.InvalidRequestError, e: # While this is a generic error meaning invalid parameters were supplied # to Stripe's API, we will usually see it because of a duplicate request. context['error_type'] = 'invalid'
def have_paid(self, user): """ Whether the user has paid for this program. """ iac = IndividualAccountingController(self.program, user) return (iac.amount_due() <= 0)
def confirmreg_forreal(self, request, tl, one, two, module, extra, prog, new_reg): """ The page that is shown once the user saves their student reg, giving them the option of printing a confirmation """ self.request = request from esp.program.modules.module_ext import DBReceipt iac = IndividualAccountingController(prog, request.user) context = {} context['one'] = one context['two'] = two context['itemizedcosts'] = iac.get_transfers() user = ESPUser(request.user) context['finaid'] = user.hasFinancialAid(prog) if user.appliedFinancialAid(prog): context['finaid_app'] = user.financialaidrequest_set.filter( program=prog).order_by('-id')[0] else: context['finaid_app'] = None context['balance'] = iac.amount_due() context['owe_money'] = (context['balance'] != Decimal("0.0")) if prog.isFull() and not user.canRegToFullProgram( prog) and not self.program.isConfirmed(user): raise ESPError( log=False ), "This program has filled! It can't accept any more students. Please try again next session." modules = prog.getModules(request.user, tl) completedAll = True for module in modules: if hasattr(module, 'onConfirm'): module.onConfirm(request) if not module.isCompleted() and module.required: completedAll = False context = module.prepare(context) if completedAll: if new_reg: rec = Record.objects.create(user=user, event="reg_confirmed", program=prog) else: raise ESPError( False ), "You must finish all the necessary steps first, then click on the Save button to finish registration." cfe = ConfirmationEmailController() cfe.send_confirmation_email(request.user, self.program) try: receipt_text = DBReceipt.objects.get(program=self.program, action='confirm').receipt context["request"] = request context["program"] = prog return HttpResponse( Template(receipt_text).render( Context(context, autoescape=False))) except DBReceipt.DoesNotExist: try: receipt = 'program/receipts/' + str( prog.id) + '_custom_receipt.html' return render_to_response(receipt, request, context) except: receipt = 'program/receipts/default.html' return render_to_response(receipt, request, context)
def charge_payment(self, request, tl, one, two, module, extra, prog): # Check for setup of module. This is also required to initialize settings. self.check_setup() context = {'postdata': request.POST.copy()} group_name = Tag.getTag('full_group_name') or '%s %s' % ( settings.INSTITUTION_NAME, settings.ORGANIZATION_SHORT_NAME) iac = IndividualAccountingController(self.program, request.user) # Set Stripe key based on settings. Also require the API version # which our code is designed for. stripe.api_key = self.settings['secret_key'] # We are using the 2014-03-13 version of the Stripe API, which is # v1.12.2. stripe.api_version = '2014-03-13' if request.POST.get('ponumber', '') != iac.get_id(): # If we received a payment for the wrong PO: # This is not a Python exception, but an error nonetheless. context['error_type'] = 'inconsistent_po' context['error_info'] = { 'request_po': request.POST.get('ponumber', ''), 'user_po': iac.get_id() } if 'error_type' not in context: # Check the amount in the POST against the amount in our records. # If they don't match, raise an error. amount_cents_post = Decimal(request.POST['totalcost_cents']) amount_cents_iac = Decimal(iac.amount_due()) * 100 if amount_cents_post != amount_cents_iac: context['error_type'] = 'inconsistent_amount' context['error_info'] = { 'amount_cents_post': amount_cents_post, 'amount_cents_iac': amount_cents_iac, } if 'error_type' not in context: try: with transaction.atomic(): # Save a record of the charge if we can uniquely identify the user/program. # If this causes an error, the user will get a 500 error # page, and the card will NOT be charged. # If an exception is later raised by # stripe.Charge.create(), then the transaction will be # rolled back. # Thus, we will never be in a state where the card has been # charged without a record being created on the site, nor # vice-versa. totalcost_dollars = Decimal( request.POST['totalcost_cents']) / 100 # Create a record of the transfer without the transaction ID. transfer = iac.submit_payment(totalcost_dollars, 'TBD') # Create the charge on Stripe's servers - this will charge # the user's card. charge = stripe.Charge.create( amount=amount_cents_post, currency="usd", card=request.POST['stripeToken'], description="Payment for %s %s - %s" % (group_name, prog.niceName(), request.user.name()), statement_descriptor=group_name[ 0: 22], #stripe limits statement descriptors to 22 characters metadata={ 'ponumber': request.POST['ponumber'], }, ) # Now that the charge has been performed by Stripe, save its # transaction ID for our records. transfer.transaction_id = charge.id transfer.save() except stripe.error.CardError, e: context['error_type'] = 'declined' context['error_info'] = e.json_body['error'] except stripe.error.InvalidRequestError, e: # While this is a generic error meaning invalid parameters were supplied # to Stripe's API, we will usually see it because of a duplicate request. context['error_type'] = 'invalid'
def payonline(self, request, tl, one, two, module, extra, prog): # Check that the user has completed all required modules so that they # are "finished" registering for the program. (In other words, they # should be registered for at least one class, and filled out other # required forms, before paying by credit card.) modules = prog.getModules(request.user, tl) completedAll = True for module in modules: if not module.isCompleted() and module.required: completedAll = False if not completedAll and not request.user.isAdmin(prog): raise ESPError( "Please go back and ensure that you have completed all required steps of registration before paying by credit card.", log=False) # Check for setup of module. This is also required to initialize settings. self.check_setup() user = request.user iac = IndividualAccountingController(self.program, request.user) context = {} context['module'] = self context['program'] = prog context['user'] = user context['invoice_id'] = iac.get_id() context['identifier'] = iac.get_identifier() payment_type = iac.default_payments_lineitemtype() sibling_type = iac.default_siblingdiscount_lineitemtype() grant_type = iac.default_finaid_lineitemtype() offer_donation = self.settings['offer_donation'] donate_type = iac.get_lineitemtypes().get( text=self.settings['donation_text']) if offer_donation else None context['itemizedcosts'] = iac.get_transfers().exclude( line_item__in=filter( None, [payment_type, sibling_type, grant_type, donate_type ])).order_by('-line_item__required') context['itemizedcosttotal'] = iac.amount_due() # This amount should be formatted as an integer in order to be # accepted by Stripe. context['totalcost_cents'] = int(context['itemizedcosttotal'] * 100) context['subtotal'] = iac.amount_requested() context['financial_aid'] = iac.amount_finaid() context['sibling_discount'] = iac.amount_siblingdiscount() context['amount_paid'] = iac.amount_paid() # Load donation amount separately, since the client-side code needs to know about it separately. donation_prefs = iac.get_preferences([ donate_type, ]) if offer_donation else None if donation_prefs: context['amount_donation'] = Decimal(donation_prefs[0][2]) context['has_donation'] = True else: context['amount_donation'] = Decimal('0.00') context['has_donation'] = False context['amount_without_donation'] = context[ 'itemizedcosttotal'] - context['amount_donation'] if 'HTTP_HOST' in request.META: context['hostname'] = request.META['HTTP_HOST'] else: context['hostname'] = Site.objects.get_current().domain context['institution'] = settings.INSTITUTION_NAME context['support_email'] = settings.DEFAULT_EMAIL_ADDRESSES['support'] return render_to_response(self.baseDir() + 'cardpay.html', request, context)