authorizenet 1.8

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 (44) hide show
  1. checksums.yaml +7 -0
  2. data/lib/app/helpers/authorize_net_helper.rb +24 -0
  3. data/lib/authorize_net.rb +92 -0
  4. data/lib/authorize_net/addresses/address.rb +29 -0
  5. data/lib/authorize_net/addresses/shipping_address.rb +26 -0
  6. data/lib/authorize_net/aim/response.rb +131 -0
  7. data/lib/authorize_net/aim/transaction.rb +184 -0
  8. data/lib/authorize_net/arb/response.rb +34 -0
  9. data/lib/authorize_net/arb/subscription.rb +72 -0
  10. data/lib/authorize_net/arb/transaction.rb +146 -0
  11. data/lib/authorize_net/authorize_net.rb +154 -0
  12. data/lib/authorize_net/cim/customer_profile.rb +19 -0
  13. data/lib/authorize_net/cim/payment_profile.rb +37 -0
  14. data/lib/authorize_net/cim/response.rb +110 -0
  15. data/lib/authorize_net/cim/transaction.rb +678 -0
  16. data/lib/authorize_net/customer.rb +27 -0
  17. data/lib/authorize_net/email_receipt.rb +24 -0
  18. data/lib/authorize_net/fields.rb +736 -0
  19. data/lib/authorize_net/key_value_response.rb +117 -0
  20. data/lib/authorize_net/key_value_transaction.rb +291 -0
  21. data/lib/authorize_net/line_item.rb +25 -0
  22. data/lib/authorize_net/order.rb +42 -0
  23. data/lib/authorize_net/payment_methods/credit_card.rb +74 -0
  24. data/lib/authorize_net/payment_methods/echeck.rb +72 -0
  25. data/lib/authorize_net/reporting/batch.rb +19 -0
  26. data/lib/authorize_net/reporting/batch_statistics.rb +19 -0
  27. data/lib/authorize_net/reporting/fds_filter.rb +11 -0
  28. data/lib/authorize_net/reporting/response.rb +127 -0
  29. data/lib/authorize_net/reporting/transaction.rb +116 -0
  30. data/lib/authorize_net/reporting/transaction_details.rb +25 -0
  31. data/lib/authorize_net/response.rb +27 -0
  32. data/lib/authorize_net/sim/hosted_payment_form.rb +38 -0
  33. data/lib/authorize_net/sim/hosted_receipt_page.rb +37 -0
  34. data/lib/authorize_net/sim/response.rb +142 -0
  35. data/lib/authorize_net/sim/transaction.rb +138 -0
  36. data/lib/authorize_net/transaction.rb +66 -0
  37. data/lib/authorize_net/xml +65 -0
  38. data/lib/authorize_net/xml_response.rb +172 -0
  39. data/lib/authorize_net/xml_transaction.rb +275 -0
  40. data/lib/authorizenet.rb +4 -0
  41. data/lib/generators/authorize_net/direct_post_generator.rb +51 -0
  42. data/lib/generators/authorize_net/sim_generator.rb +47 -0
  43. data/lib/yankl.rb +4 -0
  44. metadata +106 -0
@@ -0,0 +1,65 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <createCustomerProfileTransactionRequest xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd">
3
+ <merchantAuthentication>
4
+ <name>4Pf3E54zCsW</name>
5
+ <transactionKey>337UTt4y6DB64ft8</transactionKey>
6
+ </merchantAuthentication>
7
+ <transaction>
8
+ <profileTransRefund>
9
+ <amount>66.35</amount>
10
+ <customerProfileId>24580093</customerProfileId>
11
+ <customerPaymentProfileId>22514176</customerPaymentProfileId>
12
+ <transId>2208268756</transId>
13
+ </profileTransRefund>
14
+ </transaction>
15
+ </createCustomerProfileTransactionRequest>
16
+
17
+ <?xml version="1.0" encoding="utf-8"?>
18
+ <createCustomerProfileTransactionRequest xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd">
19
+ <merchantAuthentication>
20
+ <name>API_LOGIN_ID</name>
21
+ <transactionKey>API_TRANSACTION_KEY</transactionKey>
22
+ </merchantAuthentication>
23
+ <transaction>
24
+ <profileTransRefund>
25
+ <amount>10.95</amount>
26
+ <tax>
27
+ <amount>1.00</amount>
28
+ <name>WA state sales tax</name>
29
+ <description>Washington state sales tax</description>
30
+ </tax>
31
+ <shipping>
32
+ <amount>2.00</amount>
33
+ <name>ground based shipping</name>
34
+ <description>Ground based 5 to 10 day shipping</description>
35
+ </shipping>
36
+ <lineItems>
37
+ <itemId>ITEM00001</itemId>
38
+ <name>name of item sold</name>
39
+ <description>Description of item sold</description>
40
+ <quantity>1</quantity>
41
+ <unitPrice>6.95</unitPrice>
42
+ <taxable>true</taxable>
43
+ </lineItems>
44
+ <lineItems>
45
+ <itemId>ITEM00002</itemId>
46
+ <name>name of other item sold</name>
47
+ <description>Description of other item sold</description>
48
+ <quantity>1</quantity>
49
+ <unitPrice>1.00</unitPrice>
50
+ <taxable>true</taxable>
51
+ </lineItems>
52
+ <customerProfileId>10000</customerProfileId>
53
+ <customerPaymentProfileId>20000</customerPaymentProfileId>
54
+ <customerShippingAddressId>30000</customerShippingAddressId>
55
+ <creditCardNumberMasked>XXXX1111</creditCardNumberMasked>
56
+ <order>
57
+ <invoiceNumber>INV000001</invoiceNumber>
58
+ <description>description of transaction</description>
59
+ <purchaseOrderNumber>PONUM000001</purchaseOrderNumber>
60
+ </order>
61
+ <transId>40000</transId>
62
+ </profileTransRefund>
63
+ </transaction>
64
+ <extraOptions><![CDATA[]]></extraOptions>
65
+ </createCustomerProfileTransactionRequest>
@@ -0,0 +1,172 @@
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
+ !connection_failure? && @result_code == 'Ok'
32
+ end
33
+
34
+ # Returns true if we failed to open a connection to the gateway or got back a non-200 OK HTTP response.
35
+ def connection_failure?
36
+ !@raw_response.kind_of?(Net::HTTPOK)
37
+ end
38
+
39
+ # Returns the underlying Net::HTTPResponse object. This has the original response body along with
40
+ # headers and such. Note that if an exception is generated while making the request (which happens
41
+ # if there is no internet connection for example), you will get the exception object here instead of
42
+ # a Net::HTTPResponse object.
43
+ def raw
44
+ @raw_response
45
+ end
46
+
47
+ # Returns a deep-copy of the XML object received from the payment gateway. Or nil if there was no XML payload.
48
+ def xml
49
+ @root.dup unless @root.nil?
50
+ end
51
+
52
+ # Returns the resultCode from the XML response. resultCode will be either 'Ok' or 'Error'.
53
+ def result_code
54
+ @result_code
55
+ end
56
+
57
+ # Returns the messageCode from the XML response. This is a code indicating the details of an error
58
+ # or success.
59
+ def message_code
60
+ @message_code
61
+ end
62
+
63
+ # Returns the messageText from the XML response. This is a text description of the message_code.
64
+ def message_text
65
+ @message_text
66
+ end
67
+
68
+ # Alias for result_code.
69
+ def response_code
70
+ result_code
71
+ end
72
+
73
+ # Alias for message_code.
74
+ def response_reason_code
75
+ message_code
76
+ end
77
+
78
+ # Alias for message_text.
79
+ def response_reason_text
80
+ message_text
81
+ end
82
+
83
+ # Returns the refId from the response if there is one. Otherwise returns nil.
84
+ def reference_id
85
+ @reference_id
86
+ end
87
+
88
+ #:enddoc:
89
+ protected
90
+
91
+ def node_content_unless_nil(node)
92
+ if node.nil?
93
+ nil
94
+ else
95
+ node.content
96
+ end
97
+ end
98
+
99
+ def node_child_content_unless_nil(node)
100
+ if node.nil?
101
+ nil
102
+ else
103
+ if node.children.length > 0
104
+ node.children.collect(&:content)
105
+ else
106
+ nil
107
+ end
108
+ end
109
+ end
110
+
111
+ # Transforms a block of XML into a model Object defined by entity_desc.
112
+ def build_entity(xml, entity_desc)
113
+ args = {}
114
+ entity_desc.node_structure.each do |node_desc|
115
+ node_name = (node_desc.keys.reject {|k| k.to_s[0..0] == '_' }).first
116
+ args.merge!(handle_node_type(xml, node_desc, node_name, args, ''))
117
+ end
118
+
119
+ if args.length == 0
120
+ return nil
121
+ end
122
+
123
+ if entity_desc.arg_mapping.nil?
124
+ return entity_desc.entity_class.new(args)
125
+ else
126
+ args_list = []
127
+ entity_desc.arg_mapping.each do |arg|
128
+ args_list <<= args[arg]
129
+ args.delete(arg)
130
+ end
131
+ args_list <<= args
132
+ return entity_desc.entity_class.new(*args_list)
133
+ end
134
+ end
135
+
136
+ # Parses an XML fragment into an internal representation.
137
+ def handle_node_type(xml, node_desc, node_name, args, base_name)
138
+ case node_desc[node_name]
139
+ when Symbol
140
+ node = xml.at_css(base_name + node_name.to_s)
141
+ unless node.nil?
142
+ content = node.content
143
+ case node_desc[:_converter]
144
+ when Method, Proc
145
+ content = node_desc[:_converter].call(content)
146
+ when Symbol
147
+ content = self.send(node_desc[:_converter], content)
148
+ end
149
+ args[node_desc[node_name]] = content unless content.nil?
150
+ end
151
+ when AuthorizeNet::EntityDescription
152
+ unless node_desc[:_multivalue].nil?
153
+ xml.css(base_name + node_name.to_s).each do |node|
154
+ entity = build_entity(node, node_desc[node_name])
155
+ args[node_desc[:_multivalue]] = args[node_desc[:_multivalue]].to_a + entity.to_a unless entity.nil?
156
+ end
157
+ else
158
+ entity = build_entity(xml.css(base_name + node_name.to_s), node_desc[node_name])
159
+ args[node_desc[:_value]] = entity unless entity.nil?
160
+ end
161
+ when Array
162
+ node_desc[node_name].each do |inner_node|
163
+ inner_node_name = (inner_node.keys.reject {|k| k.to_s[0..0] == '_' }).first
164
+ args.merge!(handle_node_type(xml, inner_node, inner_node_name, args, node_name.to_s + ' '))
165
+ end
166
+ end
167
+ return args
168
+ end
169
+
170
+ end
171
+
172
+ end
@@ -0,0 +1,275 @@
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
+ CIM_CREATE_PROFILE = "createCustomerProfileRequest"
22
+ CIM_CREATE_PAYMENT = "createCustomerPaymentProfileRequest"
23
+ CIM_CREATE_ADDRESS = "createCustomerShippingAddressRequest"
24
+ CIM_CREATE_TRANSACTION = "createCustomerProfileTransactionRequest"
25
+ CIM_DELETE_PROFILE = "deleteCustomerProfileRequest"
26
+ CIM_DELETE_PAYMENT = "deleteCustomerPaymentProfileRequest"
27
+ CIM_DELETE_ADDRESS = "deleteCustomerShippingAddressRequest"
28
+ CIM_GET_PROFILE_IDS = "getCustomerProfileIdsRequest"
29
+ CIM_GET_PROFILE = "getCustomerProfileRequest"
30
+ CIM_GET_PAYMENT = "getCustomerPaymentProfileRequest"
31
+ CIM_GET_ADDRESS = "getCustomerShippingAddressRequest"
32
+ CIM_UPDATE_PROFILE = "updateCustomerProfileRequest"
33
+ CIM_UPDATE_PAYMENT = "updateCustomerPaymentProfileRequest"
34
+ CIM_UPDATE_ADDRESS = "updateCustomerShippingAddressRequest"
35
+ CIM_UPDATE_SPLIT = "updateSplitTenderGroupRequest"
36
+ CIM_VALIDATE_PAYMENT = "validateCustomerPaymentProfileRequest"
37
+ REPORT_GET_BATCH_LIST = "getSettledBatchListRequest"
38
+ REPORT_GET_TRANSACTION_LIST = "getTransactionListRequest"
39
+ REPORT_GET_TRANSACTION_DETAILS = "getTransactionDetailsRequest"
40
+ end
41
+
42
+ # Fields to convert to/from booleans.
43
+ @@boolean_fields = []
44
+
45
+ # Fields to convert to/from BigDecimal.
46
+ @@decimal_fields = []
47
+
48
+ # Fields to convert to/from Date.
49
+ @@date_fields = []
50
+
51
+ # Fields to convert to/from DateTime.
52
+ @@datetime_fields = []
53
+
54
+ # The class to wrap our response in.
55
+ @response_class = AuthorizeNet::XmlResponse
56
+
57
+ # The default options for the constructor.
58
+ @@option_defaults = {
59
+ :gateway => :production,
60
+ :verify_ssl => false,
61
+ :reference_id => nil
62
+ }
63
+
64
+ # DO NOT USE. Instantiate AuthorizeNet::ARB::Transaction or AuthorizeNet::CIM::Transaction instead.
65
+ def initialize(api_login_id, api_transaction_key, options = {})
66
+ super()
67
+ @api_login_id = api_login_id
68
+ @api_transaction_key = api_transaction_key
69
+
70
+ @response ||= nil
71
+ @type ||= nil
72
+
73
+ options = @@option_defaults.merge(options)
74
+ @verify_ssl = options[:verify_ssl]
75
+ @reference_id = options[:reference_id]
76
+ case options[:gateway]
77
+ when :sandbox, :test
78
+ @gateway = Gateway::TEST
79
+ when :production, :live
80
+ @gateway = Gateway::LIVE
81
+ else
82
+ @gateway = options[:gateway]
83
+ end
84
+ end
85
+
86
+ # Checks if the transaction has been configured for the sandbox or not. Return FALSE if the
87
+ # transaction is running against the production, TRUE otherwise.
88
+ def test?
89
+ @gateway != Gateway::LIVE
90
+ end
91
+
92
+ # Checks to see if the transaction has a response (meaning it has been submitted to the gateway).
93
+ # Returns TRUE if a response is present, FALSE otherwise.
94
+ def has_response?
95
+ !@response.nil?
96
+ end
97
+
98
+ # Retrieve the response object (or Nil if transaction hasn't been sent to the gateway).
99
+ def response
100
+ @response
101
+ end
102
+
103
+ # Submits the transaction to the gateway for processing. Returns a response object. If the transaction
104
+ # has already been run, it will return nil.
105
+ def run
106
+ make_request
107
+ end
108
+
109
+ # Returns a deep-copy of the XML object sent to the payment gateway. Or nil if there was no XML payload.
110
+ def xml
111
+ @xml
112
+ end
113
+
114
+ #:enddoc:
115
+ protected
116
+
117
+ # Takes a list of nodes (a Hash is a node, and Array is a list) and returns True if any nodes
118
+ # would be built by build_nodes. False if no new nodes would be generated.
119
+ def has_content(nodeList, data)
120
+ nodeList.each do |node|
121
+ nodeName = (node.keys.reject {|k| nodeName.to_s[0..0] == '_' }).first
122
+ multivalue = node[:_multivalue]
123
+ conditional = node[:_conditional]
124
+ value = node[nodeName]
125
+ unless conditional.nil?
126
+ value = self.send(conditional, nodeName)
127
+ end
128
+ case value
129
+ when Array
130
+ if multivalue.nil?
131
+ if has_content(value, data)
132
+ return true
133
+ end
134
+ else
135
+ data[multivalue].each do |v|
136
+ if has_content(value, v)
137
+ return true
138
+ end
139
+ end
140
+ end
141
+ when Symbol
142
+ converted = convert_field(value, data[value])
143
+ return true unless converted.nil?
144
+ else
145
+ return true
146
+ end
147
+ end
148
+ false
149
+ end
150
+
151
+ # Takes a list of nodes (a Hash is a node, and Array is a list) and recursively builds the XML by pulling
152
+ # values as needed from data.
153
+ def build_nodes(builder, nodeList, data)
154
+ nodeList.each do |node|
155
+ # TODO - ADD COMMENTS HERE
156
+ nodeName = (node.keys.reject {|k| k.to_s[0..0] == '_' }).first
157
+ multivalue = node[:_multivalue]
158
+ conditional = node[:_conditional]
159
+ value = node[nodeName]
160
+ unless conditional.nil?
161
+ value = self.send(conditional, nodeName)
162
+ end
163
+ case value
164
+ when Array # node containing other nodes
165
+ if multivalue.nil?
166
+ proc = Proc.new { build_nodes(builder, value, data) }
167
+ builder.send(nodeName, &proc) if has_content(value, data)
168
+ else
169
+ data[multivalue].to_a.each do |v|
170
+ proc = Proc.new { build_nodes(builder, value, v) }
171
+ builder.send(nodeName, &proc) if has_content(value, v)
172
+ end
173
+ end
174
+ when Symbol # node containing actual data
175
+ if data[value].kind_of?(Array)
176
+ data[value].each do |v|
177
+ converted = convert_field(value, v)
178
+ builder.send(nodeName, converted) unless converted.nil?
179
+ end
180
+ else
181
+ converted = convert_field(value, data[value])
182
+ builder.send(nodeName, converted) unless converted.nil?
183
+ end
184
+ else
185
+ builder.send(nodeName, value)
186
+ end
187
+ end
188
+ end
189
+
190
+ def convert_field(field, value)
191
+ if @@boolean_fields.include?(field) and !value.nil?
192
+ return boolean_to_value(value)
193
+ elsif @@decimal_fields.include?(field) and !value.nil?
194
+ return decimal_to_value(value)
195
+ elsif @@date_fields.include?(field) and !value.nil?
196
+ return date_to_value(value)
197
+ elsif @@datetime_fields.include?(field) and !value.nil?
198
+ return datetime_to_value(value)
199
+ elsif field == :extra_options
200
+ # handle converting extra options
201
+ options = []
202
+ unless value.nil?
203
+ value.each_pair{|k,v| options <<= self.to_param(k, v)}
204
+ end
205
+ unless @custom_fields.nil?
206
+ # special sort to maintain compatibility with AIM custom field ordering
207
+ # FIXME - This should be DRY'd up.
208
+ custom_field_keys = @custom_fields.keys.collect(&:to_s).sort.collect(&:to_sym)
209
+ for key in custom_field_keys
210
+ options <<= self.to_param(key, @custom_fields[key.to_sym], '')
211
+ end
212
+ end
213
+
214
+ if options.length > 0
215
+ return options.join('&')
216
+ else
217
+ return nil
218
+ end
219
+ elsif field == :exp_date
220
+ # convert MMYY expiration dates into the XML equivalent
221
+ unless value.nil?
222
+ begin
223
+ return value.to_s.downcase == 'xxxx' ? 'XXXX' : Date.strptime(value.to_s, '%m%y').strftime('%Y-%m')
224
+ rescue
225
+ # If we didn't get the exp_date in MMYY format, try our best to convert it
226
+ return Date.parse(value.to_s).strftime('%Y-%m')
227
+ end
228
+ end
229
+ end
230
+
231
+ value
232
+ end
233
+
234
+ # An internal method that builds the POST body, submits it to the gateway, and constructs a Response object with the response.
235
+ def make_request
236
+ if has_response?
237
+ return nil
238
+ end
239
+
240
+ fields = @fields
241
+
242
+ builder = Nokogiri::XML::Builder.new(:encoding => 'utf-8') do |x|
243
+ x.send(@type.to_sym, :xmlns => XML_NAMESPACE) {
244
+ x.merchantAuthentication {
245
+ x.name @api_login_id
246
+ x.transactionKey @api_transaction_key
247
+ }
248
+ build_nodes(x, self.class.const_get(:FIELDS)[@type], fields)
249
+ }
250
+ end
251
+ @xml = builder.to_xml
252
+
253
+ url = URI.parse(@gateway)
254
+
255
+ request = Net::HTTP::Post.new(url.path)
256
+ request.content_type = 'text/xml'
257
+ request.body = @xml
258
+ connection = Net::HTTP.new(url.host, url.port)
259
+ connection.use_ssl = true
260
+ if @verify_ssl
261
+ connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
262
+ else
263
+ connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
264
+ end
265
+
266
+ # Use our Class's @response_class variable to find the Response class we are supposed to use.
267
+ begin
268
+ @response = self.class.instance_variable_get(:@response_class).new((connection.start {|http| http.request(request)}), self)
269
+ rescue
270
+ @response = self.class.instance_variable_get(:@response_class).new($!, self)
271
+ end
272
+ end
273
+
274
+ end
275
+ end