authorizenet 1.8.1 → 1.8.2

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/lib/authorize_net.rb +5 -0
  3. data/lib/authorize_net.rb~ +95 -0
  4. data/lib/authorize_net/aim/response.rb~ +131 -0
  5. data/lib/authorize_net/arb/fields.rb +24 -0
  6. data/lib/authorize_net/arb/fields.rb~ +13 -0
  7. data/lib/authorize_net/arb/paging.rb +33 -0
  8. data/lib/authorize_net/arb/paging.rb~ +25 -0
  9. data/lib/authorize_net/arb/response.rb~ +68 -0
  10. data/lib/authorize_net/arb/sorting.rb +43 -0
  11. data/lib/authorize_net/arb/sorting.rb~ +43 -0
  12. data/lib/authorize_net/arb/subscription.rb +5 -5
  13. data/lib/authorize_net/arb/subscription_detail.rb +14 -0
  14. data/lib/authorize_net/arb/subscription_detail.rb~ +56 -0
  15. data/lib/authorize_net/arb/subscription_list_response.rb +43 -0
  16. data/lib/authorize_net/arb/subscription_list_response.rb~ +45 -0
  17. data/lib/authorize_net/arb/transaction.rb +35 -4
  18. data/lib/authorize_net/arb/transaction.rb~ +176 -0
  19. data/lib/authorize_net/fields.rb +21 -3
  20. data/lib/authorize_net/fields.rb~ +767 -0
  21. data/lib/authorize_net/key_value_transaction.rb +0 -2
  22. data/lib/authorize_net/payment_methods/credit_card.rb +20 -32
  23. data/lib/authorize_net/xml_response.rb~ +173 -0
  24. data/lib/authorize_net/xml_transaction.rb +5 -5
  25. data/lib/authorize_net/xml_transaction.rb~ +276 -0
  26. data/lib/generators/authorize_net/{direct_post_generator.rb → direct_post/direct_post_generator.rb} +4 -2
  27. data/lib/generators/authorize_net/direct_post/templates/README-AuthorizeNet +49 -0
  28. data/lib/generators/authorize_net/direct_post/templates/config.yml.erb +8 -0
  29. data/lib/generators/authorize_net/direct_post/templates/config.yml.rails3.erb +8 -0
  30. data/lib/generators/authorize_net/direct_post/templates/controller.rb.erb +31 -0
  31. data/lib/generators/authorize_net/direct_post/templates/initializer.rb +4 -0
  32. data/lib/generators/authorize_net/direct_post/templates/layout.erb +18 -0
  33. data/lib/generators/authorize_net/direct_post/templates/payment.erb +10 -0
  34. data/lib/generators/authorize_net/direct_post/templates/payment.rails3.erb +10 -0
  35. data/lib/generators/authorize_net/direct_post/templates/receipt.erb +1 -0
  36. data/lib/generators/authorize_net/direct_post/templates/relay_response.erb +1 -0
  37. data/lib/generators/authorize_net/{sim_generator.rb → sim/sim_generator.rb} +2 -2
  38. data/lib/generators/authorize_net/sim/templates/README-AuthorizeNet +52 -0
  39. data/lib/generators/authorize_net/sim/templates/config.yml.erb +8 -0
  40. data/lib/generators/authorize_net/sim/templates/config.yml.rails3.erb +8 -0
  41. data/lib/generators/authorize_net/sim/templates/controller.rb.erb +21 -0
  42. data/lib/generators/authorize_net/sim/templates/initializer.rb +4 -0
  43. data/lib/generators/authorize_net/sim/templates/layout.erb +18 -0
  44. data/lib/generators/authorize_net/sim/templates/payment.erb +6 -0
  45. data/lib/generators/authorize_net/sim/templates/payment.rails3.erb +6 -0
  46. data/lib/generators/authorize_net/sim/templates/thank_you.erb +1 -0
  47. data/lib/generators/generator_extensions.rb +75 -0
  48. metadata +41 -10
@@ -8,10 +8,8 @@ module AuthorizeNet
8
8
  module Gateway
9
9
  LIVE = 'https://secure.authorize.net/gateway/transact.dll'
10
10
  TEST = 'https://test.authorize.net/gateway/transact.dll'
11
- #TEST = 'https://qagreta1d.qa.intra/gateway/transact.dll'
12
11
  CARD_PRESENT_LIVE = 'https://cardpresent.authorize.net/gateway/transact.dll'
13
12
  CARD_PRESENT_TEST = 'https://test.authorize.net/gateway/transact.dll'
14
- #CARD_PRESENT_TEST = 'https://qagreta1d.qa.intra/gateway/transact.dll'
15
13
  end
16
14
 
17
15
  # Constants for both the various Authorize.Net payment transaction types are defined here.
@@ -1,41 +1,41 @@
1
1
  module AuthorizeNet
2
-
2
+
3
3
  # Defines constants for each payment method type.
4
4
  module PaymentMethodType
5
5
  CREDIT_CARD = 'CC'
6
6
  end
7
-
7
+
8
8
  # Models a credit card.
9
9
  class CreditCard
10
10
  PAYMENT_METHOD_CODE = AuthorizeNet::PaymentMethodType::CREDIT_CARD
11
-
11
+
12
12
  # The option defaults for the constructor.
13
13
  @@option_defaults = {
14
14
  :card_code => nil,
15
15
  :card_type => nil
16
16
  }
17
-
17
+
18
18
  attr_accessor :card_number, :expiration, :card_code, :card_type, :track_1, :track_2
19
-
19
+
20
20
  # Constructs a new credit card object. Takes a credit card number
21
21
  # and an expiration date. The CCV code can be passed as an option. So can
22
22
  # the data tracks (1 & 2). When passing in data tracks, please pass the
23
23
  # whole track. Sentinels and the LRC will be removed by the SDK. Track data
24
24
  # must be passed along as ASCII. The raw bit stream from the card is not acceptable.
25
- #
25
+ #
26
26
  # Field separators on
27
- #
27
+ #
28
28
  # +card_number+:: The credit card number as a string.
29
29
  # +expiration+:: The credit card expiration date as a string with format MMYY.
30
30
  # +options+:: A hash of options.
31
- #
31
+ #
32
32
  # Options
33
33
  # +card_code+:: Sets the CCV code for the credit card.
34
34
  # +card_type+:: Sets the type of card (Visa, MasterCard, Dinners Club, etc.)
35
35
  # +track_1+:: Sets the track 1 data. Either track 1 or track 2 data needs to be included for card present transactions (otherwise fee structure will change).
36
36
  # +track_2+:: Sets the track 2 data. Either track 1 or track 2 data needs to be included for card present transactions (otherwise fee structure will change).
37
37
  #
38
- #
38
+ #
39
39
  def initialize(card_number, expiration, options = {})
40
40
  @card_number = card_number
41
41
  @expiration = expiration
@@ -45,30 +45,18 @@ module AuthorizeNet
45
45
  @track_1 = options[:track_1]
46
46
  @track_2 = options[:track_2]
47
47
  end
48
-
48
+
49
49
  def to_hash
50
- hash = {
51
- :method => PAYMENT_METHOD_CODE,
52
- :card_num => @card_number,
53
- :exp_date => @expiration
54
- }
55
- hash[:card_code] = @card_code unless @card_code.nil?
56
- unless @track_1.nil?
57
- track_1 = @track_1
58
- if track_1[0..0] == '%'
59
- track_1 = track_1[1..(@track_1.rindex('?') - 1)]
60
- end
61
- hash[:track1] = track_1
62
- end
63
- unless @track_2.nil?
64
- track_2 = @track_2
65
- if track_2[0..0] == ';'
66
- track_2 = track_2[1..(@track_2.rindex('?') - 1)]
67
- end
68
- hash[:track2] = track_2
50
+ Hash.new.tap do |ch|
51
+ ch[:method] = PAYMENT_METHOD_CODE
52
+ ch[:card_num] = @card_number
53
+ ch[:exp_date] = @expiration
54
+ ch[:card_code] = @card_code if @card_code
55
+ ch[:track1] = @track_1.match(/(%|^)(.*?)(\?|$)/)[2] if @track_1
56
+ ch[:track2] = @track_2.match(/(;|^)(.*?)(\?|$)/)[2] if @track_2
57
+ #ch[:track1] = @track_1.match(/^%(?<fc>.)(?<p>[\d]{1,19}+)\^(?<n>.{2,26})\^(?<e>[\d]{0,4}|\^)(?<sc>[\d]{0,3}|\^)(?<dd>.*)\?\Z/) if @track_1
58
+ #ch[:track2] = @track_2.match(/\A;(?<pan>[\d]{1,19}+)=(?<expiration>[\d]{0,4}|=)(?<service_code>[\d]{0,3}|=)(?<discretionary_data>.*)\?\Z/) if @track_2
69
59
  end
70
- hash
71
60
  end
72
-
73
61
  end
74
- end
62
+ end
@@ -0,0 +1,173 @@
1
+ module AuthorizeNet
2
+
3
+ # The core, xml response class. You shouldn't instantiate this one.
4
+ # Instead you should use AuthorizeNet::ARB::Response.
5
+ class XmlResponse < AuthorizeNet::Response
6
+
7
+ # DO NOT USE. Instantiate AuthorizeNet::ARB::Response or AuthorizeNet::CIM::Response instead.
8
+ def initialize(raw_response, transaction)
9
+ @raw_response = raw_response
10
+ @transaction = transaction
11
+ unless connection_failure?
12
+ begin
13
+ xml = Nokogiri::XML(@raw_response.body) do |config|
14
+ # confirm noent is the right flag
15
+ config.recover.noent.nonet
16
+ end
17
+ @root = xml.children[0]
18
+ @result_code = node_content_unless_nil(@root.at_css('messages resultCode'))
19
+ @message_code = node_content_unless_nil(@root.at_css('messages message code'))
20
+ @message_text = node_content_unless_nil(@root.at_css('messages message text'))
21
+ @reference_id = node_content_unless_nil(@root.at_css('refId'))
22
+ rescue
23
+ @raw_response = $!
24
+ end
25
+ end
26
+ end
27
+
28
+ # Check to see if the response indicated success. Success is defined as a 200 OK response with a resultCode
29
+ # of 'Ok'.
30
+ def success?
31
+ puts "there"
32
+ !connection_failure? && @result_code == 'Ok'
33
+ end
34
+
35
+ # Returns true if we failed to open a connection to the gateway or got back a non-200 OK HTTP response.
36
+ def connection_failure?
37
+ !@raw_response.kind_of?(Net::HTTPOK)
38
+ end
39
+
40
+ # Returns the underlying Net::HTTPResponse object. This has the original response body along with
41
+ # headers and such. Note that if an exception is generated while making the request (which happens
42
+ # if there is no internet connection for example), you will get the exception object here instead of
43
+ # a Net::HTTPResponse object.
44
+ def raw
45
+ @raw_response
46
+ end
47
+
48
+ # Returns a deep-copy of the XML object received from the payment gateway. Or nil if there was no XML payload.
49
+ def xml
50
+ @root.dup unless @root.nil?
51
+ end
52
+
53
+ # Returns the resultCode from the XML response. resultCode will be either 'Ok' or 'Error'.
54
+ def result_code
55
+ @result_code
56
+ end
57
+
58
+ # Returns the messageCode from the XML response. This is a code indicating the details of an error
59
+ # or success.
60
+ def message_code
61
+ @message_code
62
+ end
63
+
64
+ # Returns the messageText from the XML response. This is a text description of the message_code.
65
+ def message_text
66
+ @message_text
67
+ end
68
+
69
+ # Alias for result_code.
70
+ def response_code
71
+ result_code
72
+ end
73
+
74
+ # Alias for message_code.
75
+ def response_reason_code
76
+ message_code
77
+ end
78
+
79
+ # Alias for message_text.
80
+ def response_reason_text
81
+ message_text
82
+ end
83
+
84
+ # Returns the refId from the response if there is one. Otherwise returns nil.
85
+ def reference_id
86
+ @reference_id
87
+ end
88
+
89
+ #:enddoc:
90
+ protected
91
+
92
+ def node_content_unless_nil(node)
93
+ if node.nil?
94
+ nil
95
+ else
96
+ node.content
97
+ end
98
+ end
99
+
100
+ def node_child_content_unless_nil(node)
101
+ if node.nil?
102
+ nil
103
+ else
104
+ if node.children.length > 0
105
+ node.children.collect(&:content)
106
+ else
107
+ nil
108
+ end
109
+ end
110
+ end
111
+
112
+ # Transforms a block of XML into a model Object defined by entity_desc.
113
+ def build_entity(xml, entity_desc)
114
+ args = {}
115
+ entity_desc.node_structure.each do |node_desc|
116
+ node_name = (node_desc.keys.reject {|k| k.to_s[0..0] == '_' }).first
117
+ args.merge!(handle_node_type(xml, node_desc, node_name, args, ''))
118
+ end
119
+
120
+ if args.length == 0
121
+ return nil
122
+ end
123
+
124
+ if entity_desc.arg_mapping.nil?
125
+ return entity_desc.entity_class.new(args)
126
+ else
127
+ args_list = []
128
+ entity_desc.arg_mapping.each do |arg|
129
+ args_list <<= args[arg]
130
+ args.delete(arg)
131
+ end
132
+ args_list <<= args
133
+ return entity_desc.entity_class.new(*args_list)
134
+ end
135
+ end
136
+
137
+ # Parses an XML fragment into an internal representation.
138
+ def handle_node_type(xml, node_desc, node_name, args, base_name)
139
+ case node_desc[node_name]
140
+ when Symbol
141
+ node = xml.at_css(base_name + node_name.to_s)
142
+ unless node.nil?
143
+ content = node.content
144
+ case node_desc[:_converter]
145
+ when Method, Proc
146
+ content = node_desc[:_converter].call(content)
147
+ when Symbol
148
+ content = self.send(node_desc[:_converter], content)
149
+ end
150
+ args[node_desc[node_name]] = content unless content.nil?
151
+ end
152
+ when AuthorizeNet::EntityDescription
153
+ unless node_desc[:_multivalue].nil?
154
+ xml.css(base_name + node_name.to_s).each do |node|
155
+ entity = build_entity(node, node_desc[node_name])
156
+ args[node_desc[:_multivalue]] = args[node_desc[:_multivalue]].to_a + entity.to_a unless entity.nil?
157
+ end
158
+ else
159
+ entity = build_entity(xml.css(base_name + node_name.to_s), node_desc[node_name])
160
+ args[node_desc[:_value]] = entity unless entity.nil?
161
+ end
162
+ when Array
163
+ node_desc[node_name].each do |inner_node|
164
+ inner_node_name = (inner_node.keys.reject {|k| k.to_s[0..0] == '_' }).first
165
+ args.merge!(handle_node_type(xml, inner_node, inner_node_name, args, node_name.to_s + ' '))
166
+ end
167
+ end
168
+ return args
169
+ end
170
+
171
+ end
172
+
173
+ end
@@ -10,7 +10,6 @@ module AuthorizeNet
10
10
  module Gateway
11
11
  LIVE = 'https://api.authorize.net/xml/v1/request.api'
12
12
  TEST = 'https://apitest.authorize.net/xml/v1/request.api'
13
- #TEST = 'https://qagrecp1d.vposdownload.qa.intra/xml/v1/request.api'
14
13
  end
15
14
 
16
15
  # Constants for both the various Authorize.Net transaction types are defined here.
@@ -19,6 +18,7 @@ module AuthorizeNet
19
18
  ARB_UPDATE = "ARBUpdateSubscriptionRequest"
20
19
  ARB_GET_STATUS = "ARBGetSubscriptionStatusRequest"
21
20
  ARB_CANCEL = "ARBCancelSubscriptionRequest"
21
+ ARB_GET_SUBSCRIPTION_LIST = "ARBGetSubscriptionListRequest"
22
22
  CIM_CREATE_PROFILE = "createCustomerProfileRequest"
23
23
  CIM_CREATE_PAYMENT = "createCustomerPaymentProfileRequest"
24
24
  CIM_CREATE_ADDRESS = "createCustomerShippingAddressRequest"
@@ -158,6 +158,7 @@ module AuthorizeNet
158
158
  multivalue = node[:_multivalue]
159
159
  conditional = node[:_conditional]
160
160
  value = node[nodeName]
161
+
161
162
  unless conditional.nil?
162
163
  value = self.send(conditional, nodeName)
163
164
  end
@@ -239,7 +240,7 @@ module AuthorizeNet
239
240
  end
240
241
 
241
242
  fields = @fields
242
-
243
+
243
244
  builder = Nokogiri::XML::Builder.new(:encoding => 'utf-8') do |x|
244
245
  x.send(@type.to_sym, :xmlns => XML_NAMESPACE) {
245
246
  x.merchantAuthentication {
@@ -249,8 +250,7 @@ module AuthorizeNet
249
250
  build_nodes(x, self.class.const_get(:FIELDS)[@type], fields)
250
251
  }
251
252
  end
252
- @xml = builder.to_xml
253
-
253
+ @xml = builder.to_xml
254
254
  url = URI.parse(@gateway)
255
255
 
256
256
  request = Net::HTTP::Post.new(url.path)
@@ -273,4 +273,4 @@ module AuthorizeNet
273
273
  end
274
274
 
275
275
  end
276
- end
276
+ end
@@ -0,0 +1,276 @@
1
+ module AuthorizeNet
2
+
3
+ # The ARB transaction class.
4
+ class XmlTransaction < AuthorizeNet::Transaction
5
+
6
+ # The XML namespace used by the ARB API.
7
+ XML_NAMESPACE = 'AnetApi/xml/v1/schema/AnetApiSchema.xsd'
8
+
9
+ # Constants for both the various Authorize.Net subscription gateways are defined here.
10
+ module Gateway
11
+ LIVE = 'https://api.authorize.net/xml/v1/request.api'
12
+ TEST = 'https://apitest.authorize.net/xml/v1/request.api'
13
+ end
14
+
15
+ # Constants for both the various Authorize.Net transaction types are defined here.
16
+ module Type
17
+ ARB_CREATE = "ARBCreateSubscriptionRequest"
18
+ ARB_UPDATE = "ARBUpdateSubscriptionRequest"
19
+ ARB_GET_STATUS = "ARBGetSubscriptionStatusRequest"
20
+ ARB_CANCEL = "ARBCancelSubscriptionRequest"
21
+ ARB_GET_SUBSCRIPTION_LIST = "ARBGetSubscriptionListRequest"
22
+ CIM_CREATE_PROFILE = "createCustomerProfileRequest"
23
+ CIM_CREATE_PAYMENT = "createCustomerPaymentProfileRequest"
24
+ CIM_CREATE_ADDRESS = "createCustomerShippingAddressRequest"
25
+ CIM_CREATE_TRANSACTION = "createCustomerProfileTransactionRequest"
26
+ CIM_DELETE_PROFILE = "deleteCustomerProfileRequest"
27
+ CIM_DELETE_PAYMENT = "deleteCustomerPaymentProfileRequest"
28
+ CIM_DELETE_ADDRESS = "deleteCustomerShippingAddressRequest"
29
+ CIM_GET_PROFILE_IDS = "getCustomerProfileIdsRequest"
30
+ CIM_GET_PROFILE = "getCustomerProfileRequest"
31
+ CIM_GET_PAYMENT = "getCustomerPaymentProfileRequest"
32
+ CIM_GET_ADDRESS = "getCustomerShippingAddressRequest"
33
+ CIM_UPDATE_PROFILE = "updateCustomerProfileRequest"
34
+ CIM_UPDATE_PAYMENT = "updateCustomerPaymentProfileRequest"
35
+ CIM_UPDATE_ADDRESS = "updateCustomerShippingAddressRequest"
36
+ CIM_UPDATE_SPLIT = "updateSplitTenderGroupRequest"
37
+ CIM_VALIDATE_PAYMENT = "validateCustomerPaymentProfileRequest"
38
+ REPORT_GET_BATCH_LIST = "getSettledBatchListRequest"
39
+ REPORT_GET_TRANSACTION_LIST = "getTransactionListRequest"
40
+ REPORT_GET_TRANSACTION_DETAILS = "getTransactionDetailsRequest"
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 => false,
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
+ case options[:gateway]
78
+ when :sandbox, :test
79
+ @gateway = Gateway::TEST
80
+ when :production, :live
81
+ @gateway = Gateway::LIVE
82
+ else
83
+ @gateway = options[:gateway]
84
+ end
85
+ end
86
+
87
+ # Checks if the transaction has been configured for the sandbox or not. Return FALSE if the
88
+ # transaction is running against the production, TRUE otherwise.
89
+ def test?
90
+ @gateway != Gateway::LIVE
91
+ end
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
+ !@response.nil?
97
+ end
98
+
99
+ # Retrieve the response object (or Nil if transaction hasn't been sent to the gateway).
100
+ def response
101
+ @response
102
+ end
103
+
104
+ # Submits the transaction to the gateway for processing. Returns a response object. If the transaction
105
+ # has already been run, it will return nil.
106
+ def run
107
+ make_request
108
+ end
109
+
110
+ # Returns a deep-copy of the XML object sent to the payment gateway. Or nil if there was no XML payload.
111
+ def xml
112
+ @xml
113
+ end
114
+
115
+ #:enddoc:
116
+ protected
117
+
118
+ # Takes a list of nodes (a Hash is a node, and Array is a list) and returns True if any nodes
119
+ # would be built by build_nodes. False if no new nodes would be generated.
120
+ def has_content(nodeList, data)
121
+ nodeList.each do |node|
122
+ nodeName = (node.keys.reject {|k| nodeName.to_s[0..0] == '_' }).first
123
+ multivalue = node[:_multivalue]
124
+ conditional = node[:_conditional]
125
+ value = node[nodeName]
126
+ unless conditional.nil?
127
+ value = self.send(conditional, nodeName)
128
+ end
129
+ case value
130
+ when Array
131
+ if multivalue.nil?
132
+ if has_content(value, data)
133
+ return true
134
+ end
135
+ else
136
+ data[multivalue].each do |v|
137
+ if has_content(value, v)
138
+ return true
139
+ end
140
+ end
141
+ end
142
+ when Symbol
143
+ converted = convert_field(value, data[value])
144
+ return true unless converted.nil?
145
+ else
146
+ return true
147
+ end
148
+ end
149
+ false
150
+ end
151
+
152
+ # Takes a list of nodes (a Hash is a node, and Array is a list) and recursively builds the XML by pulling
153
+ # values as needed from data.
154
+ def build_nodes(builder, nodeList, data)
155
+ nodeList.each do |node|
156
+ # TODO - ADD COMMENTS HERE
157
+ nodeName = (node.keys.reject {|k| k.to_s[0..0] == '_' }).first
158
+ multivalue = node[:_multivalue]
159
+ conditional = node[:_conditional]
160
+ value = node[nodeName]
161
+
162
+ unless conditional.nil?
163
+ value = self.send(conditional, nodeName)
164
+ end
165
+ case value
166
+ when Array # node containing other nodes
167
+ if multivalue.nil?
168
+ proc = Proc.new { build_nodes(builder, value, data) }
169
+ builder.send(nodeName, &proc) if has_content(value, data)
170
+ else
171
+ data[multivalue].to_a.each do |v|
172
+ proc = Proc.new { build_nodes(builder, value, v) }
173
+ builder.send(nodeName, &proc) if has_content(value, v)
174
+ end
175
+ end
176
+ when Symbol # node containing actual data
177
+ if data[value].kind_of?(Array)
178
+ data[value].each do |v|
179
+ converted = convert_field(value, v)
180
+ builder.send(nodeName, converted) unless converted.nil?
181
+ end
182
+ else
183
+ converted = convert_field(value, data[value])
184
+ builder.send(nodeName, converted) unless converted.nil?
185
+ end
186
+ else
187
+ builder.send(nodeName, value)
188
+ end
189
+ end
190
+ end
191
+
192
+ def convert_field(field, value)
193
+ if @@boolean_fields.include?(field) and !value.nil?
194
+ return boolean_to_value(value)
195
+ elsif @@decimal_fields.include?(field) and !value.nil?
196
+ return decimal_to_value(value)
197
+ elsif @@date_fields.include?(field) and !value.nil?
198
+ return date_to_value(value)
199
+ elsif @@datetime_fields.include?(field) and !value.nil?
200
+ return datetime_to_value(value)
201
+ elsif field == :extra_options
202
+ # handle converting extra options
203
+ options = []
204
+ unless value.nil?
205
+ value.each_pair{|k,v| options <<= self.to_param(k, v)}
206
+ end
207
+ unless @custom_fields.nil?
208
+ # special sort to maintain compatibility with AIM custom field ordering
209
+ # FIXME - This should be DRY'd up.
210
+ custom_field_keys = @custom_fields.keys.collect(&:to_s).sort.collect(&:to_sym)
211
+ for key in custom_field_keys
212
+ options <<= self.to_param(key, @custom_fields[key.to_sym], '')
213
+ end
214
+ end
215
+
216
+ if options.length > 0
217
+ return options.join('&')
218
+ else
219
+ return nil
220
+ end
221
+ elsif field == :exp_date
222
+ # convert MMYY expiration dates into the XML equivalent
223
+ unless value.nil?
224
+ begin
225
+ return value.to_s.downcase == 'xxxx' ? 'XXXX' : Date.strptime(value.to_s, '%m%y').strftime('%Y-%m')
226
+ rescue
227
+ # If we didn't get the exp_date in MMYY format, try our best to convert it
228
+ return Date.parse(value.to_s).strftime('%Y-%m')
229
+ end
230
+ end
231
+ end
232
+
233
+ value
234
+ end
235
+
236
+ # An internal method that builds the POST body, submits it to the gateway, and constructs a Response object with the response.
237
+ def make_request
238
+ if has_response?
239
+ return nil
240
+ end
241
+
242
+ fields = @fields
243
+
244
+ builder = Nokogiri::XML::Builder.new(:encoding => 'utf-8') do |x|
245
+ x.send(@type.to_sym, :xmlns => XML_NAMESPACE) {
246
+ x.merchantAuthentication {
247
+ x.name @api_login_id
248
+ x.transactionKey @api_transaction_key
249
+ }
250
+ build_nodes(x, self.class.const_get(:FIELDS)[@type], fields)
251
+ }
252
+ end
253
+ @xml = builder.to_xml
254
+ url = URI.parse(@gateway)
255
+
256
+ request = Net::HTTP::Post.new(url.path)
257
+ request.content_type = 'text/xml'
258
+ request.body = @xml
259
+ connection = Net::HTTP.new(url.host, url.port)
260
+ connection.use_ssl = true
261
+ if @verify_ssl
262
+ connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
263
+ else
264
+ connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
265
+ end
266
+
267
+ # Use our Class's @response_class variable to find the Response class we are supposed to use.
268
+ begin
269
+ @response = self.class.instance_variable_get(:@response_class).new((connection.start {|http| http.request(request)}), self)
270
+ rescue
271
+ @response = self.class.instance_variable_get(:@response_class).new($!, self)
272
+ end
273
+ end
274
+
275
+ end
276
+ end