<commit_before>import urllib
import urllib2
import urlparse
import xml.etree.cElementTree as ET

from datetime import datetime, tzinfo
from decimal import Decimal
from datetime import datetime, timedelta
from django.contrib.sites.models import Site
from django.core import urlresolvers
from django.utils.datastructures import SortedDict
from urllib2 import HTTPError

from hiicart.gateway.amazon import fps
from hiicart.gateway.amazon.errors import AmazonGatewayError
from hiicart.gateway.amazon.settings import default_settings
from hiicart.gateway.base import IPNBase
from hiicart.models import HiiCart, Payment

_FPS_NS = "{http://fps.amazonaws.com/doc/2008-09-17/}"

class AmazonIPN(IPNBase):
    """Payment Gateway for Amazon Payments."""

    def __init__(self):
        super(AmazonIPN, self).__init__("amazon", default_settings)

    def _process_error(self, response):
        """Process error xml into (code, message, requestId)."""
        xml = ET.XML(response)
        code = xml.find(".//Code")
        message = xml.find(".//Message")
        request_id = xml.find(".//RequestID")
        if code is not None and message is not None and request_id is not None:
            return (code.text, message.text, request_id.text)
        return None

    def accept_payment(self, cart, data):
        """Record payment received from IPN."""
        self._update_with_cart_settings(cart)
        total = None
        if "transactionAmount" in data and data["transactionAmount"].startswith("USD "):
            total = Decimal(data["transactionAmount"][4:])
        if data["transactionStatus"] == "PENDING":
            transaction_id = data["transactionId"]
            pending = cart.payments.filter(transaction_id=transaction_id)
            if not pending: # Could already be created in make_pay_request
                self._create_payment(cart, total, transaction_id, "PENDING")
        elif data["transactionStatus"] == "SUCCESS":
            # Was this a pending payment?
            transaction_id = data["transactionId"]
            pending = cart.payments.filter(transaction_id=transaction_id, state="PENDING")
            if pending:
                pending[0].state = "PAID"
                pending[0].save()
            elif cart.payments.filter(transaction_id=transaction_id).count() == 0: # No duplicate payments
                self._create_payment(cart, total, transaction_id, "PAID")
            cart.update_state()
            self.begin_recurring(cart)
        elif data["transactionStatus"] == "CANCELLED":
            message = "Purchase %i (txn:%s) was cancelled with message '%s'" % ( 
                      cart.id, data['transactionId'], data['statusMessage'])
            self.log.warn(message)
            transaction_id = data["transactionId"]
            cancelled = cart.payments.filter(transaction_id=transaction_id)
            for p in cancelled:
                p.state = "CANCELLED"
                p.notes.create(text=message)
                p.save()
            cart.update_state()
        elif data["transactionStatus"] == "FAILURE":
            self.log.warn("Purchase %i (txn:%s) failed with message '%s'" % (
                cart.id, data['transactionId'], data['statusMessage']))
            cart.update_state()

    def begin_recurring(self, cart):
        """Save token and mark recurring item as active."""
        self._update_with_cart_settings(cart)
        if cart.recurringlineitems.count() == 0:
            return
        for ri in cart.recurringlineitems.all():
            ri.is_active = True
            ri.save()
        cart.update_state()

    def end_recurring(self, cart, token):
        """Mark a recurring item as inactive."""
        self._update_with_cart_settings(cart)
        if cart.recurringlineitems.count() == 0:
            return
        for ri in cart.recurringlineitems.all():
            ri.is_active = False 
            ri.save()
        cart.update_state()

    def make_pay_request(self, cart, token, caller_reference=None):
        """
        Make a Pay request for the purchase with the given token. 
        Return status received from Amazon.
        """
        self._update_with_cart_settings(cart)
        if caller_reference is None:
            caller_reference = cart.cart_uuid
        response = fps.do_fps("Pay", "GET", self.settings, 
                   **{"CallerReference" : caller_reference,
                      "SenderTokenId" : token, 
                      "TransactionAmount.CurrencyCode" : "USD",
                      "TransactionAmount.Value" : cart.total})
        xml = ET.XML(response)
        status = xml.find(".//%sTransactionStatus" % _FPS_NS)
        if status is None:
            error = self._process_error(response)
            if not error:
                msg = "Pay request failed for purchase '%s' with response: %s" % (
                            cart, response) 
            else:
                msg = "Pay request failed for purchase '%s' with code/message: '%s' '%s' (request id: '%s')" % (
                            cart, error[0], error[1], error[2])
            self.log.warn(msg)
            cart.notes.create(text=msg)
            # An error code, maybe?
            code = xml.find(".//Code")
            if code is not None:
                return code.text
            return None
        else:
            # This work for pending payments?
            transaction_id = xml.find(".//%sTransactionId" % _FPS_NS).text
            if status.text == "Pending":
                self._create_payment(cart, cart.total, transaction_id, "PENDING")
            elif status.text == "Success":
                pending = cart.payments.filter(transaction_id=transaction_id,
                                               state="PENDING")
                if pending:
                    pending[0].state = "PAID"
                    pending[0].save()
                else:
                    self._create_payment(cart, cart.total, transaction_id, "PAID")
                self.begin_recurring(cart)
            else: # Cancelled or Failure
                msg = "Pay request for purchase '%s' returned status '%s'. \nFull response: %s" % (
                            cart, response)
                self.log.warn(msg)
                cart.notes.create(text=msg)
            cart.update_state()
            return status.text

    def save_recurring_token(self, cart, token):
        """Save recurring use token for future use."""
        self._update_with_cart_settings(cart)
        if cart.recurringlineitems.count() == 0:
            return
        ri = cart.recurringlineitems.all()[0]
        ri.payment_token = token
        ri.save()

    def verify_signature(self, raw_data, http_method, endpoint_uri, cart):
        self._update_with_cart_settings(cart)
        response = fps.do_fps("VerifySignature", http_method, self.settings,
                              UrlEndPoint=endpoint_uri,
                              HttpParameters=raw_data)
        xml = ET.XML(response)
        el = xml.find(".//{http://fps.amazonaws.com/doc/2008-09-17/}VerificationStatus")
        return el is not None and el.text == "Success"
<commit_msg>fix issue with amazon IPN where in a failed transaction state it tries to access a status message that may not be there<commit_after>import urllib
import urllib2
import urlparse
import xml.etree.cElementTree as ET

from datetime import datetime, tzinfo
from decimal import Decimal
from datetime import datetime, timedelta
from django.contrib.sites.models import Site
from django.core import urlresolvers
from django.utils.datastructures import SortedDict
from urllib2 import HTTPError

from hiicart.gateway.amazon import fps
from hiicart.gateway.amazon.errors import AmazonGatewayError
from hiicart.gateway.amazon.settings import default_settings
from hiicart.gateway.base import IPNBase
from hiicart.models import HiiCart, Payment

_FPS_NS = "{http://fps.amazonaws.com/doc/2008-09-17/}"

class AmazonIPN(IPNBase):
    """Payment Gateway for Amazon Payments."""

    def __init__(self):
        super(AmazonIPN, self).__init__("amazon", default_settings)

    def _process_error(self, response):
        """Process error xml into (code, message, requestId)."""
        xml = ET.XML(response)
        code = xml.find(".//Code")
        message = xml.find(".//Message")
        request_id = xml.find(".//RequestID")
        if code is not None and message is not None and request_id is not None:
            return (code.text, message.text, request_id.text)
        return None

    def accept_payment(self, cart, data):
        """Record payment received from IPN."""
        self._update_with_cart_settings(cart)
        total = None
        if "transactionAmount" in data and data["transactionAmount"].startswith("USD "):
            total = Decimal(data["transactionAmount"][4:])
        if data["transactionStatus"] == "PENDING":
            transaction_id = data["transactionId"]
            pending = cart.payments.filter(transaction_id=transaction_id)
            if not pending: # Could already be created in make_pay_request
                self._create_payment(cart, total, transaction_id, "PENDING")
        elif data["transactionStatus"] == "SUCCESS":
            # Was this a pending payment?
            transaction_id = data["transactionId"]
            pending = cart.payments.filter(transaction_id=transaction_id, state="PENDING")
            if pending:
                pending[0].state = "PAID"
                pending[0].save()
            elif cart.payments.filter(transaction_id=transaction_id).count() == 0: # No duplicate payments
                self._create_payment(cart, total, transaction_id, "PAID")
            cart.update_state()
            self.begin_recurring(cart)
        elif data["transactionStatus"] == "CANCELLED":
            message = "Purchase %i (txn:%s) was cancelled with message '%s'" % ( 
                      cart.id, data['transactionId'], data['statusMessage'])
            self.log.warn(message)
            transaction_id = data["transactionId"]
            cancelled = cart.payments.filter(transaction_id=transaction_id)
            for p in cancelled:
                p.state = "CANCELLED"
                p.notes.create(text=message)
                p.save()
            cart.update_state()
        elif data["transactionStatus"] == "FAILURE":
            self.log.warn("Purchase %i (txn:%s) failed with message '%s'" % (
                cart.id, data['transactionId'], data.get('statusMessage', '(no statusMessage)')))
            cart.update_state()

    def begin_recurring(self, cart):
        """Save token and mark recurring item as active."""
        self._update_with_cart_settings(cart)
        if cart.recurringlineitems.count() == 0:
            return
        for ri in cart.recurringlineitems.all():
            ri.is_active = True
            ri.save()
        cart.update_state()

    def end_recurring(self, cart, token):
        """Mark a recurring item as inactive."""
        self._update_with_cart_settings(cart)
        if cart.recurringlineitems.count() == 0:
            return
        for ri in cart.recurringlineitems.all():
            ri.is_active = False 
            ri.save()
        cart.update_state()

    def make_pay_request(self, cart, token, caller_reference=None):
        """
        Make a Pay request for the purchase with the given token. 
        Return status received from Amazon.
        """
        self._update_with_cart_settings(cart)
        if caller_reference is None:
            caller_reference = cart.cart_uuid
        response = fps.do_fps("Pay", "GET", self.settings, 
                   **{"CallerReference" : caller_reference,
                      "SenderTokenId" : token, 
                      "TransactionAmount.CurrencyCode" : "USD",
                      "TransactionAmount.Value" : cart.total})
        xml = ET.XML(response)
        status = xml.find(".//%sTransactionStatus" % _FPS_NS)
        if status is None:
            error = self._process_error(response)
            if not error:
                msg = "Pay request failed for purchase '%s' with response: %s" % (
                            cart, response) 
            else:
                msg = "Pay request failed for purchase '%s' with code/message: '%s' '%s' (request id: '%s')" % (
                            cart, error[0], error[1], error[2])
            self.log.warn(msg)
            cart.notes.create(text=msg)
            # An error code, maybe?
            code = xml.find(".//Code")
            if code is not None:
                return code.text
            return None
        else:
            # This work for pending payments?
            transaction_id = xml.find(".//%sTransactionId" % _FPS_NS).text
            if status.text == "Pending":
                self._create_payment(cart, cart.total, transaction_id, "PENDING")
            elif status.text == "Success":
                pending = cart.payments.filter(transaction_id=transaction_id,
                                               state="PENDING")
                if pending:
                    pending[0].state = "PAID"
                    pending[0].save()
                else:
                    self._create_payment(cart, cart.total, transaction_id, "PAID")
                self.begin_recurring(cart)
            else: # Cancelled or Failure
                msg = "Pay request for purchase '%s' returned status '%s'. \nFull response: %s" % (
                            cart, response)
                self.log.warn(msg)
                cart.notes.create(text=msg)
            cart.update_state()
            return status.text

    def save_recurring_token(self, cart, token):
        """Save recurring use token for future use."""
        self._update_with_cart_settings(cart)
        if cart.recurringlineitems.count() == 0:
            return
        ri = cart.recurringlineitems.all()[0]
        ri.payment_token = token
        ri.save()

    def verify_signature(self, raw_data, http_method, endpoint_uri, cart):
        self._update_with_cart_settings(cart)
        response = fps.do_fps("VerifySignature", http_method, self.settings,
                              UrlEndPoint=endpoint_uri,
                              HttpParameters=raw_data)
        xml = ET.XML(response)
        el = xml.find(".//{http://fps.amazonaws.com/doc/2008-09-17/}VerificationStatus")
        return el is not None and el.text == "Success"

{'fraction_non_alphanumeric': 0.10513954130975407, 'fraction_numerical': 0.00428295109146173, 'mean_word_length': 2.0671610169491523, 'pattern_counts': {'":': 12, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 24}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4413893', 'n_tokens_mistral': 3966, 'n_tokens_neox': 3861, 'n_words': 980}