lib/genesis_ruby/api/requests/wpf/create.rb



# frozen_string_literal: true

require 'genesis_ruby/utils/transactions/wpf_types'
require 'genesis_ruby/api/constants/transactions/parameters/sca_exemptions'
require 'genesis_ruby/api/constants/i18n'
require 'genesis_ruby/api/mixins/requests/wpf_reminders_attributes'

module GenesisRuby
  module Api
    module Requests
      module Wpf
        # Web-Payment Form Request
        class Create < Request # rubocop:disable Metrics/ClassLength

          include Mixins::Requests::AddressInfoAttributes
          include Mixins::Requests::DigitalAssetTypeAttributes
          include Mixins::Requests::Financial::AsyncAttributes
          include Mixins::Requests::Financial::Business::BusinessAttributes
          include Mixins::Requests::Financial::Cards::AccountOwnerAttributes
          include Mixins::Requests::Financial::Cards::Recurring::RecurringCategoryAttributes
          include Mixins::Requests::Financial::DynamicDescriptorAttributes
          include Mixins::Requests::Financial::FundingAttributes
          include Mixins::Requests::Financial::NotificationAttributes
          include Mixins::Requests::Financial::PaymentAttributes
          include Mixins::Requests::Financial::PendingPaymentAttributes
          include Mixins::Requests::Financial::RiskAttributes
          include Mixins::Requests::Financial::Threeds::Version2::WpfAttributes
          include Mixins::Requests::WpfRemindersAttributes

          attr_reader   :locale, :sca_preference, :sca_exemption
          attr_accessor :transaction_id, :usage, :description, :consumer_id, :return_cancel_url, :remember_card,
                        :web_payment_form_id

          MAX_LIFETIME     = 131_487
          DEFAULT_LIFETIME = 30

          private_constant :MAX_LIFETIME
          private_constant :DEFAULT_LIFETIME

          # The transaction types that the merchant is willing to accept payments for
          def add_transaction_type(name, custom_attributes = {})
            unless GenesisRuby::Utils::Transactions::WpfTypes.valid? name
              raise ParameterError, 'Invalid Transaction Type given.'
            end

            self.transaction_types = { transaction_type: {
              '@attributes': { name: name.to_s.downcase }, **custom_attributes
            } }
          end

          # Define ISO 639-1 language code to the URL
          def locale=(value)
            allowed_options attribute: __method__,
                            allowed: Constants::I18n.all,
                            value: value.to_s.downcase,
                            allow_empty: true

            init_api_wpf_configuration(language: locale)
          end

          # Signifies whether to perform SCA on the transaction. At least one 3DS transaction type has to be submitted.
          def sca_preference=(value)
            allowed_options attribute:      __method__,
                            allowed:        [true, false],
                            value:          value,
                            allow_empty:    true,
                            error_message: 'Accepts only boolean values'
          end

          # Exemption for the Strong Customer Authentication. The allowed options are low_value, low_risk
          def sca_exemption=(value)
            sca_exemptions = GenesisRuby::Api::Constants::Transactions::Parameters::ScaExemptions

            allowed_options attribute:   __method__,
                            allowed:     [sca_exemptions::LOW_VALUE, sca_exemptions::LOW_RISK],
                            value:       value.to_s.empty? ? nil : value.to_s.downcase,
                            allow_empty: true
          end

          # Signifies whether the ’Pay Later’ feature would be enabled on the WPF
          def pay_later
            @pay_later ||= false

            return nil unless @pay_later

            @pay_later
          end

          # Signifies whether the ’Pay Later’ feature would be enabled on the WPF
          def pay_later=(value)
            allowed_options attribute: __method__,
                            allowed: [true, false],
                            value: value,
                            allow_empty: true,
                            error_message: 'Accepts only boolean values'
          end

          # A number of minutes determining how long the WPF will be valid. Will be set to 30 minutes by default.
          def lifetime
            @lifetime ||= DEFAULT_LIFETIME
          end

          # A number of minutes determining how long the WPF will be valid. Will be set to 30 minutes by default.
          def lifetime=(value)
            lifetime = value.to_i

            if lifetime < 1 || lifetime > MAX_LIFETIME
              raise InvalidArgumentError,
                    "Valid value ranges between 1 minute and 3 months (#{MAX_LIFETIME} minutes) given in minutes"
            end

            @lifetime = lifetime
          end

          protected

          # Load base configuration
          def init_configuration
            init_xml_configuration
            init_api_wpf_configuration
          end

          def init_field_validations
            required_fields.push *%i[transaction_id amount currency notification_url return_success_url
              return_failure_url return_cancel_url transaction_types]

            field_values.merge!(
              { currency: Api::Constants::Currencies::Iso4217.all.map(&:upcase) },
              threeds_field_validations,
              funding_attributes_field_validations
            )
          end

          # Perform validation over the defined parameters upon request execution
          def check_requirements
            validate_reminders
            validate_threeds_card_holder_dates

            super
          end

          # Define Web-Payment Form transaction request structure
          def populate_structure # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
            self.tree_structure = {
              wpf_payment: {
                transaction_id:            transaction_id,
                amount:                    transform_amount(amount, currency),
                currency:                  currency,
                usage:                     usage,
                description:               description,
                consumer_id:               consumer_id,
                customer_email:            customer_email,
                customer_phone:            customer_phone,
                notification_url:          notification_url,
                return_success_url:        return_success_url,
                return_failure_url:        return_failure_url,
                return_cancel_url:         return_cancel_url,
                return_pending_url:        return_pending_url,
                billing_address:           billing_address_parameters_structure,
                shipping_address:          shipping_address_parameters_structure,
                transaction_types:         transaction_types,
                business_attributes:       business_attributes_structure,
                remember_card:             remember_card,
                lifetime:                  lifetime,
                sca_preference:            sca_preference,
                sca_params:                {
                  sca_exemption: sca_exemption
                },
                threeds_v2_params:         threeds_v2_wpf_attributes_structure,
                web_payment_form_id:       web_payment_form_id,
                recurring_category:        recurring_category,
                dynamic_descriptor_params: dynamic_descriptor_structure,
                risk_params:               risk_parameters_structure,
                account_owner:             account_owner_attributes_structure,
                pay_later:                 pay_later,
                reminders:                 pay_later ? reminders_structure : [],
                funding:                   funding_attributes_structure,
                digital_asset_type:        digital_asset_type
              }
            }
          end

          private

          attr_reader :transaction_types

          # Add every transaction type to the transaction types structure
          def transaction_types=(value)
            @transaction_types = [] if value.nil?

            @transaction_types ||= []

            @transaction_types.push(value)
          end

          # Validate Reminders against the lifetime
          def validate_reminders
            return if lifetime.nil?

            reminders.each do |reminder|
              next unless reminder.after >= lifetime

              raise(
                ParameterError,
                "Reminder (#{reminder.after} min) could not be greater than or equal to lifetime (#{lifetime} min)."
              )
            end
          end

        end
      end
    end
  end
end