Пример #1
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
Пример #2
0
    def process_transaction(self,
                            currency,
                            amount,
                            host=PAYMENT_HOST_NONE,
                            campaign=None,
                            user=None,
                            return_url=None,
                            paymentReason="unglue.it Pledge",
                            pledge_extra=None,
                            donation=False,
                            modification=False):
        '''
        process
        
        saves and processes a proposed transaction; decides if the transaction should be processed 
        immediately.
        
        currency: a 3-letter currency code, i.e. USD
        amount: the amount to authorize
        host: the name of the processing module; if none, send user back to decide!
        campaign: required campaign object
        user: optional user object
        return_url: url to redirect supporter to after a successful transaction
        paymentReason:  a memo line that will show up in the Payer's Amazon (and Paypal?) account
        modification: whether this authorize call is part of a modification of an existing pledge
        pledge_extra: extra pledge stuff
        
        return value: a tuple of the new transaction object and a re-direct url.  
                      If the process fails, the redirect url will be None
        donation: transaction is a donation
        '''
        # set the expiry date based on the campaign deadline
        if campaign and campaign.deadline:
            expiry = campaign.deadline + timedelta(
                days=settings.PREAPPROVAL_PERIOD_AFTER_CAMPAIGN)
        else:
            expiry = now() + timedelta(
                days=settings.PREAPPROVAL_PERIOD_AFTER_CAMPAIGN)

        t = Transaction.create(
            amount=0,
            host=host,
            max_amount=amount,
            currency=currency,
            campaign=campaign,
            user=user,
            pledge_extra=pledge_extra,
            donation=donation,
        )
        t.save()
        # does user have enough credit to transact now?
        if user.is_authenticated() and user.credit.available >= amount:
            # YES!
            return_path = "{0}?{1}".format(reverse('pledge_complete'),
                                           urllib.urlencode({'tid': t.id}))
            return_url = urlparse.urljoin(settings.BASE_URL_SECURE,
                                          return_path)
            if campaign.is_pledge():
                success = credit.pledge_transaction(t, user, amount)
                if success:
                    pledge_created.send(sender=self, transaction=t)
            else:
                success = credit.pay_transaction(t, user,
                                                 t.campaign.user_to_pay,
                                                 amount)
                if success:
                    t.amount = amount
                    t.host = PAYMENT_HOST_CREDIT
                    t.execution = EXECUTE_TYPE_INSTANT
                    t.date_executed = now()
                    t.status = TRANSACTION_STATUS_COMPLETE
                    t.save()
                    transaction_charged.send(sender=self, transaction=t)
            if success:
                return t, return_url
            else:
                # shouldn't happen
                logger.error('could not use credit for transaction %s' % t.id)

        # send user to choose payment path
        return t, reverse('fund', args=[t.id])