KentaaNL/buckaruby

View on GitHub
lib/buckaruby/response.rb

Summary

Maintainability
A
1 hr
Test Coverage
# frozen_string_literal: true

require_relative 'support/case_insensitive_hash'

require 'cgi'
require 'date'

module Buckaruby
  # Base class for any response.
  class Response
    attr_reader :response

    def initialize(body, config)
      @response = parse_response(body)

      logger = config.logger
      logger.debug("[response] params: #{params.inspect}")
    end

    def params
      @params ||= Support::CaseInsensitiveHash.new(response)
    end

    def status
      TransactionStatus.parse(params[:brq_statuscode])
    end

    def timestamp
      parse_time(params[:brq_timestamp])
    end

    def custom
      @custom ||= begin
        custom = Support::CaseInsensitiveHash.new

        params.each do |key, value|
          next unless key.upcase.start_with?('CUST_')

          new_key = key.to_s[5..-1]
          custom[new_key] = value
        end

        custom
      end
    end

    def additional
      @additional ||= begin
        additional = Support::CaseInsensitiveHash.new

        params.each do |key, value|
          next unless key.upcase.start_with?('ADD_')

          new_key = key.to_s[4..-1]
          additional[new_key] = value
        end

        additional
      end
    end

    private

    def parse_response(body)
      if body.is_a?(Hash)
        response = body
      else
        response = CGI.parse(body)
        response.each { |key, value| response[key] = value.first }
      end

      response
    end

    def parse_time(time)
      Time.strptime(time, '%Y-%m-%d %H:%M:%S') if time
    end
  end

  # Base for a transaction response.
  module TransactionResponse
    def consumer_bic
      case payment_method
      when PaymentMethod::IDEAL
        params[:brq_service_ideal_consumerbic]
      when PaymentMethod::IDEAL_PROCESSING
        params[:brq_service_idealprocessing_consumerbic]
      when PaymentMethod::SEPA_DIRECT_DEBIT
        params[:brq_service_sepadirectdebit_customerbic]
      end
    end

    def consumer_iban
      case payment_method
      when PaymentMethod::IDEAL
        params[:brq_service_ideal_consumeriban]
      when PaymentMethod::IDEAL_PROCESSING
        params[:brq_service_idealprocessing_consumeriban]
      when PaymentMethod::SEPA_DIRECT_DEBIT
        params[:brq_service_sepadirectdebit_customeriban]
      end
    end

    def consumer_name
      case payment_method
      when PaymentMethod::IDEAL
        params[:brq_service_ideal_consumername] || params[:brq_customer_name]
      when PaymentMethod::IDEAL_PROCESSING
        params[:brq_service_idealprocessing_consumername] || params[:brq_customer_name]
      when PaymentMethod::SEPA_DIRECT_DEBIT
        params[:brq_service_sepadirectdebit_customername] || params[:brq_customer_name]
      end
    end

    def collect_date
      if payment_method == PaymentMethod::SEPA_DIRECT_DEBIT
        parse_date(params[:brq_service_sepadirectdebit_collectdate])
      end
    end

    def invoicenumber
      params[:brq_invoicenumber]
    end

    def mandate_reference
      if payment_method == PaymentMethod::SEPA_DIRECT_DEBIT
        params[:brq_service_sepadirectdebit_mandatereference]
      end
    end

    def payment_id
      params[:brq_payment]
    end

    def payment_method
      parse_payment_method(params[:brq_payment_method] || params[:brq_transaction_method])
    end

    def transaction_id
      params[:brq_transactions]
    end

    def transaction_status
      status
    end

    def transaction_type
      TransactionType.parse(params[:brq_transaction_type], params[:brq_recurring])
    end

    private

    def parse_date(date)
      Date.strptime(date, '%Y-%m-%d') if date
    end

    def parse_payment_method(method)
      method&.downcase
    end
  end

  # Base class for a response via the API.
  class ApiResponse < Response
    def initialize(body, config)
      super(body, config)

      if api_result.nil? || api_result.casecmp('fail').zero?
        raise ApiException, params
      end

      Signature.verify!(config, response)
    end

    private

    def api_result
      params[:brq_apiresult]
    end
  end

  # Response when creating a new transaction.
  class SetupTransactionResponse < ApiResponse
    include TransactionResponse

    def redirect_url
      params[:brq_redirecturl]
    end
  end

  # Response when creating a recurrent transaction.
  class RecurrentTransactionResponse < ApiResponse
    include TransactionResponse

    def transaction_type
      TransactionType::PAYMENT_RECURRENT
    end
  end

  # Response when creating a refund transaction.
  class RefundTransactionResponse < ApiResponse
    include TransactionResponse

    def refunded_transaction_id
      params[:brq_relatedtransaction_refund]
    end

    def transaction_type
      TransactionType::REFUND
    end
  end

  # Response when retrieving the refund information.
  class RefundInfoResponse < ApiResponse
    def payment_method
      params[:brq_refundinfo_1_servicecode]
    end

    def refundable?
      !params[:brq_refundinfo_1_isrefundable].nil? && params[:brq_refundinfo_1_isrefundable].casecmp('true').zero?
    end

    def maximum_amount
      params[:brq_refundinfo_1_maximumrefundamount]
    end

    def invoicenumber
      params[:brq_refundinfo_1_invoice]
    end

    def currency
      params[:brq_refundinfo_1_refundcurrency]
    end
  end

  # Response when getting the status of a transaction.
  class StatusResponse < ApiResponse
    include TransactionResponse

    def cancellable?
      !params[:brq_transaction_cancelable].nil? && params[:brq_transaction_cancelable].casecmp('true').zero?
    end

    def refunded_transaction_id
      params[:brq_relatedtransaction_refund]
    end

    def reversed_transaction_id
      params[:brq_relatedtransaction_reversal]
    end
  end

  # Response when cancelling a transaction.
  class CancelResponse < ApiResponse
  end

  # Response when verifying the push response.
  class PushResponse < Response
    include TransactionResponse

    def initialize(body, config)
      super(body, config)

      Signature.verify!(config, response)
    end

    def refunded_transaction_id
      params[:brq_relatedtransaction_refund]
    end

    def reversed_transaction_id
      params[:brq_relatedtransaction_reversal]
    end
  end

  # Response when retrieving the specification for a transaction.
  class TransactionSpecificationResponse < ApiResponse
    def services
      @services ||= FieldMapper.map_fields(params, :brq_services)
    end

    def basic_fields
      @basic_fields ||= FieldMapper.map_fields(params, :brq_basicfields)
    end

    def custom_parameters
      @custom_parameters ||= FieldMapper.map_fields(params, :brq_customparameters)
    end
  end
end