exact4r 1.2 → 1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +7 -0
- data/VERSION +1 -1
- data/lib/ews/certificate_helper.rb +63 -0
- data/lib/ews/transaction/request.rb +4 -0
- data/lib/ews/transaction/validator.rb +15 -3
- data/lib/ews/transporter.rb +7 -49
- data/pkg/exact4r-1.2.gem +0 -0
- data/test/credentials.yml +19 -1
- data/test/exhaustive/forced_post_test.rb +7 -1
- data/test/exhaustive/referenced_void_test.rb +131 -0
- data/test/exhaustive/refund_correction_test.rb +1 -1
- data/test/exhaustive/tagged_update_test.rb +158 -0
- data/test/exhaustive/transaction_details_test.rb +6 -5
- data/test/exhaustive/void_test.rb +44 -7
- data/test/general/transporter_test.rb +21 -1
- data/test/test_helper.rb +6 -4
- metadata +31 -13
data/CHANGELOG
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.4
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module EWS # :nodoc:
|
2
|
+
module CertificateHelper
|
3
|
+
|
4
|
+
EXACT_ISSUER_CERT_FILE = File.dirname(__FILE__)+"/../../certs/valicert_class2_root.crt" unless defined?(EXACT_ISSUER_CERT_FILE)
|
5
|
+
EXACT_SERVER_CERT_FILE = File.dirname(__FILE__)+"/../../certs/e-xact.com.crt" unless defined?(EXACT_SERVER_CERT_FILE)
|
6
|
+
|
7
|
+
attr_accessor :issuer_cert_file, :server_cert
|
8
|
+
|
9
|
+
def configure_certificates(options)
|
10
|
+
self.issuer_cert_file = (options[:issuer_cert] || EXACT_ISSUER_CERT_FILE)
|
11
|
+
server_cert_file = (options[:server_cert] || EXACT_SERVER_CERT_FILE)
|
12
|
+
self.server_cert = File.new(server_cert_file).read
|
13
|
+
end
|
14
|
+
private :configure_certificates
|
15
|
+
|
16
|
+
def validate_certificate(is_ok, ctx)
|
17
|
+
cert = ctx.current_cert
|
18
|
+
return false if cert.nil?
|
19
|
+
|
20
|
+
# preverify failed?
|
21
|
+
return false unless is_ok
|
22
|
+
|
23
|
+
self_signed = false
|
24
|
+
ca = false
|
25
|
+
pathlen = nil
|
26
|
+
server_auth = true
|
27
|
+
self_signed = (cert.subject.cmp(cert.issuer) == 0)
|
28
|
+
|
29
|
+
# Check extensions for the certificate purpose according to http://www.openssl.org/docs/apps/x509.html (Certificate Extensions) and
|
30
|
+
# http://www.ietf.org/rfc/rfc3280.txt.
|
31
|
+
cert.extensions.each do |ex|
|
32
|
+
case ex.oid
|
33
|
+
when 'basicConstraints'
|
34
|
+
/CA:(TRUE|FALSE)(?:, pathlen:)*(\d*)/ =~ ex.value
|
35
|
+
ca ||= ($1 == 'TRUE')
|
36
|
+
pathlen = $2.to_i
|
37
|
+
when 'keyUsage'
|
38
|
+
usage = ex.value.split(/\s*,\s*/)
|
39
|
+
# a CA must have
|
40
|
+
ca &&= !usage.grep(/Certificate Sign/i).empty?
|
41
|
+
# Server Cert Must have
|
42
|
+
server_auth &&= !usage.grep(/Key Encipherment/i).empty?
|
43
|
+
when 'extendedKeyUsage'
|
44
|
+
usage = ex.value.split(/\s*,\s*/)
|
45
|
+
# Server Cert Must have
|
46
|
+
server_auth &&= !usage.grep(/TLS Web Server Authentication/i).empty?
|
47
|
+
when 'nsCertType'
|
48
|
+
usage = ex.value.split(/\s*,\s*/)
|
49
|
+
ca ||= !usage.grep(/SSL CA/i).empty?
|
50
|
+
server_auth ||= !usage.grep(/SSL Server/i).empty?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# We're looking for the server cert, so accept all CAs (which have already passed pre-verification)
|
55
|
+
return true if self_signed || ca
|
56
|
+
|
57
|
+
# ensure the server cert is the one we're expecting
|
58
|
+
return server_auth && self.server_cert == cert.to_pem
|
59
|
+
end
|
60
|
+
private :validate_certificate
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -37,6 +37,8 @@ module EWS # :nodoc:
|
|
37
37
|
# :tagged_void
|
38
38
|
# :tagged_refund
|
39
39
|
# :tagged_online_debit_refund
|
40
|
+
# :tagged_update
|
41
|
+
# :referenced_void
|
40
42
|
# :recurring_seed_pre_auth
|
41
43
|
# :recurring_seed_purchase
|
42
44
|
# :idebit_purchase
|
@@ -113,6 +115,8 @@ METHOD_EOS
|
|
113
115
|
:tagged_void => '33',
|
114
116
|
:tagged_refund => '34',
|
115
117
|
:tagged_online_debit_refund => '35',
|
118
|
+
:referenced_void => '36',
|
119
|
+
:tagged_update => '37',
|
116
120
|
:recurring_seed_pre_auth => '40',
|
117
121
|
:recurring_seed_purchase => '41',
|
118
122
|
:idebit_purchase => '50',
|
@@ -40,6 +40,8 @@ module EWS # :nodoc:
|
|
40
40
|
|
41
41
|
if self.transaction_type == "CR"
|
42
42
|
append_error(:transaction_tag, "transaction_tag must be supplied") if self.transaction_tag.to_i < 1
|
43
|
+
elsif self.transaction_type == "36"
|
44
|
+
validate_referenced_void
|
43
45
|
elsif %w(80 81).include?(self.transaction_type)
|
44
46
|
# do nothing, no validation required
|
45
47
|
elsif %w(50 54).include?(self.transaction_type)
|
@@ -166,12 +168,13 @@ module EWS # :nodoc:
|
|
166
168
|
def validate_for_transaction_tag
|
167
169
|
tt = self.transaction_type
|
168
170
|
|
169
|
-
# mandatory: transaction_type must == (30, 31, 32, 33, 34, 35)
|
170
|
-
append_error(:transaction_tag, "transaction_tag must only be set for tagged transactions") unless %w(30 31 32 33 34 35 CR).include?(tt)
|
171
|
+
# mandatory: transaction_type must == (30, 31, 32, 33, 34, 35, 37, CR)
|
172
|
+
append_error(:transaction_tag, "transaction_tag must only be set for tagged transactions") unless %w(30 31 32 33 34 35 37 CR).include?(tt)
|
171
173
|
|
172
174
|
# transaction_tag, auth_num & amount mandatory
|
173
175
|
mandatory = [:transaction_tag]
|
174
|
-
mandatory <<
|
176
|
+
mandatory << :authorization_num unless tt == 'CR'
|
177
|
+
mandatory << :amount unless %w(CR 37).include?(tt)
|
175
178
|
|
176
179
|
# ensure no cc details sent
|
177
180
|
append_error(:cc_number, "do not set cc_number for tagged transactions") unless self.cc_number.blank?
|
@@ -227,6 +230,15 @@ module EWS # :nodoc:
|
|
227
230
|
check_mandatory(mandatory)
|
228
231
|
end
|
229
232
|
|
233
|
+
def validate_referenced_void
|
234
|
+
[:cc_number, :pan, :track1, :track2, :transaction_tag].each do |attr_name|
|
235
|
+
append_error(attr_name, "#{attr_name} must not be set for referenced_void transactions") unless self.send(attr_name).nil?
|
236
|
+
end
|
237
|
+
|
238
|
+
check_mandatory([:reference_no, :customer_ref, :amount])
|
239
|
+
end
|
240
|
+
|
241
|
+
|
230
242
|
def check_mandatory(mandatory)
|
231
243
|
mandatory.each do |key|
|
232
244
|
append_error(key, "#{key} must be supplied") if self.send(key).blank?
|
data/lib/ews/transporter.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'net/https'
|
2
|
+
require File.dirname(__FILE__) + '/certificate_helper'
|
2
3
|
|
3
4
|
module EWS # :nodoc:
|
4
5
|
|
@@ -15,6 +16,7 @@ module EWS # :nodoc:
|
|
15
16
|
# Once configured to connect to a particular service, it can be used repeatedly
|
16
17
|
# to send as many transactions as required.
|
17
18
|
class Transporter
|
19
|
+
include CertificateHelper
|
18
20
|
|
19
21
|
# Initialize a Transporter.
|
20
22
|
#
|
@@ -23,6 +25,8 @@ module EWS # :nodoc:
|
|
23
25
|
#
|
24
26
|
# You can also specify a hash of options as follows:
|
25
27
|
# :transport_type the transport_type for this transporter (defaults to :rest)
|
28
|
+
# :server_cert the path to the server's certificate file (defaults to E-xact's Server Cert)
|
29
|
+
# :issuer_cert the path to the issuer's certificate file (defaults to E-xact's Issuer's Cert)
|
26
30
|
#
|
27
31
|
# The default certificates are those required to connect to https://api.e-xact.com and the
|
28
32
|
# default <tt>transport_type</tt> is <tt>:rest</tt>. The default <tt>transport_type</tt> can be overridden on a per-transaction
|
@@ -31,8 +35,7 @@ module EWS # :nodoc:
|
|
31
35
|
@url = URI.parse(url.gsub(/\/$/,''))
|
32
36
|
@transport_type = options[:transport_type] || :rest
|
33
37
|
|
34
|
-
|
35
|
-
@@server_cert ||= File.new(File.dirname(__FILE__)+"/../../certs/e-xact.com.crt").read
|
38
|
+
configure_certificates(options)
|
36
39
|
end
|
37
40
|
|
38
41
|
# Submit a transaction request to the server
|
@@ -52,7 +55,7 @@ module EWS # :nodoc:
|
|
52
55
|
request = build_http_request(transaction, transport_type, transport_details[:suffix])
|
53
56
|
request.basic_auth(transaction.gateway_id, transaction.password)
|
54
57
|
request.add_field "Accept", transport_details[:content_type]
|
55
|
-
request.add_field "User-Agent", "exact4r v1.
|
58
|
+
request.add_field "User-Agent", "exact4r v1.4"
|
56
59
|
request.add_field "Content-type", "#{transport_details[:content_type]}; charset=UTF-8"
|
57
60
|
|
58
61
|
response = get_connection.request(request)
|
@@ -106,56 +109,11 @@ private
|
|
106
109
|
@connection.use_ssl = true
|
107
110
|
@connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
108
111
|
@connection.verify_callback = method(:validate_certificate)
|
109
|
-
@connection.ca_file =
|
112
|
+
@connection.ca_file = self.issuer_cert_file
|
110
113
|
end
|
111
114
|
@connection
|
112
115
|
end
|
113
116
|
|
114
|
-
def validate_certificate(is_ok, ctx)
|
115
|
-
cert = ctx.current_cert
|
116
|
-
return false if cert.nil?
|
117
|
-
|
118
|
-
# preverify failed?
|
119
|
-
return false unless is_ok
|
120
|
-
|
121
|
-
self_signed = false
|
122
|
-
ca = false
|
123
|
-
pathlen = nil
|
124
|
-
server_auth = true
|
125
|
-
self_signed = (cert.subject.cmp(cert.issuer) == 0)
|
126
|
-
|
127
|
-
# Check extensions for the certificate purpose according to http://www.openssl.org/docs/apps/x509.html (Certificate Extensions) and
|
128
|
-
# http://www.ietf.org/rfc/rfc3280.txt.
|
129
|
-
cert.extensions.each do |ex|
|
130
|
-
case ex.oid
|
131
|
-
when 'basicConstraints'
|
132
|
-
/CA:(TRUE|FALSE)(?:, pathlen:)*(\d*)/ =~ ex.value
|
133
|
-
ca ||= ($1 == 'TRUE')
|
134
|
-
pathlen = $2.to_i
|
135
|
-
when 'keyUsage'
|
136
|
-
usage = ex.value.split(/\s*,\s*/)
|
137
|
-
# a CA must have
|
138
|
-
ca &&= !usage.grep(/Certificate Sign/i).empty?
|
139
|
-
# Server Cert Must have
|
140
|
-
server_auth &&= !usage.grep(/Key Encipherment/i).empty?
|
141
|
-
when 'extendedKeyUsage'
|
142
|
-
usage = ex.value.split(/\s*,\s*/)
|
143
|
-
# Server Cert Must have
|
144
|
-
server_auth &&= !usage.grep(/TLS Web Server Authentication/i).empty?
|
145
|
-
when 'nsCertType'
|
146
|
-
usage = ex.value.split(/\s*,\s*/)
|
147
|
-
ca ||= !usage.grep(/SSL CA/i).empty?
|
148
|
-
server_auth ||= !usage.grep(/SSL Server/i).empty?
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
# We're looking for the server cert, so accept all CAs (which have already passed pre-verification)
|
153
|
-
return true if self_signed || ca
|
154
|
-
|
155
|
-
# ensure the server cert is the one we're expecting
|
156
|
-
return server_auth && @@server_cert == cert.to_pem
|
157
|
-
end
|
158
|
-
|
159
117
|
# what transport types we support, and their corresponding suffixes
|
160
118
|
@@transport_types = {
|
161
119
|
:rest => {:suffix => "xml", :content_type => "application/xml"},
|
data/pkg/exact4r-1.2.gem
ADDED
Binary file
|
data/test/credentials.yml
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
DEV:
|
2
|
-
location: http://
|
2
|
+
location: http://localhost:3000/
|
3
3
|
tsys:
|
4
4
|
:gateway_id: AD0156-01
|
5
5
|
:password: 4apitest
|
@@ -16,6 +16,24 @@ DEV:
|
|
16
16
|
:gateway_id: AD0158-01
|
17
17
|
:password: 4apitest
|
18
18
|
|
19
|
+
QA:
|
20
|
+
location: https://api.qa.e-xact.com/
|
21
|
+
tsys:
|
22
|
+
:gateway_id: A04766-01
|
23
|
+
:password: 4apitest
|
24
|
+
emergis:
|
25
|
+
:gateway_id: A04763-01
|
26
|
+
:password: 4apitest
|
27
|
+
moneris:
|
28
|
+
:gateway_id: A04760-01
|
29
|
+
:password: 4apitest
|
30
|
+
chase:
|
31
|
+
:gateway_id: A04757-02
|
32
|
+
:password: 4apitest
|
33
|
+
chase_batch:
|
34
|
+
:gateway_id: A04757-01
|
35
|
+
:password: 4apitest
|
36
|
+
|
19
37
|
PROD:
|
20
38
|
location: https://api.e-xact.com/
|
21
39
|
tsys:
|
@@ -39,7 +39,9 @@ class ForcedPostTest < Test::Unit::TestCase
|
|
39
39
|
assert request.valid?
|
40
40
|
end
|
41
41
|
|
42
|
-
def test_by_credit_card
|
42
|
+
def test_by_credit_card
|
43
|
+
return if @@credentials.chase? || @@credentials.moneris?
|
44
|
+
|
43
45
|
request = EWS::Transaction::Request.new(cc_number_params.merge(:transaction_type => :forced_post))
|
44
46
|
request.amount = 11.25
|
45
47
|
request.authorization_num = "1234"
|
@@ -51,6 +53,8 @@ class ForcedPostTest < Test::Unit::TestCase
|
|
51
53
|
end
|
52
54
|
|
53
55
|
def test_by_track1
|
56
|
+
return if @@credentials.chase? || @@credentials.moneris?
|
57
|
+
|
54
58
|
request = EWS::Transaction::Request.new(track1_params.merge(:transaction_type => :forced_post))
|
55
59
|
request.amount = 11.25
|
56
60
|
request.authorization_num = "1234"
|
@@ -62,6 +66,8 @@ class ForcedPostTest < Test::Unit::TestCase
|
|
62
66
|
end
|
63
67
|
|
64
68
|
def test_by_track2
|
69
|
+
return if @@credentials.chase? || @@credentials.moneris?
|
70
|
+
|
65
71
|
request = EWS::Transaction::Request.new(track2_params.merge(:transaction_type => :forced_post))
|
66
72
|
request.amount = 11.25
|
67
73
|
request.authorization_num = "1234"
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../test_helper"
|
2
|
+
|
3
|
+
#
|
4
|
+
# A ReferencedVoid behaves the same as a TaggedVoid, except, instead of using transaction_tag,
|
5
|
+
# authorization_num and amount to lookup the transaction to be voided, you use reference_no,
|
6
|
+
# customer_ref and amount.
|
7
|
+
#
|
8
|
+
class ReferencedVoidTest < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@transporter = EWS::Transporter.new(@@credentials.config['location'])
|
12
|
+
|
13
|
+
# do initial purchase
|
14
|
+
request = EWS::Transaction::Request.new(cc_number_params.merge({
|
15
|
+
:transaction_type => :purchase,
|
16
|
+
:amount => 11.25,
|
17
|
+
:reference_no => "reference_no_#{rand(10)}",
|
18
|
+
:customer_ref => "customer_ref_#{rand(10)}"
|
19
|
+
}))
|
20
|
+
assert request.valid?, request.errors.inspect
|
21
|
+
|
22
|
+
@purchase = @transporter.submit(request, :json)
|
23
|
+
assert @purchase.approved?
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_mandatory
|
27
|
+
request = EWS::Transaction::Request.new(:transaction_type => :referenced_void)
|
28
|
+
assert !request.valid?
|
29
|
+
assert_equal "gateway_id must be supplied", request.errors[:gateway_id]
|
30
|
+
|
31
|
+
request.gateway_id = @@credentials.current_gateway[:gateway_id]
|
32
|
+
assert !request.valid?
|
33
|
+
assert_equal "password must be supplied", request.errors[:password]
|
34
|
+
|
35
|
+
request.password = @@credentials.current_gateway[:password]
|
36
|
+
assert !request.valid?
|
37
|
+
assert_equal "reference_no must be supplied", request.errors[:reference_no]
|
38
|
+
|
39
|
+
request.reference_no = "something"
|
40
|
+
assert !request.valid?
|
41
|
+
assert_equal "customer_ref must be supplied", request.errors[:customer_ref]
|
42
|
+
|
43
|
+
request.customer_ref = "something"
|
44
|
+
assert !request.valid?
|
45
|
+
assert_equal "amount must be supplied", request.errors[:amount]
|
46
|
+
|
47
|
+
request.amount = 11.25
|
48
|
+
assert request.valid?
|
49
|
+
|
50
|
+
# don't set any CC information
|
51
|
+
[:cc_number, :pan, :track1, :track2, :transaction_tag].each do |attr_name|
|
52
|
+
request.send("#{attr_name}=", "something")
|
53
|
+
assert !request.valid?
|
54
|
+
assert_equal "#{attr_name} must not be set for referenced_void transactions", request.errors[attr_name]
|
55
|
+
request.send("#{attr_name}=", nil)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_json
|
60
|
+
request = EWS::Transaction::Request.new({
|
61
|
+
:transaction_type => :referenced_void,
|
62
|
+
:amount => 11.25,
|
63
|
+
:reference_no => @purchase.reference_no,
|
64
|
+
:customer_ref => @purchase.customer_ref
|
65
|
+
}.merge(@@credentials.current_gateway))
|
66
|
+
assert request.valid?, request.errors.inspect
|
67
|
+
|
68
|
+
assert_details_correct request, @transporter.submit(request, :json)
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_rest
|
72
|
+
request = EWS::Transaction::Request.new({
|
73
|
+
:transaction_type => :referenced_void,
|
74
|
+
:amount => 11.25,
|
75
|
+
:reference_no => @purchase.reference_no,
|
76
|
+
:customer_ref => @purchase.customer_ref
|
77
|
+
}.merge(@@credentials.current_gateway))
|
78
|
+
assert request.valid?, request.errors.inspect
|
79
|
+
|
80
|
+
assert_details_correct request, @transporter.submit(request, :rest)
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_soap
|
84
|
+
request = EWS::Transaction::Request.new({
|
85
|
+
:transaction_type => :referenced_void,
|
86
|
+
:amount => 11.25,
|
87
|
+
:reference_no => @purchase.reference_no,
|
88
|
+
:customer_ref => @purchase.customer_ref
|
89
|
+
}.merge(@@credentials.current_gateway))
|
90
|
+
assert request.valid?, request.errors.inspect
|
91
|
+
|
92
|
+
assert_details_correct request, @transporter.submit(request, :soap)
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_requires_correct_amount
|
96
|
+
request = EWS::Transaction::Request.new({
|
97
|
+
:transaction_type => :referenced_void,
|
98
|
+
:amount => 3.50,
|
99
|
+
:reference_no => @purchase.reference_no,
|
100
|
+
:customer_ref => @purchase.customer_ref
|
101
|
+
}.merge(@@credentials.current_gateway))
|
102
|
+
assert request.valid?, request.errors.inspect
|
103
|
+
|
104
|
+
response = @transporter.submit(request, :json)
|
105
|
+
assert !response.approved?
|
106
|
+
|
107
|
+
request.amount = 13.50
|
108
|
+
response = @transporter.submit(request, :rest)
|
109
|
+
assert !response.approved?
|
110
|
+
|
111
|
+
request.amount = 11.25
|
112
|
+
assert_details_correct request, @transporter.submit(request, :soap)
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_multiple_voids_against_one_transaction
|
116
|
+
request = EWS::Transaction::Request.new({
|
117
|
+
:transaction_type => :referenced_void,
|
118
|
+
:amount => 11.25,
|
119
|
+
:reference_no => @purchase.reference_no,
|
120
|
+
:customer_ref => @purchase.customer_ref
|
121
|
+
}.merge(@@credentials.current_gateway))
|
122
|
+
assert request.valid?, request.errors.inspect
|
123
|
+
|
124
|
+
assert_details_correct request, @transporter.submit(request, :json)
|
125
|
+
|
126
|
+
# can't void an already-voided transaction
|
127
|
+
response = @transporter.submit(request, :json)
|
128
|
+
assert !response.approved?
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
@@ -43,7 +43,7 @@ class RefundCorrectionTest < Test::Unit::TestCase
|
|
43
43
|
assert request.valid?
|
44
44
|
end
|
45
45
|
|
46
|
-
def test_by_credit_card
|
46
|
+
def test_by_credit_card
|
47
47
|
return unless @@credentials.moneris?
|
48
48
|
|
49
49
|
request = EWS::Transaction::Request.new(cc_number_params.merge(:transaction_type => :refund_correction).merge(@@credentials.current_gateway))
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../test_helper"
|
2
|
+
|
3
|
+
#
|
4
|
+
# A TaggedUpdate request allows a merchant to edit the reference_no and customer_ref values of a
|
5
|
+
# previous transaction. Please note that this is NOT a financial transaction, it merely allows
|
6
|
+
# you to change the values stored in our DB for a given transaction.
|
7
|
+
#
|
8
|
+
# In common with all other tagged transactions, you supply the transaction_tag and authorization_num
|
9
|
+
# for the transaction whose values you wish to change, along with the new values for reference_no
|
10
|
+
# and/or customer_ref.
|
11
|
+
#
|
12
|
+
# NOTE: nil, empty string ("") or whitespace string (" ") are not permitted, and, although the
|
13
|
+
# request will be approved, the relevant attribute WILL NOT CHANGE.
|
14
|
+
#
|
15
|
+
class TaggedUpdateTest < Test::Unit::TestCase
|
16
|
+
|
17
|
+
def setup
|
18
|
+
@transporter = EWS::Transporter.new(@@credentials.config['location'])
|
19
|
+
|
20
|
+
# do initial purchase request
|
21
|
+
request = EWS::Transaction::Request.new(cc_number_params.merge(:transaction_type => :purchase))
|
22
|
+
request.amount = 11.25
|
23
|
+
request.reference_no = "reference_no"
|
24
|
+
request.customer_ref = "customer_ref"
|
25
|
+
assert request.valid?, request.errors.inspect
|
26
|
+
|
27
|
+
@purchase = @transporter.submit(request, :json)
|
28
|
+
assert @purchase.approved?, @purchase.to_yaml
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_mandatory
|
32
|
+
request = EWS::Transaction::Request.new(:transaction_type => :tagged_update)
|
33
|
+
assert !request.valid?
|
34
|
+
assert_equal "gateway_id must be supplied", request.errors[:gateway_id]
|
35
|
+
|
36
|
+
request.gateway_id = @@credentials.current_gateway[:gateway_id]
|
37
|
+
assert !request.valid?
|
38
|
+
assert_equal "password must be supplied", request.errors[:password]
|
39
|
+
|
40
|
+
request.password = @@credentials.current_gateway[:password]
|
41
|
+
assert !request.valid?
|
42
|
+
assert_equal "One of the following must be supplied: cc_number, track1, track2 or transaction_tag.", request.errors[:base]
|
43
|
+
|
44
|
+
request.transaction_tag = 1234
|
45
|
+
assert !request.valid?
|
46
|
+
assert_equal "authorization_num must be supplied", request.errors[:authorization_num]
|
47
|
+
|
48
|
+
request.authorization_num = "TH6754"
|
49
|
+
assert request.valid?, request.errors.inspect
|
50
|
+
|
51
|
+
request.cc_number = "4111111111111111"
|
52
|
+
assert !request.valid?
|
53
|
+
assert_equal "do not set cc_number for tagged transactions", request.errors[:cc_number]
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_json
|
57
|
+
request = tagged_update_request(@purchase)
|
58
|
+
assert request.valid?, request.errors.inspect
|
59
|
+
|
60
|
+
response = @transporter.submit(request, :json)
|
61
|
+
assert response.approved?
|
62
|
+
|
63
|
+
# we're getting the details of the original transction returned
|
64
|
+
assert_equal @purchase.transaction_tag, response.transaction_tag
|
65
|
+
assert_equal "something_new", response.reference_no
|
66
|
+
assert_equal "something_old", response.customer_ref
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_rest
|
70
|
+
request = tagged_update_request(@purchase)
|
71
|
+
assert request.valid?, request.errors.inspect
|
72
|
+
|
73
|
+
response = @transporter.submit(request, :rest)
|
74
|
+
|
75
|
+
# we're getting the details of the original transction returned
|
76
|
+
assert_equal @purchase.transaction_tag, response.transaction_tag
|
77
|
+
assert_equal "something_new", response.reference_no
|
78
|
+
assert_equal "something_old", response.customer_ref
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_soap
|
82
|
+
request = tagged_update_request(@purchase)
|
83
|
+
assert request.valid?, request.errors.inspect
|
84
|
+
|
85
|
+
response = @transporter.submit(request, :soap)
|
86
|
+
|
87
|
+
# we're getting the details of the original transction returned
|
88
|
+
assert_equal @purchase.transaction_tag, response.transaction_tag
|
89
|
+
assert_equal "something_new", response.reference_no
|
90
|
+
assert_equal "something_old", response.customer_ref
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_multiple_updates
|
94
|
+
request1 = tagged_update_request(@purchase)
|
95
|
+
assert request1.valid?, request1.errors.inspect
|
96
|
+
|
97
|
+
response1 = @transporter.submit(request1, :soap)
|
98
|
+
assert_equal "something_new", response1.reference_no
|
99
|
+
assert_equal "something_old", response1.customer_ref
|
100
|
+
|
101
|
+
request2 = tagged_update_request(@purchase, {
|
102
|
+
:reference_no => "bananas",
|
103
|
+
:customer_ref => "oranges"
|
104
|
+
})
|
105
|
+
assert request2.valid?, request2.errors.inspect
|
106
|
+
|
107
|
+
response2 = @transporter.submit(request2, :soap)
|
108
|
+
assert_equal "bananas", response2.reference_no
|
109
|
+
assert_equal "oranges", response2.customer_ref
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_updating_single_attribute
|
113
|
+
# change only reference_no
|
114
|
+
request1 = tagged_update_request(@purchase, {:customer_ref => nil})
|
115
|
+
assert request1.valid?, request1.errors.inspect
|
116
|
+
|
117
|
+
response1 = @transporter.submit(request1, :soap)
|
118
|
+
assert_equal "something_new", response1.reference_no
|
119
|
+
assert_equal "customer_ref", response1.customer_ref
|
120
|
+
|
121
|
+
# change only customer_ref
|
122
|
+
request2 = tagged_update_request(@purchase, {:reference_no => nil})
|
123
|
+
assert request2.valid?, request2.errors.inspect
|
124
|
+
|
125
|
+
response2 = @transporter.submit(request2, :soap)
|
126
|
+
assert_equal "something_new", response2.reference_no
|
127
|
+
assert_equal "something_old", response2.customer_ref
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_empty_strings_ignored
|
131
|
+
request1 = tagged_update_request(@purchase, {:customer_ref => ""})
|
132
|
+
assert request1.valid?, request1.errors.inspect
|
133
|
+
|
134
|
+
response1 = @transporter.submit(request1, :soap)
|
135
|
+
assert_equal "something_new", response1.reference_no
|
136
|
+
assert_equal "customer_ref", response1.customer_ref
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_whitepaces_strings_ignored
|
140
|
+
request1 = tagged_update_request(@purchase, {:customer_ref => " "})
|
141
|
+
assert request1.valid?, request1.errors.inspect
|
142
|
+
|
143
|
+
response1 = @transporter.submit(request1, :soap)
|
144
|
+
assert_equal "something_new", response1.reference_no
|
145
|
+
assert_equal "customer_ref", response1.customer_ref
|
146
|
+
end
|
147
|
+
|
148
|
+
def tagged_update_request(original_txn, options = {})
|
149
|
+
EWS::Transaction::Request.new({
|
150
|
+
:transaction_type => :tagged_update,
|
151
|
+
:transaction_tag => original_txn.transaction_tag,
|
152
|
+
:authorization_num => original_txn.authorization_num,
|
153
|
+
:reference_no => "something_new",
|
154
|
+
:customer_ref => "something_old"
|
155
|
+
}.merge(options).merge(@@credentials.current_gateway))
|
156
|
+
end
|
157
|
+
private :tagged_update_request
|
158
|
+
end
|
@@ -70,18 +70,19 @@ class TransactionDetailsTest < Test::Unit::TestCase
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def assert_details_match_original_response(original_response, details_response)
|
73
|
+
# exclude: client_ip, pan
|
73
74
|
[:logon_message, :error_number, :error_description, :transaction_error, :transaction_approved, :exact_resp_code, :exact_message,
|
74
75
|
:bank_resp_code, :bank_message, :bank_resp_code_2, :sequence_no, :avs, :cvv2, :retrieval_ref_no, :cavv_response, :merchant_name,
|
75
76
|
:merchant_address, :merchant_city, :merchant_province, :merchant_country, :merchant_postal, :merchant_url, :gateway_id, :password,
|
76
|
-
:transaction_type, :amount, :surcharge_amount, :cc_number, :transaction_tag, :track1, :track2, :
|
77
|
+
:transaction_type, :amount, :surcharge_amount, :cc_number, :transaction_tag, :track1, :track2, :authorization_num, :cc_expiry,
|
77
78
|
:cardholder_name, :cc_verification_str1, :cc_verification_str2, :cvd_presence_ind, :tax1_amount, :tax1_number, :tax2_amount,
|
78
79
|
:tax2_number, :secure_auth_required, :secure_auth_result, :ecommerce_flag, :xid, :cavv, :cavv_algorithm, :reference_no, :customer_ref,
|
79
|
-
:reference_3, :language, :
|
80
|
+
:reference_3, :language, :client_email, :user_name, :zip_code].each do |attr_name|
|
81
|
+
next if attr_name == :cc_expiry && %(35 50 54).include?(original_response.transaction_type)
|
82
|
+
|
80
83
|
o_value = original_response.send(attr_name).to_s
|
81
84
|
d_value = details_response.send(attr_name).to_s
|
82
|
-
|
83
|
-
puts "#{attr_name}: #{o_value} / #{d_value}"
|
84
|
-
end
|
85
|
+
assert_equal o_value, d_value, "#{attr_name}: #{o_value} / #{d_value}"
|
85
86
|
end
|
86
87
|
end
|
87
88
|
private :assert_details_match_original_response
|
@@ -40,42 +40,79 @@ class VoidTest < Test::Unit::TestCase
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def test_by_credit_card
|
43
|
-
return if @@credentials.
|
44
|
-
|
43
|
+
return if @@credentials.moneris?
|
44
|
+
|
45
|
+
response = send_purchase(cc_number_params)
|
46
|
+
|
45
47
|
request = EWS::Transaction::Request.new(cc_number_params.merge(:transaction_type => :void))
|
46
48
|
request.amount = 11.25
|
47
|
-
request.authorization_num =
|
49
|
+
request.authorization_num = response.authorization_num
|
48
50
|
assert request.valid?, request.errors.inspect
|
49
51
|
|
50
52
|
assert_details_correct request, @transporter.submit(request, :json)
|
53
|
+
|
54
|
+
response = send_purchase(cc_number_params)
|
55
|
+
request.authorization_num = response.authorization_num
|
56
|
+
|
51
57
|
assert_details_correct request, @transporter.submit(request, :rest)
|
58
|
+
|
59
|
+
response = send_purchase(cc_number_params)
|
60
|
+
request.authorization_num = response.authorization_num
|
61
|
+
|
52
62
|
assert_details_correct request, @transporter.submit(request, :soap)
|
53
63
|
end
|
54
64
|
|
55
65
|
def test_by_track1
|
56
|
-
return if @@credentials.
|
66
|
+
return if @@credentials.moneris?
|
57
67
|
|
68
|
+
response = send_purchase(track1_params)
|
69
|
+
|
58
70
|
request = EWS::Transaction::Request.new(track1_params.merge(:transaction_type => :void))
|
59
71
|
request.amount = 11.25
|
60
|
-
request.authorization_num =
|
72
|
+
request.authorization_num = response.authorization_num
|
61
73
|
assert request.valid?, request.errors.inspect
|
62
74
|
|
63
75
|
assert_details_correct request, @transporter.submit(request, :json)
|
76
|
+
|
77
|
+
response = send_purchase(track1_params)
|
78
|
+
request.authorization_num = response.authorization_num
|
79
|
+
|
64
80
|
assert_details_correct request, @transporter.submit(request, :rest)
|
81
|
+
|
82
|
+
response = send_purchase(track1_params)
|
83
|
+
request.authorization_num = response.authorization_num
|
84
|
+
|
65
85
|
assert_details_correct request, @transporter.submit(request, :soap)
|
66
86
|
end
|
67
87
|
|
68
88
|
def test_by_track2
|
69
|
-
return if @@credentials.
|
89
|
+
return if @@credentials.moneris?
|
70
90
|
|
91
|
+
response = send_purchase(track2_params)
|
92
|
+
|
71
93
|
request = EWS::Transaction::Request.new(track2_params.merge(:transaction_type => :void))
|
72
94
|
request.amount = 11.25
|
73
|
-
request.authorization_num =
|
95
|
+
request.authorization_num = response.authorization_num
|
74
96
|
assert request.valid?, request.errors.inspect
|
75
97
|
|
76
98
|
assert_details_correct request, @transporter.submit(request, :json)
|
99
|
+
|
100
|
+
response = send_purchase(track2_params)
|
101
|
+
request.authorization_num = response.authorization_num
|
102
|
+
|
77
103
|
assert_details_correct request, @transporter.submit(request, :rest)
|
104
|
+
|
105
|
+
response = send_purchase(track2_params)
|
106
|
+
request.authorization_num = response.authorization_num
|
107
|
+
|
78
108
|
assert_details_correct request, @transporter.submit(request, :soap)
|
79
109
|
end
|
80
110
|
|
111
|
+
def send_purchase(params)
|
112
|
+
request = EWS::Transaction::Request.new(params.merge(:transaction_type => :purchase, :amount => 11.25))
|
113
|
+
assert request.valid?, request.errors.inspect
|
114
|
+
response = @transporter.submit(request, :json)
|
115
|
+
assert response.approved?
|
116
|
+
response
|
117
|
+
end
|
81
118
|
end
|
@@ -48,7 +48,27 @@ class TransporterTest < Test::Unit::TestCase
|
|
48
48
|
|
49
49
|
assert_not_nil resp
|
50
50
|
assert_equal 404, resp.error_number
|
51
|
-
assert_equal "Not Found", resp.error_description
|
51
|
+
assert_equal "Not Found", resp.error_description.strip
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_uses_supplied_certificates
|
55
|
+
req = ::EWS::Transaction::Request.new({
|
56
|
+
:transaction_type => :purchase,
|
57
|
+
:amount => "10.13",
|
58
|
+
:cardholder_name => "Simon Brown",
|
59
|
+
:cc_number => "4111111111111111",
|
60
|
+
:cc_expiry => "1012",
|
61
|
+
:reference_no => "987987",
|
62
|
+
}.merge(@@credentials.current_gateway))
|
63
|
+
|
64
|
+
tr = ::EWS::Transporter.new(@@credentials.config['location'], {
|
65
|
+
:issuer_cert => File.dirname(__FILE__)+"/../../certs/valicert_class2_root.crt",
|
66
|
+
:server_cert => File.dirname(__FILE__)+"/../../certs/e-xact.com.crt"
|
67
|
+
})
|
68
|
+
resp = tr.submit(req, :json)
|
69
|
+
|
70
|
+
assert_not_nil resp
|
71
|
+
assert resp.approved?
|
52
72
|
end
|
53
73
|
|
54
74
|
end
|
data/test/test_helper.rb
CHANGED
@@ -38,7 +38,7 @@ end
|
|
38
38
|
|
39
39
|
def assert_details_correct(request, response)
|
40
40
|
assert_not_nil response, "response should not be nil"
|
41
|
-
assert response.approved?, "#{response.error_number} / #{response.error_description} / #{response.exact_message} / #{response.bank_message}"
|
41
|
+
assert response.approved?, "#{response.error_number} / #{response.error_description} / #{response.exact_resp_code} / #{response.exact_message} / #{response.bank_message}"
|
42
42
|
|
43
43
|
assert_not_nil response.transaction_tag
|
44
44
|
assert_not_nil response.authorization_num
|
@@ -49,9 +49,11 @@ def assert_details_correct(request, response)
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def assert_card_details(response)
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
unless %w(35 50 54).include?(response.transaction_type)
|
53
|
+
assert_match /............1111/, response.cc_number
|
54
|
+
assert_equal TEST_CARD_HOLDER, response.cardholder_name
|
55
|
+
assert_equal TEST_CARD_EXPIRY, response.cc_expiry
|
56
|
+
end
|
55
57
|
end
|
56
58
|
|
57
59
|
def basic_params(options = {})
|
metadata
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: exact4r
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 4
|
8
|
+
version: "1.4"
|
5
9
|
platform: ruby
|
6
10
|
authors:
|
7
11
|
- E-xact Transactions Ltd.
|
@@ -9,29 +13,37 @@ autorequire:
|
|
9
13
|
bindir: bin
|
10
14
|
cert_chain: []
|
11
15
|
|
12
|
-
date: 2010-
|
16
|
+
date: 2010-07-09 00:00:00 +10:00
|
13
17
|
default_executable:
|
14
18
|
dependencies:
|
15
19
|
- !ruby/object:Gem::Dependency
|
16
20
|
name: activesupport
|
17
|
-
|
18
|
-
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
prerelease: false
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
23
|
requirements:
|
21
24
|
- - ">="
|
22
25
|
- !ruby/object:Gem::Version
|
26
|
+
segments:
|
27
|
+
- 2
|
28
|
+
- 0
|
29
|
+
- 2
|
23
30
|
version: 2.0.2
|
24
|
-
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
25
33
|
- !ruby/object:Gem::Dependency
|
26
34
|
name: builder
|
27
|
-
|
28
|
-
|
29
|
-
version_requirements: !ruby/object:Gem::Requirement
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
37
|
requirements:
|
31
38
|
- - ">="
|
32
39
|
- !ruby/object:Gem::Version
|
40
|
+
segments:
|
41
|
+
- 2
|
42
|
+
- 1
|
43
|
+
- 2
|
33
44
|
version: 2.1.2
|
34
|
-
|
45
|
+
type: :runtime
|
46
|
+
version_requirements: *id002
|
35
47
|
description:
|
36
48
|
email: dredmond@e-xact.com
|
37
49
|
executables: []
|
@@ -49,6 +61,7 @@ files:
|
|
49
61
|
- ./certs/exact.cer
|
50
62
|
- ./certs/valicert_class2_root.crt
|
51
63
|
- ./CHANGELOG
|
64
|
+
- ./lib/ews/certificate_helper.rb
|
52
65
|
- ./lib/ews/transaction/fake_response.rb
|
53
66
|
- ./lib/ews/transaction/mapping.rb
|
54
67
|
- ./lib/ews/transaction/request.rb
|
@@ -58,6 +71,7 @@ files:
|
|
58
71
|
- ./lib/ews/transporter.rb
|
59
72
|
- ./lib/exact4r.rb
|
60
73
|
- ./LICENCE
|
74
|
+
- ./pkg/exact4r-1.2.gem
|
61
75
|
- ./Rakefile
|
62
76
|
- ./README
|
63
77
|
- ./test/credentials.rb
|
@@ -73,6 +87,7 @@ files:
|
|
73
87
|
- ./test/exhaustive/purchase_test.rb
|
74
88
|
- ./test/exhaustive/recurring_seed_pre_auth_test.rb
|
75
89
|
- ./test/exhaustive/recurring_seed_purchase_test.rb
|
90
|
+
- ./test/exhaustive/referenced_void_test.rb
|
76
91
|
- ./test/exhaustive/refund_correction_test.rb
|
77
92
|
- ./test/exhaustive/refund_test.rb
|
78
93
|
- ./test/exhaustive/secure_storage_test.rb
|
@@ -81,6 +96,7 @@ files:
|
|
81
96
|
- ./test/exhaustive/tagged_pre_auth_test.rb
|
82
97
|
- ./test/exhaustive/tagged_purchase_test.rb
|
83
98
|
- ./test/exhaustive/tagged_refund_test.rb
|
99
|
+
- ./test/exhaustive/tagged_update_test.rb
|
84
100
|
- ./test/exhaustive/tagged_void_test.rb
|
85
101
|
- ./test/exhaustive/transaction_details_test.rb
|
86
102
|
- ./test/exhaustive/void_test.rb
|
@@ -122,18 +138,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
122
138
|
requirements:
|
123
139
|
- - ">="
|
124
140
|
- !ruby/object:Gem::Version
|
141
|
+
segments:
|
142
|
+
- 0
|
125
143
|
version: "0"
|
126
|
-
version:
|
127
144
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
145
|
requirements:
|
129
146
|
- - ">="
|
130
147
|
- !ruby/object:Gem::Version
|
148
|
+
segments:
|
149
|
+
- 0
|
131
150
|
version: "0"
|
132
|
-
version:
|
133
151
|
requirements: []
|
134
152
|
|
135
153
|
rubyforge_project: exact4r
|
136
|
-
rubygems_version: 1.3.
|
154
|
+
rubygems_version: 1.3.6
|
137
155
|
signing_key:
|
138
156
|
specification_version: 3
|
139
157
|
summary: E-xact Web Services Client Library.
|