defmodule DaProductApp.MercuryISO8583.Config do @moduledoc """ Configuration management for Mercury ISO8583 processing system. Provides centralized configuration for all components including field packagers, message processors, validation rules, and routing. """ @doc """ Gets the ISO8583 field packager configuration. """ def get_packager_config do Application.get_env(:da_product_app, :mercury_iso8583_packager, %{ # Field packager configurations field_packagers: %{ # Numeric fields 2 => %{type: :variable, length: 19, prefixer: :ll, interpreter: :ascii}, # PAN 3 => %{type: :fixed, length: 6, interpreter: :ascii}, # Processing Code 4 => %{type: :fixed, length: 12, interpreter: :ascii}, # Amount, Transaction 5 => %{type: :fixed, length: 12, interpreter: :ascii}, # Amount, Settlement 6 => %{type: :fixed, length: 12, interpreter: :ascii}, # Amount, Cardholder Billing 7 => %{type: :fixed, length: 10, interpreter: :ascii}, # Transmission Date & Time 8 => %{type: :fixed, length: 8, interpreter: :ascii}, # Amount, Cardholder Billing Fee 9 => %{type: :fixed, length: 8, interpreter: :ascii}, # Conversion Rate, Settlement 10 => %{type: :fixed, length: 8, interpreter: :ascii}, # Conversion Rate, Cardholder Billing 11 => %{type: :fixed, length: 6, interpreter: :ascii}, # STAN 12 => %{type: :fixed, length: 6, interpreter: :ascii}, # Local Transaction Time 13 => %{type: :fixed, length: 4, interpreter: :ascii}, # Local Transaction Date 14 => %{type: :fixed, length: 4, interpreter: :ascii}, # Expiration Date 15 => %{type: :fixed, length: 4, interpreter: :ascii}, # Settlement Date 16 => %{type: :fixed, length: 4, interpreter: :ascii}, # Currency Conversion Date 17 => %{type: :fixed, length: 4, interpreter: :ascii}, # Capture Date 18 => %{type: :fixed, length: 4, interpreter: :ascii}, # Merchant Type 19 => %{type: :fixed, length: 3, interpreter: :ascii}, # Acquiring Institution Country Code 20 => %{type: :fixed, length: 3, interpreter: :ascii}, # PAN Extended Country Code 21 => %{type: :fixed, length: 3, interpreter: :ascii}, # Forwarding Institution Country Code 22 => %{type: :fixed, length: 3, interpreter: :ascii}, # Point of Service Entry Mode 23 => %{type: :fixed, length: 3, interpreter: :ascii}, # Application PAN Sequence Number 24 => %{type: :fixed, length: 3, interpreter: :ascii}, # Network International Identifier 25 => %{type: :fixed, length: 2, interpreter: :ascii}, # Point of Service Condition Code 26 => %{type: :fixed, length: 2, interpreter: :ascii}, # Point of Service Capture Code 27 => %{type: :fixed, length: 1, interpreter: :ascii}, # Authorization Identification Response Length 28 => %{type: :fixed, length: 8, interpreter: :ascii}, # Amount, Transaction Fee 29 => %{type: :fixed, length: 8, interpreter: :ascii}, # Amount, Settlement Fee 30 => %{type: :fixed, length: 8, interpreter: :ascii}, # Amount, Transaction Processing Fee 31 => %{type: :fixed, length: 8, interpreter: :ascii}, # Amount, Settlement Processing Fee 32 => %{type: :variable, length: 11, prefixer: :ll, interpreter: :ascii}, # Acquiring Institution Identification Code 33 => %{type: :variable, length: 11, prefixer: :ll, interpreter: :ascii}, # Forwarding Institution Identification Code 34 => %{type: :variable, length: 28, prefixer: :ll, interpreter: :ascii}, # Primary Account Number, Extended 35 => %{type: :variable, length: 37, prefixer: :ll, interpreter: :ascii}, # Track 2 Data 36 => %{type: :variable, length: 104, prefixer: :lll, interpreter: :ascii}, # Track 3 Data 37 => %{type: :fixed, length: 12, interpreter: :ascii}, # Retrieval Reference Number 38 => %{type: :fixed, length: 6, interpreter: :ascii}, # Authorization Identification Response 39 => %{type: :fixed, length: 2, interpreter: :ascii}, # Response Code 40 => %{type: :fixed, length: 3, interpreter: :ascii}, # Service Restriction Code 41 => %{type: :fixed, length: 8, interpreter: :ascii}, # Card Acceptor Terminal Identification 42 => %{type: :fixed, length: 15, interpreter: :ascii}, # Card Acceptor Identification Code 43 => %{type: :fixed, length: 40, interpreter: :ascii}, # Card Acceptor Name/Location 44 => %{type: :variable, length: 25, prefixer: :ll, interpreter: :ascii}, # Additional Response Data 45 => %{type: :variable, length: 76, prefixer: :ll, interpreter: :ascii}, # Track 1 Data 46 => %{type: :variable, length: 999, prefixer: :lll, interpreter: :ascii}, # Additional Data - ISO 47 => %{type: :variable, length: 999, prefixer: :lll, interpreter: :ascii}, # Additional Data - National 48 => %{type: :variable, length: 999, prefixer: :lll, interpreter: :ascii}, # Additional Data - Private 49 => %{type: :fixed, length: 3, interpreter: :ascii}, # Currency Code, Transaction 50 => %{type: :fixed, length: 3, interpreter: :ascii}, # Currency Code, Settlement 51 => %{type: :fixed, length: 3, interpreter: :ascii}, # Currency Code, Cardholder Billing 52 => %{type: :fixed, length: 16, interpreter: :binary}, # Personal Identification Number Data 53 => %{type: :fixed, length: 16, interpreter: :ascii}, # Security Related Control Information 54 => %{type: :variable, length: 120, prefixer: :lll, interpreter: :ascii}, # Additional Amounts 55 => %{type: :variable, length: 999, prefixer: :lll, interpreter: :binary}, # ICC Data – EMV having multiple tags 56 => %{type: :variable, length: 999, prefixer: :lll, interpreter: :ascii}, # Reserved ISO 57 => %{type: :variable, length: 999, prefixer: :lll, interpreter: :ascii}, # Reserved National 58 => %{type: :variable, length: 999, prefixer: :lll, interpreter: :ascii}, # Reserved National 59 => %{type: :variable, length: 999, prefixer: :lll, interpreter: :ascii}, # Reserved National 60 => %{type: :variable, length: 999, prefixer: :lll, interpreter: :ascii}, # Reserved Private 61 => %{type: :variable, length: 999, prefixer: :lll, interpreter: :ascii}, # Reserved Private 62 => %{type: :variable, length: 999, prefixer: :lll, interpreter: :ascii}, # Reserved Private 63 => %{type: :variable, length: 999, prefixer: :lll, interpreter: :ascii}, # Reserved Private 64 => %{type: :fixed, length: 16, interpreter: :binary}, # Message Authentication Code # Fields 65-128 follow similar patterns 123 => %{type: :variable, length: 999, prefixer: :lll, interpreter: :ascii}, # POS Data Code 124 => %{type: :variable, length: 999, prefixer: :lll, interpreter: :ascii}, # Additional Transaction Data 125 => %{type: :variable, length: 999, prefixer: :lll, interpreter: :ascii}, # Transaction Specific Data 126 => %{type: :variable, length: 999, prefixer: :lll, interpreter: :ascii}, # Authorization Agent Institution 127 => %{type: :variable, length: 999, prefixer: :lll, interpreter: :ascii} # Reserved for Private Use }, # Interpreter configurations interpreters: %{ ascii: DaProductApp.MercuryISO8583.Packagers.Interpreters.AsciiInterpreter, binary: DaProductApp.MercuryISO8583.Packagers.Interpreters.BinaryInterpreter, bcd: DaProductApp.MercuryISO8583.Packagers.Interpreters.BCDInterpreter, ebcdic: DaProductApp.MercuryISO8583.Packagers.Interpreters.EbcdicInterpreter }, # Prefixer configurations prefixers: %{ ll: DaProductApp.MercuryISO8583.Packagers.Prefixers.AsciiPrefixer, lll: DaProductApp.MercuryISO8583.Packagers.Prefixers.AsciiPrefixer, binary_ll: DaProductApp.MercuryISO8583.Packagers.Prefixers.BinaryPrefixer, bcd_ll: DaProductApp.MercuryISO8583.Packagers.Prefixers.BcdPrefixer } }) end @doc """ Gets the message processing configuration. """ def get_processor_config do Application.get_env(:da_product_app, :mercury_iso8583_processor, %{ # Processing pipeline configuration pipeline: %{ validate_input: true, enrich_message: true, validate_business_rules: true, transform_message: true, validate_output: true, timeout_ms: 30_000 }, # Message enrichment configuration enrichment: %{ add_timestamp: true, add_trace_id: true, add_processing_info: true, lookup_merchant_info: true, lookup_terminal_info: true }, # Batch processing configuration batch: %{ max_batch_size: 1000, parallel_processing: true, max_concurrency: 50, timeout_per_message_ms: 5000 } }) end @doc """ Gets the validation configuration. """ def get_validation_config do Application.get_env(:da_product_app, :mercury_iso8583_validation, %{ # Field validation rules field_validation: %{ required_fields_by_mti: %{ "0100" => [2, 3, 4, 7, 11, 41, 42], # Authorization "0110" => [2, 3, 4, 7, 11, 39, 41, 42], # Authorization Response "0200" => [2, 3, 4, 7, 11, 41, 42], # Financial "0210" => [2, 3, 4, 7, 11, 39, 41, 42], # Financial Response "0400" => [2, 3, 4, 7, 11, 37, 41, 42], # Reversal "0410" => [2, 3, 4, 7, 11, 39, 41, 42], # Reversal Response "0800" => [7, 11, 41, 42], # Network Management "0810" => [7, 11, 39, 41, 42] # Network Management Response }, conditional_fields: %{ # If field 35 (Track 2) is present, field 2 (PAN) is optional 35 => %{makes_optional: [2]}, # If field 45 (Track 1) is present, field 2 (PAN) is optional 45 => %{makes_optional: [2]} } }, # Business rule validation business_rules: %{ amount_limits: %{ max_transaction_amount: 50_000_00, # $50,000 in cents max_daily_amount: 100_000_00, # $100,000 in cents max_cash_advance: 1_000_00 # $1,000 in cents }, transaction_limits: %{ max_transactions_per_minute: 60, max_transactions_per_hour: 1000, max_transactions_per_day: 10000 }, card_validation: %{ validate_luhn: true, check_expiration: true, validate_service_code: true } }, # Error handling configuration error_handling: %{ max_validation_errors: 10, continue_on_warning: true, strict_field_validation: false } }) end @doc """ Gets the routing configuration. """ def get_routing_config do Application.get_env(:da_product_app, :mercury_iso8583_routing, %{ # BIN range routing bin_routing: %{ "411111" => %{processor: :visa_primary, backup: :visa_secondary}, "555555" => %{processor: :mc_primary, backup: :mc_secondary}, "378282" => %{processor: :amex_primary, backup: :amex_secondary}, "6011" => %{processor: :discover_primary, backup: :discover_secondary} }, # Default routing rules default_routes: %{ authorization: :auth_processor, financial: :financial_processor, reversal: :reversal_processor, network_management: :network_processor }, # Routing priorities routing_priorities: [ :bin_range, :merchant_config, :terminal_capability, :business_rules, :default ], # Load balancing configuration load_balancing: %{ strategy: :round_robin, # :round_robin, :weighted, :least_connections health_check_interval: 30_000, max_retries: 3, retry_delay: 1000 } }) end @doc """ Gets the Mercury adapter configuration. """ def get_mercury_config do Application.get_env(:da_product_app, :mercury_adapter, %{ # Command mapping command_mapping: %{ "Sale" => "0200", "Authorization" => "0100", "Void" => "0200", "Refund" => "0200", "PreAuth" => "0100", "Capture" => "0220", "Balance" => "0100", "CashAdvance" => "0100", "Reversal" => "0400", "NetworkManagement" => "0800", "Echo" => "0800" }, # Field mapping configuration field_mapping: %{ "Amount" => 4, "CardNumber" => 2, "ExpirationDate" => 14, "TransactionID" => 11, "TerminalID" => 41, "MerchantID" => 42, "CurrencyCode" => 49, "Track2Data" => 35, "Track1Data" => 45, "EntryMode" => 22 }, # Response mapping response_mapping: %{ "00" => %{status: "Approved", message: "Transaction approved"}, "01" => %{status: "Referral", message: "Refer to card issuer"}, "02" => %{status: "Referral", message: "Refer to card issuer, special condition"}, "03" => %{status: "Declined", message: "Invalid merchant"}, "04" => %{status: "Declined", message: "Pickup card"}, "05" => %{status: "Declined", message: "Do not honor"}, "96" => %{status: "Error", message: "System error"} }, # Processing options processing: %{ timeout_ms: 30_000, max_retries: 3, enable_batch_processing: true, max_batch_size: 100, parallel_batch_processing: true } }) end @doc """ Gets the error handling configuration. """ def get_error_config do Application.get_env(:da_product_app, :mercury_iso8583_errors, %{ # Error categorization error_categories: %{ validation: %{severity: :medium, recovery: :auto_decline}, processing: %{severity: :high, recovery: :retry}, routing: %{severity: :medium, recovery: :route_alternative}, network: %{severity: :high, recovery: :retry}, business: %{severity: :medium, recovery: :auto_decline}, system: %{severity: :critical, recovery: :manual_intervention} }, # Retry configuration retry: %{ max_retries: 3, base_delay_ms: 1000, max_delay_ms: 10_000, backoff_strategy: :exponential }, # Error response codes response_codes: %{ validation_error: "30", format_error: "30", invalid_amount: "13", invalid_card: "14", insufficient_funds: "51", expired_card: "54", invalid_pin: "55", transaction_not_permitted: "57", suspected_fraud: "59", withdrawal_limit_exceeded: "61", security_violation: "63", system_malfunction: "96", timeout: "68", network_error: "91", issuer_unavailable: "91", duplicate_transaction: "94", system_error: "96" }, # Logging configuration logging: %{ log_errors: true, log_level_by_severity: %{ low: :info, medium: :warning, high: :error, critical: :error }, include_stack_trace: true, mask_sensitive_data: true } }) end @doc """ Gets the security configuration. """ def get_security_config do Application.get_env(:da_product_app, :mercury_iso8583_security, %{ # Data masking configuration data_masking: %{ mask_pan: true, mask_track_data: true, mask_pin_data: true, mask_cvv: true, pan_mask_pattern: "****-****-****-{last_4}", track_mask_pattern: "{first_4}***{last_4}" }, # Encryption configuration encryption: %{ encrypt_sensitive_fields: true, encryption_algorithm: :aes_256_gcm, key_rotation_interval: 86_400_000, # 24 hours in ms encrypted_fields: [52, 55] # PIN data, ICC data }, # Audit configuration audit: %{ log_all_transactions: true, log_sensitive_operations: true, audit_retention_days: 2555, # 7 years include_request_response: true }, # Rate limiting rate_limiting: %{ enabled: true, max_requests_per_second: 100, max_requests_per_minute: 1000, burst_capacity: 200 } }) end @doc """ Gets environment-specific configuration. """ def get_env_config do env = Application.get_env(:da_product_app, :environment, :dev) case env do :prod -> %{ log_level: :info, enable_debug: false, strict_validation: true, performance_monitoring: true, error_notifications: true } :test -> %{ log_level: :warning, enable_debug: true, strict_validation: true, performance_monitoring: false, error_notifications: false, mock_external_services: true } :dev -> %{ log_level: :debug, enable_debug: true, strict_validation: false, performance_monitoring: true, error_notifications: false, mock_external_services: false } end end @doc """ Gets complete configuration for the ISO8583 system. """ def get_complete_config do %{ packager: get_packager_config(), processor: get_processor_config(), validation: get_validation_config(), routing: get_routing_config(), mercury: get_mercury_config(), errors: get_error_config(), security: get_security_config(), environment: get_env_config() } end @doc """ Validates the configuration for completeness and correctness. """ def validate_config do config = get_complete_config() validation_results = [ validate_packager_config(config.packager), validate_processor_config(config.processor), validate_validation_config(config.validation), validate_routing_config(config.routing), validate_mercury_config(config.mercury), validate_error_config(config.errors), validate_security_config(config.security) ] case Enum.find(validation_results, fn result -> match?({:error, _}, result) end) do nil -> {:ok, :configuration_valid} error -> error end end # Private validation functions defp validate_packager_config(%{field_packagers: packagers}) when is_map(packagers) do required_fields = [2, 3, 4, 7, 11, 37, 39, 41, 42] missing_fields = Enum.reject(required_fields, &Map.has_key?(packagers, &1)) if Enum.empty?(missing_fields) do :ok else {:error, {:missing_field_packagers, missing_fields}} end end defp validate_packager_config(_), do: {:error, :invalid_packager_config} defp validate_processor_config(%{pipeline: pipeline}) when is_map(pipeline), do: :ok defp validate_processor_config(_), do: {:error, :invalid_processor_config} defp validate_validation_config(%{field_validation: validation}) when is_map(validation), do: :ok defp validate_validation_config(_), do: {:error, :invalid_validation_config} defp validate_routing_config(%{bin_routing: routing}) when is_map(routing), do: :ok defp validate_routing_config(_), do: {:error, :invalid_routing_config} defp validate_mercury_config(%{command_mapping: mapping}) when is_map(mapping), do: :ok defp validate_mercury_config(_), do: {:error, :invalid_mercury_config} defp validate_error_config(%{error_categories: categories}) when is_map(categories), do: :ok defp validate_error_config(_), do: {:error, :invalid_error_config} defp validate_security_config(%{data_masking: masking}) when is_map(masking), do: :ok defp validate_security_config(_), do: {:error, :invalid_security_config} end