Exemple #1
0
class Transaction:
    def __init__(self, logging_handler, databasetables):
        self.logging = logging_handler
        self.databasetables = databasetables
        self.response = Response(self.logging, self.databasetables)
        self.frame_length = ""
        self.type_request = ""
        self.protocolversion = "0000"
        self.ParsePos = 0
        self.error = False
        self.operation_code = ''
        self.amount = 0
        self.entrymode = ''
        self.LastNSM = 0
        self.OpNum = 0
        self.Merchant = ''
        self.telephone = ''
        self.topupoperation = ''
        self.giftprodcode = ''
        self.topupanulope = 0
        self.topupamount = '000000000'
        self.track_2 = ''
        self.discount = {}
        pass

    def process(self, ped_request, environment):
        error, request = self.__getheader(ped_request)
        if self.type_request == "PDI":
            # Operación bancaria
            # data_response = self.operation.process(request)
            data_response = self.parsetrame(request)
            data_response['ProtocolVersion'] = self.protocolversion
            op_data, response = self.response.build_response(
                data_response, environment)
        elif self.type_request == "PTD":
            # Operación telecarga
            telecarga = Telecharge(self.logging)
            if re.search(ackTemplate, ped_request, re.DOTALL):
                data_response = {}
                op_data, response = telecarga.build_response(
                    data_response, True, environment)
                response = ''
            elif re.search(nackTemplate, ped_request, re.DOTALL):
                data_response = {}
                op_data, response = telecarga.build_response(
                    data_response, True, environment)
                response = ''
            else:
                data_response = telecarga.process(request)
                data_response['ProtocolVersion'] = self.protocolversion
                if self.__checkTablePInfo(environment):
                    op_data, response = telecarga.build_response(
                        data_response, False, environment)
                else:
                    raise TablePException
        elif self.type_request == "PPL":
            # Operación Polling
            pass
        else:
            self.logging.error("Type of request unknown: {0}".format(
                self.type_request))
            # Operación no conocida.
        return op_data, response

    def __getheader(self, ped_request):
        header = ped_request[:30]
        if header[:4] != "PH24":
            self.logging.error("Error in header {0}".format(header[:4]))
            return False, "Error in header {0}".format(header[:4])
        # Longitud de la trama.
        if not header[4:9].isdigit():
            self.logging.error("Error in header {0}".format(header[4:9]))
            return False, "Error in header {0}".format(header[4:9])
        self.frame_length = int(header[4:9])
        # Identificador de mensaje "PTD" para telecargas.
        if header[9:12] != "PDI" and header[9:12] != "PPL" and header[
                9:12] != "PTD":
            self.logging.error("Error in header {0}".format(header[9:12]))
            return False, "Error in header {0}".format(header[9:12])
        self.type_request = header[9:12]
        # Version del protocolo, 0300 para telecargas.
        if header[12:16] != "0400" and header[12:16] != "0500" and header[12:16] != "0200" and header[12:16] != "0401" \
                and header[12:16] != "0300":
            self.logging.error("Error in header {0}".format(header[12:16]))
            return False, "Error in header {0}".format(header[12:16])
        self.protocolversion = header[12:16]
        # Identificador del mensaje.
        if not header[16:27].isdigit():
            self.logging.error("Error in header {0}".format(header[16:27]))
            return False, "Error in header {0}".format(header[16:27])
        # Error Code, no usado, siempre "000".
        if header[27:] != "000":
            self.logging.error("Error in header {0}".format(header[27:]))
            return False, "Error in header {0}".format(header[27:])
        return True, ped_request[30:]

    def parsetrame(self, ped_request):
        #Reset position
        self.ParsePos = 0
        # Type of Request.
        type_request, b_error = self.__type_request(ped_request)
        if b_error:
            self.logging.error("Error in Operation.")
            self.error = True
        self.Merchant, b_error = self.__business_id(
            ped_request[self.ParsePos:])
        if b_error:
            self.logging.error("Error in Merchant ID.")
            self.error = True
        type_machine, b_error = self.__type_machine(
            ped_request[self.ParsePos:])
        if b_error:
            self.logging.error("Error in Type of machine.")
            self.error = True
        id_terminal, b_error = self.__id_terminal(ped_request[self.ParsePos:])
        if b_error:
            self.logging.error("Error in ID of terminal.")
            self.error = True
        tables_version, b_error = self.__tables_version(
            ped_request[self.ParsePos:])
        if b_error:
            self.logging.error("Error in tables version.")
            self.error = True
        last_NSM, b_error = self.__last_NSM(ped_request[self.ParsePos:])
        if b_error:
            self.logging.error("Error in last NSM.")
            self.error = True
        operation_number, b_error = self.__operation_number(
            ped_request[self.ParsePos:])
        if b_error:
            self.logging.error("Error in Operation number.")
            self.error = True
        offline_transaction, b_error = self.__offline_transaction(
            ped_request[self.ParsePos:])
        if b_error:
            self.logging.error("Error in Offline Transaction.")
            self.error = True
        operation_mode, b_error = self.__operation_mode(
            ped_request[self.ParsePos:])
        if b_error:
            self.logging.error("Error in Operation Mode.")
            self.error = True
        currency, b_error = self.__currency(ped_request[self.ParsePos:])
        if b_error:
            self.logging.error("Error in Currency.")
            self.error = True
        language, b_error = self.__language(ped_request[self.ParsePos:])
        if b_error:
            self.logging.error("Error in Language.")
            self.error = True
        # This fields are not present in the command PAR
        if ped_request[self.ParsePos] is not DC2_DELIMETER:
            type_card, b_error = self.__type_card(ped_request[self.ParsePos:])
            if b_error:
                self.logging.error("Error in Language.")
                self.error = True
            track_1, b_error = self.__track_1(ped_request[self.ParsePos:])
            if b_error:
                self.logging.error("Error in Track 1.")
                self.error = True
            self.track_2, b_error = self.__track_2(ped_request[self.ParsePos:])
            if b_error:
                self.logging.error("Error in Track 2.")
                self.error = True
        else:
            self.ParsePos = self.ParsePos + 1
        extra_data_card, b_error = self.__extra_data_card(
            ped_request[self.ParsePos:])
        if b_error:
            self.logging.error("Error in Extra Data Card.")
            self.error = True
        chip_data, b_error = self.__chip_data(ped_request[self.ParsePos:])
        if b_error:
            self.logging.error("Error in Chip Data.")
            self.error = True
        type_payment, b_error = self.__type_payment(
            ped_request[self.ParsePos:])
        if b_error:
            self.logging.error("Error in Type Payment.")
            self.error = True
        operation_code, b_error = self.__operation_code(
            ped_request[self.ParsePos:])
        if b_error:
            self.logging.error("Error in Operation Code.")
            self.error = True
        number_products, b_error = self.__number_products(
            ped_request[self.ParsePos:])
        if b_error:
            self.logging.error("Error in Number of products.")
            self.error = True
        total_amount, b_error = self.__total_amount(
            ped_request[self.ParsePos:])
        if b_error:
            self.logging.error("Error in Total amount.")
            self.error = True
        euro_litres_point, b_error = self.__euro_l_point(
            ped_request[self.ParsePos:])
        if b_error:
            self.logging.error("Error in Euro Litres/Point.")
            self.error = True
        if operation_code != 'AML':
            self.giftprodcode, b_error = self.__gift_prodcode(
                ped_request[self.ParsePos:])
            if b_error:
                self.logging.error("Error in Gift Product Code.")
                self.error = True
            unit_price, b_error = self.__unit_price(
                ped_request[self.ParsePos:])
            if b_error:
                self.logging.error("Error in Unit Price.")
                self.error = True
            quantity, b_error = self.__quantity_litres(
                ped_request[self.ParsePos:])
            if b_error:
                self.logging.error("Error in Quantity.")
                self.error = True
            amount, b_error = self.__amount(ped_request[self.ParsePos:])
            if b_error:
                self.logging.error("Error in Amount.")
                self.error = True
            self.discount = {
                'Code': self.giftprodcode,
                'UnitPrice': unit_price,
                'Quantity': quantity,
                'Amount': amount
            }
        discount_product, b_error = self.__discount_product(
            ped_request[self.ParsePos:])
        if b_error:
            self.logging.error("Error in Discount product.")
            self.error = True
        extra_data, b_error = self.__extradata(ped_request[self.ParsePos:])
        # type of entry
        if '1' == type_card:
            self.entrymode = 'SWP'
        elif '2' == type_card:
            self.entrymode = chip_data[:3]
        elif '' == type_card and 'MRL' == self.operation_code:
            self.entrymode = 'MRL'
        elif '' == type_card and 'AML' == self.operation_code:
            self.entrymode = 'AML'
        else:
            self.error = True
            self.entrymode = 'ERR'
        mydata = self.__build_data()
        return mydata

    def __type_request(self, ped_request):
        (type_request, position) = self.__get_field(DC1_DELIMETER, ped_request)
        self.logging.info("Type of Request\t{0}".format(type_request))
        if (type_request != "M") and (type_request != "R") and (type_request !=
                                                                "D"):
            self.logging.info("Error in Type of request. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
        return type_request, b_error

    def __business_id(self, ped_request):
        (merchant, position) = self.__get_field(HASH_DELIMETER, ped_request)
        self.logging.info("Business ID\t{0}".format(merchant))
        if (not merchant.isdigit()) or merchant == "0000000":
            self.logging.info("Error in Business. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
        return merchant, b_error

    def __type_machine(self, ped_request):
        (type_machine, position) = self.__get_field(HASH_DELIMETER,
                                                    ped_request)
        self.logging.info("Type of machine\t{0}".format(type_machine))
        if type_machine not in ['3', '8', '6', 'T', 't']:
            self.logging.info("Error in Type of machine. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
        return type_machine, b_error

    def __id_terminal(self, ped_request):
        (id_terminal, position) = self.__get_field(HASH_DELIMETER, ped_request)
        self.logging.info("Id. of terminal\t{0}".format(id_terminal))
        if id_terminal == '':
            self.logging.info("Error in ID Terminal. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
        return id_terminal, b_error

    def __tables_version(self, ped_request):
        (tables_version, position) = self.__get_field(HASH_DELIMETER,
                                                      ped_request)
        self.logging.info("Tables version \t{0}".format(tables_version))
        if tables_version == '':
            self.logging.info("Error in tables version. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
        return tables_version, b_error

    def __last_NSM(self, ped_request):
        (last_NSM, position) = self.__get_field(HASH_DELIMETER, ped_request)
        self.logging.info("Last NSM \t{0}".format(last_NSM))
        if int(last_NSM) <= 0:
            self.logging.info("Error in last NSM. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            self.LastNSM = last_NSM
            b_error = False
        return last_NSM, b_error

    def __operation_number(self, ped_request):
        (operation_number,
         position) = self.__get_field(HASH_DELIMETER, ped_request)
        self.logging.info("operation number \t{0}".format(operation_number))
        if not operation_number.isdigit():
            self.logging.info("Error in operation number. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            self.OpNum = operation_number
            b_error = False
        return operation_number, b_error

    def __offline_transaction(self, ped_request):
        (offline_transaction,
         position) = self.__get_field(HASH_DELIMETER, ped_request)
        self.logging.info(
            "Offline Transaction \t{0}".format(offline_transaction))
        if offline_transaction == '' or len(offline_transaction) != 3:
            self.logging.info("Error in offline transaction. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
        return offline_transaction, b_error

    def __operation_mode(self, ped_request):
        operation_mode_key = {
            '0': 'Autonomous',
            '1': 'Unattended',
            '2': 'Polling Attended',
            '3': 'Polling Unattended'
        }
        (operation_mode, position) = self.__get_field(HASH_DELIMETER,
                                                      ped_request)
        self.logging.info("Operation Mode \t{0} : {1}".format(
            operation_mode, operation_mode_key[operation_mode]))
        if operation_mode == '' or len(
                operation_mode) > 2 or operation_mode not in [
                    '0', '1', '2', '3'
                ]:
            self.logging.info("Error in operation mode. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
        return operation_mode, b_error

    def __currency(self, ped_request):
        currency_key = {'978': 'EUR', '840': 'USD', '826': 'GBP', '756': 'CHF'}
        (currency, position) = self.__get_field(HASH_DELIMETER, ped_request)
        self.logging.info("Currency \t{0} : {1}".format(
            currency, currency_key[currency]))
        if currency not in currency_key:
            self.logging.info("Error in Currency. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
        return currency, b_error

    def __language(self, ped_request):
        language_key = {
            'DA': 'Danish',
            'DE': 'German',
            'EL': 'Greek',
            'EN': 'English',
            'ES': 'Spanish',
            'FI': 'Finnish',
            'FR': 'French',
            'NL': 'Dutch',
            'PT': 'Portuguese',
            'SW': 'Swedish'
        }
        (language, position) = self.__get_field(DC1_DELIMETER, ped_request)
        self.logging.info("Language \t{0} : {1}".format(
            language, language_key[language]))
        if language not in language_key:
            self.logging.info("Error in language. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
        return language, b_error

    def __type_card(self, ped_request):
        type_card_key = {'1': 'Swiped', '2': 'EMV', '': 'REC'}
        (type_card, position) = self.__get_field(HASH_DELIMETER, ped_request)
        self.logging.info("Type card \t{0} : {1}".format(
            type_card, type_card_key[type_card]))
        if type_card not in type_card_key:
            self.logging.info("Error in type card. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
        return type_card, b_error

    def __track_1(self, ped_request):
        (track_1, position) = self.__get_field(HASH_DELIMETER, ped_request)
        self.logging.info("Track 1 \t{0}".format(track_1))
        self.ParsePos = self.ParsePos + position
        b_error = False
        return track_1, b_error

    def __track_2(self, ped_request):
        (track_2, position) = self.__get_field(DC2_DELIMETER, ped_request)
        self.logging.info("Track 1 \t{0}".format(track_2))
        self.ParsePos = self.ParsePos + position
        return track_2, False

    def __extra_data_card(self, ped_request):
        (extra_data_card, position) = self.__get_field(DC3_DELIMETER,
                                                       ped_request)
        self.logging.info("Extra Data Card \t{0}".format(extra_data_card))
        self.ParsePos = self.ParsePos + position
        b_error = False
        return extra_data_card, b_error

    def __chip_data(self, ped_request):
        (chip_data, position) = self.__get_field(DC1_DELIMETER, ped_request)
        self.logging.info("Chip Data \t{0}".format(chip_data))
        self.ParsePos = self.ParsePos + position
        b_error = False
        return chip_data, b_error

    def __type_payment(self, ped_request):
        (type_payment, position) = self.__get_field(HASH_DELIMETER,
                                                    ped_request)
        self.logging.info("Type Payment \t{0}".format(type_payment))
        if (type_payment and type_payment != '1') and (type_payment and
                                                       type_payment != '2'):
            self.logging.info("Error in type Payment. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
        return type_payment, b_error

    def __operation_code(self, ped_request):
        operation_code_key = {
            'V ': 'Venta',
            'A ': "Anulacion Generica",
            'CP': 'Consulta Puntos',
            'AV': 'Anulacion Venta',
            'BF': 'Bonus sale',
            'AF': 'Bonus redemption',
            'RF': 'Bonus balance sale',
            'BA': 'FG points accumulation',
            'B ': 'Bonus Credit',
            'CF': 'Bonus query - CEPSA',
            'PAP': 'Unattended Preauthorization',
            'PAC': 'Preauthorization confirmation',
            'PAN': 'Preauthorization cancellation',
            'AT': 'Partial Refund operation',
            'APP': 'Explicit transaction reversal',
            'AP': 'Automatic cancellation',
            'VY': 'DCC query',
            'MRL': 'TopUps',
            'AML': 'Anulacion TopUps'
        }
        (operation_code, position) = self.__get_field(HASH_DELIMETER,
                                                      ped_request)
        self.logging.info("Operation Code \t{0} : {1}".format(
            operation_code, operation_code_key[operation_code]))
        if (operation_code
                not in operation_code_key) and len(operation_code) != 3:
            self.logging.info("Error in operation code. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
            self.operation_code = operation_code
        return operation_code, b_error

    def __number_products(self, ped_request):
        (number_products, position) = self.__get_field(HASH_DELIMETER,
                                                       ped_request)
        self.logging.info("Number of products \t{0}".format(number_products))
        if (number_products.isdigit()) and (len(number_products) != 2):
            self.logging.info("Error in Number of products. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
        return number_products, b_error

    def __total_amount(self, ped_request):
        (total_amount, position) = self.__get_field(HASH_DELIMETER,
                                                    ped_request)
        self.logging.info("Total amount \t{0}".format(total_amount))
        if not total_amount:
            self.ParsePos = self.ParsePos + position
            b_error = False
            self.amount = 0
        elif (total_amount.isdigit()) and (len(total_amount) < 2):
            self.logging.info("Error in total amount. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
            self.amount = float(total_amount) / 100
        return total_amount, b_error

    def __euro_l_point(self, ped_request):
        (euro_l_point, position) = self.__get_field(DC2_DELIMETER, ped_request)
        self.logging.info("Euro Litres/Point \t{0}".format(euro_l_point))
        if euro_l_point and ((euro_l_point.isdigit()) or
                             (int(euro_l_point) < 0)):
            self.logging.info("Error in Euro Litres/Point. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
        return euro_l_point, b_error

    def __gift_prodcode(self, ped_request):
        (gift_prodcode, position) = self.__get_field(HASH_DELIMETER,
                                                     ped_request)
        self.logging.info("Gift Product Code \t{0}".format(gift_prodcode))
        if gift_prodcode and (
            (gift_prodcode.isdigit()) or
            (int(gift_prodcode) < 0)) and (len(gift_prodcode) > 6):
            self.logging.info("Error in Gift Product Code. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
        return gift_prodcode, b_error

    def __unit_price(self, ped_request):
        (unit_price, position) = self.__get_field(HASH_DELIMETER, ped_request)
        self.logging.info("Unit Price \t{0}".format(unit_price))
        if unit_price and (not unit_price.isdigit()):
            self.logging.info("Error in Unit Price. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
        return unit_price, b_error

    def __quantity_litres(self, ped_request):
        (quantity_litres, position) = self.__get_field(HASH_DELIMETER,
                                                       ped_request)
        self.logging.info("Quantity Litres \t{0}".format(quantity_litres))
        if quantity_litres and (not quantity_litres.isdigit() or
                                (int(quantity_litres) < 0)):
            self.logging.info("Error in Quantity Litres. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
        return quantity_litres, b_error

    def __amount(self, ped_request):
        (amount, position) = self.__get_field(HASH_DELIMETER, ped_request)
        self.logging.info("Amount \t{0}".format(amount))
        self.ParsePos = self.ParsePos + position
        b_error = False
        return amount, b_error

    def __discount_product(self, ped_request):
        (discount_product,
         position) = self.__get_field(DC1_DELIMETER, ped_request)
        self.logging.info("Discount Product \t{0}".format(discount_product))
        if discount_product and (not discount_product.isdigit() or
                                 (int(discount_product) < 0)):
            self.logging.info("Error in Discount Product. Bad format.")
            b_error = True
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
        return discount_product, b_error

    def __extradata(self, ped_request):
        (extra_data, position) = self.__get_field(DC2_DELIMETER, ped_request)
        self.logging.info("Extra data \t{0}".format(extra_data))
        if not extra_data:
            self.logging.info("No Extra data.")
            self.ParsePos = self.ParsePos + position
            b_error = False
        else:
            self.ParsePos = self.ParsePos + position
            b_error = False
            dataextra = extra_data.split('/')
            for value in dataextra:
                if not value:
                    pass
                elif value[0] == 'T':
                    self.telephone = value[1:]
                elif value[0] == 'X':
                    self.topupoperation = value[1:]
                elif value[0] == 'A':
                    self.topupanulope = value[1:]
                elif value[0] == 'K':
                    self.giftprodcode = value[1:]
                elif value[0] == 'P':
                    self.topupamount = value[1:]
        return extra_data, b_error

    @staticmethod
    def __get_field(delimeter, string):
        get_field = ""
        position = 0
        for i in string:
            position = position + 1
            if i == delimeter:
                return get_field, position
            else:
                get_field = get_field + i

    def __checkTablePInfo(self, environment):
        noerror = False
        if 'TablePinfo' in environment:
            if environment['TablePinfo'].keys() >= {
                    'swversion', 'ftpuser', 'passftp'
            }:
                if environment['TablePinfo']['swversion'] and environment['TablePinfo']['ftpuser'] \
                        and environment['TablePinfo']['passftp']:
                    noerror = True
        return noerror

    def __build_data(self):
        if 'MRL' == self.entrymode or 'AML' == self.entrymode:
            data = {
                'Error': self.error,
                'Amount': self.amount,
                'EntryMode': self.entrymode,
                'OpCode': self.operation_code,
                'lastNSM': self.LastNSM,
                'OpNum': self.OpNum,
                'MerchantID': self.Merchant,
                'track2': self.track_2,
                'TopUp': self.giftprodcode,
                'Telephone': self.telephone,
                'TopUpOps': self.topupoperation,
                'TopUpOpsAmount': self.topupamount,
                'TopUpOpsAnulaOP': self.topupanulope
            }
        else:
            data = {
                'Error': self.error,
                'Amount': self.amount,
                'EntryMode': self.entrymode,
                'OpCode': self.operation_code,
                'lastNSM': self.LastNSM,
                'OpNum': self.OpNum,
                'MerchantID': self.Merchant,
                'track2': self.track_2,
                'Discount': self.discount
            }
        return data