authorizenet_blaq 1.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/lib/app/helpers/authorize_net_helper.rb +24 -0
  3. data/lib/authorize_net/addresses/address.rb +29 -0
  4. data/lib/authorize_net/addresses/shipping_address.rb +26 -0
  5. data/lib/authorize_net/aim/response.rb +131 -0
  6. data/lib/authorize_net/aim/transaction.rb +190 -0
  7. data/lib/authorize_net/api/api_transaction.rb +123 -0
  8. data/lib/authorize_net/api/constants.yml +1 -0
  9. data/lib/authorize_net/api/schema.rb +4985 -0
  10. data/lib/authorize_net/api/transaction.rb +258 -0
  11. data/lib/authorize_net/arb/fields.rb +24 -0
  12. data/lib/authorize_net/arb/paging.rb +33 -0
  13. data/lib/authorize_net/arb/response.rb +34 -0
  14. data/lib/authorize_net/arb/sorting.rb +43 -0
  15. data/lib/authorize_net/arb/subscription.rb +72 -0
  16. data/lib/authorize_net/arb/subscription_detail.rb +14 -0
  17. data/lib/authorize_net/arb/subscription_list_response.rb +43 -0
  18. data/lib/authorize_net/arb/transaction.rb +177 -0
  19. data/lib/authorize_net/authorize_net.rb +154 -0
  20. data/lib/authorize_net/cim/customer_profile.rb +19 -0
  21. data/lib/authorize_net/cim/payment_profile.rb +37 -0
  22. data/lib/authorize_net/cim/response.rb +116 -0
  23. data/lib/authorize_net/cim/transaction.rb +727 -0
  24. data/lib/authorize_net/customer.rb +27 -0
  25. data/lib/authorize_net/email_receipt.rb +24 -0
  26. data/lib/authorize_net/fields.rb +779 -0
  27. data/lib/authorize_net/key_value_response.rb +117 -0
  28. data/lib/authorize_net/key_value_transaction.rb +291 -0
  29. data/lib/authorize_net/line_item.rb +25 -0
  30. data/lib/authorize_net/order.rb +42 -0
  31. data/lib/authorize_net/payment_methods/credit_card.rb +62 -0
  32. data/lib/authorize_net/payment_methods/echeck.rb +72 -0
  33. data/lib/authorize_net/reporting/batch.rb +19 -0
  34. data/lib/authorize_net/reporting/batch_statistics.rb +19 -0
  35. data/lib/authorize_net/reporting/fds_filter.rb +11 -0
  36. data/lib/authorize_net/reporting/response.rb +163 -0
  37. data/lib/authorize_net/reporting/returned_item.rb +46 -0
  38. data/lib/authorize_net/reporting/transaction.rb +133 -0
  39. data/lib/authorize_net/reporting/transaction_details.rb +25 -0
  40. data/lib/authorize_net/response.rb +27 -0
  41. data/lib/authorize_net/sim/hosted_payment_form.rb +38 -0
  42. data/lib/authorize_net/sim/hosted_receipt_page.rb +37 -0
  43. data/lib/authorize_net/sim/response.rb +142 -0
  44. data/lib/authorize_net/sim/transaction.rb +138 -0
  45. data/lib/authorize_net/transaction.rb +66 -0
  46. data/lib/authorize_net/xml_response.rb +172 -0
  47. data/lib/authorize_net/xml_transaction.rb +298 -0
  48. data/lib/authorize_net.rb +107 -0
  49. data/lib/authorizenet_blaq.rb +4 -0
  50. data/lib/generators/authorize_net/direct_post/direct_post_generator.rb +53 -0
  51. data/lib/generators/authorize_net/direct_post/templates/README-AuthorizeNet +49 -0
  52. data/lib/generators/authorize_net/direct_post/templates/config.yml.erb +8 -0
  53. data/lib/generators/authorize_net/direct_post/templates/config.yml.rails3.erb +8 -0
  54. data/lib/generators/authorize_net/direct_post/templates/controller.rb.erb +31 -0
  55. data/lib/generators/authorize_net/direct_post/templates/initializer.rb +4 -0
  56. data/lib/generators/authorize_net/direct_post/templates/layout.erb +18 -0
  57. data/lib/generators/authorize_net/direct_post/templates/payment.erb +10 -0
  58. data/lib/generators/authorize_net/direct_post/templates/payment.rails3.erb +10 -0
  59. data/lib/generators/authorize_net/direct_post/templates/receipt.erb +1 -0
  60. data/lib/generators/authorize_net/direct_post/templates/relay_response.erb +1 -0
  61. data/lib/generators/authorize_net/sim/sim_generator.rb +47 -0
  62. data/lib/generators/authorize_net/sim/templates/README-AuthorizeNet +52 -0
  63. data/lib/generators/authorize_net/sim/templates/config.yml.erb +8 -0
  64. data/lib/generators/authorize_net/sim/templates/config.yml.rails3.erb +8 -0
  65. data/lib/generators/authorize_net/sim/templates/controller.rb.erb +21 -0
  66. data/lib/generators/authorize_net/sim/templates/initializer.rb +4 -0
  67. data/lib/generators/authorize_net/sim/templates/layout.erb +18 -0
  68. data/lib/generators/authorize_net/sim/templates/payment.erb +6 -0
  69. data/lib/generators/authorize_net/sim/templates/payment.rails3.erb +6 -0
  70. data/lib/generators/authorize_net/sim/templates/thank_you.erb +1 -0
  71. data/lib/generators/generator_extensions.rb +75 -0
  72. metadata +196 -0
@@ -0,0 +1,138 @@
1
+ module AuthorizeNet::SIM
2
+
3
+ # The SIM transaction class. Handles building the transaction payload and
4
+ # generating a set of hidden form fields to be POSTed to the gateway.
5
+ class Transaction < AuthorizeNet::KeyValueTransaction
6
+
7
+ RANDOM_SEQUENCE_MAX = (1 << 32) - 1
8
+
9
+ # Our MD5 digest generator.
10
+ @@digest = OpenSSL::Digest.new('md5')
11
+
12
+ # The default options for the constructor.
13
+ @@option_defaults = {
14
+ :sequence => nil,
15
+ :timestamp => nil,
16
+ :test => false,
17
+ :hosted_payment_form => false,
18
+ :relay_response => true,
19
+ :relay_url => nil,
20
+ :transaction_type => Type::AUTHORIZE_AND_CAPTURE
21
+ }
22
+
23
+ # Constructs a SIM transaction. You can use the new SIM transaction object
24
+ # to build the hidden field payload needed to process a SIM transaction with
25
+ # the gateway. In particular, this class handles generating the MD5 fingerprint
26
+ # used to authenticate transactions at the gateway. Since the fingerprint includes
27
+ # the amount to charge, you should not construct this object until you know EXACTLY
28
+ # how much you want to charge (or authorize).
29
+ #
30
+ # +api_login_id+:: Your API login ID, as a string.
31
+ # +api_transaction_key+:: Your API transaction key, as a string.
32
+ # +amount+:: The amount of the transaction, as a string, Float or BigDecimal.
33
+ # +options+:: A hash of options. See below for values.
34
+ #
35
+ # Options
36
+ # +sequence+:: The sequence number of the transaction as a string or Fixnum. This is usually something like an invoice number. If none is provided, the SDK generates one at random.
37
+ # +timestamp+:: The time the transaction was initiated as a string or Fixnum. This needs to be within 15 minutes of when the gateway receives the transaction. If no value is provided, the SDK defaults it to Time.now().
38
+ # +test+:: A boolean indicating if the transaction should be run in test mode or not (defaults to false).
39
+ # +hosted_payment_form+:: A boolean indicating if the transaction should use a hosted payment form (defaults to false).
40
+ # +relay_response+:: A boolean indicating if the transaction should use the relay response feature to return a receipt to the customer (defaults to true). Direct Post Method requires using a relay response.
41
+ # +relay_url+:: A string of the URL that the gateway should hit to get the relay response (defaults to nil).
42
+ # +transaction_type+:: The type of transaction to perform. Defaults to AuthorizeNet::Type::AUTHORIZE_AND_CAPTURE. This value is only used if run is called directly.
43
+ #
44
+ def initialize(api_login_id, api_transaction_key, amount, options = {})
45
+ super()
46
+ @api_transaction_key = api_transaction_key
47
+ @api_login_id = api_login_id
48
+ @amount = decimal_to_value(amount)
49
+ options = @@option_defaults.merge(options)
50
+ @sequence = options[:sequence]
51
+ @timestamp = options[:timestamp]
52
+ @test_mode = options[:test]
53
+ @hosted_payment_form = options[:hosted_payment_form]
54
+ @relay_url = options[:relay_url]
55
+ @type = options[:transaction_type]
56
+ unless @relay_url.nil?
57
+ @relay_response = true
58
+ else
59
+ @relay_response = !!options[:relay_response]
60
+ end
61
+ @delim_data = !@relay_response
62
+ end
63
+
64
+ # Calculates and returns the HMAC-MD5 fingerprint needed to authenticate the transaction
65
+ # with the SIM gateway.
66
+ def fingerprint
67
+ if @timestamp.nil?
68
+ @timestamp = Time.now.to_i
69
+ end
70
+
71
+ if @sequence.nil?
72
+ @sequence = rand(RANDOM_SEQUENCE_MAX)
73
+ end
74
+ OpenSSL::HMAC.hexdigest(@@digest, @api_transaction_key, "#{@api_login_id.to_s.rstrip}^#{@sequence.to_s.rstrip}^#{@timestamp.to_s.rstrip}^#{@amount.to_s.rstrip}^")
75
+ end
76
+
77
+ # Returns all the fields needed for the fingerprint. These must all be passed to the SIM
78
+ # exactly as returned. And these values are time sensitive.
79
+ def fingerprint_fields
80
+ {
81
+ :login => @api_login_id,
82
+ :fp_hash => fingerprint,
83
+ :fp_sequence => @sequence,
84
+ :fp_timestamp => @timestamp,
85
+ :amount => @amount
86
+ }
87
+ end
88
+
89
+ # Returns all the fields (including custom) exactly as they should be named
90
+ # in the SIM form. Fields with multiple values are returned with an array
91
+ # for the key's value.
92
+ def form_fields
93
+ form_fields = {}
94
+ form_fields[:x_test_request] = boolean_to_value(@test_mode)
95
+ if @hosted_payment_form
96
+ form_fields[:x_show_form] = 'PAYMENT_FORM'
97
+ end
98
+ if @relay_response && !@relay_url.nil?
99
+ form_fields[:x_relay_url] = @relay_url
100
+ end
101
+ fields.merge(:type => @type, :version => @version, :delim_data => boolean_to_value(@delim_data), :relay_response => boolean_to_value(@relay_response)).each do |k, v|
102
+ form_fields[to_external_field(k)] = v
103
+ end
104
+ fingerprint_fields.each do |k, v|
105
+ form_fields[to_external_field(k)] = v
106
+ end
107
+ form_fields.merge(custom_fields)
108
+ end
109
+
110
+
111
+ # Takes an instance of AuthorizeNet::SIM::HostedPaymentForm and adds it to the transaction. Note that
112
+ # many of the fields in AuthorizeNet::SIM::HostedPaymentForm are shared with those in
113
+ # AuthorizeNet::SIM::HostedReceiptPage. For the duplicate fields, which ever value
114
+ # is added to the transaction last will be the one used.
115
+ def set_hosted_payment_form(form)
116
+ @fields.merge!(form.to_hash)
117
+ @hosted_payment_form = true
118
+ end
119
+
120
+ # Takes an instance of AuthorizeNet::SIM::HostedReceiptPage and adds it to the transaction. Note that
121
+ # many of the fields in AuthorizeNet::SIM::HostedReceiptPage are shared with those in
122
+ # AuthorizeNet::SIM::HostedPaymentForm. For the duplicate fields, which ever value
123
+ # is added to the transaction last will be the one used. If you set a hosted payment receipt,
124
+ # the relay response will be disabled.
125
+ def set_hosted_payment_receipt(form)
126
+ @fields.merge!(form.to_hash)
127
+ @relay_response = false
128
+ @delim_data = true
129
+ end
130
+
131
+ # An alias for form_fields.
132
+ def run
133
+ form_fields
134
+ end
135
+
136
+ end
137
+
138
+ end
@@ -0,0 +1,66 @@
1
+ module AuthorizeNet
2
+
3
+ # The core, API agnostic transaction class. You shouldn't instantiate this one.
4
+ # Instead you should use AuthorizeNet::AIM::Transaction, AuthorizeNet::SIM::Transaction or AuthorizeNet::ARB::Transaction.
5
+ class Transaction
6
+
7
+ include AuthorizeNet::TypeConversions
8
+
9
+ # Fields to convert to/from booleans.
10
+ @@boolean_fields = []
11
+
12
+ # Fields to convert to/from BigDecimal.
13
+ @@decimal_fields = []
14
+
15
+ # DO NOT USE. Instantiate AuthorizeNet::AIM::Transaction, AuthorizeNet::SIM::Transaction or AuthorizeNet::ARB::Transaction instead.
16
+ def initialize()
17
+ @fields ||= {}
18
+ end
19
+
20
+ # Sets arbitrary API fields, overwriting existing values if they exist. Takes a hash of key/value pairs,
21
+ # where the keys are the field names without the "x_" prefix. You can set a field to Nil to unset it. If
22
+ # the value is an array, each value in the array will be added. For example, set_fields({:line_item =>
23
+ # ["item1<|>golf balls<|><|>2<|>18.95<|>Y", "item2<|>golf bag<|>Wilson golf carry bag, red<|>1<|>39.99<|>"]})
24
+ # would generate two x_line_item fields in the transaction. One for each value in the array.
25
+ def set_fields(fields = {})
26
+ @fields.merge!(fields)
27
+ @fields.reject! {|k, v| v.nil?}
28
+ @fields
29
+ end
30
+
31
+ # Returns the current hash of API fields.
32
+ def fields
33
+ @fields
34
+ end
35
+
36
+ # Takes an instance of AuthorizeNet::Address and adds it to the transaction.
37
+ def set_address(address)
38
+ @fields.merge!(address.to_hash)
39
+ end
40
+
41
+ # Takes an instance of AuthorizeNet::ShippingAddress and adds it to the transaction.
42
+ def set_shipping_address(address)
43
+ @fields.merge!(address.to_hash)
44
+ end
45
+
46
+ # Takes an instance of AuthorizeNet::Customer and adds it to the transaction.
47
+ def set_customer(customer)
48
+ @fields.merge!(customer.to_hash)
49
+ end
50
+
51
+ #:enddoc:
52
+ protected
53
+
54
+ # Internal method to handle multiple types of payment arguments.
55
+ def handle_payment_argument(payment)
56
+ case payment
57
+ when AuthorizeNet::CreditCard, AuthorizeNet::ECheck
58
+ set_fields(payment.to_hash)
59
+ else
60
+ set_fields(:card_num => payment)
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -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,298 @@
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://api2.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_GET_HOSTED_PROFILE = "getHostedProfilePageRequest"
34
+ CIM_UPDATE_PROFILE = "updateCustomerProfileRequest"
35
+ CIM_UPDATE_PAYMENT = "updateCustomerPaymentProfileRequest"
36
+ CIM_UPDATE_ADDRESS = "updateCustomerShippingAddressRequest"
37
+ CIM_UPDATE_SPLIT = "updateSplitTenderGroupRequest"
38
+ CIM_VALIDATE_PAYMENT = "validateCustomerPaymentProfileRequest"
39
+ REPORT_GET_BATCH_LIST = "getSettledBatchListRequest"
40
+ REPORT_GET_TRANSACTION_LIST = "getTransactionListRequest"
41
+ REPORT_GET_UNSETTLED_TRANSACTION_LIST = "getUnsettledTransactionListRequest"
42
+ REPORT_GET_TRANSACTION_DETAILS = "getTransactionDetailsRequest"
43
+ end
44
+
45
+ # Fields to convert to/from booleans.
46
+ @@boolean_fields = []
47
+
48
+ # Fields to convert to/from BigDecimal.
49
+ @@decimal_fields = []
50
+
51
+ # Fields to convert to/from Date.
52
+ @@date_fields = []
53
+
54
+ # Fields to convert to/from DateTime.
55
+ @@datetime_fields = []
56
+
57
+ # The class to wrap our response in.
58
+ @response_class = AuthorizeNet::XmlResponse
59
+
60
+ # The default options for the constructor.
61
+ @@option_defaults = {
62
+ :gateway => :production,
63
+ :verify_ssl => true,
64
+ :reference_id => nil
65
+ }
66
+
67
+ # DO NOT USE. Instantiate AuthorizeNet::ARB::Transaction or AuthorizeNet::CIM::Transaction instead.
68
+ def initialize(api_login_id, api_transaction_key, options = {})
69
+ super()
70
+ @api_login_id = api_login_id
71
+ @api_transaction_key = api_transaction_key
72
+
73
+ @response ||= nil
74
+ @type ||= nil
75
+
76
+ options = @@option_defaults.merge(options)
77
+ @verify_ssl = options[:verify_ssl]
78
+ @reference_id = options[:reference_id]
79
+ @gateway = case options[:gateway].to_s
80
+ when 'sandbox', 'test'
81
+ Gateway::TEST
82
+ when 'production', 'live'
83
+ Gateway::LIVE
84
+ else
85
+ @gateway = options[:gateway]
86
+ options[:gateway]
87
+ end
88
+ end
89
+
90
+ def setOAuthOptions()
91
+ if !@options_OAuth.blank?
92
+ @options_OAuth = @@option_defaults.merge(@options_OAuth)
93
+ @verify_ssl = options_OAuth[:verify_ssl]
94
+ @reference_id = options_OAuth[:reference_id]
95
+
96
+ @gateway = case options_OAuth[:gateway].to_s
97
+ when 'sandbox', 'test'
98
+ Gateway::TEST
99
+ when 'production', 'live'
100
+ Gateway::LIVE
101
+ else
102
+ @gateway = options_OAuth[:gateway]
103
+ options_OAuth[:gateway]
104
+ end
105
+ end
106
+ end
107
+
108
+ # Checks if the transaction has been configured for the sandbox or not. Return FALSE if the
109
+ # transaction is running against the production, TRUE otherwise.
110
+ def test?
111
+ @gateway != Gateway::LIVE
112
+ end
113
+
114
+ # Checks to see if the transaction has a response (meaning it has been submitted to the gateway).
115
+ # Returns TRUE if a response is present, FALSE otherwise.
116
+ def has_response?
117
+ !@response.nil?
118
+ end
119
+
120
+ # Retrieve the response object (or Nil if transaction hasn't been sent to the gateway).
121
+ def response
122
+ @response
123
+ end
124
+
125
+ # Submits the transaction to the gateway for processing. Returns a response object. If the transaction
126
+ # has already been run, it will return nil.
127
+ def run
128
+ make_request
129
+ end
130
+
131
+ # Returns a deep-copy of the XML object sent to the payment gateway. Or nil if there was no XML payload.
132
+ def xml
133
+ @xml
134
+ end
135
+
136
+ #:enddoc:
137
+ protected
138
+
139
+ # Takes a list of nodes (a Hash is a node, and Array is a list) and returns True if any nodes
140
+ # would be built by build_nodes. False if no new nodes would be generated.
141
+ def has_content(nodeList, data)
142
+ nodeList.each do |node|
143
+ nodeName = (node.keys.reject {|k| nodeName.to_s[0..0] == '_' }).first
144
+ multivalue = node[:_multivalue]
145
+ conditional = node[:_conditional]
146
+ value = node[nodeName]
147
+ unless conditional.nil?
148
+ value = self.send(conditional, nodeName)
149
+ end
150
+ case value
151
+ when Array
152
+ if multivalue.nil?
153
+ if has_content(value, data)
154
+ return true
155
+ end
156
+ else
157
+ data[multivalue].each do |v|
158
+ if has_content(value, v)
159
+ return true
160
+ end
161
+ end
162
+ end
163
+ when Symbol
164
+ converted = convert_field(value, data[value])
165
+ return true unless converted.nil?
166
+ else
167
+ return true
168
+ end
169
+ end
170
+ false
171
+ end
172
+
173
+ # Takes a list of nodes (a Hash is a node, and Array is a list) and recursively builds the XML by pulling
174
+ # values as needed from data.
175
+ def build_nodes(builder, nodeList, data)
176
+ nodeList.each do |node|
177
+ # TODO - ADD COMMENTS HERE
178
+ nodeName = (node.keys.reject {|k| k.to_s[0..0] == '_' }).first
179
+ multivalue = node[:_multivalue]
180
+ conditional = node[:_conditional]
181
+ value = node[nodeName]
182
+
183
+ unless conditional.nil?
184
+ value = self.send(conditional, nodeName)
185
+ end
186
+ case value
187
+ when Array # node containing other nodes
188
+ if multivalue.nil?
189
+ proc = Proc.new { build_nodes(builder, value, data) }
190
+ builder.send(nodeName, &proc) if has_content(value, data)
191
+ else
192
+ data[multivalue].to_a.each do |v|
193
+ proc = Proc.new { build_nodes(builder, value, v) }
194
+ builder.send(nodeName, &proc) if has_content(value, v)
195
+ end
196
+ end
197
+ when Symbol # node containing actual data
198
+ if data[value].kind_of?(Array)
199
+ data[value].each do |v|
200
+ converted = convert_field(value, v)
201
+ builder.send(nodeName, converted) unless converted.nil?
202
+ end
203
+ else
204
+ converted = convert_field(value, data[value])
205
+ builder.send(nodeName, converted) unless converted.nil?
206
+ end
207
+ else
208
+ builder.send(nodeName, value)
209
+ end
210
+ end
211
+ end
212
+
213
+ def convert_field(field, value)
214
+ if @@boolean_fields.include?(field) and !value.nil?
215
+ return boolean_to_value(value)
216
+ elsif @@decimal_fields.include?(field) and !value.nil?
217
+ return decimal_to_value(value)
218
+ elsif @@date_fields.include?(field) and !value.nil?
219
+ return date_to_value(value)
220
+ elsif @@datetime_fields.include?(field) and !value.nil?
221
+ return datetime_to_value(value)
222
+ elsif field == :extra_options
223
+ # handle converting extra options
224
+ options = []
225
+ unless value.nil?
226
+ value.each_pair{|k,v| options <<= self.to_param(k, v)}
227
+ end
228
+ unless @custom_fields.nil?
229
+ # special sort to maintain compatibility with AIM custom field ordering
230
+ # FIXME - This should be DRY'd up.
231
+ custom_field_keys = @custom_fields.keys.collect(&:to_s).sort.collect(&:to_sym)
232
+ for key in custom_field_keys
233
+ options <<= self.to_param(key, @custom_fields[key.to_sym], '')
234
+ end
235
+ end
236
+
237
+ if options.length > 0
238
+ return options.join('&')
239
+ else
240
+ return nil
241
+ end
242
+ elsif field == :exp_date
243
+ # convert MMYY expiration dates into the XML equivalent
244
+ unless value.nil?
245
+ begin
246
+ return value.to_s.downcase == 'xxxx' ? 'XXXX' : Date.strptime(value.to_s, '%m%y').strftime('%Y-%m')
247
+ rescue
248
+ # If we didn't get the exp_date in MMYY format, try our best to convert it
249
+ return Date.parse(value.to_s).strftime('%Y-%m')
250
+ end
251
+ end
252
+ end
253
+
254
+ value
255
+ end
256
+
257
+ # An internal method that builds the POST body, submits it to the gateway, and constructs a Response object with the response.
258
+ def make_request
259
+ if has_response?
260
+ return nil
261
+ end
262
+
263
+ fields = @fields
264
+
265
+ builder = Nokogiri::XML::Builder.new(:encoding => 'utf-8') do |x|
266
+ x.send(@type.to_sym, :xmlns => XML_NAMESPACE) {
267
+ x.merchantAuthentication {
268
+ x.name @api_login_id
269
+ x.transactionKey @api_transaction_key
270
+ }
271
+ build_nodes(x, self.class.const_get(:FIELDS)[@type], fields)
272
+ }
273
+ end
274
+ @xml = builder.to_xml
275
+
276
+ url = URI.parse(@gateway)
277
+
278
+ request = Net::HTTP::Post.new(url.path)
279
+ request.content_type = 'text/xml'
280
+ request.body = @xml
281
+ connection = Net::HTTP.new(url.host, url.port)
282
+ connection.use_ssl = true
283
+ if @verify_ssl
284
+ connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
285
+ else
286
+ connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
287
+ end
288
+
289
+ # Use our Class's @response_class variable to find the Response class we are supposed to use.
290
+ begin
291
+ @response = self.class.instance_variable_get(:@response_class).new((connection.start {|http| http.request(request)}), self)
292
+ rescue
293
+ @response = self.class.instance_variable_get(:@response_class).new($!, self)
294
+ end
295
+ end
296
+
297
+ end
298
+ end