Пример #1
0
    def cancel_related_transaction(self,
                                   transaction,
                                   status=TRANSACTION_STATUS_ACTIVE,
                                   campaign=None):
        '''
        Cancels any other similar status transactions for the same campaign.  Used with modify code
        
        Returns the number of transactions successfully canceled
        '''

        related_transactions = Transaction.objects.filter(
            status=status, user=transaction.user)

        if len(related_transactions) == 0:
            return 0

        if campaign:
            related_transactions = related_transactions.filter(
                campaign=campaign)

        canceled = 0

        for t in related_transactions:

            if t.id == transaction.id:
                # keep our transaction
                continue

            if self.cancel_transaction(t):
                canceled = canceled + 1
                # send notice about modification of transaction
                if transaction.amount > t.amount:
                    # this should be the only one that happens
                    up_or_down = "increased"
                elif transaction.amount < t.amount:
                    # we shouldn't expect any case in which this happens
                    up_or_down = "decreased"
                else:
                    # we shouldn't expect any case in which this happens
                    up_or_down = None

                pledge_modified.send(sender=self,
                                     transaction=transaction,
                                     up_or_down=up_or_down)
            else:
                logger.error(
                    "Failed to cancel transaction {0} for related transaction {1} "
                    .format(t, transaction))

        return canceled
Пример #2
0
    def modify_transaction(self,
                           transaction,
                           amount,
                           expiry=None,
                           pledge_extra=None,
                           return_url=None,
                           nevermind_url=None,
                           paymentReason=None):
        '''
        modify
        
        Modifies a transaction.  
        2 main situations:  if the new amount is less than max_amount, no need to go out to Stripe again
        if new amount is greater than max_amount...need to go out and get new approval.
        to start with, we can use the standard pledge_complete, pledge_cancel machinery
        might have to modify the pledge_complete, pledge_cancel because the messages are going to be
        different because we're modifying a pledge rather than a new one.
        
        amount: the new amount
        expiry: the new expiration date, or if none the current expiration date will be used
        return_url: the return URL after the preapproval(if needed)
        paymentReason: a memo line that will show up in the Payer's Amazon (and Paypal?) account
        
        return value: True if successful, False otherwise.  An optional second parameter for the forward URL if a new authorhization is needed
        '''

        logger.info("transaction.id: {0}, amount:{1}".format(
            transaction.id, amount))
        if amount < transaction.amount:
            up_or_down = "decreased"
        elif amount > transaction.amount:
            up_or_down = "increased"
        else:
            up_or_down = "modified"

        # if expiry is None, use the existing value
        if expiry is None:
            expiry = transaction.date_expired

        # does this transaction explicity require preapprovals?

        requires_explicit_preapprovals = transaction.get_payment_class(
        ).requires_explicit_preapprovals

        if transaction.type != PAYMENT_TYPE_AUTHORIZATION:
            logger.info("Error, attempt to modify an invalid transaction type")
            return False, None

        # Can only modify an active, pending transaction.  If it is completed, we need to do a refund.  If it is incomplete,
        # then an IPN may be pending and we cannot touch it
        if transaction.status != TRANSACTION_STATUS_ACTIVE:
            logger.info(
                "Error, attempt to modify a transaction that is not active")
            return False, None

        if transaction.host == PAYMENT_HOST_CREDIT:
            # does user have enough credit to pledge now?
            if transaction.user.credit.available >= amount - transaction.amount:
                # YES!
                transaction.set_pledge_extra(pledge_extra)
                credit.pledge_transaction(transaction, transaction.user,
                                          amount)
                return_path = "{0}?{1}".format(
                    reverse('pledge_complete'),
                    urllib.urlencode({'tid': transaction.id}))
                return_url = urlparse.urljoin(settings.BASE_URL_SECURE,
                                              return_path)

                logger.info("Updated amount of transaction to %f" % amount)
                pledge_modified.send(sender=self,
                                     transaction=transaction,
                                     up_or_down=up_or_down)
                return transaction, return_url
            else:
                # cancel old transaction, send user to choose new payment path
                # set the expiry date based on the campaign deadline
                expiry = transaction.deadline_or_now + timedelta(
                    days=settings.PREAPPROVAL_PERIOD_AFTER_CAMPAIGN)
                t = Transaction.create(amount=0,
                                       max_amount=amount,
                                       currency=transaction.currency,
                                       status=TRANSACTION_STATUS_MODIFIED,
                                       campaign=transaction.campaign,
                                       user=transaction.user,
                                       pledge_extra=pledge_extra)
                t.save()
                credit.Processor.CancelPreapproval(transaction)
                return t, reverse('fund_%s' % campaign.type, args=[t.id])

        elif requires_explicit_preapprovals and (
                amount > transaction.max_amount
                or expiry != transaction.date_expired):

            # set the expiry date based on the campaign deadline
            expiry = transaction.deadline_or_now + timedelta(
                days=settings.PREAPPROVAL_PERIOD_AFTER_CAMPAIGN)

            # Start a new transaction for the new amount
            t = Transaction.create(amount=amount,
                                   max_amount=amount,
                                   host=transaction.host,
                                   currency=transaction.currency,
                                   status=TRANSACTION_STATUS_CREATED,
                                   campaign=transaction.campaign,
                                   user=transaction.user,
                                   pledge_extra=pledge_extra)
            t.save()
            t, url = self.authorize(
                transaction,
                expiry=expiry if expiry else transaction.date_expired,
                return_url=return_url,
                paymentReason=paymentReason,
                modification=True)

            if t and url:
                # Need to re-direct to approve the transaction
                logger.info("New authorization needed, redirection to url %s" %
                            url)

                # Do not cancel the transaction here, wait until we get confirmation that the transaction is complete
                # then cancel all other active transactions for this campaign
                #self.cancel_transaction(transaction)

                # while it would seem to make sense to send a pledge notification change here
                # if we do, we will also send notifications when we initiate but do not
                # successfully complete a pledge modification

                return True, url
            else:
                # a problem in authorize
                logger.info("Error, unable to start a new authorization")
                # should we send a pledge_modified signal with state="failed" and a
                # corresponding notification to the user? that would go here.
                return False, None

        elif (requires_explicit_preapprovals and amount <=
              transaction.max_amount) or (not requires_explicit_preapprovals):
            # Update transaction but leave the preapproval alone
            transaction.amount = amount
            transaction.set_pledge_extra(pledge_extra)

            transaction.save()
            logger.info("Updated amount of transaction to %f" % amount)
            # when modifying pledges happens immediately and only within our
            # db, we don't have to wait until we hear back to be assured of
            # success; send the notification immediately
            pledge_modified.send(sender=self,
                                 transaction=transaction,
                                 up_or_down=up_or_down)
            return True, None
        else:
            # this shouldn't happen
            return False, None