Completed
Pull Request — master (#126)
by
unknown
01:39
created

Transaction   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 166
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 166
rs 10
c 0
b 0
f 0
wmc 18

6 Methods

Rating   Name   Duplication   Size   Complexity  
F make_request() 0 45 9
A has_response? 0 3 1
A test? 0 3 1
A split_transaction_allowed? 0 3 1
B initialize() 0 31 5
A run() 0 3 1
1
module AuthorizeNet::AIM
2
  # The AIM transaction class. Handles building the transaction payload and
3
  # transmitting it to the gateway.
4
  class Transaction < AuthorizeNet::KeyValueTransaction
5
    # The default options for the constructor.
6
    @@option_defaults = {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using a class variable like @@option_defaults is generally not recommended; did you consider
using an class instance variable instead?
Loading history...
7
      transaction_type: Type::AUTHORIZE_AND_CAPTURE,
8
      gateway: :production,
9
      test: false,
10
      allow_split: false,
11
      delimiter: ',',
12
      encapsulation_character: nil,
13
      verify_ssl: true,
14
      device_type: DeviceType::UNKNOWN,
15
      market_type: MarketType::RETAIL
16
    }
17
18
    # Fields to convert to/from booleans.
19
    @@boolean_fields = %i[tax_exempt test_request recurring_billing allow_partial_auth delim_data email_customer relay_response]
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using a class variable like @@boolean_fields is generally not recommended; did you consider
using an class instance variable instead?
Loading history...
20
21
    # Fields to convert to/from BigDecimal.
22
    @@decimal_fields = [:amount]
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using a class variable like @@decimal_fields is generally not recommended; did you consider
using an class instance variable instead?
Loading history...
23
24
    # Constructs an AIM transaction. You can use the new AIM transaction object
25
    # to issue a request to the payment gateway and parse the response into a new
26
    # AuthorizeNet::AIM::Response object.
27
    #
28
    # +api_login_id+:: Your API login ID, as a string.
29
    # +api_transaction_key+:: Your API transaction key, as a string.
30
    # +options+:: A hash of options. See below for values.
31
    #
32
    # Options
33
    # +transaction_type+:: The type of transaction to perform. Defaults to AuthorizeNet::Type::AUTHORIZE_AND_CAPTURE. This value is only used if run is called directly.
34
    # +gateway+:: The gateway to submit the transaction to. Can be a URL string, an AuthorizeNet::AIM::Transaction::Gateway constant, or one of the convenience symbols :sandbox, :test, :card_present_test, :card_present_live, :card_present_sandbox, :card_present_production, :production, or :live (:test is an alias for :sandbox, :card_present_test is an alias for :card_present_sandbox, :card_present_production is an alias for :card_present_live, and :live is an alias for :production).
35
    # +test+:: A boolean indicating if the transaction should be run in test mode or not (defaults to false).
36
    # +allow_split+:: A boolean indicating if split transactions should be allowed (defaults to false).
37
    # +delimiter+:: A single character (as a string) that will be used to delimit the response from the gateway. Defaults to ','.
38
    # +encapsulation_character+:: A single character (as a string) that will be used to encapsulate each field in the response from the gateway. Defaults to nil.
39
    # +verify_ssl+:: A boolean indicating if the SSL certificate of the +gateway+ should be verified. Defaults to true.
40
    # +device_type+:: A constant from DeviceType indicating the type of POS device used in a card present transaction. Defaults to DeviceType::UNKNOWN.
41
    # +market_type+:: A constant from MarketType indicating your industry. Used for card present transactions. Defaults to MarketType::RETAIL.
42
    #
43
    def initialize(api_login_id, api_transaction_key, options = {})
44
      super()
45
      options = @@option_defaults.merge(options)
46
      @api_login_id = api_login_id
47
      @api_transaction_key = api_transaction_key
48
      @test_mode = options[:test]
49
      @response ||= nil
50
      @delimiter = options[:delimiter]
51
      @type = options[:transaction_type]
52
      @cp_version = nil
53
      case options[:gateway]
54
      when :sandbox, :test
55
        @gateway = Gateway::TEST
56
      when :production, :live
57
        @gateway = Gateway::LIVE
58
      when :card_present_live, :card_present_production
59
        @gateway = Gateway::CARD_PRESENT_LIVE
60
        @cp_version = '1.0'
61
      when :card_present_test, :card_present_sandbox
62
        @gateway = Gateway::CARD_PRESENT_TEST
63
        @cp_version = '1.0'
64
      else
65
        @gateway = options[:gateway]
66
      end
67
      @allow_split_transaction = options[:allow_split]
68
      @encapsulation_character = options[:encapsulation_character]
69
      @verify_ssl = options[:verify_ssl]
70
      @market_type = options[:market_type]
71
      @device_type = options[:device_type]
72
      @solution_id = options[:solution_id]
73
    end
74
75
    # Checks if the transaction has been configured for test mode or not. Return TRUE if the
76
    # transaction is a test transaction, FALSE otherwise. All transactions run against the sandbox
77
    # are considered test transactions.
78
    def test?
79
      super || @gateway == Gateway::TEST
80
    end
81
82
    # Returns TRUE if split transactions are allowed, FALSE otherwise.
83
    def split_transaction_allowed?
84
      !!@allow_split_transaction
85
    end
86
87
    # Returns the current encapsulation character unless there is none, in which case Nil is returned.
88
    attr_reader :encapsulation_character
89
90
    # Returns the gateway to be used for this transaction.
91
    attr_reader :gateway
92
93
    # Checks to see if the transaction has a response (meaning it has been submitted to the gateway).
94
    # Returns TRUE if a response is present, FALSE otherwise.
95
    def has_response?
96
      [email protected]?
97
    end
98
99
    # Retrieve the response object (or Nil if transaction hasn't been sent to the gateway).
100
    attr_reader :response
101
102
    # Returns the current delimiter we are using to parse the fields returned by the
103
    # gateway.
104
    attr_reader :delimiter
105
106
    # Sets the delimiter used to parse the response from the gateway.
107
    attr_writer :delimiter
108
109
    # Submits the transaction to the gateway for processing. Returns a response object. If the transaction
110
    # has already been run, it will return nil.
111
    def run
112
      make_request
113
    end
114
115
    # Returns the current card present API version that we are adhering to.
116
    attr_reader :cp_version
117
118
    attr_reader :solution_id
119
120
    #:enddoc:
121
    protected
122
123
    # An internal method that builds the POST body, submits it to the gateway, and constructs a Response object with the response.
124
    def make_request
125
      return nil if has_response?
126
      url = URI.parse(@gateway)
127
      fields = @fields.merge(type: @type, delim_char: @delimiter, delim_data: "TRUE", login: @api_login_id, tran_key: @api_transaction_key, relay_response: "FALSE")
128
129
      if @cp_version.nil?
130
        fields[:version] = @version
131
      else
132
        fields.merge!(cp_version: @cp_version, market_type: @market_type, device_type: @device_type, response_format: "1")
133
      end
134
      fields[:test_request] = boolean_to_value(@test_mode)
135
      fields[:allow_partial_auth] = 'TRUE' if @allow_split_transaction
136
      fields[:encap_char] = @encapsulation_character unless @encapsulation_character.nil?
137
      fields[:solution_id] = @solution_id unless @solution_id.nil?
138
      fields.each do |k, v|
139
        if @@boolean_fields.include?(k)
140
          fields[k] = boolean_to_value(v)
141
        elsif @@decimal_fields.include?(k)
142
          fields[k] = decimal_to_value(v)
143
        end
144
      end
145
      data = fields.collect do |key, val|
146
        to_param(key, val)
147
      end
148
      custom_field_keys = @custom_fields.keys.collect(&:to_s).sort.collect(&:to_sym)
149
      for key in custom_field_keys
150
        data += [to_param(key, @custom_fields[key.to_sym], '')]
151
      end
152
      data.flatten!
153
      request = Net::HTTP::Post.new(url.path)
154
      request.content_type = 'application/x-www-form-urlencoded'
155
      request.body = data.join("&")
156
      connection = Net::HTTP.new(url.host, url.port)
157
      connection.use_ssl = true
158
      if @verify_ssl
159
        connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
160
      else
161
        connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
162
      end
163
      begin
164
        @response = AuthorizeNet::AIM::Response.new((connection.start { |http| http.request(request) }), self)
165
      rescue
166
        @response = AuthorizeNet::AIM::Response.new($ERROR_INFO, self)
167
      end
168
    end
169
  end
170
end
171