def ipn(request):
    ipnObj = None

    if request.method == 'GET' or len(request.POST) == 0:
        return views_common.tracker_response(request, "tracker/badobject.html", {})

    try:
        ipnObj = paypalutil.create_ipn(request)
        ipnObj.save()

        donation = paypalutil.initialize_paypal_donation(ipnObj)
        donation.save()

        if donation.transactionstate == 'PENDING':
            reasonExplanation, ourFault = paypalutil.get_pending_reason_details(
                ipnObj.pending_reason)
            if donation.event.pendingdonationemailtemplate:
                formatContext = {
                    'event': donation.event,
                    'donation': donation,
                    'donor': donation.donor,
                    'pending_reason': ipnObj.pending_reason,
                    'reason_info': reasonExplanation if not ourFault else '',
                }
                post_office.mail.send(recipients=[donation.donor.email], sender=donation.event.donationemailsender,
                                      template=donation.event.pendingdonationemailtemplate, context=formatContext)
            # some pending reasons can be a problem with the receiver account, we should keep track of them
            if ourFault:
                paypalutil.log_ipn(ipnObj, 'Unhandled pending error')
        elif donation.transactionstate == 'COMPLETED':
            if donation.event.donationemailtemplate != None:
                formatContext = {
                    'donation': donation,
                    'donor': donation.donor,
                    'event': donation.event,
                    'prizes': viewutil.get_donation_prize_info(donation),
                }
                post_office.mail.send(recipients=[donation.donor.email], sender=donation.event.donationemailsender,
                                      template=donation.event.donationemailtemplate, context=formatContext)
            eventutil.post_donation_to_postbacks(donation)

        elif donation.transactionstate == 'CANCELLED':
            # eventually we may want to send out e-mail for some of the possible cases
            # such as payment reversal due to double-transactions (this has happened before)
            paypalutil.log_ipn(ipnObj, 'Cancelled/reversed payment')

    except Exception as inst:
        # just to make sure we have a record of it somewhere
        print("ERROR IN IPN RESPONSE, FIX IT")
        if ipnObj:
            paypalutil.log_ipn(ipnObj, "{0} \n {1}. POST data : {2}".format(
                inst, traceback.format_exc(inst), request.POST))
        else:
            viewutil.tracker_log('paypal', 'IPN creation failed: {0} \n {1}. POST data : {2}'.format(
                inst, traceback.format_exc(inst), request.POST))
    return HttpResponse("OKAY")
Beispiel #2
0
def ipn(request):
    ipnObj = None

    if request.method == 'GET' or len(request.POST) == 0:
        return views_common.tracker_response(request, "tracker/badobject.html", {})

    try:
        ipnObj = paypalutil.create_ipn(request)
        ipnObj.save()

        donation = paypalutil.initialize_paypal_donation(ipnObj)
        donation.save()

        if donation.transactionstate == 'PENDING':
            reasonExplanation, ourFault = paypalutil.get_pending_reason_details(
                ipnObj.pending_reason)
            if donation.event.pendingdonationemailtemplate:
                formatContext = {
                    'event': donation.event,
                    'donation': donation,
                    'donor': donation.donor,
                    'pending_reason': ipnObj.pending_reason,
                    'reason_info': reasonExplanation if not ourFault else '',
                }
                post_office.mail.send(recipients=[donation.donor.email], sender=donation.event.donationemailsender,
                                      template=donation.event.pendingdonationemailtemplate, context=formatContext)
            # some pending reasons can be a problem with the receiver account, we should keep track of them
            if ourFault:
                paypalutil.log_ipn(ipnObj, 'Unhandled pending error')
        elif donation.transactionstate == 'COMPLETED':
            if donation.event.donationemailtemplate != None:
                formatContext = {
                    'donation': donation,
                    'donor': donation.donor,
                    'event': donation.event,
                    'prizes': viewutil.get_donation_prize_info(donation),
                }
                post_office.mail.send(recipients=[donation.donor.email], sender=donation.event.donationemailsender,
                                      template=donation.event.donationemailtemplate, context=formatContext)
            eventutil.post_donation_to_postbacks(donation)

        elif donation.transactionstate == 'CANCELLED':
            # eventually we may want to send out e-mail for some of the possible cases
            # such as payment reversal due to double-transactions (this has happened before)
            paypalutil.log_ipn(ipnObj, 'Cancelled/reversed payment')

    except Exception as inst:
        # just to make sure we have a record of it somewhere
        print("ERROR IN IPN RESPONSE, FIX IT")
        if ipnObj:
            paypalutil.log_ipn(ipnObj, "{0} \n {1}. POST data : {2}".format(
                inst, traceback.format_exc(inst), request.POST))
        else:
            viewutil.tracker_log('paypal', 'IPN creation failed: {0} \n {1}. POST data : {2}'.format(
                inst, traceback.format_exc(inst), request.POST))
    return HttpResponse("OKAY")
    def test_unicode_decode_error_caught(self):
        donation = Donation.objects.create(
            timereceived=datetime.datetime(2018, 1, 1),
            comment='é',
            amount=Decimal(1.5),
            domain='PAYPAL',
            donor=self.donor,
            event=self.event,
        )

        eventutil.post_donation_to_postbacks(donation)
        log = Log.objects.filter(category='postback_url',
                                 event=self.event).last()
        assert "UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 1: ordinal not in range(128)" in log.message
    def test_unicode_decode_error_caught(self):
        donation = Donation.objects.create(
            timereceived=datetime.datetime(2018, 1, 1),
            comment='é',
            amount=Decimal(1.5),
            domain='PAYPAL',
            donor=self.donor,
            event=self.event,
        )

        eventutil.post_donation_to_postbacks(donation)
        log = Log.objects.filter(
            category='postback_url',
            event=self.event).last()
        assert "UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 1: ordinal not in range(128)" in log.message
    def test_request_made(self):
        responses.add(responses.GET, 'https://example.com', status=200)

        donation = Donation.objects.create(
            timereceived=datetime.datetime(2018, 1, 1),
            comment='',
            amount=Decimal(1.5),
            domain='PAYPAL',
            donor=self.donor,
            event=self.event,
        )

        eventutil.post_donation_to_postbacks(donation)

        assert len(responses.calls) == 1
        assert responses.calls[0].request.url == 'https://example.com/?comment=&amount=1.5&timereceived=2018-01-01+00%3A00%3A00&donor__visibility=FIRST&domain=PAYPAL&id=1&donor__visiblename=%28No+Name%29'
        assert responses.calls[0].response.status_code == 200
    def test_request_made(self):
        responses.add(responses.GET, 'https://example.com', status=200)

        donation = Donation.objects.create(
            timereceived=datetime.datetime(2018, 1, 1),
            comment='',
            amount=Decimal(1.5),
            domain='PAYPAL',
            donor=self.donor,
            event=self.event,
        )

        eventutil.post_donation_to_postbacks(donation)

        assert len(responses.calls) == 1
        assert responses.calls[
            0].request.url == 'https://example.com/?comment=&amount=1.5&timereceived=2018-01-01+00%3A00%3A00&donor__visibility=FIRST&domain=PAYPAL&id=1&donor__visiblename=%28No+Name%29'
        assert responses.calls[0].response.status_code == 200
    def commit_scraptf_donation(self, donation):
        '''
        From a ScrapTF donation, create a Donor and Donation. If
        incentives are present, create all DonationBids and
        BidSuggestions as well. We try very hard to ensure everything
        is valid for insertion. If anything doesn't look right, we
        fail the entire transaction.
        '''

        # Create the Donor
        user = donation['user']
        email = donation.get('email')
        if not email:
            raise Exception('Donation is missing an email.')
        validate_email(email)
        alias = user.get('name', '')[:DONOR_ALIAS_MAX_LENGTH]

        donor = Donor(email=email, alias=alias, visibility='ALIAS')

        # The only unique constraint on Donors is paypalemail, which is
        # not a concern here, so forget about IntegrityError.
        donor.full_clean()
        donor.save()

        # Create the Donation
        domain_id = donation['id']
        amount = Decimal(str(donation['cash_value']))
        time = datetime.fromtimestamp(donation['confirmed_time'], tz=pytz.utc)
        comment = donation.get('message')
        commentstate = 'PENDING' if comment else 'ABSENT'
        steamid = user.get('steamid')

        d = Donation(donor=donor,
                     domain='SCRAPTF',
                     domainId=domain_id,
                     transactionstate='COMPLETED',
                     amount=amount,
                     timereceived=time,
                     currency='USD',
                     comment=comment,
                     commentstate=commentstate,
                     requestedalias=alias,
                     requestedemail=email,
                     steamid=steamid)

        try:
            d.full_clean()
            d.save()
        except IntegrityError:
            raise Exception('Donation already exists')

        # Only proceed if incentives are present
        incentives = donation.get('incentives')
        if not incentives:
            return
        log.info('Donation has incentives, creating them now.',
                 incentives_total=len(incentives))

        # Ensure the incentives don't exceed the total donation amount
        inc_amounts = [Decimal(inc['amount']) for inc in incentives]
        inc_total = sum(inc_amounts)
        if inc_total > amount:
            raise Exception('sum of incentive amounts exceeds donation amount')

        for inc in incentives:
            # First grab the bid to make sure it exists
            bid = Bid.objects.get(pk=inc['incentive'])

            dbid = DonationBid(donation=d,
                               bid=bid,
                               amount=Decimal(inc['amount']))
            dbid.full_clean()
            dbid.save()

            # If the incentive has a custom value, create a BidSuggestion
            suggestion = inc.get('custom')
            if suggestion:
                s = BidSuggestion(bid=bid, name=suggestion)
                s.full_clean()
                s.save()

        eventutil.post_donation_to_postbacks(d)
    def commit_scraptf_donation(self, donation):
        '''
        From a ScrapTF donation, create a Donor and Donation. If
        incentives are present, create all DonationBids and
        BidSuggestions as well. We try very hard to ensure everything
        is valid for insertion. If anything doesn't look right, we
        fail the entire transaction.
        '''

        # Create the Donor
        user = donation['user']
        email = donation.get('email')
        if not email:
            raise Exception('Donation is missing an email.')
        validate_email(email)
        alias = user.get('name', '')[:DONOR_ALIAS_MAX_LENGTH]

        donor = Donor(email=email,
                      alias=alias,
                      visibility='ALIAS')

        # The only unique constraint on Donors is paypalemail, which is
        # not a concern here, so forget about IntegrityError.
        donor.full_clean()
        donor.save()

        # Create the Donation
        domain_id = donation['id']
        amount = Decimal(str(donation['cash_value']))
        time = datetime.fromtimestamp(donation['confirmed_time'], tz=pytz.utc)
        comment = donation.get('message')
        commentstate = 'PENDING' if comment else 'ABSENT'
        steamid = user.get('steamid')

        d = Donation(donor=donor,
                     domain='SCRAPTF',
                     domainId=domain_id,
                     transactionstate='COMPLETED',
                     amount=amount,
                     timereceived=time,
                     currency='USD',
                     comment=comment,
                     commentstate=commentstate,
                     requestedalias=alias,
                     requestedemail=email,
                     steamid=steamid)

        try:
            d.full_clean()
            d.save()
        except IntegrityError:
            raise Exception('Donation already exists')

        # Only proceed if incentives are present
        incentives = donation.get('incentives')
        if not incentives:
            return
        log.info('Donation has incentives, creating them now.',
                 incentives_total=len(incentives))

        # Ensure the incentives don't exceed the total donation amount
        inc_amounts = [Decimal(inc['amount']) for inc in incentives]
        inc_total = sum(inc_amounts)
        if inc_total > amount:
            raise Exception('sum of incentive amounts exceeds donation amount')

        for inc in incentives:
            # First grab the bid to make sure it exists
            bid = Bid.objects.get(pk=inc['incentive'])

            dbid = DonationBid(donation=d,
                               bid=bid,
                               amount=Decimal(inc['amount']))
            dbid.full_clean()
            dbid.save()

            # If the incentive has a custom value, create a BidSuggestion
            suggestion = inc.get('custom')
            if suggestion:
                s = BidSuggestion(bid=bid,
                                  name=suggestion)
                s.full_clean()
                s.save()

        eventutil.post_donation_to_postbacks(d)