def cancelPayment(request, pmtId): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) if not Payment.objects.filter(pk=pmtId).count() > 0: return HttpResponseRedirect('/payments/') pmt = Payment.objects.get(pk=pmtId) if pmt.payer_id != userNode.id: return HttpResponseRedirect('/payments/') if pmt.status not in ('RQ', 'PE'): # requested, pending return HttpResponseRedirect('/payments/') if pmt.status == 'RQ': pmt.status = 'RF' # refused # *** todo: Send refusal email to requester. t = template_loader.get_template('emailPmtRequestRefused.txt') c = RequestContext(request, {'req': pmt}) sendEmail("Payment request refusal notification", t.render(c), pmt.recipient.getPrimaryEmail()) else: pmt.status = 'CA' # cancelled pmt.save() request.session['infos'] = ["Payment cancelled."] return HttpResponseRedirect('/payments/')
def accountAction(request, acctId, action): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/accounts/%s/' % acctId) if request.method == 'POST': if request.POST['submit'] == 'Cancel': return HttpResponseRedirect('/accounts/%s/' % acctId) if Account.objects.filter(pk=acctId).count() == 0: return HttpResponseRedirect('/accounts/') acct = Account.objects.get(pk=acctId) if acct.owner_id != userNode.id: return HttpResponseRedirect('/accounts/') if action == 'close': sharedData = acct.shared_data if abs(sharedData.balance) >= 0.005: request.session['infos'] = ["Account balance must be zero before it can be closed."] return HttpResponseRedirect('/accounts/%d/' % acct.id) # to be sure a transaction doesn't hit this account before it gets # deactivated, set limits to zero, save, and then check for zero balance again myIOULimit = acct.iou_limit partnerAcct = acct.get_partner_acct() partnerIOULimit = partnerAcct.iou_limit acct.iou_limit = 0.0 acct.save() partnerAcct.iou_limit = 0.0 partnerAcct.save() if abs(sharedData.balance) >= 0.005: request.session['infos'] = ["Account balance must be zero before it can be closed."] # restore limits on accts acct.iou_limit = myIOULimit acct.save() partnerAcct.iou_limit = partnerIOULimit partnerAcct.save() return HttpResponseRedirect('/accounts/%d/' % acct.id) # now it's impossible for a transaction to change the balance, and the balance is zero, so: sharedData.active = False sharedData.save() # notify user request.session['infos'] = ["Account %d (%s) closed." % (sharedData.id, acct.name)] # notify partner by email note = '' if request.method == 'POST': note = request.POST['note'] t = template_loader.get_template('emailAcctClosed.txt') c = RequestContext(request, {'acct':partnerAcct, 'note':note}) sendEmail("One of your accounts has been closed", t.render(c), acct.partner.getPrimaryEmail()) pass return HttpResponseRedirect('/accounts/')
def accept_proposed_rate(request, shared_data): # this is node_to_confirm's account acct = Account.objects.get(shared_data=shared_data, owner__pk=shared_data.node_to_confirm_id) pmt = None if acct.getBalance( ) and shared_data.interest_rate: # otherwise no update necessary pmt = Payment(payer=acct.owner, payer_email=acct.owner.getPrimaryEmail(), recipient=acct.partner, recipient_email=acct.partner.getPrimaryEmail(), currency=shared_data.currency, amount=0.0, status='PE', description='Interest rate change: %s%% to %s%%.' % \ (shared_data.displayRate(), shared_data.displayProposedRate())) pmt.save() # set pmt time #pmt = Payment.objects.get(pk=pmt.id) # reload because django doesn't update date that is auto-set on save interest = computeInterest(acct.getBalance(), shared_data.interest_rate, shared_data.last_update, pmt.date) path = PaymentPath(payment=pmt, amount=pmt.amount) path.save() link = PaymentLink(path=path, payer_account=acct, position=1, amount=pmt.amount, interest=-interest, interest_rate=shared_data.interest_rate) link.save() shared_data.balance += interest * acct.balance_multiplier if pmt: shared_data.last_update = pmt.date else: shared_data.last_update = datetime.datetime.now() shared_data.interest_rate = shared_data.proposed_rate shared_data.proposed_rate = None shared_data.node_to_confirm_id = None shared_data.save() if pmt: # could do this initially now that this is wrapped in a transaction pmt.status = 'OK' pmt.save() t = template_loader.get_template('emailAcceptRateProposal.txt') c = RequestContext(request, {'acct': acct.get_partner_acct()}) sendEmail("Interest rate proposal accepted", t.render(c), acct.partner.getPrimaryEmail())
def sendPaymentEmails(pmt, request): d = {'pmt': pmt} t = template_loader.get_template('emailPmtSent.txt') c = RequestContext(request, d) sendEmail("Payment confirmation", t.render(c), pmt.payer_email) t = template_loader.get_template('emailPmtReceived.txt') c = RequestContext(request, d) sendEmail("Payment received", t.render(c), pmt.recipient_email)
def sendPaymentEmails(pmt, request): d = {'pmt':pmt} t = template_loader.get_template('emailPmtSent.txt') c = RequestContext(request, d) sendEmail("Payment confirmation", t.render(c), pmt.payer_email) t = template_loader.get_template('emailPmtReceived.txt') c = RequestContext(request, d) sendEmail("Payment received", t.render(c), pmt.recipient_email)
def reject_proposed_rate(request, shared_data): proposed_rate = shared_data.proposed_rate * 100.0 partner_acct = Account.objects.exclude(owner__pk=shared_data.node_to_confirm_id).get(shared_data=shared_data) shared_data.proposed_rate = None shared_data.node_to_confirm_id = None shared_data.save() t = template_loader.get_template('emailRejectRateProposal.txt') c = RequestContext(request, {'acct': partner_acct, 'proposed_rate': proposed_rate}) sendEmail("Interest rate proposal not accepted", t.render(c), partner_acct.owner.getPrimaryEmail())
def accept_proposed_rate(request, shared_data): # this is node_to_confirm's account acct = Account.objects.get(shared_data=shared_data, owner__pk=shared_data.node_to_confirm_id) pmt = None if acct.getBalance() and shared_data.interest_rate: # otherwise no update necessary pmt = Payment(payer=acct.owner, payer_email=acct.owner.getPrimaryEmail(), recipient=acct.partner, recipient_email=acct.partner.getPrimaryEmail(), currency=shared_data.currency, amount=0.0, status='PE', description='Interest rate change: %s%% to %s%%.' % \ (shared_data.displayRate(), shared_data.displayProposedRate())) pmt.save() # set pmt time #pmt = Payment.objects.get(pk=pmt.id) # reload because django doesn't update date that is auto-set on save interest = computeInterest(acct.getBalance(), shared_data.interest_rate, shared_data.last_update, pmt.date) path = PaymentPath(payment=pmt, amount=pmt.amount) path.save() link = PaymentLink(path=path, payer_account=acct, position=1, amount=pmt.amount, interest=-interest, interest_rate=shared_data.interest_rate) link.save() shared_data.balance += interest * acct.balance_multiplier if pmt: shared_data.last_update = pmt.date else: shared_data.last_update = datetime.datetime.now() shared_data.interest_rate = shared_data.proposed_rate shared_data.proposed_rate = None shared_data.node_to_confirm_id = None shared_data.save() if pmt: # could do this initially now that this is wrapped in a transaction pmt.status = 'OK' pmt.save() t = template_loader.get_template('emailAcceptRateProposal.txt') c = RequestContext(request, {'acct': acct.get_partner_acct()}) sendEmail("Interest rate proposal accepted", t.render(c), acct.partner.getPrimaryEmail())
def reject_proposed_rate(request, shared_data): proposed_rate = shared_data.proposed_rate * 100.0 partner_acct = Account.objects.exclude( owner__pk=shared_data.node_to_confirm_id).get(shared_data=shared_data) shared_data.proposed_rate = None shared_data.node_to_confirm_id = None shared_data.save() t = template_loader.get_template('emailRejectRateProposal.txt') c = RequestContext(request, { 'acct': partner_acct, 'proposed_rate': proposed_rate }) sendEmail("Interest rate proposal not accepted", t.render(c), partner_acct.owner.getPrimaryEmail())
def propose_rate(request, shared_data, interest_rate, node_to_confirm): acct_to_confirm = Account.objects.get(shared_data=shared_data, owner=node_to_confirm) shared_data.proposed_rate = interest_rate shared_data.node_to_confirm_id = node_to_confirm.id shared_data.save() t = template_loader.get_template('emailInterestRateProposal.txt') c = RequestContext(request, {'acct': acct_to_confirm}) if sendEmail("Interest rate proposal", t.render(c), node_to_confirm.getPrimaryEmail(), attempts=2): return "Your partner has been notified of your proposed rate change." else: return "Your rate proposal has been recorded, however, the system could not send an email notifying them. Please contact them manually."
def offerAction(request, offerId, action): """Accept or reject somebodys offer of credit""" userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) message = '' if Offer.objects.filter(pk=offerId).count() == 1: offer = Offer.objects.get(pk=offerId) else: # offer has been deleted since action link was displayed to user if action == 'accept' or action == 'reject': message = "Offer has been withdrawn and cannot be accepted or rejected." else: message = "Offer has already been acted on and cannot be withdrawn." request.session['infos'] = [message] return HttpResponseRedirect('/summary/') # check the offer is for the logged-in user if action is accept or reject if (action == 'accept' or action == 'reject') and \ offer.recipient_email not in getEmails(userNode): return HttpResponseRedirect('/summary/') if action == 'accept': initiator = offer.initiator # check for existing account between nodes # *** temporary until multiple accounts permitted if Account.objects.filter(owner=userNode, partner=initiator, shared_data__active=True).count() > 0: request.session['infos'] = [ "Account already exists with %s. Multiple accounts with the same neighbour are not permitted (yet)." % escape(initiator.name) ] offer.delete() return HttpResponseRedirect('/summary/') currencyId = offer.currency_id shared = SharedAccountData(currency_id=currencyId, balance=0.0, active=True, interest_rate=offer.interest_rate / 100.0) shared.save() acct1 = Account(owner=initiator, partner=userNode, shared_data=shared, balance_multiplier=1, iou_limit=0.0, partner_limit=offer.amount, name=userNode.name) acct1.save() acct2 = Account(owner=userNode, partner=initiator, shared_data=shared, balance_multiplier=-1, iou_limit=offer.amount, partner_limit=0.0, name=initiator.name) acct2.save() offer.delete() # shuffle node locations *** slow - consider doing as a regular script instead circle = routing.Circle() circle.shuffle(10 * Node.objects.count()) # send email informing initiator that offer was accepted t = template_loader.get_template('emailInvitationAccepted.txt') c = RequestContext(request, {'offer': offer, 'acct': acct1}) initiator_email = initiator.getPrimaryEmail() sendEmail("Invitation accepted", t.render(c), initiator_email) request.session['infos'] = [ "This is your new connection account with %s. Please take the time to give your account a meaningful name and assign your new neighbour a credit limit." % escape(initiator_email) ] return HttpResponseRedirect('/accounts/%d/' % acct2.id) elif action == 'reject': offer.delete() note = '' if request.method == 'POST': note = request.POST['note'] # send email informing initiator that offer was rejected t = template_loader.get_template('emailInvitationRejected.txt') c = RequestContext(request, {'offer': offer, 'note': note}) sendEmail("Your invitation was not accepted", t.render(c), offer.initiator.getPrimaryEmail()) message = "Offer from %s rejected." % escape( offer.initiator.getPrimaryEmail()) elif action == 'withdraw': if userNode != offer.initiator: return HttpResponseRedirect('/accounts/') offer.delete() message = "Offer to %s withdrawn." % offer.recipient_email if message: request.session['infos'] = [message] return HttpResponseRedirect('/accounts/')
def offer(request): """offer credit to multiple nodes""" userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) if not request.POST: return HttpResponseRedirect('/summary/') if request.POST[ 'phase'] == 'make_offers': # check if offers are finalized, or if we're just listing emails and the form # extract info from POST dictionary rows = [] for i in range(0, int(request.POST['count'])): row = Struct() row.email = request.POST['email_%d' % i] # already parsed as email addresses row.amount = request.POST['amount_%d' % i] row.currency = request.POST['currency_%d' % i] row.interest = request.POST['interest_%d' % i] rows.append(row) # find errors in entries error = False for row in rows: # ensure no double-offers or double-accounts emailList, errors = validateOfferEmails([row.email], userNode) if not emailList: # if row.email is in list, then offer is valid error = True row.error = errors[0] # only show first error continue # only one error per row # validate amounts as numbers try: row.amount = float(row.amount) if row.amount <= 0.0: raise ValueError except ValueError: error = True row.error = "Credit limit must be a number greater than zero." else: if row.amount >= 10**12: error = True row.error = "Amount is too large to be stored in the database. Please enter another amount." elif row.amount <= 10**-12: error = True row.error = "Amount is too small to be stored in the database. Please enter another amount." if error: continue # only one error per row try: row.interest = float(row.interest) except ValueError: error = True row.error = "Interest rate must be a number." note = request.POST['note'] if note == '': note = None if error: # show form again with errors and values still there (in row objects) d = {'rows': rows, 'count': len(rows)} d['infos'] = [ "One of more of your entries had errors. Please make corrections below." ] else: # create and save offers, and send out emails infos = [] infos.append( 'The system has recorded your offers of credit and is sending out invitation emails. <a href="/accounts/#offers">Click here to see your offers.</a>' ) #infos.append("If the invitation does not arrive, it may be in the destination's spam folder...") for row in rows: currency = CurrencyUnit.objects.get(short_name=row.currency) offer = Offer(initiator=userNode, recipient_email=row.email, amount=row.amount, currency=currency, interest_rate=row.interest) offer.save() # send out email t = template_loader.get_template('emailInvitation.txt') c = RequestContext( request, { 'row': row, 'userNode': userNode, 'currency': currency, 'note': note }) sender = '"%s" <%s>' % (userNode.name, userNode.getPrimaryEmail()) if not sendEmail('Invitation to Ripple', t.render(c), row.email, sender=sender, attempts=2, includeServiceName=False): infos.append( "An email could not be sent to %s, but your offer has been stored. Please contact %s manually." % (escape(row.email), escape(row.email))) request.session['infos'] = infos return HttpResponseRedirect('/accounts/') else: # else, list emails + form emails = parseEmails(request.POST['emailText']) emails, infos = validateOfferEmails(emails, userNode) if emails == [] and infos == []: infos.append("You didn't enter any valid email addresses.") rows = [] for email in emails: row = Struct() row.email = email row.currency = None row.interest = 0 rows.append(row) d = {'infos': infos, 'rows': rows, 'count': len(rows)} d['currencies'] = CurrencyUnit.objects.order_by('long_name') return render_to_response('offer.html', d, context_instance=RequestContext(request))
def accountAction(request, acctId, action): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/accounts/%s/' % acctId) if request.method == 'POST': if request.POST['submit'] == 'Cancel': return HttpResponseRedirect('/accounts/%s/' % acctId) if Account.objects.filter(pk=acctId).count() == 0: return HttpResponseRedirect('/accounts/') acct = Account.objects.get(pk=acctId) if acct.owner_id != userNode.id: return HttpResponseRedirect('/accounts/') if action == 'close': sharedData = acct.shared_data if abs(sharedData.balance) >= 0.005: request.session['infos'] = [ "Account balance must be zero before it can be closed." ] return HttpResponseRedirect('/accounts/%d/' % acct.id) # to be sure a transaction doesn't hit this account before it gets # deactivated, set limits to zero, save, and then check for zero balance again myIOULimit = acct.iou_limit partnerAcct = acct.get_partner_acct() partnerIOULimit = partnerAcct.iou_limit acct.iou_limit = 0.0 acct.save() partnerAcct.iou_limit = 0.0 partnerAcct.save() if abs(sharedData.balance) >= 0.005: request.session['infos'] = [ "Account balance must be zero before it can be closed." ] # restore limits on accts acct.iou_limit = myIOULimit acct.save() partnerAcct.iou_limit = partnerIOULimit partnerAcct.save() return HttpResponseRedirect('/accounts/%d/' % acct.id) # now it's impossible for a transaction to change the balance, and the balance is zero, so: sharedData.active = False sharedData.save() # notify user request.session['infos'] = [ "Account %d (%s) closed." % (sharedData.id, acct.name) ] # notify partner by email note = '' if request.method == 'POST': note = request.POST['note'] t = template_loader.get_template('emailAcctClosed.txt') c = RequestContext(request, {'acct': partnerAcct, 'note': note}) sendEmail("One of your accounts has been closed", t.render(c), acct.partner.getPrimaryEmail()) pass return HttpResponseRedirect('/accounts/')
def paymentForm(request, pmtId=None, otherUserId=None, is_request=False): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) errors = [] d = {} if request.method == 'POST': # get post data and attempt payment email = request.POST['email'] d['email'] = email # for maintaining form value in case of error if EmailAddr.objects.filter(email=email, confirmed=True).count() == 0: errors.append("Email '%s' does not belong to any Ripple user," "or has not yet been confirmed." % escape(email)) else: emailAddr = EmailAddr.objects.get(email=email) otherUser = emailAddr.node if otherUser == userNode: action = is_request and 'request payment from' or 'pay' errors.append("You can't %s yourself." % action) amountStr = request.POST['amount'] d['amount'] = amountStr try: amount = float(amountStr) if amount <= 0.0: raise Exception except: errors.append( "Amount must be a positive number. '%s' is not valid." % escape(amountStr)) currencyName = request.POST[ 'currency'] # this should be OK, since it's from a generated list currency = CurrencyUnit.objects.get(short_name=currencyName) d['selectedUnitId'] = currency.id d['description'] = request.POST['description'] if not errors: # check for enough credit available = availableCredit(userNode, currency, incoming=is_request) if available < amount: action = is_request and 'request' or 'make' errors.append( "You do not have enough available credit to %s payment. " "Available: %s %.2f." % (action, currency.short_name, available)) if availableCredit(otherUser, currency, incoming=not is_request) < amount: if is_request: pass # Other user might get more credit to fulfill request else: errors.append( "Your intended recipient has not offered enough credit " "to receive your payment.") pmt = None if request.POST['pmtId'] != 'None': pmt = Payment.objects.get(pk=request.POST['pmtId']) # Check if user can edit if (not is_request and pmt.payer_id != userNode.id) or \ (is_request and pmt.recipient_id != userNode.id): return HttpResponseRedirect('/payments/') if pmt.status == 'OK': # can't edit completed payment d = { 'message': 'Payment has already been completed.', 'link': '/payments/' } return render_to_response( 'error.html', d, context_instance=RequestContext(request)) d['pmtId'] = pmt.id # in case of errors, re-post pmtId if not errors: payer = is_request and otherUser or userNode recipient = is_request and userNode or otherUser if pmt is None: pmt = Payment() # Create new Payment. pmt.payer = payer pmt.payer_email = is_request and email or payer.getPrimaryEmail() pmt.recipient = recipient pmt.recipient_email = is_request and recipient.getPrimaryEmail( ) or email pmt.amount = amount pmt.currency = currency pmt.date = datetime.now() pmt.status = is_request and 'RQ' or 'PE' pmt.description = d['description'] pmt.save() if is_request: # Send payment request email t = template_loader.get_template('emailPmtRequestReceived.txt') c = RequestContext(request, {'req': pmt}) sendEmail("Payment request notification", t.render(c), pmt.payer_email) request.session['infos'] = [ "Your payment request has been recorded " "and a notification email sent to the payer." ] return HttpResponseRedirect('/payments/') else: # Go to payment confirmation page return HttpResponseRedirect('/payments/%s/confirm/' % pmt.id) # present make payment form d['infos'] = errors if otherUserId: # paying a specific user - URL = payUser/id/ if Node.objects.filter( pk=otherUserId).count() > 0 and userNode.id != otherUserId: d['email'] = Node.objects.get(pk=otherUserId).getPrimaryEmail() if pmtId: # editing a pending payment from DB - URL = paymentForm/id/ if Payment.objects.filter(pk=pmtId).count() > 0: pmt = Payment.objects.get(pk=pmtId) if pmt.status == 'OK': # can't edit completed payment d = { 'message': 'Payment has already been completed.', 'link': '/payments/' } return render_to_response( 'error.html', d, context_instance=RequestContext(request)) d['email'] = is_request and pmt.payer_email or pmt.recipient_email d['amount'] = pmt.amount d['selectedUnitId'] = pmt.currency_id d['description'] = pmt.description d['pmtId'] = pmtId d['paymentUnits'] = makeUnitsList(userNode) # set selected units to account units, if paying account partner, or to default units if not d.has_key('selectedUnitId'): if otherUserId and userNode.account_set.filter( partner__pk=otherUserId).count() > 0: acct = userNode.account_set.filter(partner__pk=otherUserId)[0] d['selectedUnitId'] = acct.shared_data.currency_id else: d['selectedUnitId'] = userNode.display_units_id d['is_request'] = is_request return render_to_response('paymentForm.html', d, context_instance=RequestContext(request))
def offerAction(request, offerId, action): """Accept or reject somebodys offer of credit""" userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) message = '' if Offer.objects.filter(pk=offerId).count() == 1: offer = Offer.objects.get(pk=offerId) else: # offer has been deleted since action link was displayed to user if action == 'accept' or action == 'reject': message = "Offer has been withdrawn and cannot be accepted or rejected." else: message = "Offer has already been acted on and cannot be withdrawn." request.session['infos'] = [message] return HttpResponseRedirect('/summary/') # check the offer is for the logged-in user if action is accept or reject if (action == 'accept' or action == 'reject') and \ offer.recipient_email not in getEmails(userNode): return HttpResponseRedirect('/summary/') if action == 'accept': initiator = offer.initiator # check for existing account between nodes # *** temporary until multiple accounts permitted if Account.objects.filter(owner=userNode, partner=initiator, shared_data__active=True).count() > 0: request.session['infos'] = ["Account already exists with %s. Multiple accounts with the same neighbour are not permitted (yet)." % escape(initiator.name)] offer.delete() return HttpResponseRedirect('/summary/') currencyId = offer.currency_id shared = SharedAccountData(currency_id=currencyId, balance=0.0, active=True, interest_rate=offer.interest_rate/100.0) shared.save() acct1 = Account(owner=initiator, partner=userNode, shared_data=shared, balance_multiplier=1, iou_limit=0.0, partner_limit=offer.amount, name=userNode.name) acct1.save() acct2 = Account(owner=userNode, partner=initiator, shared_data=shared, balance_multiplier=-1, iou_limit=offer.amount, partner_limit=0.0, name=initiator.name) acct2.save() offer.delete() # shuffle node locations *** slow - consider doing as a regular script instead circle = routing.Circle() circle.shuffle(10 * Node.objects.count()) # send email informing initiator that offer was accepted t = template_loader.get_template('emailInvitationAccepted.txt') c = RequestContext(request, {'offer':offer, 'acct':acct1}) sendEmail("Invitation accepted", t.render(c), initiator.getPrimaryEmail()) request.session['infos'] = ["This is your new connection account with %s. Please take the time to give your account a meaningful name and assign your new neighbour a credit limit." % escape(initiator.getPrimaryEmail())] return HttpResponseRedirect('/accounts/%d/' % acct2.id) elif action == 'reject': offer.delete() note = '' if request.method == 'POST': note = request.POST['note'] # send email informing initiator that offer was rejected t = template_loader.get_template('emailInvitationRejected.txt') c = RequestContext(request, {'offer':offer, 'note':note}) sendEmail("Your invitation was not accepted", t.render(c), offer.initiator.getPrimaryEmail()) message = "Offer from %s rejected." % escape(offer.initiator.getPrimaryEmail()) elif action == 'withdraw': if userNode != offer.initiator: return HttpResponseRedirect('/accounts/') offer.delete() message = "Offer to %s withdrawn." % offer.recipient_email if message: request.session['infos'] = [message] return HttpResponseRedirect('/accounts/')
def offer(request): """offer credit to multiple nodes""" userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) if not request.POST: return HttpResponseRedirect('/summary/') if request.POST['phase'] == 'make_offers': # check if offers are finalized, or if we're just listing emails and the form # extract info from POST dictionary rows = [] for i in range(0, int(request.POST['count'])): row = Struct() row.email = request.POST['email_%d' % i] # already parsed as email addresses row.amount = request.POST['amount_%d' % i] row.currency = request.POST['currency_%d' % i] row.interest = request.POST['interest_%d' % i] rows.append(row) # find errors in entries error = False for row in rows: # ensure no double-offers or double-accounts emailList, errors = validateOfferEmails([row.email], userNode) if not emailList: # if row.email is in list, then offer is valid error = True row.error = errors[0] # only show first error continue # only one error per row # validate amounts as numbers try: row.amount = float(row.amount) if row.amount <= 0.0: raise ValueError except ValueError: error = True row.error = "Credit limit must be a number greater than zero." else: if row.amount >= 10**12: error = True row.error = "Amount is too large to be stored in the database. Please enter another amount." elif row.amount <= 10**-12: error = True row.error = "Amount is too small to be stored in the database. Please enter another amount." if error: continue # only one error per row try: row.interest = float(row.interest) except ValueError: error = True row.error = "Interest rate must be a number." note = request.POST['note'] if note == '': note = None if error: # show form again with errors and values still there (in row objects) d = {'rows':rows, 'count':len(rows)} d['infos'] = ["One of more of your entries had errors. Please make corrections below."] else: # create and save offers, and send out emails infos = [] infos.append('The system has recorded your offers of credit and is sending out invitation emails. <a href="/accounts/#offers">Click here to see your offers.</a>') #infos.append("If the invitation does not arrive, it may be in the destination's spam folder...") for row in rows: currency = CurrencyUnit.objects.get(short_name=row.currency) offer = Offer(initiator=userNode, recipient_email=row.email, amount=row.amount, currency=currency, interest_rate=row.interest) offer.save() # send out email t = template_loader.get_template('emailInvitation.txt') c = RequestContext(request, {'row':row, 'userNode':userNode, 'currency':currency, 'note':note}) sender = '"%s" <%s>' % (userNode.name, userNode.getPrimaryEmail()) if not sendEmail('Invitation to Ripple', t.render(c), row.email, sender=sender, attempts=2, includeServiceName=False): infos.append("An email could not be sent to %s, but your offer has been stored. Please contact %s manually." % (escape(row.email), escape(row.email))) request.session['infos'] = infos return HttpResponseRedirect('/accounts/') else: # else, list emails + form emails = parseEmails(request.POST['emailText']) emails, infos = validateOfferEmails(emails, userNode) if emails == [] and infos == []: infos.append("You didn't enter any valid email addresses.") rows = [] for email in emails: row = Struct() row.email = email row.currency = None row.interest = 0 rows.append(row) d = {'infos':infos, 'rows':rows, 'count':len(rows)} d['currencies'] = CurrencyUnit.objects.order_by('long_name') return render_to_response('offer.html', d, context_instance=RequestContext(request))
def paymentForm(request, pmtId=None, otherUserId=None, is_request=False): userNode = checkLogin(request) if not userNode: return HttpResponseRedirect('/login/?redirect=%s' % request.path) errors = [] d = {} if request.method == 'POST': # get post data and attempt payment email = request.POST['email'] d['email'] = email # for maintaining form value in case of error if EmailAddr.objects.filter(email=email, confirmed=True).count() == 0: errors.append("Email '%s' does not belong to any Ripple user," "or has not yet been confirmed." % escape(email)) else: emailAddr = EmailAddr.objects.get(email=email) otherUser = emailAddr.node if otherUser == userNode: action = is_request and 'request payment from' or 'pay' errors.append("You can't %s yourself." % action) amountStr = request.POST['amount'] d['amount'] = amountStr try: amount = float(amountStr) if amount <= 0.0: raise Exception except: errors.append("Amount must be a positive number. '%s' is not valid." % escape(amountStr)) currencyName = request.POST['currency'] # this should be OK, since it's from a generated list currency = CurrencyUnit.objects.get(short_name=currencyName) d['selectedUnitId'] = currency.id d['description'] = request.POST['description'] if not errors: # check for enough credit available = availableCredit(userNode, currency, incoming=is_request) if available < amount: action = is_request and 'request' or 'make' errors.append("You do not have enough available credit to %s payment. " "Available: %s %.2f." % (action, currency.short_name, available)) if availableCredit(otherUser, currency, incoming=not is_request) < amount: if is_request: pass # Other user might get more credit to fulfill request else: errors.append("Your intended recipient has not offered enough credit " "to receive your payment.") pmt = None if request.POST['pmtId'] != 'None': pmt = Payment.objects.get(pk=request.POST['pmtId']) # Check if user can edit if (not is_request and pmt.payer_id != userNode.id) or \ (is_request and pmt.recipient_id != userNode.id): return HttpResponseRedirect('/payments/') if pmt.status == 'OK': # can't edit completed payment d = {'message':'Payment has already been completed.', 'link':'/payments/'} return render_to_response('error.html', d, context_instance=RequestContext(request)) d['pmtId'] = pmt.id # in case of errors, re-post pmtId if not errors: payer = is_request and otherUser or userNode recipient = is_request and userNode or otherUser if pmt is None: pmt = Payment() # Create new Payment. pmt.payer = payer pmt.payer_email = is_request and email or payer.getPrimaryEmail() pmt.recipient = recipient pmt.recipient_email = is_request and recipient.getPrimaryEmail() or email pmt.amount = amount pmt.currency = currency pmt.date = datetime.now() pmt.status = is_request and 'RQ' or 'PE' pmt.description = d['description'] pmt.save() if is_request: # Send payment request email t = template_loader.get_template('emailPmtRequestReceived.txt') c = RequestContext(request, {'req': pmt}) sendEmail("Payment request notification", t.render(c), pmt.payer_email) request.session['infos'] = ["Your payment request has been recorded " "and a notification email sent to the payer."] return HttpResponseRedirect('/payments/') else: # Go to payment confirmation page return HttpResponseRedirect('/payments/%s/confirm/' % pmt.id) # present make payment form d['infos'] = errors if otherUserId: # paying a specific user - URL = payUser/id/ if Node.objects.filter(pk=otherUserId).count() > 0 and userNode.id != otherUserId: d['email'] = Node.objects.get(pk=otherUserId).getPrimaryEmail() if pmtId: # editing a pending payment from DB - URL = paymentForm/id/ if Payment.objects.filter(pk=pmtId).count() > 0: pmt = Payment.objects.get(pk=pmtId) if pmt.status == 'OK': # can't edit completed payment d = {'message':'Payment has already been completed.', 'link':'/payments/'} return render_to_response('error.html', d, context_instance=RequestContext(request)) d['email'] = is_request and pmt.payer_email or pmt.recipient_email d['amount'] = pmt.amount d['selectedUnitId'] = pmt.currency_id d['description'] = pmt.description d['pmtId'] = pmtId d['paymentUnits'] = makeUnitsList(userNode) # set selected units to account units, if paying account partner, or to default units if not d.has_key('selectedUnitId'): if otherUserId and userNode.account_set.filter(partner__pk=otherUserId).count() > 0: acct = userNode.account_set.filter(partner__pk=otherUserId)[0] d['selectedUnitId'] = acct.shared_data.currency_id else: d['selectedUnitId'] = userNode.display_units_id d['is_request'] = is_request return render_to_response('paymentForm.html', d, context_instance=RequestContext(request))