recurly 0.4.8 → 0.4.10

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of recurly might be problematic. Click here for more details.

Files changed (114) hide show
  1. data/README.md +2 -2
  2. data/lib/recurly.rb +2 -1
  3. data/lib/recurly/account.rb +2 -2
  4. data/lib/recurly/base.rb +30 -12
  5. data/lib/recurly/billing_info.rb +2 -2
  6. data/lib/recurly/exceptions.rb +13 -0
  7. data/lib/recurly/plan.rb +1 -1
  8. data/lib/recurly/subscription.rb +2 -2
  9. data/lib/recurly/transaction.rb +2 -2
  10. data/lib/recurly/transparent.rb +1 -1
  11. data/lib/recurly/verification.rb +77 -0
  12. data/lib/recurly/version.rb +1 -1
  13. data/spec/config/recurly.yml +4 -4
  14. data/spec/integration/account_spec.rb +12 -0
  15. data/spec/integration/billing_info_spec.rb +2 -2
  16. data/spec/integration/charge_spec.rb +0 -1
  17. data/spec/integration/plan_spec.rb +2 -2
  18. data/spec/support/vcr.rb +2 -2
  19. data/spec/unit/verification_spec.rb +76 -0
  20. data/spec/vcr/account/accept-language-account/1313708721.yml +39 -0
  21. data/spec/vcr/account/close/1313708721.yml +104 -0
  22. data/spec/vcr/account/create-blank/1313708721.yml +33 -0
  23. data/spec/vcr/account/create-duplicate/1313708721.yml +69 -0
  24. data/spec/vcr/account/create-min/1313708721.yml +38 -0
  25. data/spec/vcr/account/create/1313708721.yml +39 -0
  26. data/spec/vcr/account/find/1313708721.yml +107 -0
  27. data/spec/vcr/account/list/1313708721.yml +878 -0
  28. data/spec/vcr/account/update/1313708721.yml +158 -0
  29. data/spec/vcr/billing/create/1313708721.yml +118 -0
  30. data/spec/vcr/billing/destroy/1313708721.yml +177 -0
  31. data/spec/vcr/billing/errors/1313708721.yml +71 -0
  32. data/spec/vcr/billing/find/1313708721.yml +156 -0
  33. data/spec/vcr/billing/update/1313708721.yml +159 -0
  34. data/spec/vcr/charge/create/1313708721.yml +153 -0
  35. data/spec/vcr/charge/delete-uninvoiced/1313708721.yml +206 -0
  36. data/spec/vcr/charge/list-all/1313708721.yml +198 -0
  37. data/spec/vcr/charge/list-invoiced/1313708721.yml +294 -0
  38. data/spec/vcr/charge/list-pending/1313708721.yml +200 -0
  39. data/spec/vcr/charge/lookup/1313708721.yml +112 -0
  40. data/spec/vcr/coupon/create/1296774903.yml +96 -170
  41. data/spec/vcr/coupon/destroy/1296774903.yml +114 -190
  42. data/spec/vcr/credit/create/1313708090.yml +112 -0
  43. data/spec/vcr/credit/delete/1313708090.yml +171 -0
  44. data/spec/vcr/credit/list/1313708090.yml +198 -0
  45. data/spec/vcr/credit/lookup/1313708090.yml +112 -0
  46. data/spec/vcr/invoice/create-no-charges/1296674173.yml +100 -185
  47. data/spec/vcr/invoice/create/1296674173.yml +182 -328
  48. data/spec/vcr/invoice/list/1296674173.yml +255 -442
  49. data/spec/vcr/invoice/lookup/1296674173.yml +187 -331
  50. data/spec/vcr/plan/all.yml +210 -253
  51. data/spec/vcr/plan/find.yml +115 -187
  52. data/spec/vcr/plan/update.yml +105 -187
  53. data/spec/vcr/subscription/addons/add/1296674173.yml +269 -212
  54. data/spec/vcr/subscription/addons/create/1296674173.yml +172 -212
  55. data/spec/vcr/subscription/addons/remove/1296674173.yml +257 -212
  56. data/spec/vcr/subscription/cancel-with-code/1296674173.yml +271 -452
  57. data/spec/vcr/subscription/cancel/1296674173.yml +234 -394
  58. data/spec/vcr/subscription/change1/1296674173.yml +237 -402
  59. data/spec/vcr/subscription/change2/1296674173.yml +238 -402
  60. data/spec/vcr/subscription/create/1296674173.yml +123 -221
  61. data/spec/vcr/subscription/find/1296674173.yml +161 -279
  62. data/spec/vcr/subscription/reactivate/1296674173.yml +261 -431
  63. data/spec/vcr/subscription/refund-full/1296674173.yml +223 -373
  64. data/spec/vcr/subscription/refund-none/1296674173.yml +223 -373
  65. data/spec/vcr/subscription/refund-partial/1296674173.yml +223 -373
  66. data/spec/vcr/transaction/all/1311126815.yml +215 -0
  67. data/spec/vcr/transaction/create-no-account/1311126815.yml +51 -0
  68. data/spec/vcr/transaction/create-with-account/1311126815.yml +125 -0
  69. data/spec/vcr/transaction/list-filled/1311126815.yml +349 -0
  70. data/spec/vcr/transaction/list-initial/1311126815.yml +123 -0
  71. data/spec/vcr/transaction/lookup/1311126815.yml +335 -0
  72. data/spec/vcr/transaction/refund/1311126815.yml +163 -0
  73. data/spec/vcr/transaction/void/1311126815.yml +215 -0
  74. data/spec/vcr/transparent/post-url/1311239470.yml +76 -0
  75. metadata +207 -154
  76. data/spec/vcr/account/accept-language-account/1296674173.yml +0 -70
  77. data/spec/vcr/account/close/1296674173.yml +0 -164
  78. data/spec/vcr/account/create-blank/1296674173.yml +0 -52
  79. data/spec/vcr/account/create-duplicate/1296674173.yml +0 -116
  80. data/spec/vcr/account/create-min/1296674173.yml +0 -66
  81. data/spec/vcr/account/create/1296674173.yml +0 -70
  82. data/spec/vcr/account/find/1296674173.yml +0 -169
  83. data/spec/vcr/account/list/1296674173.yml +0 -55
  84. data/spec/vcr/account/update/1296674173.yml +0 -258
  85. data/spec/vcr/billing/create/1296690812.yml +0 -215
  86. data/spec/vcr/billing/destroy/1296690812.yml +0 -294
  87. data/spec/vcr/billing/find/1296690812.yml +0 -280
  88. data/spec/vcr/billing/update/1296690812.yml +0 -297
  89. data/spec/vcr/charge/create/1296674173.yml +0 -262
  90. data/spec/vcr/charge/delete-uninvoiced/1296674173.yml +0 -307
  91. data/spec/vcr/charge/list-all/1296674173.yml +0 -316
  92. data/spec/vcr/charge/list-invoiced/1296674173.yml +0 -468
  93. data/spec/vcr/charge/list-pending/1296674173.yml +0 -316
  94. data/spec/vcr/charge/lookup/1296674173.yml +0 -180
  95. data/spec/vcr/coupon/create/1296688818.yml +0 -183
  96. data/spec/vcr/coupon/destroy/1296688818.yml +0 -207
  97. data/spec/vcr/credit/create/1296674173.yml +0 -121
  98. data/spec/vcr/credit/delete/1296674173.yml +0 -121
  99. data/spec/vcr/credit/list/1296674173.yml +0 -121
  100. data/spec/vcr/credit/lookup/1296674173.yml +0 -121
  101. data/spec/vcr/plan/delete/1296674173.yml +0 -225
  102. data/spec/vcr/transaction/all/1296674173.yml +0 -389
  103. data/spec/vcr/transaction/create-no-account/1296674173.yml +0 -93
  104. data/spec/vcr/transaction/create-with-account/1296674173.yml +0 -225
  105. data/spec/vcr/transaction/list-filled/1296674173.yml +0 -487
  106. data/spec/vcr/transaction/list-initial/1296674173.yml +0 -217
  107. data/spec/vcr/transaction/lookup/1296674173.yml +0 -563
  108. data/spec/vcr/transaction/refund/1296674173.yml +0 -287
  109. data/spec/vcr/transaction/void/1296674173.yml +0 -352
  110. data/spec/vcr/transparent/post-url/1311127536.yml +0 -0
  111. data/spec/vcr/transparent/post-url/1311127583.yml +0 -0
  112. data/spec/vcr/transparent/post-url/1311128042.yml +0 -0
  113. data/spec/vcr/transparent/post-url/1311128073.yml +0 -0
  114. data/spec/vcr/transparent/post-url/1311128087.yml +0 -0
data/README.md CHANGED
@@ -26,7 +26,7 @@ Installation
26
26
  Setup (Rails 3)
27
27
  --------------
28
28
 
29
- The Recurly Ruby Client requires an API user to connect. Please see the [Authentication](http://support.recurly.com/faqs/api/authentication) documentation for more information.
29
+ The Recurly Ruby Client requires an API user to connect. Please see the [Authentication](http://docs.recurly.com/api/authentication/) documentation for more information.
30
30
 
31
31
  If using Rails 3, the easiest way to get Recurly set up is to run `rake recurly:setup`. This will create a config/recurly.yml that has your recurly account authentication, and the Recurly rails initializer will pick it up on restart of your web app.
32
32
 
@@ -135,4 +135,4 @@ This will run `recurly:clear_test_data` (using your spec/config/recurly.yml auth
135
135
  API Documentation
136
136
  -----------------
137
137
 
138
- Please see the [Recurly API](http://docs.recurly.com/api/basics) for more information and examples.
138
+ Please see the [Recurly API](http://docs.recurly.com/api/basics) for more information and examples.
data/lib/recurly.rb CHANGED
@@ -38,6 +38,7 @@ module Recurly
38
38
  autoload :Subscription, 'recurly/subscription'
39
39
  autoload :Transaction, 'recurly/transaction'
40
40
  autoload :Transparent, 'recurly/transparent'
41
+ autoload :Verification, 'recurly/verification'
41
42
 
42
43
  class << self
43
44
  attr_accessor :username, :password, :environment, :subdomain, :private_key
@@ -79,7 +80,7 @@ module Recurly
79
80
  environment = :production if environment.nil? # Default to production
80
81
  case environment.to_sym
81
82
  when :development
82
- "http://app.recurly.local:3000"
83
+ "http://app.lvh.me:3000"
83
84
  when :production
84
85
  "https://api-production.recurly.com"
85
86
  when :sandbox
@@ -18,10 +18,10 @@ module Recurly
18
18
  end
19
19
 
20
20
  # initialize associations
21
- def initialize(attributes = {})
21
+ def initialize(attributes = {}, persisted = false)
22
22
  attributes = attributes.with_indifferent_access
23
23
  attributes[:billing_info] ||= {}
24
- super(attributes)
24
+ super
25
25
  end
26
26
 
27
27
  attr_accessor :account_code_was
data/lib/recurly/base.rb CHANGED
@@ -15,6 +15,14 @@ module Recurly
15
15
  self.errors.blank?
16
16
  end
17
17
 
18
+ # Define #initialize with persisted for our Rails 2.3 and 3.0 friends
19
+ def initialize(attributes = {}, persisted = false)
20
+ @attributes = {}.with_indifferent_access
21
+ @prefix_options = {}
22
+ @persisted = persisted
23
+ load(attributes)
24
+ end
25
+
18
26
  # See http://github.com/rails/rails/commit/1488c6cc9e6237ce794e3c4a6201627b9fd4ca09
19
27
  # Errors in Rails 2.3.4 are not parsed correctly.
20
28
  def save
@@ -26,6 +34,8 @@ module Recurly
26
34
  true
27
35
  rescue ActiveResource::ResourceInvalid => e
28
36
  load_errors e.response.body
37
+ #raise Recurly::ResourceInvalid.new(e.response, self.errors.full_messages.join(', '))
38
+ false
29
39
  end
30
40
 
31
41
  # patch persisted? so it looks to see if it actually is persisted
@@ -51,9 +61,10 @@ module Recurly
51
61
  @attributes[key.to_s] =
52
62
  case value
53
63
  when Array
54
- resource = find_or_create_resource_for_collection(key)
64
+ resource = nil
55
65
  value.map do |attrs|
56
66
  if attrs.is_a?(Hash)
67
+ resource ||= find_or_create_resource_for_collection(key)
57
68
  resource.new(attrs)
58
69
  else
59
70
  attrs.duplicable? ? attrs.dup : attrs
@@ -78,20 +89,21 @@ module Recurly
78
89
  protected
79
90
  # patch load_attributes_from_response so it marks result records as persisted
80
91
  def load_attributes_from_response(response)
81
- super
82
- @persisted = true
92
+ if (response['Content-Length'].blank? || response['Content-Length'] != "0") && !response.body.nil? && response.body.strip.size > 0
93
+ load(self.class.format.decode(response.body), true)
94
+ @persisted = true
95
+ end
83
96
  end
84
97
 
85
98
  def load_errors xml
86
99
  load_errors_from_array(
87
- Recurly::Formats::XmlWithErrorsFormat.new.decode(error)
100
+ Recurly::Formats::XmlWithErrorsFormat.new.decode(xml)
88
101
  )
89
102
  rescue => e
90
103
  logger.warn "Recurly::Base#load_errors exception parsing nested error information"
91
104
  # Fallback to default errors parsing
92
105
  errors.from_xml xml
93
- ensure
94
- return false
106
+ raise
95
107
  end
96
108
 
97
109
  # Patched to read errors with field information
@@ -113,6 +125,12 @@ module Recurly
113
125
  humanized_name = field.to_s.humanize.downcase
114
126
  message = message[(humanized_name.size + 1)..-1] if message[0, humanized_name.size + 1].downcase == "#{humanized_name} "
115
127
 
128
+ # HACK: Special case nested billing errors
129
+ if self.is_a?(Recurly::BillingInfo) && Recurly::BillingInfo::CreditCard.known_attributes.include?(field)
130
+ self.credit_card.errors.add field.to_sym, message
131
+ next
132
+ end
133
+
116
134
  errors.add field.to_sym, message
117
135
  elsif error.is_a?(String)
118
136
  message = error
@@ -129,11 +147,11 @@ module Recurly
129
147
  end
130
148
 
131
149
  private
132
- # patch instantiate_record so it marks result records as persisted
133
- def self.instantiate_record(record, prefix_options)
134
- result = super
135
- result.instance_eval{ @persisted = true }
136
- result
150
+ # Fix @persisted for Rails 2.3, 3.0
151
+ def self.instantiate_record(record, prefix_options = {})
152
+ new(record, true).tap do |resource|
153
+ resource.prefix_options = prefix_options
154
+ end
137
155
  end
138
156
 
139
157
  def handle_response(response)
@@ -149,7 +167,7 @@ module Recurly
149
167
  raise ResourceNotFound.new(response, message)
150
168
  when 422
151
169
  message = Hash.from_xml(response.body)['errors']['error'] rescue nil
152
- raise ResourceInvalid.new(response, message)
170
+ raise Recurly::ResourceInvalid.new(response, message)
153
171
  when 412
154
172
  message = Hash.from_xml(response.body)['errors']['error'] rescue nil
155
173
  raise ClientError.new(response, message)
@@ -36,10 +36,10 @@ module Recurly
36
36
  end
37
37
 
38
38
  # initialize associations
39
- def initialize(attributes = {})
39
+ def initialize(attributes = {}, persisted = false)
40
40
  attributes = attributes.with_indifferent_access
41
41
  attributes[:credit_card] ||= {}
42
- super(attributes)
42
+ super
43
43
  end
44
44
 
45
45
  def update_only
@@ -1,3 +1,6 @@
1
+ require 'active_resource/exceptions'
2
+ require 'active_resource/validations'
3
+
1
4
  module Recurly
2
5
  class RecurlyError < StandardError; end
3
6
 
@@ -15,5 +18,15 @@ module Recurly
15
18
  end
16
19
 
17
20
  end
21
+
22
+ class ResourceInvalid < ::ActiveResource::ResourceInvalid
23
+ # Overridden to print the actual error message
24
+ def to_s
25
+ message = "Failed."
26
+ message << " Response code = #{response.code}." if response.respond_to?(:code)
27
+ message << " Response message = #{@message}." if @message
28
+ message
29
+ end
30
+ end
18
31
 
19
32
  end
data/lib/recurly/plan.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module Recurly
2
2
  class Plan < Base
3
3
  self.element_name = "plan"
4
- self.prefix = "/company/"
4
+ self.prefix = "/site/"
5
5
  self.primary_key = :plan_code
6
6
 
7
7
  def self.known_attributes
@@ -13,11 +13,11 @@ module Recurly
13
13
  end
14
14
 
15
15
  # initialize associations
16
- def initialize(attributes = {})
16
+ def initialize(attributes = {}, persisted = false)
17
17
  attributes = attributes.with_indifferent_access
18
18
  attributes[:account] ||= {}
19
19
  attributes[:addons] ||= []
20
- super(attributes)
20
+ super
21
21
  end
22
22
 
23
23
  def self.refund(account_code, refund_type = :partial)
@@ -24,11 +24,11 @@ module Recurly
24
24
  end
25
25
 
26
26
  # initialize fields with blank data
27
- def initialize(attributes = {})
27
+ def initialize(attributes = {}, persisted = false)
28
28
  # initialize embedded attributes
29
29
  attributes = attributes.with_indifferent_access
30
30
  attributes[:account] ||= {}
31
- super(attributes)
31
+ super
32
32
  end
33
33
 
34
34
  def self.list(status = :all)
@@ -111,7 +111,7 @@ module Recurly
111
111
  return model.new.from_transparent_results(response)
112
112
 
113
113
  rescue ActiveResource::ResourceInvalid => ex
114
- model_result = model.new.from_transparent_results(ex.response)
114
+ model_result = model.new.from_transparent_results(ex.response, true)
115
115
  raise Recurly::ValidationsFailed.new(model_result)
116
116
  end
117
117
  end
@@ -0,0 +1,77 @@
1
+ module Recurly
2
+ module Verification
3
+ class PreEscapedString < String
4
+ end
5
+
6
+ def digest_data(data)
7
+ if data.is_a? Array
8
+ return nil if data.empty?
9
+ PreEscapedString.new( '[%s]' % data.map{|v|digest_data(v)}.compact.join(',') )
10
+ elsif data.is_a? Hash
11
+ digest_data Hash[data.sort].map {|k,v|
12
+ prefix = (k =~ /\A\d+\Z/) ? '' : (k.to_s+':')
13
+ (v=digest_data(v)).nil? ? nil : PreEscapedString.new('%s%s' % [prefix,v])
14
+ }
15
+ elsif data.is_a?(String) && !data.instance_of?(PreEscapedString)
16
+ PreEscapedString.new( data.gsub(/([\[\]\,\:\\])/) { |c| '\\' + c } )
17
+ else
18
+ data
19
+ end
20
+ end
21
+
22
+ def generate_signature(claim, args, timestamp=nil)
23
+ raise Recurly::ConfigurationError.new("Recurly gem not configured. 'private_key' missing.") if Recurly.private_key.blank?
24
+
25
+ timestamp ||= Time.now.to_i
26
+ timestamp = timestamp.to_s
27
+ input_data = [timestamp,claim,args]
28
+ input_string = digest_data(input_data)
29
+
30
+ digest_key = ::Digest::SHA1.digest(Recurly.private_key)
31
+ sha1_hash = ::OpenSSL::Digest::Digest.new("sha1")
32
+ signature = ::OpenSSL::HMAC.hexdigest(sha1_hash, digest_key, input_string.to_s)
33
+ signature + '-' + timestamp
34
+ end
35
+
36
+ # Raises a Recurly::ForgedQueryString exception if the signature cannot be validated
37
+ def verify_params!(claim, args)
38
+ args = Hash[args.map { |k, v| [k.to_s, v] }]
39
+ signature = args.delete('signature') or raise Recurly::ForgedQueryString.new('Signature is missing')
40
+ hmac, timestamp = signature.split('-')
41
+ age = Time.now.to_i - timestamp.to_i
42
+ raise Recurly::ForgedQueryString.new('Timestamp is too old or new') if age > 3600 || age < 0
43
+
44
+ if signature != generate_signature(claim, args, timestamp)
45
+ raise Recurly::ForgedQueryString.new('Signature cannot be verified')
46
+ end
47
+ end
48
+
49
+ def sign_billing_info_update(account_code)
50
+ generate_signature('billinginfoupdate', {
51
+ account_code: account_code
52
+ })
53
+ end
54
+
55
+ def sign_transaction(account_code, currency, amount_in_cents)
56
+ generate_signature('transactioncreate', {
57
+ account_code: account_code,
58
+ currency: currency,
59
+ amount_in_cents: amount_in_cents
60
+ })
61
+ end
62
+
63
+ def verify_subscription!(params)
64
+ verify_params! 'subscriptioncreated', params
65
+ end
66
+
67
+ def verify_transaction!(params)
68
+ verify_params! 'transactioncreated', params
69
+ end
70
+
71
+ def verify_billing_info_update!(params)
72
+ verify_params! 'billinginfoupdated', params
73
+ end
74
+
75
+ extend self
76
+ end
77
+ end
@@ -1,3 +1,3 @@
1
1
  module Recurly #:nodoc
2
- VERSION = "0.4.8"
2
+ VERSION = "0.4.10"
3
3
  end
@@ -1,6 +1,6 @@
1
1
  ---
2
- username: api-test@litle.com
3
- password: 6499f1cd3ae14875beb91417d31e91b7
4
- private_key: 78753d36439c423aba60e581be828a2a
5
- subdomain: litle-test
2
+ username: api-test@facebook-test.com
3
+ password: 69d95a9768c745ecb5ac6864032ec970
4
+ private_key: 4cdbdfbb90f84b3c8fb4ca337d1b2cfc
5
+ subdomain: facebook-test
6
6
  environment: :development
@@ -13,6 +13,10 @@ module Recurly
13
13
  it "should be valid" do
14
14
  @account.should be_valid
15
15
  end
16
+
17
+ it "should not be persisted" do
18
+ @account.should_not be_persisted
19
+ end
16
20
 
17
21
  it "should set the attributes correctly" do
18
22
  attributes.each do |key, val|
@@ -30,6 +34,10 @@ module Recurly
30
34
  @account = Account.create(attributes)
31
35
  end
32
36
 
37
+ it "should be persisted" do
38
+ @account.should be_persisted
39
+ end
40
+
33
41
  it "should be valid" do
34
42
  @account.should be_valid
35
43
  end
@@ -82,6 +90,10 @@ module Recurly
82
90
  @account.should_not be_nil
83
91
  end
84
92
 
93
+ it "should be persisted" do
94
+ @account.should be_persisted
95
+ end
96
+
85
97
  describe "returned account" do
86
98
  it "should have a created_at date" do
87
99
  @account.created_at.should_not be_nil
@@ -127,7 +127,7 @@ module Recurly
127
127
  })
128
128
 
129
129
  billing_info = BillingInfo.create(@billing_attributes)
130
- billing_info.errors[:base].count.should == 1
130
+ billing_info.credit_card.errors[:number].count.should == 1
131
131
  end
132
132
 
133
133
  it "should set an error if the card number is invalid" do
@@ -141,7 +141,7 @@ module Recurly
141
141
  })
142
142
 
143
143
  billing_info = BillingInfo.create(@billing_attributes)
144
- billing_info.errors.count.should == 1
144
+ billing_info.credit_card.errors.count.should == 1
145
145
  end
146
146
  end
147
147
  end
@@ -140,7 +140,6 @@ module Recurly
140
140
  :description => "inconvenience fee"
141
141
 
142
142
  @charge = Charge.lookup(account.account_code, charge.id)
143
-
144
143
  @charge.destroy
145
144
  end
146
145
 
@@ -64,7 +64,7 @@ module Recurly
64
64
  Plan.new({
65
65
  :plan_code => "test",
66
66
  :name => "Test Plan",
67
- :amount_in_cents => 100,
67
+ :unit_amount_in_cents => 100,
68
68
  :plan_interval_length => 1,
69
69
  :plan_interval_unit => "months",
70
70
  :trial_interval_length => 0,
@@ -75,7 +75,7 @@ module Recurly
75
75
  end
76
76
 
77
77
  it "should update the plan" do
78
- @test_plan.amount_in_cents = 200
78
+ @test_plan.unit_amount_in_cents = 200
79
79
  @test_plan.save!
80
80
 
81
81
  @test_plan = test_plan
data/spec/support/vcr.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  require 'vcr'
2
- require 'vcr/rspec'
2
+
3
3
  VCR.config do |c|
4
4
  c.cassette_library_dir = 'spec/vcr'
5
- c.http_stubbing_library = :webmock
5
+ c.stub_with :webmock
6
6
  c.default_cassette_options = { :record => :new_episodes }
7
7
  end
8
8
 
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ module Recurly
4
+ describe Verification do
5
+ origin_time = 1312806801
6
+ test_sig = 'fb5194a51aa97996cdb995a89064764c5c1bfd93-1312806801'
7
+
8
+ before(:each) do
9
+ Recurly.configure_from_yaml("#{File.dirname(__FILE__)}/../config/recurly.yml")
10
+ Recurly::private_key = '0123456789abcdef0123456789abcdef' # Used for testing
11
+ end
12
+
13
+ it "should ignore empty arrays, hashes, strings, and nil" do
14
+ Verification::digest_data([
15
+ [], {}, '', nil
16
+ ]).should == '[]'
17
+ end
18
+
19
+ it "should encode nested arrays and hashes" do
20
+ Verification::digest_data(
21
+ {a: [1,2,3], b: {c: '123', d:'456'}}
22
+ ).should == '[a:[1,2,3],b:[c:123,d:456]]'
23
+ end
24
+
25
+ it "should treat hashes with numeric indexes as arrays" do
26
+ Verification::digest_data(
27
+ {'1' => 4, '2' => 5, '3' => 6}
28
+ ).should == '[4,5,6]'
29
+ end
30
+
31
+ it "should escape syntax characters" do
32
+ Verification::digest_data(
33
+ {syntaxchars: ' \\ [ ] : , '}
34
+ ).should == '[syntaxchars: \\\\ \[ \] \: \, ]'
35
+ end
36
+
37
+ it "should generate proper signatures" do
38
+ Time.stub!(:now).and_return(origin_time) # gen at origin time
39
+ sig = Verification.generate_signature('update',{a:'foo',b:'bar'})
40
+ sig.should == test_sig
41
+ end
42
+
43
+ it "should validate proper signatures" do
44
+ Time.stub!(:now).and_return(Time.at(origin_time+60)) # one minute passed
45
+ lambda {
46
+ good = Verification.verify_params!('update',
47
+ {a:'foo',b:'bar',signature:test_sig})
48
+ }.should_not raise_error
49
+ end
50
+
51
+ it "should reject invalid signature" do
52
+ Time.stub!(:now).and_return(Time.at(origin_time+60)) # one minute passed
53
+ lambda {
54
+ good = Verification.verify_params!('update',
55
+ {a:'foo',b:'bar',signature:'badsig'})
56
+ }.should raise_error(Recurly::ForgedQueryString)
57
+ end
58
+
59
+ it "should reject expired signature" do
60
+ Time.stub!(:now).and_return(Time.at(origin_time+7200)) # two hours passed
61
+ lambda {
62
+ good = Verification.verify_params!('update',
63
+ {a:'foo',b:'bar',signature:test_sig})
64
+ }.should raise_error(Recurly::ForgedQueryString)
65
+ end
66
+
67
+ it "should reject time traveling signatures from the future" do
68
+ Time.stub!(:now).and_return(Time.at(origin_time-60)) # one minute earlier
69
+ lambda {
70
+ good = Verification.verify_params!('update',
71
+ {a:'foo',b:'bar',signature:test_sig})
72
+ }.should raise_error(Recurly::ForgedQueryString)
73
+ end
74
+ end
75
+
76
+ end