# frozen_string_literal: true require 'date' require 'net/http' require 'openssl' require 'uri' module Buckaruby # Base class for any request. class Request def initialize(config) @config = config end def execute(options) uri = URI.parse(@config.api_url) uri.query = URI.encode_www_form(op: operation) if operation post_buckaroo(uri, build_request_data(options)) end # Returns the service operation for this request. def operation nil end # Returns the request parameters (to be implemented by subclasses). def build_request_params(_options) raise NotImplementedError end private def post_buckaroo(uri, params) http = Net::HTTP.new(uri.host, uri.port) if uri.scheme == 'https' http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_PEER end raw_response = http.post(uri.request_uri, URI.encode_www_form(params)) unless raw_response.is_a?(Net::HTTPSuccess) raise InvalidResponseException, raw_response end raw_response.body # Try to catch some common exceptions Net::HTTP might raise rescue Errno::ETIMEDOUT, Errno::EINVAL, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, IOError, SocketError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::OpenTimeout, Net::ProtocolError, Net::ReadTimeout, OpenSSL::SSL::SSLError => e raise ConnectionException, e end def build_request_data(options) params = { brq_websitekey: @config.website } params.merge!(build_request_params(options)) params.merge!(build_custom_params(options[:custom])) if options[:custom] params.merge!(build_additional_params(options[:additional])) if options[:additional] params[:add_buckaruby] = "Buckaruby #{Buckaruby::VERSION}" # Sign the data with our secret key. params[:brq_signature] = Signature.generate(@config, params) params end def build_custom_params(options) options.transform_keys { |key| :"cust_#{key}" } end def build_additional_params(options) options.transform_keys { |key| :"add_#{key}" } end end # Base class for a transaction request. class TransactionRequest < Request def operation Operation::TRANSACTION_REQUEST end def build_request_params(options) params = { brq_payment_method: options[:payment_method], brq_culture: options[:culture] || Language::DUTCH, brq_currency: options[:currency] || Currency::EURO, brq_amount: Amount.new(options[:amount]).to_s, brq_invoicenumber: options[:invoicenumber] } params.merge!(build_transaction_request_params(options)) params[:brq_clientip] = options[:client_ip] if options[:client_ip] params[:brq_description] = options[:description] if options[:description] params[:brq_return] = options[:return_url] if options[:return_url] params end def build_transaction_request_params(_options) raise NotImplementedError end end # Request for a creating a new transaction. class SetupTransactionRequest < TransactionRequest def build_transaction_request_params(options) params = {} case options[:payment_method] when PaymentMethod::IDEAL params.merge!( brq_service_ideal_action: Action::PAY, brq_service_ideal_issuer: options[:issuer], brq_service_ideal_version: '2' ) when PaymentMethod::IDEAL_PROCESSING params.merge!( brq_service_idealprocessing_action: Action::PAY, brq_service_idealprocessing_issuer: options[:issuer], brq_service_idealprocessing_version: '2' ) when PaymentMethod::SEPA_DIRECT_DEBIT params.merge!( brq_service_sepadirectdebit_action: Action::PAY, brq_service_sepadirectdebit_customeriban: options[:consumer_iban], brq_service_sepadirectdebit_customeraccountname: options[:consumer_name] ) if options[:consumer_bic] params[:brq_service_sepadirectdebit_customerbic] = options[:consumer_bic] end if options[:collect_date] params[:brq_service_sepadirectdebit_collectdate] = options[:collect_date].strftime('%Y-%m-%d') end if options[:mandate_reference] params.merge!( brq_service_sepadirectdebit_action: [Action::PAY, Action::EXTRA_INFO].join(','), brq_service_sepadirectdebit_mandatereference: options[:mandate_reference], brq_service_sepadirectdebit_mandatedate: Date.today.strftime('%Y-%m-%d') ) end end params[:brq_startrecurrent] = options[:recurring] if options[:recurring] params end end # Request for a creating a recurrent transaction. class RecurrentTransactionRequest < TransactionRequest def build_transaction_request_params(options) params = {} key = :"brq_service_#{options[:payment_method]}_action" params[key] = Action::PAY_RECURRENT # Indicate that this is a request without user redirection to a webpage. # This is needed to make recurrent payments working. params[:brq_channel] = 'backoffice' params[:brq_originaltransaction] = options[:transaction_id] params end end # Request for a creating a transaction specification. class TransactionSpecificationRequest < Request def operation Operation::TRANSACTION_REQUEST_SPECIFICATION end def build_request_params(options) params = {} if options[:payment_method] params[:brq_services] = if options[:payment_method].respond_to?(:join) options[:payment_method].join(',') else options[:payment_method] end end params[:brq_latestversiononly] = 'true' params[:brq_culture] = options[:culture] || Language::DUTCH params end end # Request for a creating a refund. class RefundTransactionRequest < Request def operation Operation::TRANSACTION_REQUEST end def build_request_params(options) params = { brq_payment_method: options[:payment_method], brq_amount_credit: Amount.new(options[:amount]).to_s, brq_currency: options[:currency] || Currency::EURO, brq_invoicenumber: options[:invoicenumber] } key = :"brq_service_#{options[:payment_method]}_action" params[key] = Action::REFUND params[:brq_originaltransaction] = options[:transaction_id] params end end # Request for retrieving refund information. class RefundInfoRequest < Request def operation Operation::REFUND_INFO end def build_request_params(options) params = {} params[:brq_transaction] = options[:transaction_id] params end end # Request for getting the status of a transaction. class StatusRequest < Request def operation Operation::TRANSACTION_STATUS end def build_request_params(options) params = {} params[:brq_transaction] = options[:transaction_id] if options[:transaction_id] params[:brq_payment] = options[:payment_id] if options[:payment_id] params end end # Request for cancelling a transaction. class CancelRequest < Request def operation Operation::CANCEL_TRANSACTION end def build_request_params(options) { brq_transaction: options[:transaction_id] } end end end