authorizenet 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,154 +1,154 @@
1
- module AuthorizeNet
2
- # The core, xml response class. You shouldn't instantiate this one.
3
- # Instead you should use AuthorizeNet::ARB::Response.
4
- class XmlResponse < AuthorizeNet::Response
5
- # DO NOT USE. Instantiate AuthorizeNet::ARB::Response or AuthorizeNet::CIM::Response instead.
6
- def initialize(raw_response, transaction)
7
- @raw_response = raw_response
8
- @transaction = transaction
9
- unless connection_failure?
10
- begin
11
- xml = Nokogiri::XML(@raw_response.body) do |config|
12
- # confirm noent is the right flag
13
- config.recover.noent.nonet
14
- end
15
- @root = xml.children[0]
16
- @result_code = node_content_unless_nil(@root.at_css('messages resultCode'))
17
- @message_code = node_content_unless_nil(@root.at_css('messages message code'))
18
- @message_text = node_content_unless_nil(@root.at_css('messages message text'))
19
- @reference_id = node_content_unless_nil(@root.at_css('refId'))
20
- rescue StandardError
21
- @raw_response = $ERROR_INFO
22
- end
23
- end
24
- end
25
-
26
- # Check to see if the response indicated success. Success is defined as a 200 OK response with a resultCode
27
- # of 'Ok'.
28
- def success?
29
- !connection_failure? && @result_code == 'Ok'
30
- end
31
-
32
- # Returns true if we failed to open a connection to the gateway or got back a non-200 OK HTTP response.
33
- def connection_failure?
34
- !@raw_response.is_a?(Net::HTTPOK)
35
- end
36
-
37
- # Returns the underlying Net::HTTPResponse object. This has the original response body along with
38
- # headers and such. Note that if an exception is generated while making the request (which happens
39
- # if there is no internet connection for example), you will get the exception object here instead of
40
- # a Net::HTTPResponse object.
41
- def raw
42
- @raw_response
43
- end
44
-
45
- # Returns a deep-copy of the XML object received from the payment gateway. Or nil if there was no XML payload.
46
- def xml
47
- @root.dup unless @root.nil?
48
- end
49
-
50
- # Returns the resultCode from the XML response. resultCode will be either 'Ok' or 'Error'.
51
- attr_reader :result_code
52
-
53
- # Returns the messageCode from the XML response. This is a code indicating the details of an error
54
- # or success.
55
- attr_reader :message_code
56
-
57
- # Returns the messageText from the XML response. This is a text description of the message_code.
58
- attr_reader :message_text
59
-
60
- # Alias for result_code.
61
- def response_code
62
- result_code
63
- end
64
-
65
- # Alias for message_code.
66
- def response_reason_code
67
- message_code
68
- end
69
-
70
- # Alias for message_text.
71
- def response_reason_text
72
- message_text
73
- end
74
-
75
- # Returns the refId from the response if there is one. Otherwise returns nil.
76
- attr_reader :reference_id
77
-
78
- #:enddoc:
79
- protected
80
-
81
- def node_content_unless_nil(node)
82
- if node.nil?
83
- nil
84
- else
85
- node.content
86
- end
87
- end
88
-
89
- def node_child_content_unless_nil(node)
90
- if node.nil?
91
- nil
92
- else
93
- node.children.collect(&:content) unless node.children.empty?
94
- end
95
- end
96
-
97
- # Transforms a block of XML into a model Object defined by entity_desc.
98
- def build_entity(xml, entity_desc)
99
- args = {}
100
- entity_desc.node_structure.each do |node_desc|
101
- node_name = (node_desc.keys.reject { |k| k.to_s[0..0] == '_' }).first
102
- args.merge!(handle_node_type(xml, node_desc, node_name, args, ''))
103
- end
104
-
105
- return nil if args.empty?
106
-
107
- if entity_desc.arg_mapping.nil?
108
- return entity_desc.entity_class.new(args)
109
- else
110
- args_list = []
111
- entity_desc.arg_mapping.each do |arg|
112
- args_list <<= args[arg]
113
- args.delete(arg)
114
- end
115
- args_list <<= args
116
- return entity_desc.entity_class.new(*args_list)
117
- end
118
- end
119
-
120
- # Parses an XML fragment into an internal representation.
121
- def handle_node_type(xml, node_desc, node_name, args, base_name)
122
- case node_desc[node_name]
123
- when Symbol
124
- node = xml.at_css(base_name + node_name.to_s)
125
- unless node.nil?
126
- content = node.content
127
- case node_desc[:_converter]
128
- when Method, Proc
129
- content = node_desc[:_converter].call(content)
130
- when Symbol
131
- content = send(node_desc[:_converter], content)
132
- end
133
- args[node_desc[node_name]] = content unless content.nil?
134
- end
135
- when AuthorizeNet::EntityDescription
136
- if node_desc[:_multivalue].nil?
137
- entity = build_entity(xml.css(base_name + node_name.to_s), node_desc[node_name])
138
- args[node_desc[:_value]] = entity unless entity.nil?
139
- else
140
- xml.css(base_name + node_name.to_s).each do |node|
141
- entity = build_entity(node, node_desc[node_name])
142
- args[node_desc[:_multivalue]] = args[node_desc[:_multivalue]].to_a + entity.to_a unless entity.nil?
143
- end
144
- end
145
- when Array
146
- node_desc[node_name].each do |inner_node|
147
- inner_node_name = (inner_node.keys.reject { |k| k.to_s[0..0] == '_' }).first
148
- args.merge!(handle_node_type(xml, inner_node, inner_node_name, args, node_name.to_s + ' '))
149
- end
150
- end
151
- args
152
- end
153
- end
154
- end
1
+ module AuthorizeNet
2
+ # The core, xml response class. You shouldn't instantiate this one.
3
+ # Instead you should use AuthorizeNet::ARB::Response.
4
+ class XmlResponse < AuthorizeNet::Response
5
+ # DO NOT USE. Instantiate AuthorizeNet::ARB::Response or AuthorizeNet::CIM::Response instead.
6
+ def initialize(raw_response, transaction)
7
+ @raw_response = raw_response
8
+ @transaction = transaction
9
+ unless connection_failure?
10
+ begin
11
+ xml = Nokogiri::XML(@raw_response.body) do |config|
12
+ # confirm noent is the right flag
13
+ config.recover.noent.nonet
14
+ end
15
+ @root = xml.children[0]
16
+ @result_code = node_content_unless_nil(@root.at_css('messages resultCode'))
17
+ @message_code = node_content_unless_nil(@root.at_css('messages message code'))
18
+ @message_text = node_content_unless_nil(@root.at_css('messages message text'))
19
+ @reference_id = node_content_unless_nil(@root.at_css('refId'))
20
+ rescue StandardError
21
+ @raw_response = $ERROR_INFO
22
+ end
23
+ end
24
+ end
25
+
26
+ # Check to see if the response indicated success. Success is defined as a 200 OK response with a resultCode
27
+ # of 'Ok'.
28
+ def success?
29
+ !connection_failure? && @result_code == 'Ok'
30
+ end
31
+
32
+ # Returns true if we failed to open a connection to the gateway or got back a non-200 OK HTTP response.
33
+ def connection_failure?
34
+ !@raw_response.is_a?(Net::HTTPOK)
35
+ end
36
+
37
+ # Returns the underlying Net::HTTPResponse object. This has the original response body along with
38
+ # headers and such. Note that if an exception is generated while making the request (which happens
39
+ # if there is no internet connection for example), you will get the exception object here instead of
40
+ # a Net::HTTPResponse object.
41
+ def raw
42
+ @raw_response
43
+ end
44
+
45
+ # Returns a deep-copy of the XML object received from the payment gateway. Or nil if there was no XML payload.
46
+ def xml
47
+ @root.dup unless @root.nil?
48
+ end
49
+
50
+ # Returns the resultCode from the XML response. resultCode will be either 'Ok' or 'Error'.
51
+ attr_reader :result_code
52
+
53
+ # Returns the messageCode from the XML response. This is a code indicating the details of an error
54
+ # or success.
55
+ attr_reader :message_code
56
+
57
+ # Returns the messageText from the XML response. This is a text description of the message_code.
58
+ attr_reader :message_text
59
+
60
+ # Alias for result_code.
61
+ def response_code
62
+ result_code
63
+ end
64
+
65
+ # Alias for message_code.
66
+ def response_reason_code
67
+ message_code
68
+ end
69
+
70
+ # Alias for message_text.
71
+ def response_reason_text
72
+ message_text
73
+ end
74
+
75
+ # Returns the refId from the response if there is one. Otherwise returns nil.
76
+ attr_reader :reference_id
77
+
78
+ #:enddoc:
79
+ protected
80
+
81
+ def node_content_unless_nil(node)
82
+ if node.nil?
83
+ nil
84
+ else
85
+ node.content
86
+ end
87
+ end
88
+
89
+ def node_child_content_unless_nil(node)
90
+ if node.nil?
91
+ nil
92
+ else
93
+ node.children.collect(&:content) unless node.children.empty?
94
+ end
95
+ end
96
+
97
+ # Transforms a block of XML into a model Object defined by entity_desc.
98
+ def build_entity(xml, entity_desc)
99
+ args = {}
100
+ entity_desc.node_structure.each do |node_desc|
101
+ node_name = (node_desc.keys.reject { |k| k.to_s[0..0] == '_' }).first
102
+ args.merge!(handle_node_type(xml, node_desc, node_name, args, ''))
103
+ end
104
+
105
+ return nil if args.empty?
106
+
107
+ if entity_desc.arg_mapping.nil?
108
+ return entity_desc.entity_class.new(args)
109
+ else
110
+ args_list = []
111
+ entity_desc.arg_mapping.each do |arg|
112
+ args_list <<= args[arg]
113
+ args.delete(arg)
114
+ end
115
+ args_list <<= args
116
+ return entity_desc.entity_class.new(*args_list)
117
+ end
118
+ end
119
+
120
+ # Parses an XML fragment into an internal representation.
121
+ def handle_node_type(xml, node_desc, node_name, args, base_name)
122
+ case node_desc[node_name]
123
+ when Symbol
124
+ node = xml.at_css(base_name + node_name.to_s)
125
+ unless node.nil?
126
+ content = node.content
127
+ case node_desc[:_converter]
128
+ when Method, Proc
129
+ content = node_desc[:_converter].call(content)
130
+ when Symbol
131
+ content = send(node_desc[:_converter], content)
132
+ end
133
+ args[node_desc[node_name]] = content unless content.nil?
134
+ end
135
+ when AuthorizeNet::EntityDescription
136
+ if node_desc[:_multivalue].nil?
137
+ entity = build_entity(xml.css(base_name + node_name.to_s), node_desc[node_name])
138
+ args[node_desc[:_value]] = entity unless entity.nil?
139
+ else
140
+ xml.css(base_name + node_name.to_s).each do |node|
141
+ entity = build_entity(node, node_desc[node_name])
142
+ args[node_desc[:_multivalue]] = args[node_desc[:_multivalue]].to_a + entity.to_a unless entity.nil?
143
+ end
144
+ end
145
+ when Array
146
+ node_desc[node_name].each do |inner_node|
147
+ inner_node_name = (inner_node.keys.reject { |k| k.to_s[0..0] == '_' }).first
148
+ args.merge!(handle_node_type(xml, inner_node, inner_node_name, args, node_name.to_s + ' '))
149
+ end
150
+ end
151
+ args
152
+ end
153
+ end
154
+ end
@@ -1,279 +1,279 @@
1
- module AuthorizeNet
2
- # The ARB transaction class.
3
- class XmlTransaction < AuthorizeNet::Transaction
4
- # The XML namespace used by the ARB API.
5
- XML_NAMESPACE = 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'.freeze
6
-
7
- # Constants for both the various Authorize.Net subscription gateways are defined here.
8
- module Gateway
9
- LIVE = 'https://api2.authorize.net/xml/v1/request.api'.freeze
10
- TEST = 'https://apitest.authorize.net/xml/v1/request.api'.freeze
11
- end
12
-
13
- # Constants for both the various Authorize.Net transaction types are defined here.
14
- module Type
15
- ARB_CREATE = "ARBCreateSubscriptionRequest".freeze
16
- ARB_UPDATE = "ARBUpdateSubscriptionRequest".freeze
17
- ARB_GET_STATUS = "ARBGetSubscriptionStatusRequest".freeze
18
- ARB_CANCEL = "ARBCancelSubscriptionRequest".freeze
19
- ARB_GET_SUBSCRIPTION_LIST = "ARBGetSubscriptionListRequest".freeze
20
- CIM_CREATE_PROFILE = "createCustomerProfileRequest".freeze
21
- CIM_CREATE_PAYMENT = "createCustomerPaymentProfileRequest".freeze
22
- CIM_CREATE_ADDRESS = "createCustomerShippingAddressRequest".freeze
23
- CIM_CREATE_TRANSACTION = "createCustomerProfileTransactionRequest".freeze
24
- CIM_DELETE_PROFILE = "deleteCustomerProfileRequest".freeze
25
- CIM_DELETE_PAYMENT = "deleteCustomerPaymentProfileRequest".freeze
26
- CIM_DELETE_ADDRESS = "deleteCustomerShippingAddressRequest".freeze
27
- CIM_GET_PROFILE_IDS = "getCustomerProfileIdsRequest".freeze
28
- CIM_GET_PROFILE = "getCustomerProfileRequest".freeze
29
- CIM_GET_PAYMENT = "getCustomerPaymentProfileRequest".freeze
30
- CIM_GET_ADDRESS = "getCustomerShippingAddressRequest".freeze
31
- CIM_GET_HOSTED_PROFILE = "getHostedProfilePageRequest".freeze
32
- CIM_UPDATE_PROFILE = "updateCustomerProfileRequest".freeze
33
- CIM_UPDATE_PAYMENT = "updateCustomerPaymentProfileRequest".freeze
34
- CIM_UPDATE_ADDRESS = "updateCustomerShippingAddressRequest".freeze
35
- CIM_UPDATE_SPLIT = "updateSplitTenderGroupRequest".freeze
36
- CIM_VALIDATE_PAYMENT = "validateCustomerPaymentProfileRequest".freeze
37
- REPORT_GET_BATCH_LIST = "getSettledBatchListRequest".freeze
38
- REPORT_GET_TRANSACTION_LIST = "getTransactionListRequest".freeze
39
- REPORT_GET_UNSETTLED_TRANSACTION_LIST = "getUnsettledTransactionListRequest".freeze
40
- REPORT_GET_TRANSACTION_DETAILS = "getTransactionDetailsRequest".freeze
41
- end
42
-
43
- # Fields to convert to/from booleans.
44
- @@boolean_fields = []
45
-
46
- # Fields to convert to/from BigDecimal.
47
- @@decimal_fields = []
48
-
49
- # Fields to convert to/from Date.
50
- @@date_fields = []
51
-
52
- # Fields to convert to/from DateTime.
53
- @@datetime_fields = []
54
-
55
- # The class to wrap our response in.
56
- @response_class = AuthorizeNet::XmlResponse
57
-
58
- # The default options for the constructor.
59
- @@option_defaults = {
60
- gateway: :production,
61
- verify_ssl: true,
62
- reference_id: nil
63
- }
64
-
65
- # DO NOT USE. Instantiate AuthorizeNet::ARB::Transaction or AuthorizeNet::CIM::Transaction instead.
66
- def initialize(api_login_id, api_transaction_key, options = {})
67
- super()
68
- @api_login_id = api_login_id
69
- @api_transaction_key = api_transaction_key
70
-
71
- @response ||= nil
72
- @type ||= nil
73
-
74
- options = @@option_defaults.merge(options)
75
- @verify_ssl = options[:verify_ssl]
76
- @reference_id = options[:reference_id]
77
- @gateway = case options[:gateway].to_s
78
- when 'sandbox', 'test'
79
- Gateway::TEST
80
- when 'production', 'live'
81
- Gateway::LIVE
82
- else
83
- @gateway = options[:gateway]
84
- options[:gateway]
85
- end
86
- end
87
-
88
- def setOAuthOptions
89
- unless @options_OAuth.blank?
90
- @options_OAuth = @@option_defaults.merge(@options_OAuth)
91
- @verify_ssl = options_OAuth[:verify_ssl]
92
- @reference_id = options_OAuth[:reference_id]
93
-
94
- @gateway = case options_OAuth[:gateway].to_s
95
- when 'sandbox', 'test'
96
- Gateway::TEST
97
- when 'production', 'live'
98
- Gateway::LIVE
99
- else
100
- @gateway = options_OAuth[:gateway]
101
- options_OAuth[:gateway]
102
- end
103
- end
104
- end
105
-
106
- # Checks if the transaction has been configured for the sandbox or not. Return FALSE if the
107
- # transaction is running against the production, TRUE otherwise.
108
- def test?
109
- @gateway != Gateway::LIVE
110
- end
111
-
112
- # Checks to see if the transaction has a response (meaning it has been submitted to the gateway).
113
- # Returns TRUE if a response is present, FALSE otherwise.
114
- def has_response?
115
- !@response.nil?
116
- end
117
-
118
- # Retrieve the response object (or Nil if transaction hasn't been sent to the gateway).
119
- attr_reader :response
120
-
121
- # Submits the transaction to the gateway for processing. Returns a response object. If the transaction
122
- # has already been run, it will return nil.
123
- def run
124
- make_request
125
- end
126
-
127
- # Returns a deep-copy of the XML object sent to the payment gateway. Or nil if there was no XML payload.
128
- attr_reader :xml
129
-
130
- #:enddoc:
131
- protected
132
-
133
- # Takes a list of nodes (a Hash is a node, and Array is a list) and returns True if any nodes
134
- # would be built by build_nodes. False if no new nodes would be generated.
135
- def has_content(nodeList, data)
136
- nodeList.each do |node|
137
- nodeName = (node.keys.reject { |_k| nodeName.to_s[0..0] == '_' }).first
138
- multivalue = node[:_multivalue]
139
- conditional = node[:_conditional]
140
- value = node[nodeName]
141
- value = send(conditional, nodeName) unless conditional.nil?
142
- case value
143
- when Array
144
- if multivalue.nil?
145
- return true if has_content(value, data)
146
- else
147
- data[multivalue].each do |v|
148
- return true if has_content(value, v)
149
- end
150
- end
151
- when Symbol
152
- converted = convert_field(value, data[value])
153
- return true unless converted.nil?
154
- else
155
- return true
156
- end
157
- end
158
- false
159
- end
160
-
161
- # Takes a list of nodes (a Hash is a node, and Array is a list) and recursively builds the XML by pulling
162
- # values as needed from data.
163
- def build_nodes(builder, nodeList, data)
164
- nodeList.each do |node|
165
- # TODO: - ADD COMMENTS HERE
166
- nodeName = (node.keys.reject { |k| k.to_s[0..0] == '_' }).first
167
- multivalue = node[:_multivalue]
168
- conditional = node[:_conditional]
169
- value = node[nodeName]
170
-
171
- value = send(conditional, nodeName) unless conditional.nil?
172
- case value
173
- when Array # node containing other nodes
174
- if multivalue.nil?
175
- proc = proc { build_nodes(builder, value, data) }
176
- builder.send(nodeName, &proc) if has_content(value, data)
177
- else
178
- data[multivalue].to_a.each do |v|
179
- proc = proc { build_nodes(builder, value, v) }
180
- builder.send(nodeName, &proc) if has_content(value, v)
181
- end
182
- end
183
- when Symbol # node containing actual data
184
- if data[value].is_a?(Array)
185
- data[value].each do |v|
186
- converted = convert_field(value, v)
187
- builder.send(nodeName, converted) unless converted.nil?
188
- end
189
- else
190
- converted = convert_field(value, data[value])
191
- builder.send(nodeName, converted) unless converted.nil?
192
- end
193
- else
194
- builder.send(nodeName, value)
195
- end
196
- end
197
- end
198
-
199
- def convert_field(field, value)
200
- if @@boolean_fields.include?(field) && !value.nil?
201
- return boolean_to_value(value)
202
- elsif @@decimal_fields.include?(field) && !value.nil?
203
- return decimal_to_value(value)
204
- elsif @@date_fields.include?(field) && !value.nil?
205
- return date_to_value(value)
206
- elsif @@datetime_fields.include?(field) && !value.nil?
207
- return datetime_to_value(value)
208
- elsif field == :extra_options
209
- # handle converting extra options
210
- options = []
211
- value.each_pair { |k, v| options <<= to_param(k, v) } unless value.nil?
212
- unless @custom_fields.nil?
213
- # special sort to maintain compatibility with AIM custom field ordering
214
- # FIXME - This should be DRY'd up.
215
- custom_field_keys = @custom_fields.keys.collect(&:to_s).sort.collect(&:to_sym)
216
- for key in custom_field_keys
217
- options <<= to_param(key, @custom_fields[key.to_sym], '')
218
- end
219
- end
220
-
221
- if !options.empty?
222
- return options.join('&')
223
- else
224
- return nil
225
- end
226
- elsif field == :exp_date
227
- # convert MMYY expiration dates into the XML equivalent
228
- unless value.nil?
229
- begin
230
- return value.to_s.casecmp('xxxx').zero? ? 'XXXX' : Date.strptime(value.to_s, '%m%y').strftime('%Y-%m')
231
- rescue StandardError
232
- # If we didn't get the exp_date in MMYY format, try our best to convert it
233
- return Date.parse(value.to_s).strftime('%Y-%m')
234
- end
235
- end
236
- end
237
-
238
- value
239
- end
240
-
241
- # An internal method that builds the POST body, submits it to the gateway, and constructs a Response object with the response.
242
- def make_request
243
- return nil if has_response?
244
-
245
- fields = @fields
246
-
247
- builder = Nokogiri::XML::Builder.new(encoding: 'utf-8') do |x|
248
- x.send(@type.to_sym, xmlns: XML_NAMESPACE) do
249
- x.merchantAuthentication do
250
- x.name @api_login_id
251
- x.transactionKey @api_transaction_key
252
- end
253
- build_nodes(x, self.class.const_get(:FIELDS)[@type], fields)
254
- end
255
- end
256
- @xml = builder.to_xml
257
-
258
- url = URI.parse(@gateway)
259
-
260
- request = Net::HTTP::Post.new(url.path)
261
- request.content_type = 'text/xml'
262
- request.body = @xml
263
- connection = Net::HTTP.new(url.host, url.port)
264
- connection.use_ssl = true
265
- if @verify_ssl
266
- connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
267
- else
268
- connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
269
- end
270
-
271
- # Use our Class's @response_class variable to find the Response class we are supposed to use.
272
- begin
273
- @response = self.class.instance_variable_get(:@response_class).new((connection.start { |http| http.request(request) }), self)
274
- rescue StandardError
275
- @response = self.class.instance_variable_get(:@response_class).new($ERROR_INFO, self)
276
- end
277
- end
278
- end
279
- end
1
+ module AuthorizeNet
2
+ # The ARB transaction class.
3
+ class XmlTransaction < AuthorizeNet::Transaction
4
+ # The XML namespace used by the ARB API.
5
+ XML_NAMESPACE = 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'.freeze
6
+
7
+ # Constants for both the various Authorize.Net subscription gateways are defined here.
8
+ module Gateway
9
+ LIVE = 'https://api2.authorize.net/xml/v1/request.api'.freeze
10
+ TEST = 'https://apitest.authorize.net/xml/v1/request.api'.freeze
11
+ end
12
+
13
+ # Constants for both the various Authorize.Net transaction types are defined here.
14
+ module Type
15
+ ARB_CREATE = "ARBCreateSubscriptionRequest".freeze
16
+ ARB_UPDATE = "ARBUpdateSubscriptionRequest".freeze
17
+ ARB_GET_STATUS = "ARBGetSubscriptionStatusRequest".freeze
18
+ ARB_CANCEL = "ARBCancelSubscriptionRequest".freeze
19
+ ARB_GET_SUBSCRIPTION_LIST = "ARBGetSubscriptionListRequest".freeze
20
+ CIM_CREATE_PROFILE = "createCustomerProfileRequest".freeze
21
+ CIM_CREATE_PAYMENT = "createCustomerPaymentProfileRequest".freeze
22
+ CIM_CREATE_ADDRESS = "createCustomerShippingAddressRequest".freeze
23
+ CIM_CREATE_TRANSACTION = "createCustomerProfileTransactionRequest".freeze
24
+ CIM_DELETE_PROFILE = "deleteCustomerProfileRequest".freeze
25
+ CIM_DELETE_PAYMENT = "deleteCustomerPaymentProfileRequest".freeze
26
+ CIM_DELETE_ADDRESS = "deleteCustomerShippingAddressRequest".freeze
27
+ CIM_GET_PROFILE_IDS = "getCustomerProfileIdsRequest".freeze
28
+ CIM_GET_PROFILE = "getCustomerProfileRequest".freeze
29
+ CIM_GET_PAYMENT = "getCustomerPaymentProfileRequest".freeze
30
+ CIM_GET_ADDRESS = "getCustomerShippingAddressRequest".freeze
31
+ CIM_GET_HOSTED_PROFILE = "getHostedProfilePageRequest".freeze
32
+ CIM_UPDATE_PROFILE = "updateCustomerProfileRequest".freeze
33
+ CIM_UPDATE_PAYMENT = "updateCustomerPaymentProfileRequest".freeze
34
+ CIM_UPDATE_ADDRESS = "updateCustomerShippingAddressRequest".freeze
35
+ CIM_UPDATE_SPLIT = "updateSplitTenderGroupRequest".freeze
36
+ CIM_VALIDATE_PAYMENT = "validateCustomerPaymentProfileRequest".freeze
37
+ REPORT_GET_BATCH_LIST = "getSettledBatchListRequest".freeze
38
+ REPORT_GET_TRANSACTION_LIST = "getTransactionListRequest".freeze
39
+ REPORT_GET_UNSETTLED_TRANSACTION_LIST = "getUnsettledTransactionListRequest".freeze
40
+ REPORT_GET_TRANSACTION_DETAILS = "getTransactionDetailsRequest".freeze
41
+ end
42
+
43
+ # Fields to convert to/from booleans.
44
+ @@boolean_fields = []
45
+
46
+ # Fields to convert to/from BigDecimal.
47
+ @@decimal_fields = []
48
+
49
+ # Fields to convert to/from Date.
50
+ @@date_fields = []
51
+
52
+ # Fields to convert to/from DateTime.
53
+ @@datetime_fields = []
54
+
55
+ # The class to wrap our response in.
56
+ @response_class = AuthorizeNet::XmlResponse
57
+
58
+ # The default options for the constructor.
59
+ @@option_defaults = {
60
+ gateway: :production,
61
+ verify_ssl: true,
62
+ reference_id: nil
63
+ }
64
+
65
+ # DO NOT USE. Instantiate AuthorizeNet::ARB::Transaction or AuthorizeNet::CIM::Transaction instead.
66
+ def initialize(api_login_id, api_transaction_key, options = {})
67
+ super()
68
+ @api_login_id = api_login_id
69
+ @api_transaction_key = api_transaction_key
70
+
71
+ @response ||= nil
72
+ @type ||= nil
73
+
74
+ options = @@option_defaults.merge(options)
75
+ @verify_ssl = options[:verify_ssl]
76
+ @reference_id = options[:reference_id]
77
+ @gateway = case options[:gateway].to_s
78
+ when 'sandbox', 'test'
79
+ Gateway::TEST
80
+ when 'production', 'live'
81
+ Gateway::LIVE
82
+ else
83
+ @gateway = options[:gateway]
84
+ options[:gateway]
85
+ end
86
+ end
87
+
88
+ def setOAuthOptions
89
+ unless @options_OAuth.blank?
90
+ @options_OAuth = @@option_defaults.merge(@options_OAuth)
91
+ @verify_ssl = options_OAuth[:verify_ssl]
92
+ @reference_id = options_OAuth[:reference_id]
93
+
94
+ @gateway = case options_OAuth[:gateway].to_s
95
+ when 'sandbox', 'test'
96
+ Gateway::TEST
97
+ when 'production', 'live'
98
+ Gateway::LIVE
99
+ else
100
+ @gateway = options_OAuth[:gateway]
101
+ options_OAuth[:gateway]
102
+ end
103
+ end
104
+ end
105
+
106
+ # Checks if the transaction has been configured for the sandbox or not. Return FALSE if the
107
+ # transaction is running against the production, TRUE otherwise.
108
+ def test?
109
+ @gateway != Gateway::LIVE
110
+ end
111
+
112
+ # Checks to see if the transaction has a response (meaning it has been submitted to the gateway).
113
+ # Returns TRUE if a response is present, FALSE otherwise.
114
+ def has_response?
115
+ !@response.nil?
116
+ end
117
+
118
+ # Retrieve the response object (or Nil if transaction hasn't been sent to the gateway).
119
+ attr_reader :response
120
+
121
+ # Submits the transaction to the gateway for processing. Returns a response object. If the transaction
122
+ # has already been run, it will return nil.
123
+ def run
124
+ make_request
125
+ end
126
+
127
+ # Returns a deep-copy of the XML object sent to the payment gateway. Or nil if there was no XML payload.
128
+ attr_reader :xml
129
+
130
+ #:enddoc:
131
+ protected
132
+
133
+ # Takes a list of nodes (a Hash is a node, and Array is a list) and returns True if any nodes
134
+ # would be built by build_nodes. False if no new nodes would be generated.
135
+ def has_content(nodeList, data)
136
+ nodeList.each do |node|
137
+ nodeName = (node.keys.reject { |_k| nodeName.to_s[0..0] == '_' }).first
138
+ multivalue = node[:_multivalue]
139
+ conditional = node[:_conditional]
140
+ value = node[nodeName]
141
+ value = send(conditional, nodeName) unless conditional.nil?
142
+ case value
143
+ when Array
144
+ if multivalue.nil?
145
+ return true if has_content(value, data)
146
+ else
147
+ data[multivalue].each do |v|
148
+ return true if has_content(value, v)
149
+ end
150
+ end
151
+ when Symbol
152
+ converted = convert_field(value, data[value])
153
+ return true unless converted.nil?
154
+ else
155
+ return true
156
+ end
157
+ end
158
+ false
159
+ end
160
+
161
+ # Takes a list of nodes (a Hash is a node, and Array is a list) and recursively builds the XML by pulling
162
+ # values as needed from data.
163
+ def build_nodes(builder, nodeList, data)
164
+ nodeList.each do |node|
165
+ # TODO: - ADD COMMENTS HERE
166
+ nodeName = (node.keys.reject { |k| k.to_s[0..0] == '_' }).first
167
+ multivalue = node[:_multivalue]
168
+ conditional = node[:_conditional]
169
+ value = node[nodeName]
170
+
171
+ value = send(conditional, nodeName) unless conditional.nil?
172
+ case value
173
+ when Array # node containing other nodes
174
+ if multivalue.nil?
175
+ proc = proc { build_nodes(builder, value, data) }
176
+ builder.send(nodeName, &proc) if has_content(value, data)
177
+ else
178
+ data[multivalue].to_a.each do |v|
179
+ proc = proc { build_nodes(builder, value, v) }
180
+ builder.send(nodeName, &proc) if has_content(value, v)
181
+ end
182
+ end
183
+ when Symbol # node containing actual data
184
+ if data[value].is_a?(Array)
185
+ data[value].each do |v|
186
+ converted = convert_field(value, v)
187
+ builder.send(nodeName, converted) unless converted.nil?
188
+ end
189
+ else
190
+ converted = convert_field(value, data[value])
191
+ builder.send(nodeName, converted) unless converted.nil?
192
+ end
193
+ else
194
+ builder.send(nodeName, value)
195
+ end
196
+ end
197
+ end
198
+
199
+ def convert_field(field, value)
200
+ if @@boolean_fields.include?(field) && !value.nil?
201
+ return boolean_to_value(value)
202
+ elsif @@decimal_fields.include?(field) && !value.nil?
203
+ return decimal_to_value(value)
204
+ elsif @@date_fields.include?(field) && !value.nil?
205
+ return date_to_value(value)
206
+ elsif @@datetime_fields.include?(field) && !value.nil?
207
+ return datetime_to_value(value)
208
+ elsif field == :extra_options
209
+ # handle converting extra options
210
+ options = []
211
+ value.each_pair { |k, v| options <<= to_param(k, v) } unless value.nil?
212
+ unless @custom_fields.nil?
213
+ # special sort to maintain compatibility with AIM custom field ordering
214
+ # FIXME - This should be DRY'd up.
215
+ custom_field_keys = @custom_fields.keys.collect(&:to_s).sort.collect(&:to_sym)
216
+ for key in custom_field_keys
217
+ options <<= to_param(key, @custom_fields[key.to_sym], '')
218
+ end
219
+ end
220
+
221
+ if !options.empty?
222
+ return options.join('&')
223
+ else
224
+ return nil
225
+ end
226
+ elsif field == :exp_date
227
+ # convert MMYY expiration dates into the XML equivalent
228
+ unless value.nil?
229
+ begin
230
+ return value.to_s.casecmp('xxxx').zero? ? 'XXXX' : Date.strptime(value.to_s, '%m%y').strftime('%Y-%m')
231
+ rescue StandardError
232
+ # If we didn't get the exp_date in MMYY format, try our best to convert it
233
+ return Date.parse(value.to_s).strftime('%Y-%m')
234
+ end
235
+ end
236
+ end
237
+
238
+ value
239
+ end
240
+
241
+ # An internal method that builds the POST body, submits it to the gateway, and constructs a Response object with the response.
242
+ def make_request
243
+ return nil if has_response?
244
+
245
+ fields = @fields
246
+
247
+ builder = Nokogiri::XML::Builder.new(encoding: 'utf-8') do |x|
248
+ x.send(@type.to_sym, xmlns: XML_NAMESPACE) do
249
+ x.merchantAuthentication do
250
+ x.name @api_login_id
251
+ x.transactionKey @api_transaction_key
252
+ end
253
+ build_nodes(x, self.class.const_get(:FIELDS)[@type], fields)
254
+ end
255
+ end
256
+ @xml = builder.to_xml
257
+
258
+ url = URI.parse(@gateway)
259
+
260
+ request = Net::HTTP::Post.new(url.path)
261
+ request.content_type = 'text/xml'
262
+ request.body = @xml
263
+ connection = Net::HTTP.new(url.host, url.port)
264
+ connection.use_ssl = true
265
+ if @verify_ssl
266
+ connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
267
+ else
268
+ connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
269
+ end
270
+
271
+ # Use our Class's @response_class variable to find the Response class we are supposed to use.
272
+ begin
273
+ @response = self.class.instance_variable_get(:@response_class).new((connection.start { |http| http.request(request) }), self)
274
+ rescue StandardError
275
+ @response = self.class.instance_variable_get(:@response_class).new($ERROR_INFO, self)
276
+ end
277
+ end
278
+ end
279
+ end