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/')
Exemple #2
0
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/')
Exemple #4
0
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)
Exemple #6
0
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())
Exemple #9
0
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."
Exemple #11
0
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."
Exemple #12
0
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/')
Exemple #13
0
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))
Exemple #14
0
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))
Exemple #18
0
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))