authorizenet 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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