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.
- checksums.yaml +7 -0
- data/lib/app/helpers/authorize_net_helper.rb +24 -0
- data/lib/authorize_net/addresses/address.rb +29 -0
- data/lib/authorize_net/addresses/shipping_address.rb +26 -0
- data/lib/authorize_net/aim/response.rb +131 -0
- data/lib/authorize_net/aim/transaction.rb +190 -0
- data/lib/authorize_net/api/api_transaction.rb +123 -0
- data/lib/authorize_net/api/constants.yml +1 -0
- data/lib/authorize_net/api/schema.rb +4985 -0
- data/lib/authorize_net/api/transaction.rb +258 -0
- data/lib/authorize_net/arb/fields.rb +24 -0
- data/lib/authorize_net/arb/paging.rb +33 -0
- data/lib/authorize_net/arb/response.rb +34 -0
- data/lib/authorize_net/arb/sorting.rb +43 -0
- data/lib/authorize_net/arb/subscription.rb +72 -0
- data/lib/authorize_net/arb/subscription_detail.rb +14 -0
- data/lib/authorize_net/arb/subscription_list_response.rb +43 -0
- data/lib/authorize_net/arb/transaction.rb +177 -0
- data/lib/authorize_net/authorize_net.rb +154 -0
- data/lib/authorize_net/cim/customer_profile.rb +19 -0
- data/lib/authorize_net/cim/payment_profile.rb +37 -0
- data/lib/authorize_net/cim/response.rb +116 -0
- data/lib/authorize_net/cim/transaction.rb +727 -0
- data/lib/authorize_net/customer.rb +27 -0
- data/lib/authorize_net/email_receipt.rb +24 -0
- data/lib/authorize_net/fields.rb +779 -0
- data/lib/authorize_net/key_value_response.rb +117 -0
- data/lib/authorize_net/key_value_transaction.rb +291 -0
- data/lib/authorize_net/line_item.rb +25 -0
- data/lib/authorize_net/order.rb +42 -0
- data/lib/authorize_net/payment_methods/credit_card.rb +62 -0
- data/lib/authorize_net/payment_methods/echeck.rb +72 -0
- data/lib/authorize_net/reporting/batch.rb +19 -0
- data/lib/authorize_net/reporting/batch_statistics.rb +19 -0
- data/lib/authorize_net/reporting/fds_filter.rb +11 -0
- data/lib/authorize_net/reporting/response.rb +163 -0
- data/lib/authorize_net/reporting/returned_item.rb +46 -0
- data/lib/authorize_net/reporting/transaction.rb +133 -0
- data/lib/authorize_net/reporting/transaction_details.rb +25 -0
- data/lib/authorize_net/response.rb +27 -0
- data/lib/authorize_net/sim/hosted_payment_form.rb +38 -0
- data/lib/authorize_net/sim/hosted_receipt_page.rb +37 -0
- data/lib/authorize_net/sim/response.rb +142 -0
- data/lib/authorize_net/sim/transaction.rb +138 -0
- data/lib/authorize_net/transaction.rb +66 -0
- data/lib/authorize_net/xml_response.rb +172 -0
- data/lib/authorize_net/xml_transaction.rb +298 -0
- data/lib/authorize_net.rb +107 -0
- data/lib/authorizenet_blaq.rb +4 -0
- data/lib/generators/authorize_net/direct_post/direct_post_generator.rb +53 -0
- data/lib/generators/authorize_net/direct_post/templates/README-AuthorizeNet +49 -0
- data/lib/generators/authorize_net/direct_post/templates/config.yml.erb +8 -0
- data/lib/generators/authorize_net/direct_post/templates/config.yml.rails3.erb +8 -0
- data/lib/generators/authorize_net/direct_post/templates/controller.rb.erb +31 -0
- data/lib/generators/authorize_net/direct_post/templates/initializer.rb +4 -0
- data/lib/generators/authorize_net/direct_post/templates/layout.erb +18 -0
- data/lib/generators/authorize_net/direct_post/templates/payment.erb +10 -0
- data/lib/generators/authorize_net/direct_post/templates/payment.rails3.erb +10 -0
- data/lib/generators/authorize_net/direct_post/templates/receipt.erb +1 -0
- data/lib/generators/authorize_net/direct_post/templates/relay_response.erb +1 -0
- data/lib/generators/authorize_net/sim/sim_generator.rb +47 -0
- data/lib/generators/authorize_net/sim/templates/README-AuthorizeNet +52 -0
- data/lib/generators/authorize_net/sim/templates/config.yml.erb +8 -0
- data/lib/generators/authorize_net/sim/templates/config.yml.rails3.erb +8 -0
- data/lib/generators/authorize_net/sim/templates/controller.rb.erb +21 -0
- data/lib/generators/authorize_net/sim/templates/initializer.rb +4 -0
- data/lib/generators/authorize_net/sim/templates/layout.erb +18 -0
- data/lib/generators/authorize_net/sim/templates/payment.erb +6 -0
- data/lib/generators/authorize_net/sim/templates/payment.rails3.erb +6 -0
- data/lib/generators/authorize_net/sim/templates/thank_you.erb +1 -0
- data/lib/generators/generator_extensions.rb +75 -0
- 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
|