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