amazon_flex_pay 0.9.14 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,23 +1,9 @@
1
1
  module AmazonFlexPay
2
- class << self
3
- # Returns a signature for the given URL and parameters.
4
- def signature(endpoint, params)
5
- uri = URI.parse(endpoint)
6
-
7
- signable_string = [
8
- 'GET',
9
- uri.host,
10
- uri.path,
11
- query_string(params)
12
- ].join("\n")
13
-
14
- Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, AmazonFlexPay.secret_key, signable_string)).strip
15
- end
16
-
2
+ module Util
17
3
  # Flattens a possibly-nested hash into a query string for Amazon.
18
4
  # With Amazon, nested hashes are flattened with a period, as follows:
19
5
  #
20
- # AmazonFlexPay.query_string(:foo => {:hello => 'world'})
6
+ # AmazonFlexPay::Util.query_string(:foo => {:hello => 'world'})
21
7
  # => "foo.hello=world"
22
8
  #
23
9
  def query_string(params, prefix = nil) #:nodoc:
@@ -38,5 +24,7 @@ module AmazonFlexPay
38
24
  # note that URI.escape(' ') => '%20', and CGI.escape(' ') => '+'
39
25
  URI.escape(value.to_s, UNSAFE)
40
26
  end
27
+
28
+ extend self
41
29
  end
42
30
  end
@@ -0,0 +1,67 @@
1
+ require_relative '../../test_helper'
2
+
3
+ class AmazonFlexPay::API::BaseRequestTest < AmazonFlexPay::Test
4
+
5
+ class TestRequest < AmazonFlexPay::API::BaseRequest
6
+ attribute :foo
7
+ attribute :amount, :type => :amount
8
+ attribute :stuffs, :collection => :amount
9
+ attribute :method, :enumeration => :payment_method
10
+ end
11
+
12
+ should "respond with data structures even when models are empty" do
13
+ tr = TestRequest.new
14
+ assert tr.stuffs.is_a?(Array)
15
+ assert tr.amount.respond_to?(:value)
16
+ assert !tr.to_hash.has_key?('Stuffs')
17
+ assert !tr.to_hash.has_key?('Amount')
18
+ end
19
+
20
+ should "hash api requests" do
21
+ request = TestRequest.new(:foo => 'bar', :amount => {:value => '3.14', :currency_code => 'USD'})
22
+ hash = request.to_hash
23
+
24
+ # simple attributes
25
+ assert_equal 'bar', hash['Foo']
26
+
27
+ # complex attributes
28
+ assert_equal '3.14', hash['Amount']['Value']
29
+ assert_equal 'USD', hash['Amount']['CurrencyCode']
30
+
31
+ # standard additions
32
+ assert_equal 'TestRequest', hash['Action']
33
+ assert_equal '2011-09-20', hash['Version']
34
+ end
35
+
36
+ should "parameterize api requests" do
37
+ Time.stubs(:now).returns(Time.parse('Jan 1 2011')) # so the signature remains constant
38
+
39
+ request = TestRequest.new(:foo => 'bar', :amount => {:value => '3.14', :currency_code => 'USD'})
40
+ param = request.to_param
41
+
42
+ {
43
+ 'Foo' => 'bar',
44
+ 'AWSAccessKeyId' => 'foo',
45
+ 'SignatureVersion' => 2,
46
+ 'SignatureMethod' => 'HmacSHA256',
47
+ 'Signature' => 'WVrkmK7qt%2FT%2BgtHWcdzqtkLRH8c06l%2FmPv3ZfxyvNyg%3D'
48
+ }.each do |key, value|
49
+ assert param.match(/#{key}=#{value}/), "#{param} should contain #{key}=#{value}"
50
+ end
51
+ end
52
+
53
+ should "not allow unknown values for enumerated attributes" do
54
+ assert_raises ArgumentError do TestRequest.new(:method => 'UNKOWN') end
55
+ end
56
+
57
+ should "allow value sets for enumerated attributes" do
58
+ assert_nothing_raised do
59
+ TestRequest.new(:method => ['CC', 'ACH'])
60
+ end
61
+ end
62
+
63
+ should "convert value sets into comma-separated lists" do
64
+ assert_equal 'a,b', TestRequest.new(:foo => ['a', 'b']).to_hash['Foo']
65
+ end
66
+
67
+ end
@@ -0,0 +1,418 @@
1
+ require_relative '../test_helper'
2
+
3
+ class AmazonFlexPay::APITest < AmazonFlexPay::Test
4
+ include ResponseSamples
5
+
6
+ ## Cancel
7
+
8
+ should "construct a Cancel request" do
9
+ AmazonFlexPay.expects(:submit).with do |request|
10
+ assert request.is_a? AmazonFlexPay::API::Cancel
11
+ assert_equal 'txid', request.transaction_id
12
+ assert_equal 'test', request.description
13
+ end
14
+ AmazonFlexPay.cancel('txid', {:description => 'test'})
15
+ end
16
+
17
+ should "parse a Cancel response" do
18
+ response = nil
19
+ assert_nothing_raised do
20
+ response = AmazonFlexPay::API::Cancel::Response.from_xml(cancel_response)
21
+ end
22
+ assert response.request_id
23
+ assert response.transaction_id
24
+ assert response.transaction_status
25
+ end
26
+
27
+ ## CancelToken
28
+
29
+ should "construct a CancelToken request" do
30
+ AmazonFlexPay.expects(:submit).with do |request|
31
+ assert request.is_a? AmazonFlexPay::API::CancelToken
32
+ assert_equal 'token', request.token_id
33
+ assert_equal 'test', request.reason_text
34
+ end
35
+ AmazonFlexPay.cancel_token('token', {:reason_text => 'test'})
36
+ end
37
+
38
+ should "parse a CancelToken response" do
39
+ response = nil
40
+ assert_nothing_raised do
41
+ response = AmazonFlexPay::API::CancelToken::Response.from_xml(cancel_token_response)
42
+ end
43
+ assert response.request_id
44
+ end
45
+
46
+ ## GetAccountActivity
47
+
48
+ should "construct a GetAccountActivity request" do
49
+ since = Time.now - 60*60*24 # 1.day
50
+ to = Time.now
51
+ AmazonFlexPay.expects(:submit).with do |request|
52
+ assert request.is_a? AmazonFlexPay::API::GetAccountActivity
53
+ assert_equal since, request.start_date
54
+ assert_equal to, request.end_date
55
+ end
56
+ AmazonFlexPay.get_account_activity(since, to)
57
+ end
58
+
59
+ should "parse a GetAccountActivity response" do
60
+ response = nil
61
+ assert_nothing_raised do
62
+ response = AmazonFlexPay::API::GetAccountActivity::Response.from_xml(get_account_activity_response)
63
+ end
64
+ assert response.request_id
65
+ assert_equal 5, response.transactions.count
66
+ end
67
+
68
+ ## GetAccountBalance
69
+
70
+ should "construct a GetAccountBalance request" do
71
+ AmazonFlexPay.expects(:submit).with do |request|
72
+ assert request.is_a? AmazonFlexPay::API::GetAccountBalance
73
+ end
74
+ AmazonFlexPay.get_account_balance
75
+ end
76
+
77
+ should "parse a GetAccountBalance response" do
78
+ response = nil
79
+ assert_nothing_raised do
80
+ response = AmazonFlexPay::API::GetAccountBalance::Response.from_xml(get_account_balance_response)
81
+ end
82
+ assert response.request_id
83
+ assert_equal '7.400000', response.account_balance.total_balance.value
84
+ end
85
+
86
+ ## GetRecipientVerificationStatus
87
+
88
+ should "construct a GetRecipientVerificationStatus request" do
89
+ AmazonFlexPay.expects(:submit).with do |request|
90
+ assert request.is_a? AmazonFlexPay::API::GetRecipientVerificationStatus
91
+ assert_equal 'token', request.recipient_token_id
92
+ end
93
+ AmazonFlexPay.get_recipient_verification_status('token')
94
+ end
95
+
96
+ should "parse a GetRecipientVerificationStatus response" do
97
+ response = nil
98
+ assert_nothing_raised do
99
+ response = AmazonFlexPay::API::GetRecipientVerificationStatus::Response.from_xml(get_recipient_verification_status_response)
100
+ end
101
+ assert response.request_id
102
+ assert response.recipient_verification_status
103
+ end
104
+
105
+ ## GetTokenByCaller
106
+
107
+ should "construct a GetTokenByCaller request by reference" do
108
+ AmazonFlexPay.expects(:submit).with do |request|
109
+ assert request.is_a? AmazonFlexPay::API::GetTokenByCaller
110
+ assert_equal 'reference', request.caller_reference
111
+ end
112
+ AmazonFlexPay.get_token_by_caller_reference('reference')
113
+ end
114
+
115
+ should "construct a GetTokenByCaller request by token id" do
116
+ AmazonFlexPay.expects(:submit).with do |request|
117
+ assert request.is_a? AmazonFlexPay::API::GetTokenByCaller
118
+ assert_equal 'token', request.token_id
119
+ end
120
+ AmazonFlexPay.get_token_by_id('token')
121
+ end
122
+
123
+ should "parse a GetTokenByCaller response" do
124
+ response = nil
125
+ assert_nothing_raised do
126
+ response = AmazonFlexPay::API::GetTokenByCaller::Response.from_xml(get_token_by_caller_response)
127
+ end
128
+ assert response.request_id
129
+ assert response.token.token_id
130
+ assert response.token.token_status
131
+ end
132
+
133
+ ## GetTokenUsage
134
+
135
+ should "construct a GetTokenUsage request" do
136
+ AmazonFlexPay.expects(:submit).with do |request|
137
+ assert request.is_a? AmazonFlexPay::API::GetTokenUsage
138
+ assert_equal 'token', request.token_id
139
+ end
140
+ AmazonFlexPay.get_token_usage('token')
141
+ end
142
+
143
+ should "parse a GetTokenUsage response" do
144
+ response = nil
145
+ assert_nothing_raised do
146
+ response = AmazonFlexPay::API::GetTokenUsage::Response.from_xml(get_token_usage_response)
147
+ end
148
+ assert_equal 2, response.token_usage_limits.count
149
+ assert_equal '10.000000', response.token_usage_limits.first.amount.value
150
+ assert_equal '1', response.token_usage_limits.last.count
151
+ end
152
+
153
+ ## GetTokens
154
+
155
+ should "construct a GetTokens request" do
156
+ AmazonFlexPay.expects(:submit).with do |request|
157
+ assert request.is_a? AmazonFlexPay::API::GetTokens
158
+ end
159
+ AmazonFlexPay.get_tokens
160
+ end
161
+
162
+ should "parse a GetTokens response" do
163
+ response = nil
164
+ assert_nothing_raised do
165
+ response = AmazonFlexPay::API::GetTokens::Response.from_xml(get_tokens_response)
166
+ end
167
+ assert_equal 1, response.tokens.count
168
+ end
169
+
170
+ ## GetTransaction
171
+
172
+ should "construct a GetTransaction request" do
173
+ AmazonFlexPay.expects(:submit).with do |request|
174
+ assert request.is_a? AmazonFlexPay::API::GetTransaction
175
+ assert_equal 'txid', request.transaction_id
176
+ end
177
+ AmazonFlexPay.get_transaction('txid')
178
+ end
179
+
180
+ should "parse a GetTransaction response" do
181
+ response = nil
182
+ assert_nothing_raised do
183
+ response = AmazonFlexPay::API::GetTransaction::Response.from_xml(get_transaction_response)
184
+ end
185
+ assert response.request_id
186
+ assert response.transaction.caller_reference
187
+ assert response.transaction.payment_method
188
+ end
189
+
190
+ should "delegate to transaction attributes" do
191
+ response = AmazonFlexPay::API::GetTransaction::Response.from_xml(get_transaction_response)
192
+ assert_equal response.caller_reference, response.transaction.caller_reference
193
+ assert_equal response.date_completed, response.transaction.date_completed
194
+ end
195
+
196
+ ## GetTransactionStatus
197
+
198
+ should "construct a GetTransactionStatus request" do
199
+ AmazonFlexPay.expects(:submit).with do |request|
200
+ assert request.is_a? AmazonFlexPay::API::GetTransactionStatus
201
+ assert_equal 'txid', request.transaction_id
202
+ end
203
+ AmazonFlexPay.get_transaction_status('txid')
204
+ end
205
+
206
+ should "parse a GetTransactionStatus response" do
207
+ response = nil
208
+ assert_nothing_raised do
209
+ response = AmazonFlexPay::API::GetTransactionStatus::Response.from_xml(get_transaction_status_response)
210
+ end
211
+ assert response.request_id
212
+ assert response.transaction_id
213
+ assert_equal 'Success', response.transaction_status
214
+ end
215
+
216
+ ## Pay
217
+
218
+ should "construct a Pay request" do
219
+ AmazonFlexPay.expects(:submit).with do |request|
220
+ assert request.is_a? AmazonFlexPay::API::Pay
221
+ assert_equal '1.00', request.transaction_amount.value
222
+ assert_equal 'USD', request.transaction_amount.currency_code
223
+ assert_equal 'token', request.sender_token_id
224
+ assert_equal 'myid', request.caller_reference
225
+ end
226
+ AmazonFlexPay.pay('1.00', 'USD', 'token', 'myid')
227
+ end
228
+
229
+ should "parse a Pay response" do
230
+ response = nil
231
+ assert_nothing_raised do
232
+ response = AmazonFlexPay::API::Pay::Response.from_xml(reserve_response)
233
+ end
234
+ assert response.request_id
235
+ assert response.transaction_id
236
+ assert response.transaction_status
237
+ end
238
+
239
+ ## Refund
240
+
241
+ should "construct a Refund request" do
242
+ AmazonFlexPay.expects(:submit).with do |request|
243
+ assert request.is_a? AmazonFlexPay::API::Refund
244
+ assert_equal 'txid', request.transaction_id
245
+ assert_equal 'myid', request.caller_reference
246
+ assert_equal '1.00', request.refund_amount.value
247
+ end
248
+ AmazonFlexPay.refund('txid', 'myid', :refund_amount => {:currency_code => 'USD', :value => '1.00'})
249
+ end
250
+
251
+ should "parse a Refund response" do
252
+ response = nil
253
+ assert_nothing_raised do
254
+ response = AmazonFlexPay::API::Refund::Response.from_xml(refund_response)
255
+ end
256
+ assert response.request_id
257
+ assert response.transaction_id
258
+ assert response.transaction_status
259
+ end
260
+
261
+ ## Reserve
262
+
263
+ should "construct a Reserve request" do
264
+ AmazonFlexPay.expects(:submit).with do |request|
265
+ assert request.is_a? AmazonFlexPay::API::Reserve
266
+ assert_equal '1.00', request.transaction_amount.value
267
+ assert_equal 'USD', request.transaction_amount.currency_code
268
+ assert_equal 'token', request.sender_token_id
269
+ assert_equal 'myid', request.caller_reference
270
+ assert_equal 'Caller', request.descriptor_policy.cs_owner
271
+
272
+ assert request.to_hash['DescriptorPolicy'].has_key?('CSOwner') # funky casing
273
+ end
274
+ AmazonFlexPay.reserve('1.00', 'USD', 'token', 'myid', :descriptor_policy => {:cs_owner => 'Caller', :soft_descriptor_type => 'Static'})
275
+ end
276
+
277
+ should "parse a Reserve response" do
278
+ response = nil
279
+ assert_nothing_raised do
280
+ response = AmazonFlexPay::API::Reserve::Response.from_xml(reserve_response)
281
+ end
282
+ assert response.request_id
283
+ assert response.transaction_id
284
+ assert response.transaction_status
285
+ end
286
+
287
+ ## Settle
288
+
289
+ should "construct a Settle request" do
290
+ AmazonFlexPay.expects(:submit).with do |request|
291
+ assert request.is_a? AmazonFlexPay::API::Settle
292
+ assert_equal 'txid', request.reserve_transaction_id
293
+ assert_equal 'USD', request.transaction_amount.currency_code
294
+ assert_equal '3.14', request.transaction_amount.value
295
+ end
296
+ AmazonFlexPay.settle('txid', :transaction_amount => {:currency_code => 'USD', :value => '3.14'})
297
+ end
298
+
299
+ should "parse a Settle response" do
300
+ response = nil
301
+ assert_nothing_raised do
302
+ response = AmazonFlexPay::API::Settle::Response.from_xml(settle_response)
303
+ end
304
+ assert response.request_id
305
+ assert response.transaction_id
306
+ assert response.transaction_status
307
+ end
308
+
309
+ ## VerifySignature
310
+
311
+ should "construct a VerifySignature request" do
312
+ AmazonFlexPay.expects(:submit).with do |request|
313
+ assert request.is_a? AmazonFlexPay::API::VerifySignature
314
+ assert_equal 'http://example.com/api', request.url_end_point
315
+ assert_equal 'foo=bar', request.http_parameters
316
+ end
317
+ AmazonFlexPay.verify_signature('http://example.com/api', 'foo=bar')
318
+ end
319
+
320
+ should "parse a VerifySignature response" do
321
+ response = nil
322
+ assert_nothing_raised do
323
+ response = AmazonFlexPay::API::VerifySignature::Response.from_xml(verify_signature_response)
324
+ end
325
+ assert response.request_id
326
+ assert response.verification_status
327
+ end
328
+
329
+ ## verifying a request
330
+
331
+ should "verify a GET request" do
332
+ request = stub(:get? => true, :protocol => 'http://', :host_with_port => 'example.com', :path => '/foo/bar', :query_string => 'a=1&b=2')
333
+ AmazonFlexPay.expects(:verify_signature).with('http://example.com/foo/bar', 'a=1&b=2').returns(true)
334
+ assert AmazonFlexPay.verify_request(request)
335
+ end
336
+
337
+ should "verify a POST request" do
338
+ request = stub(:get? => false, :protocol => 'http://', :host_with_port => 'example.com', :path => '/foo/bar', :raw_post => 'a=1&b=2')
339
+ AmazonFlexPay.expects(:verify_signature).with('http://example.com/foo/bar', 'a=1&b=2').returns(true)
340
+ assert AmazonFlexPay.verify_request(request)
341
+ end
342
+
343
+ ## Submit
344
+
345
+ class TestRequest < AmazonFlexPay::API::BaseRequest
346
+ attribute :foo
347
+
348
+ class Response < AmazonFlexPay::API::BaseRequest::BaseResponse; end
349
+ end
350
+
351
+
352
+ should "store the request in the response" do
353
+ RestClient.expects(:get).returns(stub(:body => cancel_token_response, :code => 200))
354
+ request = TestRequest.new(:foo => 'bar')
355
+ response = AmazonFlexPay.send(:submit, request)
356
+ assert_equal 'bar', response.request.foo
357
+ end
358
+
359
+ should "instrument successful responses" do
360
+ events = []
361
+ callback = proc{ |*args| events << ActiveSupport::Notifications::Event.new(*args) }
362
+
363
+ ActiveSupport::Notifications.subscribed(callback, "amazon_flex_pay.api") do
364
+ RestClient.expects(:get).returns(stub(:body => cancel_token_response, :code => 200))
365
+ request = TestRequest.new(:foo => 'bar')
366
+ AmazonFlexPay.send(:submit, request)
367
+ end
368
+
369
+ assert_equal 1, events.size
370
+ assert_equal 'TestRequest', events.first.payload[:action]
371
+ assert_equal 200, events.first.payload[:code]
372
+ assert events.first.payload.has_key?(:request)
373
+ assert events.first.payload.has_key?(:response)
374
+ assert events.first.duration > 0.1, events.first.duration.to_s
375
+ end
376
+
377
+ should "catch and parse errors" do
378
+ net_http_res = stub(:code => 400)
379
+ http_response = RestClient::Response.create(error_response, net_http_res, nil)
380
+ RestClient.expects(:get).raises(RestClient::BadRequest.new(http_response))
381
+
382
+ error = nil
383
+ begin
384
+ request = TestRequest.new(:foo => 'bar')
385
+ AmazonFlexPay.send(:submit, request)
386
+ rescue AmazonFlexPay::API::InvalidParams => e
387
+ error = e
388
+ end
389
+ assert error.request_id
390
+ assert_equal 'InvalidParams', error.code
391
+ assert error.message.match(/has to be a valid/)
392
+ end
393
+
394
+ should "instrument error responses" do
395
+ events = []
396
+ callback = proc{ |*args| events << ActiveSupport::Notifications::Event.new(*args) }
397
+
398
+ ActiveSupport::Notifications.subscribed(callback, "amazon_flex_pay.api") do
399
+ net_http_res = stub(:code => 400)
400
+ http_response = RestClient::Response.create(error_response, net_http_res, nil)
401
+ RestClient.expects(:get).raises(RestClient::BadRequest.new(http_response))
402
+
403
+ begin
404
+ request = TestRequest.new(:foo => 'bar')
405
+ AmazonFlexPay.send(:submit, request)
406
+ rescue AmazonFlexPay::API::Error
407
+ end
408
+ end
409
+
410
+ assert_equal 1, events.size
411
+ assert_equal 'TestRequest', events.first.payload[:action]
412
+ assert_equal 400, events.first.payload[:code]
413
+ assert events.first.payload.has_key?(:request)
414
+ assert events.first.payload.has_key?(:response)
415
+ assert events.first.duration > 0.1, events.first.duration.to_s
416
+ end
417
+
418
+ end