authorizenet_blaq 1.9.3

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 (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