sage_pay 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,28 +1,28 @@
1
1
  module SagePay
2
2
  module Server
3
- class TransactionRegistrationResponse
4
- attr_reader :vps_protocol, :status, :status_detail
3
+ class Response
4
+ class_inheritable_hash :key_converter, :value_converter, :instance_writer => false
5
5
 
6
- def self.from_response_body(response_body)
7
- key_converter = {
8
- "VPSProtocol" => :vps_protocol,
9
- "Status" => :status,
10
- "StatusDetail" => :status_detail,
11
- "VPSTxId" => :vps_tx_id,
12
- "SecurityKey" => :security_key,
13
- "NextURL" => :next_url
14
- }
6
+ self.key_converter = {
7
+ "VPSProtocol" => :vps_protocol,
8
+ "Status" => :status,
9
+ "StatusDetail" => :status_detail,
10
+ }
15
11
 
16
- value_converter = {
17
- :status => {
18
- "OK" => :ok,
19
- "MALFORMED" => :malformed,
20
- "INVALID" => :invalid,
21
- "ERROR" => :error
22
- }
12
+ self.value_converter = {
13
+ :status => {
14
+ "OK" => :ok,
15
+ "MALFORMED" => :malformed,
16
+ "INVALID" => :invalid,
17
+ "ERROR" => :error
23
18
  }
19
+ }
20
+
21
+ attr_reader :vps_protocol, :status, :status_detail
24
22
 
23
+ def self.from_response_body(response_body)
25
24
  attributes = {}
25
+
26
26
  response_body.each_line do |line|
27
27
  key, value = line.split('=', 2)
28
28
  unless key.nil? || value.nil?
@@ -65,30 +65,6 @@ module SagePay
65
65
  def error?
66
66
  status == :error
67
67
  end
68
-
69
- def vps_tx_id
70
- if ok?
71
- @vps_tx_id
72
- else
73
- raise RuntimeError, "Unable to retrieve the transaction id as the status was not OK."
74
- end
75
- end
76
-
77
- def security_key
78
- if ok?
79
- @security_key
80
- else
81
- raise RuntimeError, "Unable to retrieve the security key as the status was not OK."
82
- end
83
- end
84
-
85
- def next_url
86
- if ok?
87
- @next_url
88
- else
89
- raise RuntimeError, "Unable to retrieve the next URL as the status was not OK."
90
- end
91
- end
92
68
  end
93
69
  end
94
70
  end
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
7
7
  ## If your rubyforge_project name is different, then edit it and comment out
8
8
  ## the sub! line in the Rakefile
9
9
  s.name = 'sage_pay'
10
- s.version = '0.2.5'
10
+ s.version = '0.2.6'
11
11
  s.date = '2010-04-25'
12
12
  s.rubyforge_project = 'sage_pay'
13
13
 
@@ -56,24 +56,27 @@ gateway for accepting credit card payments through your web app.
56
56
  lib/sage_pay.rb
57
57
  lib/sage_pay/server.rb
58
58
  lib/sage_pay/server/address.rb
59
+ lib/sage_pay/server/command.rb
60
+ lib/sage_pay/server/notification.rb
61
+ lib/sage_pay/server/notification_response.rb
62
+ lib/sage_pay/server/registration.rb
63
+ lib/sage_pay/server/registration_response.rb
64
+ lib/sage_pay/server/release.rb
65
+ lib/sage_pay/server/response.rb
59
66
  lib/sage_pay/server/signature_verification_details.rb
60
67
  lib/sage_pay/server/transaction_code.rb
61
- lib/sage_pay/server/transaction_notification.rb
62
- lib/sage_pay/server/transaction_notification_response.rb
63
- lib/sage_pay/server/transaction_registration.rb
64
- lib/sage_pay/server/transaction_registration_response.rb
65
68
  lib/sage_pay/uri_fixups.rb
66
69
  lib/validatable-ext.rb
67
70
  lib/validations/validates_inclusion_of.rb
68
71
  sage_pay.gemspec
69
72
  spec/integration/sage_pay/server_spec.rb
70
73
  spec/sage_pay/server/address_spec.rb
74
+ spec/sage_pay/server/notification_response_spec.rb
75
+ spec/sage_pay/server/notification_spec.rb
76
+ spec/sage_pay/server/registration_response_spec.rb
77
+ spec/sage_pay/server/registration_spec.rb
71
78
  spec/sage_pay/server/signature_verification_details_spec.rb
72
79
  spec/sage_pay/server/transaction_code_spec.rb
73
- spec/sage_pay/server/transaction_notification_response_spec.rb
74
- spec/sage_pay/server/transaction_notification_spec.rb
75
- spec/sage_pay/server/transaction_registration_response_spec.rb
76
- spec/sage_pay/server/transaction_registration_spec.rb
77
80
  spec/sage_pay/server_spec.rb
78
81
  spec/sage_pay_spec.rb
79
82
  spec/spec_helper.rb
@@ -2,14 +2,20 @@ require 'spec_helper'
2
2
 
3
3
  if run_integration_specs?
4
4
  describe SagePay::Server, "integration specs" do
5
+ before(:each) do
6
+ SagePay::Server.default_registration_options = {
7
+ :mode => :simulator,
8
+ :vendor => "rubaidh",
9
+ :notification_url => "http://test.host/notification"
10
+ }
11
+ end
12
+
13
+ after(:each) do
14
+ SagePay::Server.default_registration_options = {}
15
+ end
16
+
5
17
  describe ".payment" do
6
18
  before(:each) do
7
- SagePay::Server.default_registration_options = {
8
- :mode => :simulator,
9
- :vendor => "rubaidh",
10
- :notification_url => "http://test.host/notification"
11
- }
12
-
13
19
  @payment = SagePay::Server.payment(
14
20
  :description => "Demo payment",
15
21
  :amount => 12.34,
@@ -18,26 +24,67 @@ if run_integration_specs?
18
24
  )
19
25
  end
20
26
 
21
- after(:each) do
22
- SagePay::Server.default_registration_options = {}
23
- end
24
-
25
27
  it "should successfully register the payment with SagePay" do
26
- @payment.register!.should_not be_nil
28
+ @payment.run!.should_not be_nil
27
29
  end
28
30
 
29
31
  it "should be a valid registered payment" do
30
- registration = @payment.register!
32
+ registration = @payment.run!
33
+ registration.should be_ok
34
+ end
35
+
36
+ it "should have a next URL" do
37
+ registration = @payment.run!
38
+ registration.next_url.should_not be_nil
39
+ end
40
+
41
+ it "should allow us to follow the next URL and the response should be successful" do
42
+ registration = @payment.run!
43
+ uri = URI.parse(registration.next_url)
44
+ request = Net::HTTP::Get.new(uri.request_uri)
45
+ http = Net::HTTP.new(uri.host, uri.port)
46
+ http.use_ssl = true if uri.scheme == "https"
47
+ http.start { |http|
48
+ http.request(request)
49
+ }
50
+ end
51
+
52
+ it "should allow us to retrieve signature verification details" do
53
+ @payment.run!
54
+ sig_details = @payment.signature_verification_details
55
+
56
+ sig_details.should_not be_nil
57
+ sig_details.security_key.should_not be_nil
58
+ sig_details.vendor.should_not be_nil
59
+ end
60
+ end
61
+
62
+ describe ".deferred" do
63
+ before(:each) do
64
+ @payment = SagePay::Server.deferred(
65
+ :description => "Demo payment",
66
+ :amount => 12.34,
67
+ :currency => "GBP",
68
+ :billing_address => address_factory
69
+ )
70
+ end
71
+
72
+ it "should successfully register the deferred payment with SagePay" do
73
+ @payment.run!.should_not be_nil
74
+ end
75
+
76
+ it "should be a valid deferred payment" do
77
+ registration = @payment.run!
31
78
  registration.should be_ok
32
79
  end
33
80
 
34
81
  it "should have a next URL" do
35
- registration = @payment.register!
82
+ registration = @payment.run!
36
83
  registration.next_url.should_not be_nil
37
84
  end
38
85
 
39
86
  it "should allow us to follow the next URL and the response should be successful" do
40
- registration = @payment.register!
87
+ registration = @payment.run!
41
88
  uri = URI.parse(registration.next_url)
42
89
  request = Net::HTTP::Get.new(uri.request_uri)
43
90
  http = Net::HTTP.new(uri.host, uri.port)
@@ -48,7 +95,7 @@ if run_integration_specs?
48
95
  end
49
96
 
50
97
  it "should allow us to retrieve signature verification details" do
51
- @payment.register!
98
+ @payment.run!
52
99
  sig_details = @payment.signature_verification_details
53
100
 
54
101
  sig_details.should_not be_nil
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ include SagePay::Server
4
+
5
+ describe NotificationResponse do
6
+ it "should work straight from the factory" do
7
+ lambda {
8
+ notification_response_factory.should be_valid
9
+ }.should_not raise_error
10
+ end
11
+
12
+ describe "validations" do
13
+ it { validates_the_presence_of(:notification_response, :status) }
14
+ it { validates_the_presence_of(:notification_response, :redirect_url) }
15
+
16
+ it "validates the presence of the status_detail field only if the status is something other than OK" do
17
+ notification_response = notification_response_factory(:status => :ok, :status_detail => nil)
18
+ notification_response.should be_valid
19
+
20
+ notification_response = notification_response_factory(:status => :invalid, :status_detail => "Invalid request!")
21
+ notification_response.should be_valid
22
+
23
+ notification_response = notification_response_factory(:status => :invalid, :status_detail => "")
24
+ notification_response.should_not be_valid
25
+ notification_response.errors.on(:status_detail).should include("can't be empty")
26
+ end
27
+
28
+ it { validates_the_length_of(:notification_response, :redirect_url, :max => 255) }
29
+ it { validates_the_length_of(:notification_response, :status_detail, :max => 255) }
30
+
31
+ it "should allow the status to be one of :ok, :invalid or :error" do
32
+ notification_response = notification_response_factory(:status => :ok)
33
+ notification_response.should be_valid
34
+
35
+ notification_response = notification_response_factory(:status => :invalid)
36
+ notification_response.should be_valid
37
+
38
+ notification_response = notification_response_factory(:status => :error)
39
+ notification_response.should be_valid
40
+
41
+ notification_response = notification_response_factory(:status => :chickens)
42
+ notification_response.should_not be_valid
43
+ notification_response.errors.on(:status).should include("is not in the list")
44
+ end
45
+ end
46
+
47
+ describe "#response" do
48
+ it "should produce the expected response for an OK status" do
49
+ notification_response = notification_response_factory(
50
+ :status => :ok,
51
+ :redirect_url => "http://test.host/some/redirect",
52
+ :status_detail => nil
53
+ )
54
+ notification_response.response.should == <<-RESPONSE.chomp
55
+ Status=OK\r
56
+ RedirectURL=http://test.host/some/redirect
57
+ RESPONSE
58
+ end
59
+
60
+ it "should produce the expected response for an invalid status" do
61
+ notification_response = notification_response_factory(
62
+ :status => :invalid,
63
+ :redirect_url => "http://test.host/some/redirect",
64
+ :status_detail => "Totally didn't expect that notification, dude."
65
+ )
66
+ # FIXME: I'm asserting here that I don't have to URI-encode the body
67
+ # here. OK?
68
+ notification_response.response.should == <<-RESPONSE.chomp
69
+ Status=INVALID\r
70
+ RedirectURL=http://test.host/some/redirect\r
71
+ StatusDetail=Totally didn't expect that notification, dude.\r
72
+ RESPONSE
73
+ end
74
+ end
75
+ end
@@ -2,14 +2,40 @@ require 'spec_helper'
2
2
 
3
3
  include SagePay::Server
4
4
 
5
- describe TransactionNotification do
5
+ describe Notification do
6
6
  it "should work straight from the factory" do
7
7
  lambda {
8
- transaction_notification_factory.should_not be_nil
8
+ notification_factory.should_not be_nil
9
9
  }.should_not raise_error
10
10
  end
11
11
 
12
12
  describe ".from_params" do
13
+ before(:each) do
14
+ @params = {
15
+ "VPSProtocol" => "2.23",
16
+ "TxType" => "PAYMENT",
17
+ "VendorTxCode" => "unique-tx-code",
18
+ "VPSTxId" => "{728A5721-B45F-4570-937E-90A16B0A5000}",
19
+ "Status" => "OK",
20
+ "StatusDetail" => "2000 : Card processed successfully.", # FIXME: Make this match reality
21
+ "TxAuthNo" => "1234567890",
22
+ "AVSCV2" => "ALL MATCH",
23
+ "AddressResult" => "MATCHED",
24
+ "PostCodeResult" => "MATCHED",
25
+ "CV2Result" => "MATCHED",
26
+ "GiftAid" => "0",
27
+ "3DSecureStatus" => "OK",
28
+ "CAVV" => "Something?",
29
+ "AddressStatus" => "CONFIRMED",
30
+ "PayerStatus" => "VERIFIED",
31
+ "CardType" => "VISA",
32
+ "Last4Digits" => "1234",
33
+ # FIXME: Calculated manually using the information above. Should
34
+ # really get one from a sample transaction...
35
+ "VPSSignature" => "6AB7A7FFB5369AF953CD57A84D5C2979"
36
+ }
37
+ end
38
+
13
39
  context "with an OK status" do
14
40
  before(:each) do
15
41
  signature_verification_details = mock("Signature verification details",
@@ -17,36 +43,12 @@ describe TransactionNotification do
17
43
  :security_key => "17F13DCBD8"
18
44
  )
19
45
 
20
- @params = {
21
- "VPSProtocol" => "2.23",
22
- "TxType" => "PAYMENT",
23
- "VendorTxCode" => "unique-tx-code",
24
- "VPSTxId" => "{728A5721-B45F-4570-937E-90A16B0A5000}",
25
- "Status" => "OK",
26
- "StatusDetail" => "2000 : Card processed successfully.", # FIXME: Make this match reality
27
- "TxAuthNo" => "1234567890",
28
- "AVSCV2" => "ALL MATCH",
29
- "AddressResult" => "MATCHED",
30
- "PostCodeResult" => "MATCHED",
31
- "CV2Result" => "MATCHED",
32
- "GiftAid" => "0",
33
- "3DSecureStatus" => "OK",
34
- "CAVV" => "Something?",
35
- "AddressStatus" => "CONFIRMED",
36
- "PayerStatus" => "VERIFIED",
37
- "CardType" => "VISA",
38
- "Last4Digits" => "1234",
39
- # FIXME: Calculated manually using the information above. Should
40
- # really get one from a sample transaction...
41
- "VPSSignature" => "6AB7A7FFB5369AF953CD57A84D5C2979"
42
- }
43
-
44
- @notification = TransactionNotification.from_params(@params, signature_verification_details)
46
+ @notification = Notification.from_params(@params, signature_verification_details)
45
47
  end
46
48
 
47
49
  it "should successfully parse the params" do
48
50
  lambda {
49
- TransactionNotification.from_params(@params).should_not be_nil
51
+ Notification.from_params(@params).should_not be_nil
50
52
  }.should_not raise_error
51
53
  end
52
54
 
@@ -149,6 +151,42 @@ describe TransactionNotification do
149
151
  it "should report that the vps signature is valid" do
150
152
  @notification.should be_valid_signature
151
153
  end
154
+
155
+ it "should generate a successful response" do
156
+ mock_notification_response = mock(NotificationResponse, :response => "some response")
157
+ NotificationResponse.should_receive(:new).with(:status => :ok, :redirect_url => "mock redirect url").and_return(mock_notification_response)
158
+ @notification.response("mock redirect url").should == "some response"
159
+ end
160
+ end
161
+
162
+ context "with an invalid signature" do
163
+ before(:each) do
164
+ signature_verification_details = mock("Signature verification details",
165
+ :vendor => "rubaidh",
166
+ :security_key => "different security key"
167
+ )
168
+
169
+ @notification = Notification.from_params(@params, signature_verification_details)
170
+ end
171
+
172
+ it "should generate a failed response" do
173
+ mock_notification_response = mock(NotificationResponse, :response => "can haz failure")
174
+ NotificationResponse.should_receive(:new).with(:status => :invalid, :redirect_url => "mock redirect url", :status_detail => "Signature did not match our expectations").and_return(mock_notification_response)
175
+ @notification.response("mock redirect url").should == "can haz failure"
176
+ end
177
+ end
178
+
179
+ context "with a block supplied for the signature verification details" do
180
+ it "should still validate the signature correctly" do
181
+ notification = Notification.from_params(@params) do |attributes|
182
+ mock("Signature verification details",
183
+ :vendor => "rubaidh",
184
+ :security_key => "17F13DCBD8"
185
+ )
186
+ end
187
+
188
+ notification.should be_valid_signature
189
+ end
152
190
  end
153
191
  end
154
192
  end
@@ -2,10 +2,10 @@ require 'spec_helper'
2
2
 
3
3
  include SagePay::Server
4
4
 
5
- describe TransactionRegistrationResponse do
5
+ describe RegistrationResponse do
6
6
  it "should work straight from the factory" do
7
7
  lambda {
8
- transaction_registration_response_factory.should_not be_nil
8
+ registration_response_factory.should_not be_nil
9
9
  }.should_not raise_error
10
10
  end
11
11
 
@@ -17,12 +17,12 @@ VPSProtocol=2.23
17
17
  Status=INVALID
18
18
  StatusDetail=4000 : The VendorName is invalid or the account is not active.
19
19
  RESPONSE
20
- @response = TransactionRegistrationResponse.from_response_body(@response_body)
20
+ @response = RegistrationResponse.from_response_body(@response_body)
21
21
  end
22
22
 
23
23
  it "should successfully parse the body" do
24
24
  lambda {
25
- TransactionRegistrationResponse.from_response_body(@response_body)
25
+ RegistrationResponse.from_response_body(@response_body)
26
26
  }.should_not raise_error
27
27
  end
28
28
 
@@ -72,12 +72,12 @@ VPSProtocol=2.23
72
72
  Status=MALFORMED
73
73
  StatusDetail=5000 : Your request had too many toes.
74
74
  RESPONSE
75
- @response = TransactionRegistrationResponse.from_response_body(@response_body)
75
+ @response = RegistrationResponse.from_response_body(@response_body)
76
76
  end
77
77
 
78
78
  it "should successfully parse the body" do
79
79
  lambda {
80
- TransactionRegistrationResponse.from_response_body(@response_body)
80
+ RegistrationResponse.from_response_body(@response_body)
81
81
  }.should_not raise_error
82
82
  end
83
83
 
@@ -127,12 +127,12 @@ VPSProtocol=2.23
127
127
  Status=ERROR
128
128
  StatusDetail=5000 : SagePay blew up.
129
129
  RESPONSE
130
- @response = TransactionRegistrationResponse.from_response_body(@response_body)
130
+ @response = RegistrationResponse.from_response_body(@response_body)
131
131
  end
132
132
 
133
133
  it "should successfully parse the body" do
134
134
  lambda {
135
- TransactionRegistrationResponse.from_response_body(@response_body)
135
+ RegistrationResponse.from_response_body(@response_body)
136
136
  }.should_not raise_error
137
137
  end
138
138
 
@@ -186,12 +186,12 @@ SecurityKey=17F13DCBD8
186
186
  NextURL=https://test.sagepay.com/Simulator/VSPServerPaymentPage.asp?TransactionID={728A5721-B45F-4570-937E-90A16B0A5000}
187
187
  RESPONSE
188
188
 
189
- @response = TransactionRegistrationResponse.from_response_body(@response_body)
189
+ @response = RegistrationResponse.from_response_body(@response_body)
190
190
  end
191
191
 
192
192
  it "should successfully parse the body" do
193
193
  lambda {
194
- TransactionRegistrationResponse.from_response_body(@response_body)
194
+ RegistrationResponse.from_response_body(@response_body)
195
195
  }.should_not raise_error
196
196
  end
197
197