def _generate_parameters(self, basket, use_sop_profile, **kwargs): """ Generates the parameters dict. A signature is NOT included in the parameters. Arguments: basket (Basket): Basket from which the pricing and item details are pulled. use_sop_profile (bool, optional): Indicates if the Silent Order POST profile should be used. **kwargs: Additional parameters to add to the generated dict. Returns: dict: Dictionary containing the payment parameters that should be sent to CyberSource. """ site = basket.site access_key = self.access_key profile_id = self.profile_id if use_sop_profile: access_key = self.sop_access_key profile_id = self.sop_profile_id parameters = { 'access_key': access_key, 'profile_id': profile_id, 'transaction_uuid': uuid.uuid4().hex, 'signed_field_names': '', 'unsigned_field_names': '', 'signed_date_time': datetime.datetime.utcnow().strftime(ISO_8601_FORMAT), 'locale': self.language_code, 'transaction_type': 'sale', 'reference_number': basket.order_number, 'amount': str(basket.total_incl_tax), 'currency': basket.currency, 'override_custom_receipt_page': get_receipt_page_url( site_configuration=site.siteconfiguration, order_number=basket.order_number, override_url=site.siteconfiguration.build_ecommerce_url( reverse('cybersource:redirect'))), 'override_custom_cancel_page': self.cancel_page_url, } # Level 2/3 details if self.send_level_2_3_details: parameters['amex_data_taa1'] = site.name parameters['purchasing_level'] = '3' parameters['line_item_count'] = basket.all_lines().count() # Note (CCB): This field (purchase order) is required for Visa; # but, is not actually used by us/exposed on the order form. parameters['user_po'] = 'BLANK' for index, line in enumerate(basket.all_lines()): parameters['item_{}_code'.format( index)] = line.product.get_product_class().slug parameters['item_{}_discount_amount '.format(index)] = str( line.discount_value) # Note (CCB): This indicates that the total_amount field below includes tax. parameters['item_{}_gross_net_indicator'.format(index)] = 'Y' parameters['item_{}_name'.format(index)] = clean_field_value( line.product.title) parameters['item_{}_quantity'.format(index)] = line.quantity parameters['item_{}_sku'.format( index)] = line.stockrecord.partner_sku parameters['item_{}_tax_amount'.format(index)] = str( line.line_tax) parameters['item_{}_tax_rate'.format(index)] = '0' parameters['item_{}_total_amount '.format(index)] = str( line.line_price_incl_tax_incl_discounts) # Note (CCB): Course seat is not a unit of measure. Use item (ITM). parameters['item_{}_unit_of_measure'.format(index)] = 'ITM' parameters['item_{}_unit_price'.format(index)] = str( line.unit_price_incl_tax) # Only send consumer_id for hosted payment page if not use_sop_profile: parameters['consumer_id'] = basket.owner.username # Add the extra parameters parameters.update(kwargs.get('extra_parameters', {})) # Mitigate PCI compliance issues signed_field_names = parameters.keys() if any(pci_field in signed_field_names for pci_field in self.PCI_FIELDS): raise PCIViolation( 'One or more PCI-related fields is contained in the payment parameters. ' 'This service is NOT PCI-compliant! Deactivate this service immediately!' ) return parameters
def _generate_parameters(self, basket, use_sop_profile, **kwargs): """ Generates the parameters dict. A signature is NOT included in the parameters. Arguments: basket (Basket): Basket from which the pricing and item details are pulled. use_sop_profile (bool, optional): Indicates if the Silent Order POST profile should be used. **kwargs: Additional parameters to add to the generated dict. Returns: dict: Dictionary containing the payment parameters that should be sent to CyberSource. """ site = basket.site access_key = self.access_key profile_id = self.profile_id if use_sop_profile: access_key = self.sop_access_key profile_id = self.sop_profile_id parameters = { 'access_key': access_key, 'profile_id': profile_id, 'transaction_uuid': uuid.uuid4().hex, 'signed_field_names': '', 'unsigned_field_names': '', 'signed_date_time': datetime.datetime.utcnow().strftime(ISO_8601_FORMAT), 'locale': self.language_code, 'transaction_type': 'sale', 'reference_number': basket.order_number, 'amount': str(basket.total_incl_tax), 'currency': basket.currency, 'override_custom_receipt_page': get_receipt_page_url( site_configuration=site.siteconfiguration, order_number=basket.order_number, override_url=site.siteconfiguration.build_ecommerce_url( reverse('cybersource:redirect')), disable_back_button=True, ), 'override_custom_cancel_page': self.cancel_page_url, } extra_data = [] # Level 2/3 details if self.send_level_2_3_details: parameters['amex_data_taa1'] = site.name parameters['purchasing_level'] = '3' parameters['line_item_count'] = basket.all_lines().count() # Note (CCB): This field (purchase order) is required for Visa; # but, is not actually used by us/exposed on the order form. parameters['user_po'] = 'BLANK' # Add a parameter specifying the basket's program, None if not present. # This program UUID will *always* be in the merchant_defined_data1, if exists. program_uuid = get_basket_program_uuid(basket) if program_uuid: extra_data.append( "program,{program_uuid}".format(program_uuid=program_uuid)) else: extra_data.append(None) for index, line in enumerate(basket.all_lines()): parameters['item_{}_code'.format( index)] = line.product.get_product_class().slug parameters['item_{}_discount_amount '.format(index)] = str( line.discount_value) # Note (CCB): This indicates that the total_amount field below includes tax. parameters['item_{}_gross_net_indicator'.format(index)] = 'Y' parameters['item_{}_name'.format(index)] = clean_field_value( line.product.title) parameters['item_{}_quantity'.format(index)] = line.quantity parameters['item_{}_sku'.format( index)] = line.stockrecord.partner_sku parameters['item_{}_tax_amount'.format(index)] = str( line.line_tax) parameters['item_{}_tax_rate'.format(index)] = '0' parameters['item_{}_total_amount '.format(index)] = str( line.line_price_incl_tax_incl_discounts) # Note (CCB): Course seat is not a unit of measure. Use item (ITM). parameters['item_{}_unit_of_measure'.format(index)] = 'ITM' parameters['item_{}_unit_price'.format(index)] = str( line.unit_price_incl_tax) # For each basket line having a course product, add course_id and course type # as an extra CSV-formatted parameter sent to Cybersource. # These extra course parameters will be in parameters merchant_defined_data2+. line_course = line.product.course if line_course: extra_data.append( "course,{course_id},{course_type}".format( course_id=line_course.id if line_course else None, course_type=line_course.type if line_course else None)) # Only send consumer_id for hosted payment page if not use_sop_profile: parameters['consumer_id'] = basket.owner.username # Add the extra parameters parameters.update(kwargs.get('extra_parameters', {})) # Mitigate PCI compliance issues signed_field_names = list(parameters.keys()) if any(pci_field in signed_field_names for pci_field in self.PCI_FIELDS): raise PCIViolation( 'One or more PCI-related fields is contained in the payment parameters. ' 'This service is NOT PCI-compliant! Deactivate this service immediately!' ) if extra_data: # CyberSource allows us to send additional data in merchant_defined_data# fields. for num, item in enumerate(extra_data, start=1): if item: key = u"merchant_defined_data{num}".format(num=num) parameters[key] = item return parameters