Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Last revisionBoth sides next revision
integration:python [2017/06/01 04:31] – [Code] marijaintegration:python [2017/06/01 04:36] marija
Line 3: Line 3:
 systems       : Python # the system(s) that the integration is for, separated with commas if more than one systems       : Python # the system(s) that the integration is for, separated with commas if more than one
 name          : Python Datafeed Classes # the name of the integration name          : Python Datafeed Classes # the name of the integration
-description   : Python classes to decrypt and parse an XML datafeed.+description   : Python classes to decrypt and parse an XML datafeed. Includes examples for Python 2 and Python 3.
 tags          : python      # tags, separated by commas. don't include the "system" here. tags          : python      # tags, separated by commas. don't include the "system" here.
 date_dt       : 2008-02-13 # the date in YYYY-MM-DD format date_dt       : 2008-02-13 # the date in YYYY-MM-DD format
Line 359: Line 359:
 data = FoxyData.from_crypted_str(urllib.unquote_plus(request.POST['FoxyData']), settings.FOXYCART_DATAFEED_KEY) data = FoxyData.from_crypted_str(urllib.unquote_plus(request.POST['FoxyData']), settings.FOXYCART_DATAFEED_KEY)
 </blockquote> </blockquote>
 +
 +===== Python 3 Example Code =====
 +view.py
 +<code python>
 +"""
 +Example Django view for receiving and decrypting datafeed.
 +"""
 +## My View, I have a session variable I submit to foxy cart, that's my booking_id.
 +
 +@csrf_exempt
 +def FoxyCartIPNView(request):
 +    if request.POST and 'FoxyData' in request.POST:
 +        try:
 +            # IMPORTANT: unquote_plus is necessary for the non-ASCII binary that
 +            # FoxyCart sends.
 +            # MKR als important to set the encoding when using python3!
 +
 +            data = FoxyData.from_crypted_str((unquote_plus(request.POST['FoxyData'], encoding='latin-1')),
 +                                             FOXYCART_DATAFEED_KEY)
 +
 +            for transaction in data.transactions:
 +
 +                # get the booking id, and verify it so BMCM can see it in admin
 +                # also save foxy's xml for archive purposes, finally, we mail the customer.
 +                booking = transaction.booking_id  # a custom field from foxycart
 +                booking = RetreatBooking.objects.get(pk=booking)
 +                booking.verified_transaction = True
 +                booking.foxycart_xml = data.markup
 +                booking.save()
 +
 +                mail_customer(request, booking, transaction)
 +                print("transaction: {}, mail customer {}\n".format(transaction.id, transaction.customer_email) )
 +
 +            return HttpResponse('foxy')
 +
 +
 +        except:
 +            # Something went wrong, handle the error... right? :P
 +            raise
 +
 +    return HttpResponseForbidden('Unauthorized request.' # No FoxyData?  Not a POST?  We don't speak that.
 +
 +
 +## my utils for the view - Python 3 compatible!
 +
 +class ARC4:
 +    def __init__(self, key = None):
 +        self.state = list(range(256)) # Initialize state array with values 0 .. 255
 +        self.x = self.y = 0 # Our indexes. x, y instead of i, j
 +
 +        if key is not None:
 +            self.init(key)
 +
 +    # KSA
 +    def init(self, key):
 +        for i in list(range(256)):
 +            self.x = (ord(key[i % len(key)]) + self.state[i] + self.x) & 0xFF
 +            self.state[i], self.state[self.x] = self.state[self.x], self.state[i]
 +        self.x = 0
 +
 +    # PRGA
 +    def crypt(self, input):
 +        output = [None]*len(input)
 +        for i in range(len(input)):
 +            self.x = (self.x + 1) & 0xFF
 +            self.y = (self.state[self.x] + self.y) & 0xFF
 +            self.state[self.x], self.state[self.y] = self.state[self.y], self.state[self.x]
 +            r = self.state[(self.state[self.x] + self.state[self.y]) & 0xFF]
 +            output[i] = chr(ord(input[i]) ^ r)
 +        return ''.join(output)
 +
 +    @classmethod
 +    def from_crypted_str(self, data_str, crypt_key):
 +        a = ARC4(crypt_key)
 +        return FoxyData.from_str(a.crypt(data_str))
 +
 +
 +class FoxyData:
 +    DateFmt = '%Y-%m-%d'
 +    DateTimeFmt = '%Y-%m-%d %H:%M:%S'
 +
 +    class Transaction:
 +        def __init__(self, node):
 +            def extract_kv_node(node, key_name):
 +                # print('getting node {}\n{}'.format(key_name, node))
 +                el = node.getElementsByTagName(key_name)
 +                return len(el) > 0 and el[0].firstChild.data or ''
 +
 +            self.id = extract_kv_node(node, 'id')
 +            self.date = datetime.strptime(
 +                extract_kv_node(node, 'transaction_date'), FoxyData.DateTimeFmt)
 +            self.customer_id = extract_kv_node(node, 'customer_id')
 +            self.customer_email = extract_kv_node(node, 'customer_email')
 +
 +            self.attributes = attrs = {}
 +            self.items = items = attrs['items'] = []
 +
 +            self.custom_fields = attrs['custom_fields'] = {}
 +            for custom_field in node.getElementsByTagName('custom_field'):
 +                self.custom_fields[extract_kv_node(custom_field, 'custom_field_name')] = \
 +                    extract_kv_node(custom_field, 'custom_field_value')
 +
 +            # import pdb;pdb.set_trace()
 +            self.booking_id = self.custom_fields.get('booking_id')
 +
 +            self.transaction_details = attrs['detail'] = []
 +
 +
 +    def __init__(self, markup):
 +        self.markup = markup
 +        self.doc = parseString(self.markup)
 +        self.transactions = []
 +
 +        for transaction in self.doc.getElementsByTagName('transaction'):
 +            self.transactions.append(FoxyData.Transaction(transaction))
 +
 +
 +    def __str__(self):
 +        return str(self.markup)
 +
 +
 +    @classmethod
 +    def from_str(self, data_str):
 +        return FoxyData(data_str)
 +
 +
 +    """
 +    Given a string containing RC4-crypted FoxyCart datafeed XML and the
 +    cryptographic key, decrypt the contents and create a FoxyData object
 +    containing all of the Transactions in the data feed.
 +    """
 +
 +    @classmethod
 +    def from_crypted_str(self, data_str, crypt_key):
 +        a = ARC4(crypt_key)
 +        return FoxyData.from_str(a.crypt(data_str))
 +
 +
 +    def __len__(self):
 +        return len(self.transactions)</code>
 +

Site Tools